Update #0 - First Release

This commit is contained in:
LAX1DUDE
2022-12-25 01:12:28 -08:00
commit e7179fad45
2154 changed files with 256324 additions and 0 deletions

View File

@ -0,0 +1,118 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.text;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
/**
* Formats using one formatter and parses using a different formatter. An
* example of use for this would be a webapp where data is taken in one way and
* stored in a database another way.
*
* @deprecated as of 3.6, use commons-text <a href=
* "https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/CompositeFormat.html">
* CompositeFormat</a> instead
*/
@Deprecated
public class CompositeFormat extends Format {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = -4329119827877627683L;
/** The parser to use. */
private final Format parser;
/** The formatter to use. */
private final Format formatter;
/**
* Create a format that points its parseObject method to one implementation and
* its format method to another.
*
* @param parser implementation
* @param formatter implementation
*/
public CompositeFormat(final Format parser, final Format formatter) {
this.parser = parser;
this.formatter = formatter;
}
/**
* Uses the formatter Format instance.
*
* @param obj the object to format
* @param toAppendTo the {@link StringBuffer} to append to
* @param pos the FieldPosition to use (or ignore).
* @return {@code toAppendTo}
* @see Format#format(Object, StringBuffer, FieldPosition)
*/
@Override // Therefore has to use StringBuffer
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
return formatter.format(obj, toAppendTo, pos);
}
/**
* Uses the parser Format instance.
*
* @param source the String source
* @param pos the ParsePosition containing the position to parse from, will
* be updated according to parsing success (index) or failure
* (error index)
* @return the parsed Object
* @see Format#parseObject(String, ParsePosition)
*/
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parser.parseObject(source, pos);
}
/**
* Provides access to the parser Format implementation.
*
* @return parser Format implementation
*/
public Format getParser() {
return this.parser;
}
/**
* Provides access to the parser Format implementation.
*
* @return formatter Format implementation
*/
public Format getFormatter() {
return this.formatter;
}
/**
* Utility method to parse and then reformat a String.
*
* @param input String to reformat
* @return A reformatted String
* @throws ParseException thrown by parseObject(String) call
*/
public String reformat(final String input) throws ParseException {
return format(parseObject(input));
}
}

View File

@ -0,0 +1,530 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.text;
import java.text.Format;
import java.text.MessageFormat;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
/**
* Extends {@code java.text.MessageFormat} to allow pluggable/additional
* formatting options for embedded format elements. Client code should specify a
* registry of {@code FormatFactory} instances associated with {@code String}
* format names. This registry will be consulted when the format elements are
* parsed from the message pattern. In this way custom patterns can be
* specified, and the formats supported by {@code java.text.MessageFormat} can
* be overridden at the format and/or format style level (see MessageFormat). A
* "format element" embedded in the message pattern is specified (<b>()?</b>
* signifies optionality):<br>
* <code>{</code><i>argument-number</i><b>(</b>{@code ,}<i>format-name</i><b>
* (</b>{@code ,}<i>format-style</i><b>)?)?</b><code>}</code>
*
* <p>
* <i>format-name</i> and <i>format-style</i> values are trimmed of surrounding
* whitespace in the manner of {@code java.text.MessageFormat}. If
* <i>format-name</i> denotes {@code FormatFactory formatFactoryInstance} in
* {@code registry}, a {@code Format} matching <i>format-name</i> and
* <i>format-style</i> is requested from {@code formatFactoryInstance}. If this
* is successful, the {@code Format} found is used for this format element.
* </p>
*
* <p>
* <b>NOTICE:</b> The various subformat mutator methods are considered
* unnecessary; they exist on the parent class to allow the type of
* customization which it is the job of this class to provide in a configurable
* fashion. These methods have thus been disabled and will throw
* {@code UnsupportedOperationException} if called.
* </p>
*
* <p>
* Limitations inherited from {@code java.text.MessageFormat}:
* </p>
* <ul>
* <li>When using "choice" subformats, support for nested formatting
* instructions is limited to that provided by the base class.</li>
* <li>Thread-safety of {@code Format}s, including {@code MessageFormat} and
* thus {@code ExtendedMessageFormat}, is not guaranteed.</li>
* </ul>
*
* @since 2.4
* @deprecated as of 3.6, use commons-text <a href=
* "https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/ExtendedMessageFormat.html">
* ExtendedMessageFormat</a> instead
*/
@Deprecated
public class ExtendedMessageFormat extends MessageFormat {
private static final long serialVersionUID = -2362048321261811743L;
private static final int HASH_SEED = 31;
private static final String DUMMY_PATTERN = "";
private static final char START_FMT = ',';
private static final char END_FE = '}';
private static final char START_FE = '{';
private static final char QUOTE = '\'';
private String toPattern;
private final Map<String, ? extends FormatFactory> registry;
/**
* Create a new ExtendedMessageFormat for the default locale.
*
* @param pattern the pattern to use, not null
* @throws IllegalArgumentException in case of a bad pattern.
*/
public ExtendedMessageFormat(final String pattern) {
this(pattern, Locale.getDefault());
}
/**
* Create a new ExtendedMessageFormat.
*
* @param pattern the pattern to use, not null
* @param locale the locale to use, not null
* @throws IllegalArgumentException in case of a bad pattern.
*/
public ExtendedMessageFormat(final String pattern, final Locale locale) {
this(pattern, locale, null);
}
/**
* Create a new ExtendedMessageFormat for the default locale.
*
* @param pattern the pattern to use, not null
* @param registry the registry of format factories, may be null
* @throws IllegalArgumentException in case of a bad pattern.
*/
public ExtendedMessageFormat(final String pattern, final Map<String, ? extends FormatFactory> registry) {
this(pattern, Locale.getDefault(), registry);
}
/**
* Create a new ExtendedMessageFormat.
*
* @param pattern the pattern to use, not null.
* @param locale the locale to use.
* @param registry the registry of format factories, may be null.
* @throws IllegalArgumentException in case of a bad pattern.
*/
public ExtendedMessageFormat(final String pattern, final Locale locale,
final Map<String, ? extends FormatFactory> registry) {
super(DUMMY_PATTERN);
setLocale(LocaleUtils.toLocale(locale));
this.registry = registry;
applyPattern(pattern);
}
/**
* {@inheritDoc}
*/
@Override
public String toPattern() {
return toPattern;
}
/**
* Apply the specified pattern.
*
* @param pattern String
*/
@Override
public final void applyPattern(final String pattern) {
if (registry == null) {
super.applyPattern(pattern);
toPattern = super.toPattern();
return;
}
final ArrayList<Format> foundFormats = new ArrayList<>();
final ArrayList<String> foundDescriptions = new ArrayList<>();
final StringBuilder stripCustom = new StringBuilder(pattern.length());
final ParsePosition pos = new ParsePosition(0);
final char[] c = pattern.toCharArray();
int fmtCount = 0;
while (pos.getIndex() < pattern.length()) {
switch (c[pos.getIndex()]) {
case QUOTE:
appendQuotedString(pattern, pos, stripCustom);
break;
case START_FE:
fmtCount++;
seekNonWs(pattern, pos);
final int start = pos.getIndex();
final int index = readArgumentIndex(pattern, next(pos));
stripCustom.append(START_FE).append(index);
seekNonWs(pattern, pos);
Format format = null;
String formatDescription = null;
if (c[pos.getIndex()] == START_FMT) {
formatDescription = parseFormatDescription(pattern, next(pos));
format = getFormat(formatDescription);
if (format == null) {
stripCustom.append(START_FMT).append(formatDescription);
}
}
foundFormats.add(format);
foundDescriptions.add(format == null ? null : formatDescription);
Validate.isTrue(foundFormats.size() == fmtCount);
Validate.isTrue(foundDescriptions.size() == fmtCount);
if (c[pos.getIndex()] != END_FE) {
throw new IllegalArgumentException("Unreadable format element at position " + start);
}
//$FALL-THROUGH$
default:
stripCustom.append(c[pos.getIndex()]);
next(pos);
}
}
super.applyPattern(stripCustom.toString());
toPattern = insertFormats(super.toPattern(), foundDescriptions);
if (containsElements(foundFormats)) {
final Format[] origFormats = getFormats();
// only loop over what we know we have, as MessageFormat on Java 1.3
// seems to provide an extra format element:
int i = 0;
for (final Iterator<Format> it = foundFormats.iterator(); it.hasNext(); i++) {
final Format f = it.next();
if (f != null) {
origFormats[i] = f;
}
}
super.setFormats(origFormats);
}
}
/**
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param formatElementIndex format element index
* @param newFormat the new format
* @throws UnsupportedOperationException always thrown since this isn't
* supported by ExtendMessageFormat
*/
@Override
public void setFormat(final int formatElementIndex, final Format newFormat) {
throw new UnsupportedOperationException();
}
/**
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param argumentIndex argument index
* @param newFormat the new format
* @throws UnsupportedOperationException always thrown since this isn't
* supported by ExtendMessageFormat
*/
@Override
public void setFormatByArgumentIndex(final int argumentIndex, final Format newFormat) {
throw new UnsupportedOperationException();
}
/**
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param newFormats new formats
* @throws UnsupportedOperationException always thrown since this isn't
* supported by ExtendMessageFormat
*/
@Override
public void setFormats(final Format[] newFormats) {
throw new UnsupportedOperationException();
}
/**
* Throws UnsupportedOperationException - see class Javadoc for details.
*
* @param newFormats new formats
* @throws UnsupportedOperationException always thrown since this isn't
* supported by ExtendMessageFormat
*/
@Override
public void setFormatsByArgumentIndex(final Format[] newFormats) {
throw new UnsupportedOperationException();
}
/**
* Check if this extended message format is equal to another object.
*
* @param obj the object to compare to
* @return true if this object equals the other, otherwise false
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if (!super.equals(obj)) {
return false;
}
if (ObjectUtils.notEqual(getClass(), obj.getClass())) {
return false;
}
final ExtendedMessageFormat rhs = (ExtendedMessageFormat) obj;
if (ObjectUtils.notEqual(toPattern, rhs.toPattern)) {
return false;
}
return !ObjectUtils.notEqual(registry, rhs.registry);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int result = super.hashCode();
result = HASH_SEED * result + Objects.hashCode(registry);
result = HASH_SEED * result + Objects.hashCode(toPattern);
return result;
}
/**
* Gets a custom format from a format description.
*
* @param desc String
* @return Format
*/
private Format getFormat(final String desc) {
if (registry != null) {
String name = desc;
String args = null;
final int i = desc.indexOf(START_FMT);
if (i > 0) {
name = desc.substring(0, i).trim();
args = desc.substring(i + 1).trim();
}
final FormatFactory factory = registry.get(name);
if (factory != null) {
return factory.getFormat(name, args, getLocale());
}
}
return null;
}
/**
* Read the argument index from the current format element
*
* @param pattern pattern to parse
* @param pos current parse position
* @return argument index
*/
private int readArgumentIndex(final String pattern, final ParsePosition pos) {
final int start = pos.getIndex();
seekNonWs(pattern, pos);
final StringBuilder result = new StringBuilder();
boolean error = false;
for (; !error && pos.getIndex() < pattern.length(); next(pos)) {
char c = pattern.charAt(pos.getIndex());
if (Character.isWhitespace(c)) {
seekNonWs(pattern, pos);
c = pattern.charAt(pos.getIndex());
if (c != START_FMT && c != END_FE) {
error = true;
continue;
}
}
if ((c == START_FMT || c == END_FE) && result.length() > 0) {
try {
return Integer.parseInt(result.toString());
} catch (final NumberFormatException e) { // NOPMD
// we've already ensured only digits, so unless something
// outlandishly large was specified we should be okay.
}
}
error = !Character.isDigit(c);
result.append(c);
}
if (error) {
throw new IllegalArgumentException("Invalid format argument index at position " + start + ": "
+ pattern.substring(start, pos.getIndex()));
}
throw new IllegalArgumentException("Unterminated format element at position " + start);
}
/**
* Parse the format component of a format element.
*
* @param pattern string to parse
* @param pos current parse position
* @return Format description String
*/
private String parseFormatDescription(final String pattern, final ParsePosition pos) {
final int start = pos.getIndex();
seekNonWs(pattern, pos);
final int text = pos.getIndex();
int depth = 1;
for (; pos.getIndex() < pattern.length(); next(pos)) {
switch (pattern.charAt(pos.getIndex())) {
case START_FE:
depth++;
break;
case END_FE:
depth--;
if (depth == 0) {
return pattern.substring(text, pos.getIndex());
}
break;
case QUOTE:
getQuotedString(pattern, pos);
break;
default:
break;
}
}
throw new IllegalArgumentException("Unterminated format element at position " + start);
}
/**
* Insert formats back into the pattern for toPattern() support.
*
* @param pattern source
* @param customPatterns The custom patterns to re-insert, if any
* @return full pattern
*/
private String insertFormats(final String pattern, final ArrayList<String> customPatterns) {
if (!containsElements(customPatterns)) {
return pattern;
}
final StringBuilder sb = new StringBuilder(pattern.length() * 2);
final ParsePosition pos = new ParsePosition(0);
int fe = -1;
int depth = 0;
while (pos.getIndex() < pattern.length()) {
final char c = pattern.charAt(pos.getIndex());
switch (c) {
case QUOTE:
appendQuotedString(pattern, pos, sb);
break;
case START_FE:
depth++;
sb.append(START_FE).append(readArgumentIndex(pattern, next(pos)));
// do not look for custom patterns when they are embedded, e.g. in a choice
if (depth == 1) {
fe++;
final String customPattern = customPatterns.get(fe);
if (customPattern != null) {
sb.append(START_FMT).append(customPattern);
}
}
break;
case END_FE:
depth--;
//$FALL-THROUGH$
default:
sb.append(c);
next(pos);
}
}
return sb.toString();
}
/**
* Consume whitespace from the current parse position.
*
* @param pattern String to read
* @param pos current position
*/
private void seekNonWs(final String pattern, final ParsePosition pos) {
int len = 0;
final char[] buffer = pattern.toCharArray();
do {
len = StrMatcher.splitMatcher().isMatch(buffer, pos.getIndex());
pos.setIndex(pos.getIndex() + len);
} while (len > 0 && pos.getIndex() < pattern.length());
}
/**
* Convenience method to advance parse position by 1
*
* @param pos ParsePosition
* @return {@code pos}
*/
private ParsePosition next(final ParsePosition pos) {
pos.setIndex(pos.getIndex() + 1);
return pos;
}
/**
* Consume a quoted string, adding it to {@code appendTo} if specified.
*
* @param pattern pattern to parse
* @param pos current parse position
* @param appendTo optional StringBuilder to append
* @return {@code appendTo}
*/
private StringBuilder appendQuotedString(final String pattern, final ParsePosition pos,
final StringBuilder appendTo) {
assert pattern.toCharArray()[pos.getIndex()] == QUOTE : "Quoted string must start with quote character";
// handle quote character at the beginning of the string
if (appendTo != null) {
appendTo.append(QUOTE);
}
next(pos);
final int start = pos.getIndex();
final char[] c = pattern.toCharArray();
final int lastHold = start;
for (int i = pos.getIndex(); i < pattern.length(); i++) {
if (c[pos.getIndex()] == QUOTE) {
next(pos);
return appendTo == null ? null : appendTo.append(c, lastHold, pos.getIndex() - lastHold);
}
next(pos);
}
throw new IllegalArgumentException("Unterminated quoted string at position " + start);
}
/**
* Consume quoted string only
*
* @param pattern pattern to parse
* @param pos current parse position
*/
private void getQuotedString(final String pattern, final ParsePosition pos) {
appendQuotedString(pattern, pos, null);
}
/**
* Learn whether the specified Collection contains non-null elements.
*
* @param coll to check
* @return {@code true} if some Object was found, {@code false} otherwise.
*/
private boolean containsElements(final Collection<?> coll) {
if (coll == null || coll.isEmpty()) {
return false;
}
for (final Object name : coll) {
if (name != null) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.text;
import java.text.Format;
import java.util.Locale;
/**
* Format factory.
*
* @since 2.4
* @deprecated as of 3.6, use commons-text <a href=
* "https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/FormatFactory.html">
* FormatFactory</a> instead
*/
@Deprecated
public interface FormatFactory {
/**
* Create or retrieve a format instance.
*
* @param name The format type name
* @param arguments Arguments used to create the format instance. This allows
* the {@code FormatFactory} to implement the "format style"
* concept from {@code java.text.MessageFormat}.
* @param locale The locale, may be null
* @return The format instance
*/
Format getFormat(String name, String arguments, Locale locale);
}

View File

@ -0,0 +1,162 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.text;
import static java.util.FormattableFlags.LEFT_JUSTIFY;
import java.util.Formattable;
import java.util.Formatter;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import net.lax1dude.eaglercraft.v1_8.HString;
/**
* <p>
* Provides utilities for working with the {@code Formattable} interface.
* </p>
*
* <p>
* The {@link Formattable} interface provides basic control over formatting when
* using a {@code Formatter}. It is primarily concerned with numeric precision
* and padding, and is not designed to allow generalised alternate formats.
* </p>
*
* @since 3.0
* @deprecated as of 3.6, use commons-text <a href=
* "https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/FormattableUtils.html">
* FormattableUtils</a> instead
*/
@Deprecated
public class FormattableUtils {
/**
* A format that simply outputs the value as a string.
*/
private static final String SIMPLEST_FORMAT = "%s";
/**
* <p>
* {@code FormattableUtils} instances should NOT be constructed in standard
* programming. Instead, the methods of the class should be invoked statically.
* </p>
*
* <p>
* This constructor is public to permit tools that require a JavaBean instance
* to operate.
* </p>
*/
public FormattableUtils() {
}
// -----------------------------------------------------------------------
/**
* Gets the default formatted representation of the specified
* {@code Formattable}.
*
* @param formattable the instance to convert to a string, not null
* @return the resulting string, not null
*/
public static String toString(final Formattable formattable) {
return HString.format(SIMPLEST_FORMAT, formattable);
}
/**
* Handles the common {@code Formattable} operations of truncate-pad-append,
* with no ellipsis on precision overflow, and padding width underflow with
* spaces.
*
* @param seq the string to handle, not null
* @param formatter the destination formatter, not null
* @param flags the flags for formatting, see {@code Formattable}
* @param width the width of the output, see {@code Formattable}
* @param precision the precision of the output, see {@code Formattable}
* @return the {@code formatter} instance, not null
*/
public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
final int precision) {
return append(seq, formatter, flags, width, precision, ' ', null);
}
/**
* Handles the common {@link Formattable} operations of truncate-pad-append,
* with no ellipsis on precision overflow.
*
* @param seq the string to handle, not null
* @param formatter the destination formatter, not null
* @param flags the flags for formatting, see {@code Formattable}
* @param width the width of the output, see {@code Formattable}
* @param precision the precision of the output, see {@code Formattable}
* @param padChar the pad character to use
* @return the {@code formatter} instance, not null
*/
public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
final int precision, final char padChar) {
return append(seq, formatter, flags, width, precision, padChar, null);
}
/**
* Handles the common {@link Formattable} operations of truncate-pad-append,
* padding width underflow with spaces.
*
* @param seq the string to handle, not null
* @param formatter the destination formatter, not null
* @param flags the flags for formatting, see {@code Formattable}
* @param width the width of the output, see {@code Formattable}
* @param precision the precision of the output, see {@code Formattable}
* @param ellipsis the ellipsis to use when precision dictates truncation, null
* or empty causes a hard truncation
* @return the {@code formatter} instance, not null
*/
public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
final int precision, final CharSequence ellipsis) {
return append(seq, formatter, flags, width, precision, ' ', ellipsis);
}
/**
* Handles the common {@link Formattable} operations of truncate-pad-append.
*
* @param seq the string to handle, not null
* @param formatter the destination formatter, not null
* @param flags the flags for formatting, see {@code Formattable}
* @param width the width of the output, see {@code Formattable}
* @param precision the precision of the output, see {@code Formattable}
* @param padChar the pad character to use
* @param ellipsis the ellipsis to use when precision dictates truncation, null
* or empty causes a hard truncation
* @return the {@code formatter} instance, not null
*/
public static Formatter append(final CharSequence seq, final Formatter formatter, final int flags, final int width,
final int precision, final char padChar, final CharSequence ellipsis) {
Validate.isTrue(ellipsis == null || precision < 0 || ellipsis.length() <= precision,
"Specified ellipsis '%1$s' exceeds precision of %2$s", ellipsis, Integer.valueOf(precision));
final StringBuilder buf = new StringBuilder(seq);
if (precision >= 0 && precision < seq.length()) {
final CharSequence _ellipsis = ObjectUtils.defaultIfNull(ellipsis, StringUtils.EMPTY);
buf.replace(precision - _ellipsis.length(), seq.length(), _ellipsis.toString());
}
final boolean leftJustify = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY;
for (int i = buf.length(); i < width; i++) {
buf.insert(leftJustify ? i : 0, padChar);
}
formatter.format(buf.toString());
return formatter;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,186 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.text;
import java.util.Map;
/**
* Lookup a String key to a String value.
* <p>
* This class represents the simplest form of a string to string map. It has a
* benefit over a map in that it can create the result on demand based on the
* key.
* <p>
* This class comes complete with various factory methods. If these do not
* suffice, you can subclass and implement your own matcher.
* <p>
* For example, it would be possible to implement a lookup that used the key as
* a primary key, and looked up the value on demand from the database.
*
* @param <V> Unused.
* @since 2.2
* @deprecated as of 3.6, use commons-text <a href=
* "https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/lookup/StringLookupFactory.html">
* StringLookupFactory</a> instead
*/
@Deprecated
public abstract class StrLookup<V> {
/**
* Lookup that always returns null.
*/
private static final StrLookup<String> NONE_LOOKUP = new MapStrLookup<>(null);
/**
* Lookup based on system properties.
*/
private static final StrLookup<String> SYSTEM_PROPERTIES_LOOKUP = new SystemPropertiesStrLookup();
// -----------------------------------------------------------------------
/**
* Returns a lookup which always returns null.
*
* @return a lookup that always returns null, not null
*/
public static StrLookup<?> noneLookup() {
return NONE_LOOKUP;
}
/**
* Returns a new lookup which uses a copy of the current
* {@link System#getProperties() System properties}.
* <p>
* If a security manager blocked access to system properties, then null will be
* returned from every lookup.
* <p>
* If a null key is used, this lookup will throw a NullPointerException.
*
* @return a lookup using system properties, not null
*/
public static StrLookup<String> systemPropertiesLookup() {
return SYSTEM_PROPERTIES_LOOKUP;
}
/**
* Returns a lookup which looks up values using a map.
* <p>
* If the map is null, then null will be returned from every lookup. The map
* result object is converted to a string using toString().
*
* @param <V> the type of the values supported by the lookup
* @param map the map of keys to values, may be null
* @return a lookup using the map, not null
*/
public static <V> StrLookup<V> mapLookup(final Map<String, V> map) {
return new MapStrLookup<>(map);
}
// -----------------------------------------------------------------------
/**
* Constructor.
*/
protected StrLookup() {
}
/**
* Looks up a String key to a String value.
* <p>
* The internal implementation may use any mechanism to return the value. The
* simplest implementation is to use a Map. However, virtually any
* implementation is possible.
* <p>
* For example, it would be possible to implement a lookup that used the key as
* a primary key, and looked up the value on demand from the database Or, a
* numeric based implementation could be created that treats the key as an
* integer, increments the value and return the result as a string - converting
* 1 to 2, 15 to 16 etc.
* <p>
* The {@link #lookup(String)} method always returns a String, regardless of the
* underlying data, by converting it as necessary. For example:
*
* <pre>
* Map&lt;String, Object&gt; map = new HashMap&lt;String, Object&gt;();
* map.put("number", Integer.valueOf(2));
* assertEquals("2", StrLookup.mapLookup(map).lookup("number"));
* </pre>
*
* @param key the key to be looked up, may be null
* @return the matching value, null if no match
*/
public abstract String lookup(String key);
// -----------------------------------------------------------------------
/**
* Lookup implementation that uses a Map.
*/
static class MapStrLookup<V> extends StrLookup<V> {
/** Map keys are variable names and value. */
private final Map<String, V> map;
/**
* Creates a new instance backed by a Map.
*
* @param map the map of keys to values, may be null
*/
MapStrLookup(final Map<String, V> map) {
this.map = map;
}
/**
* Looks up a String key to a String value using the map.
* <p>
* If the map is null, then null is returned. The map result object is converted
* to a string using toString().
*
* @param key the key to be looked up, may be null
* @return the matching value, null if no match
*/
@Override
public String lookup(final String key) {
if (map == null) {
return null;
}
final Object obj = map.get(key);
if (obj == null) {
return null;
}
return obj.toString();
}
}
// -----------------------------------------------------------------------
/**
* Lookup implementation based on system properties.
*/
private static class SystemPropertiesStrLookup extends StrLookup<String> {
/**
* {@inheritDoc} This implementation directly accesses system properties.
*/
@Override
public String lookup(final String key) {
if (!key.isEmpty()) {
try {
return System.getProperty(key);
} catch (final SecurityException scex) {
// Squelched. All lookup(String) will return null.
}
}
return null;
}
}
}

View File

@ -0,0 +1,439 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.text;
import java.util.Arrays;
import org.apache.commons.lang3.ArraySorter;
import org.apache.commons.lang3.StringUtils;
/**
* A matcher class that can be queried to determine if a character array portion
* matches.
* <p>
* This class comes complete with various factory methods. If these do not
* suffice, you can subclass and implement your own matcher.
*
* @since 2.2
* @deprecated as of 3.6, use commons-text <a href=
* "https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/matcher/StringMatcherFactory.html">
* StringMatcherFactory</a> instead
*/
@Deprecated
public abstract class StrMatcher {
/**
* Matches the comma character.
*/
private static final StrMatcher COMMA_MATCHER = new CharMatcher(',');
/**
* Matches the tab character.
*/
private static final StrMatcher TAB_MATCHER = new CharMatcher('\t');
/**
* Matches the space character.
*/
private static final StrMatcher SPACE_MATCHER = new CharMatcher(' ');
/**
* Matches the same characters as StringTokenizer, namely space, tab, newline,
* formfeed.
*/
private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray());
/**
* Matches the String trim() whitespace characters.
*/
private static final StrMatcher TRIM_MATCHER = new TrimMatcher();
/**
* Matches the double quote character.
*/
private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher('\'');
/**
* Matches the double quote character.
*/
private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher('"');
/**
* Matches the single or double quote character.
*/
private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray());
/**
* Matches no characters.
*/
private static final StrMatcher NONE_MATCHER = new NoMatcher();
// -----------------------------------------------------------------------
/**
* Returns a matcher which matches the comma character.
*
* @return a matcher for a comma
*/
public static StrMatcher commaMatcher() {
return COMMA_MATCHER;
}
/**
* Returns a matcher which matches the tab character.
*
* @return a matcher for a tab
*/
public static StrMatcher tabMatcher() {
return TAB_MATCHER;
}
/**
* Returns a matcher which matches the space character.
*
* @return a matcher for a space
*/
public static StrMatcher spaceMatcher() {
return SPACE_MATCHER;
}
/**
* Matches the same characters as StringTokenizer, namely space, tab, newline
* and formfeed.
*
* @return the split matcher
*/
public static StrMatcher splitMatcher() {
return SPLIT_MATCHER;
}
/**
* Matches the String trim() whitespace characters.
*
* @return the trim matcher
*/
public static StrMatcher trimMatcher() {
return TRIM_MATCHER;
}
/**
* Returns a matcher which matches the single quote character.
*
* @return a matcher for a single quote
*/
public static StrMatcher singleQuoteMatcher() {
return SINGLE_QUOTE_MATCHER;
}
/**
* Returns a matcher which matches the double quote character.
*
* @return a matcher for a double quote
*/
public static StrMatcher doubleQuoteMatcher() {
return DOUBLE_QUOTE_MATCHER;
}
/**
* Returns a matcher which matches the single or double quote character.
*
* @return a matcher for a single or double quote
*/
public static StrMatcher quoteMatcher() {
return QUOTE_MATCHER;
}
/**
* Matches no characters.
*
* @return a matcher that matches nothing
*/
public static StrMatcher noneMatcher() {
return NONE_MATCHER;
}
/**
* Constructor that creates a matcher from a character.
*
* @param ch the character to match, must not be null
* @return a new Matcher for the given char
*/
public static StrMatcher charMatcher(final char ch) {
return new CharMatcher(ch);
}
/**
* Constructor that creates a matcher from a set of characters.
*
* @param chars the characters to match, null or empty matches nothing
* @return a new matcher for the given char[]
*/
public static StrMatcher charSetMatcher(final char... chars) {
if (chars == null || chars.length == 0) {
return NONE_MATCHER;
}
if (chars.length == 1) {
return new CharMatcher(chars[0]);
}
return new CharSetMatcher(chars);
}
/**
* Constructor that creates a matcher from a string representing a set of
* characters.
*
* @param chars the characters to match, null or empty matches nothing
* @return a new Matcher for the given characters
*/
public static StrMatcher charSetMatcher(final String chars) {
if (StringUtils.isEmpty(chars)) {
return NONE_MATCHER;
}
if (chars.length() == 1) {
return new CharMatcher(chars.charAt(0));
}
return new CharSetMatcher(chars.toCharArray());
}
/**
* Constructor that creates a matcher from a string.
*
* @param str the string to match, null or empty matches nothing
* @return a new Matcher for the given String
*/
public static StrMatcher stringMatcher(final String str) {
if (StringUtils.isEmpty(str)) {
return NONE_MATCHER;
}
return new StringMatcher(str);
}
// -----------------------------------------------------------------------
/**
* Constructor.
*/
protected StrMatcher() {
}
/**
* Returns the number of matching characters, zero for no match.
* <p>
* This method is called to check for a match. The parameter {@code pos}
* represents the current position to be checked in the string {@code buffer} (a
* character array which must not be changed). The API guarantees that
* {@code pos} is a valid index for {@code buffer}.
* <p>
* The character array may be larger than the active area to be matched. Only
* values in the buffer between the specified indices may be accessed.
* <p>
* The matching code may check one character or many. It may check characters
* preceding {@code pos} as well as those after, so long as no checks exceed the
* bounds specified.
* <p>
* It must return zero for no match, or a positive number if a match was found.
* The number indicates the number of characters that matched.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index (exclusive) of the active buffer, valid for
* buffer
* @return the number of matching characters, zero for no match
*/
public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd);
/**
* Returns the number of matching characters, zero for no match.
* <p>
* This method is called to check for a match. The parameter {@code pos}
* represents the current position to be checked in the string {@code buffer} (a
* character array which must not be changed). The API guarantees that
* {@code pos} is a valid index for {@code buffer}.
* <p>
* The matching code may check one character or many. It may check characters
* preceding {@code pos} as well as those after.
* <p>
* It must return zero for no match, or a positive number if a match was found.
* The number indicates the number of characters that matched.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @return the number of matching characters, zero for no match
* @since 2.4
*/
public int isMatch(final char[] buffer, final int pos) {
return isMatch(buffer, pos, 0, buffer.length);
}
// -----------------------------------------------------------------------
/**
* Class used to define a set of characters for matching purposes.
*/
static final class CharSetMatcher extends StrMatcher {
/** The set of characters to match. */
private final char[] chars;
/**
* Constructor that creates a matcher from a character array.
*
* @param chars the characters to match, must not be null
*/
CharSetMatcher(final char[] chars) {
this.chars = ArraySorter.sort(chars.clone());
}
/**
* Returns whether or not the given character matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0;
}
}
// -----------------------------------------------------------------------
/**
* Class used to define a character for matching purposes.
*/
static final class CharMatcher extends StrMatcher {
/** The character to match. */
private final char ch;
/**
* Constructor that creates a matcher that matches a single character.
*
* @param ch the character to match
*/
CharMatcher(final char ch) {
this.ch = ch;
}
/**
* Returns whether or not the given character matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return ch == buffer[pos] ? 1 : 0;
}
}
// -----------------------------------------------------------------------
/**
* Class used to define a set of characters for matching purposes.
*/
static final class StringMatcher extends StrMatcher {
/** The string to match, as a character array. */
private final char[] chars;
/**
* Constructor that creates a matcher from a String.
*
* @param str the string to match, must not be null
*/
StringMatcher(final String str) {
chars = str.toCharArray();
}
/**
* Returns whether or not the given text matches the stored string.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, int pos, final int bufferStart, final int bufferEnd) {
final int len = chars.length;
if (pos + len > bufferEnd) {
return 0;
}
for (int i = 0; i < chars.length; i++, pos++) {
if (chars[i] != buffer[pos]) {
return 0;
}
}
return len;
}
@Override
public String toString() {
return super.toString() + ' ' + Arrays.toString(chars);
}
}
// -----------------------------------------------------------------------
/**
* Class used to match no characters.
*/
static final class NoMatcher extends StrMatcher {
/**
* Constructs a new instance of {@code NoMatcher}.
*/
NoMatcher() {
}
/**
* Always returns {@code false}.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return 0;
}
}
// -----------------------------------------------------------------------
/**
* Class used to match whitespace as per trim().
*/
static final class TrimMatcher extends StrMatcher {
/**
* Constructs a new instance of {@code TrimMatcher}.
*/
TrimMatcher() {
}
/**
* Returns whether or not the given character matches.
*
* @param buffer the text content to match against, do not change
* @param pos the starting position for the match, valid for buffer
* @param bufferStart the first active index in the buffer, valid for buffer
* @param bufferEnd the end index of the active buffer, valid for buffer
* @return the number of matching characters, zero for no match
*/
@Override
public int isMatch(final char[] buffer, final int pos, final int bufferStart, final int bufferEnd) {
return buffer[pos] <= 32 ? 1 : 0;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,833 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.text;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
/**
* <p>
* Operations on Strings that contain words.
* </p>
*
* <p>
* This class tries to handle {@code null} input gracefully. An exception will
* not be thrown for a {@code null} input. Each method documents its behavior in
* more detail.
* </p>
*
* @since 2.0
* @deprecated as of 3.6, use commons-text <a href=
* "https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/WordUtils.html">
* WordUtils</a> instead
*/
@Deprecated
public class WordUtils {
/**
* <p>
* {@code WordUtils} instances should NOT be constructed in standard
* programming. Instead, the class should be used as
* {@code WordUtils.wrap("foo bar", 20);}.
* </p>
*
* <p>
* This constructor is public to permit tools that require a JavaBean instance
* to operate.
* </p>
*/
public WordUtils() {
}
// Wrapping
// --------------------------------------------------------------------------
/**
* <p>
* Wraps a single line of text, identifying words by {@code ' '}.
* </p>
*
* <p>
* New lines will be separated by the system property line separator. Very long
* words, such as URLs will <i>not</i> be wrapped.
* </p>
*
* <p>
* Leading spaces on a new line are stripped. Trailing spaces are not stripped.
* </p>
*
* <table border="1">
* <caption>Examples</caption>
* <tr>
* <th>input</th>
* <th>wrapLength</th>
* <th>result</th>
* </tr>
* <tr>
* <td>null</td>
* <td>*</td>
* <td>null</td>
* </tr>
* <tr>
* <td>""</td>
* <td>*</td>
* <td>""</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20
* columns."</td>
* <td>20</td>
* <td>"Here is one line of\ntext that is going\nto be wrapped after\n20
* columns."</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website -
* https://commons.apache.org"</td>
* <td>20</td>
* <td>"Click here to jump\nto the commons\nwebsite
* -\nhttps://commons.apache.org"</td>
* </tr>
* <tr>
* <td>"Click here, https://commons.apache.org, to jump to the commons
* website"</td>
* <td>20</td>
* <td>"Click here,\nhttps://commons.apache.org,\nto jump to the\ncommons
* website"</td>
* </tr>
* </table>
*
* (assuming that '\n' is the systems line separator)
*
* @param str the String to be word wrapped, may be null
* @param wrapLength the column to wrap the words at, less than 1 is treated as
* 1
* @return a line with newlines inserted, {@code null} if null input
*/
public static String wrap(final String str, final int wrapLength) {
return wrap(str, wrapLength, null, false);
}
/**
* <p>
* Wraps a single line of text, identifying words by {@code ' '}.
* </p>
*
* <p>
* Leading spaces on a new line are stripped. Trailing spaces are not stripped.
* </p>
*
* <table border="1">
* <caption>Examples</caption>
* <tr>
* <th>input</th>
* <th>wrapLength</th>
* <th>newLineString</th>
* <th>wrapLongWords</th>
* <th>result</th>
* </tr>
* <tr>
* <td>null</td>
* <td>*</td>
* <td>*</td>
* <td>true/false</td>
* <td>null</td>
* </tr>
* <tr>
* <td>""</td>
* <td>*</td>
* <td>*</td>
* <td>true/false</td>
* <td>""</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20
* columns."</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true/false</td>
* <td>"Here is one line of\ntext that is going\nto be wrapped after\n20
* columns."</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20
* columns."</td>
* <td>20</td>
* <td>"&lt;br /&gt;"</td>
* <td>true/false</td>
* <td>"Here is one line of&lt;br /&gt;text that is going&lt;br /&gt;to be
* wrapped after&lt;br /&gt;20 columns."</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20
* columns."</td>
* <td>20</td>
* <td>null</td>
* <td>true/false</td>
* <td>"Here is one line of" + systemNewLine + "text that is going" +
* systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website -
* https://commons.apache.org"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>false</td>
* <td>"Click here to jump\nto the commons\nwebsite
* -\nhttps://commons.apache.org"</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website -
* https://commons.apache.org"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true</td>
* <td>"Click here to jump\nto the commons\nwebsite
* -\nhttp://commons.apach\ne.org"</td>
* </tr>
* </table>
*
* @param str the String to be word wrapped, may be null
* @param wrapLength the column to wrap the words at, less than 1 is treated
* as 1
* @param newLineStr the string to insert for a new line, {@code null} uses
* the system property line separator
* @param wrapLongWords true if long words (such as URLs) should be wrapped
* @return a line with newlines inserted, {@code null} if null input
*/
public static String wrap(final String str, final int wrapLength, final String newLineStr,
final boolean wrapLongWords) {
return wrap(str, wrapLength, newLineStr, wrapLongWords, " ");
}
/**
* <p>
* Wraps a single line of text, identifying words by {@code wrapOn}.
* </p>
*
* <p>
* Leading spaces on a new line are stripped. Trailing spaces are not stripped.
* </p>
*
* <table border="1">
* <caption>Examples</caption>
* <tr>
* <th>input</th>
* <th>wrapLength</th>
* <th>newLineString</th>
* <th>wrapLongWords</th>
* <th>wrapOn</th>
* <th>result</th>
* </tr>
* <tr>
* <td>null</td>
* <td>*</td>
* <td>*</td>
* <td>true/false</td>
* <td>*</td>
* <td>null</td>
* </tr>
* <tr>
* <td>""</td>
* <td>*</td>
* <td>*</td>
* <td>true/false</td>
* <td>*</td>
* <td>""</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20
* columns."</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true/false</td>
* <td>" "</td>
* <td>"Here is one line of\ntext that is going\nto be wrapped after\n20
* columns."</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20
* columns."</td>
* <td>20</td>
* <td>"&lt;br /&gt;"</td>
* <td>true/false</td>
* <td>" "</td>
* <td>"Here is one line of&lt;br /&gt;text that is going&lt;br /&gt;to be
* wrapped after&lt;br /&gt;20 columns."</td>
* </tr>
* <tr>
* <td>"Here is one line of text that is going to be wrapped after 20
* columns."</td>
* <td>20</td>
* <td>null</td>
* <td>true/false</td>
* <td>" "</td>
* <td>"Here is one line of" + systemNewLine + "text that is going" +
* systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website -
* https://commons.apache.org"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>false</td>
* <td>" "</td>
* <td>"Click here to jump\nto the commons\nwebsite
* -\nhttps://commons.apache.org"</td>
* </tr>
* <tr>
* <td>"Click here to jump to the commons website -
* https://commons.apache.org"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true</td>
* <td>" "</td>
* <td>"Click here to jump\nto the commons\nwebsite
* -\nhttp://commons.apach\ne.org"</td>
* </tr>
* <tr>
* <td>"flammable/inflammable"</td>
* <td>20</td>
* <td>"\n"</td>
* <td>true</td>
* <td>"/"</td>
* <td>"flammable\ninflammable"</td>
* </tr>
* </table>
*
* @param str the String to be word wrapped, may be null
* @param wrapLength the column to wrap the words at, less than 1 is treated
* as 1
* @param newLineStr the string to insert for a new line, {@code null} uses
* the system property line separator
* @param wrapLongWords true if long words (such as URLs) should be wrapped
* @param wrapOn regex expression to be used as a breakable characters,
* if blank string is provided a space character will be
* used
* @return a line with newlines inserted, {@code null} if null input
*/
public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords,
String wrapOn) {
if (str == null) {
return null;
}
if (newLineStr == null) {
newLineStr = System.lineSeparator();
}
if (wrapLength < 1) {
wrapLength = 1;
}
if (StringUtils.isBlank(wrapOn)) {
wrapOn = " ";
}
final Pattern patternToWrapOn = Pattern.compile(wrapOn);
final int inputLineLength = str.length();
int offset = 0;
final StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32);
while (offset < inputLineLength) {
int spaceToWrapAt = -1;
Matcher matcher = patternToWrapOn.matcher(str.substring(offset,
Math.min((int) Math.min(Integer.MAX_VALUE, offset + wrapLength + 1L), inputLineLength)));
if (matcher.find()) {
if (matcher.start() == 0) {
offset += matcher.end();
continue;
}
spaceToWrapAt = matcher.start() + offset;
}
// only last line without leading spaces is left
if (inputLineLength - offset <= wrapLength) {
break;
}
while (matcher.find()) {
spaceToWrapAt = matcher.start() + offset;
}
if (spaceToWrapAt >= offset) {
// normal case
wrappedLine.append(str, offset, spaceToWrapAt);
wrappedLine.append(newLineStr);
offset = spaceToWrapAt + 1;
} else // really long word or URL
if (wrapLongWords) {
// wrap really long word one line at a time
wrappedLine.append(str, offset, wrapLength + offset);
wrappedLine.append(newLineStr);
offset += wrapLength;
} else {
// do not wrap really long word, just extend beyond limit
matcher = patternToWrapOn.matcher(str.substring(offset + wrapLength));
if (matcher.find()) {
spaceToWrapAt = matcher.start() + offset + wrapLength;
}
if (spaceToWrapAt >= 0) {
wrappedLine.append(str, offset, spaceToWrapAt);
wrappedLine.append(newLineStr);
offset = spaceToWrapAt + 1;
} else {
wrappedLine.append(str, offset, str.length());
offset = inputLineLength;
}
}
}
// Whatever is left in line is short enough to just pass through
wrappedLine.append(str, offset, str.length());
return wrappedLine.toString();
}
// Capitalizing
// -----------------------------------------------------------------------
/**
* <p>
* Capitalizes all the whitespace separated words in a String. Only the first
* character of each word is changed. To convert the rest of each word to
* lowercase at the same time, use {@link #capitalizeFully(String)}.
* </p>
*
* <p>
* Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null}
* input String returns {@code null}. Capitalization uses the Unicode title
* case, normally equivalent to upper case.
* </p>
*
* <pre>
* WordUtils.capitalize(null) = null
* WordUtils.capitalize("") = ""
* WordUtils.capitalize("i am FINE") = "I Am FINE"
* </pre>
*
* @param str the String to capitalize, may be null
* @return capitalized String, {@code null} if null String input
* @see #uncapitalize(String)
* @see #capitalizeFully(String)
*/
public static String capitalize(final String str) {
return capitalize(str, null);
}
/**
* <p>
* Capitalizes all the delimiter separated words in a String. Only the first
* character of each word is changed. To convert the rest of each word to
* lowercase at the same time, use {@link #capitalizeFully(String, char[])}.
* </p>
*
* <p>
* The delimiters represent a set of characters understood to separate words.
* The first string character and the first non-delimiter character after a
* delimiter will be capitalized.
* </p>
*
* <p>
* A {@code null} input String returns {@code null}. Capitalization uses the
* Unicode title case, normally equivalent to upper case.
* </p>
*
* <pre>
* WordUtils.capitalize(null, *) = null
* WordUtils.capitalize("", *) = ""
* WordUtils.capitalize(*, new char[0]) = *
* WordUtils.capitalize("i am fine", null) = "I Am Fine"
* WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @param delimiters set of characters to determine capitalization, null means
* whitespace
* @return capitalized String, {@code null} if null String input
* @see #uncapitalize(String)
* @see #capitalizeFully(String)
* @since 2.1
*/
public static String capitalize(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
final char[] buffer = str.toCharArray();
boolean capitalizeNext = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (isDelimiter(ch, delimiters)) {
capitalizeNext = true;
} else if (capitalizeNext) {
buffer[i] = Character.toTitleCase(ch);
capitalizeNext = false;
}
}
return new String(buffer);
}
// -----------------------------------------------------------------------
/**
* <p>
* Converts all the whitespace separated words in a String into capitalized
* words, that is each word is made up of a titlecase character and then a
* series of lowercase characters.
* </p>
*
* <p>
* Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null}
* input String returns {@code null}. Capitalization uses the Unicode title
* case, normally equivalent to upper case.
* </p>
*
* <pre>
* WordUtils.capitalizeFully(null) = null
* WordUtils.capitalizeFully("") = ""
* WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @return capitalized String, {@code null} if null String input
*/
public static String capitalizeFully(final String str) {
return capitalizeFully(str, null);
}
/**
* <p>
* Converts all the delimiter separated words in a String into capitalized
* words, that is each word is made up of a titlecase character and then a
* series of lowercase characters.
* </p>
*
* <p>
* The delimiters represent a set of characters understood to separate words.
* The first string character and the first non-delimiter character after a
* delimiter will be capitalized.
* </p>
*
* <p>
* A {@code null} input String returns {@code null}. Capitalization uses the
* Unicode title case, normally equivalent to upper case.
* </p>
*
* <pre>
* WordUtils.capitalizeFully(null, *) = null
* WordUtils.capitalizeFully("", *) = ""
* WordUtils.capitalizeFully(*, null) = *
* WordUtils.capitalizeFully(*, new char[0]) = *
* WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
* </pre>
*
* @param str the String to capitalize, may be null
* @param delimiters set of characters to determine capitalization, null means
* whitespace
* @return capitalized String, {@code null} if null String input
* @since 2.1
*/
public static String capitalizeFully(String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
str = str.toLowerCase();
return capitalize(str, delimiters);
}
// -----------------------------------------------------------------------
/**
* <p>
* Uncapitalizes all the whitespace separated words in a String. Only the first
* character of each word is changed.
* </p>
*
* <p>
* Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null}
* input String returns {@code null}.
* </p>
*
* <pre>
* WordUtils.uncapitalize(null) = null
* WordUtils.uncapitalize("") = ""
* WordUtils.uncapitalize("I Am FINE") = "i am fINE"
* </pre>
*
* @param str the String to uncapitalize, may be null
* @return uncapitalized String, {@code null} if null String input
* @see #capitalize(String)
*/
public static String uncapitalize(final String str) {
return uncapitalize(str, null);
}
/**
* <p>
* Uncapitalizes all the whitespace separated words in a String. Only the first
* character of each word is changed.
* </p>
*
* <p>
* The delimiters represent a set of characters understood to separate words.
* The first string character and the first non-delimiter character after a
* delimiter will be uncapitalized.
* </p>
*
* <p>
* Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null}
* input String returns {@code null}.
* </p>
*
* <pre>
* WordUtils.uncapitalize(null, *) = null
* WordUtils.uncapitalize("", *) = ""
* WordUtils.uncapitalize(*, null) = *
* WordUtils.uncapitalize(*, new char[0]) = *
* WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
* </pre>
*
* @param str the String to uncapitalize, may be null
* @param delimiters set of characters to determine uncapitalization, null means
* whitespace
* @return uncapitalized String, {@code null} if null String input
* @see #capitalize(String)
* @since 2.1
*/
public static String uncapitalize(final String str, final char... delimiters) {
final int delimLen = delimiters == null ? -1 : delimiters.length;
if (StringUtils.isEmpty(str) || delimLen == 0) {
return str;
}
final char[] buffer = str.toCharArray();
boolean uncapitalizeNext = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (isDelimiter(ch, delimiters)) {
uncapitalizeNext = true;
} else if (uncapitalizeNext) {
buffer[i] = Character.toLowerCase(ch);
uncapitalizeNext = false;
}
}
return new String(buffer);
}
// -----------------------------------------------------------------------
/**
* <p>
* Swaps the case of a String using a word based algorithm.
* </p>
*
* <ul>
* <li>Upper case character converts to Lower case</li>
* <li>Title case character converts to Lower case</li>
* <li>Lower case character after Whitespace or at start converts to Title
* case</li>
* <li>Other Lower case character converts to Upper case</li>
* </ul>
*
* <p>
* Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null}
* input String returns {@code null}.
* </p>
*
* <pre>
* StringUtils.swapCase(null) = null
* StringUtils.swapCase("") = ""
* StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
* </pre>
*
* @param str the String to swap case, may be null
* @return the changed String, {@code null} if null String input
*/
public static String swapCase(final String str) {
if (StringUtils.isEmpty(str)) {
return str;
}
final char[] buffer = str.toCharArray();
boolean whitespace = true;
for (int i = 0; i < buffer.length; i++) {
final char ch = buffer[i];
if (Character.isUpperCase(ch) || Character.isTitleCase(ch)) {
buffer[i] = Character.toLowerCase(ch);
whitespace = false;
} else if (Character.isLowerCase(ch)) {
if (whitespace) {
buffer[i] = Character.toTitleCase(ch);
whitespace = false;
} else {
buffer[i] = Character.toUpperCase(ch);
}
} else {
whitespace = Character.isWhitespace(ch);
}
}
return new String(buffer);
}
// -----------------------------------------------------------------------
/**
* <p>
* Extracts the initial characters from each word in the String.
* </p>
*
* <p>
* All first characters after whitespace are returned as a new string. Their
* case is not changed.
* </p>
*
* <p>
* Whitespace is defined by {@link Character#isWhitespace(char)}. A {@code null}
* input String returns {@code null}.
* </p>
*
* <pre>
* WordUtils.initials(null) = null
* WordUtils.initials("") = ""
* WordUtils.initials("Ben John Lee") = "BJL"
* WordUtils.initials("Ben J.Lee") = "BJ"
* </pre>
*
* @param str the String to get initials from, may be null
* @return String of initial letters, {@code null} if null String input
* @see #initials(String,char[])
* @since 2.2
*/
public static String initials(final String str) {
return initials(str, null);
}
/**
* <p>
* Extracts the initial characters from each word in the String.
* </p>
*
* <p>
* All first characters after the defined delimiters are returned as a new
* string. Their case is not changed.
* </p>
*
* <p>
* If the delimiters array is null, then Whitespace is used. Whitespace is
* defined by {@link Character#isWhitespace(char)}. A {@code null} input String
* returns {@code null}. An empty delimiter array returns an empty String.
* </p>
*
* <pre>
* WordUtils.initials(null, *) = null
* WordUtils.initials("", *) = ""
* WordUtils.initials("Ben John Lee", null) = "BJL"
* WordUtils.initials("Ben J.Lee", null) = "BJ"
* WordUtils.initials("Ben J.Lee", [' ','.']) = "BJL"
* WordUtils.initials(*, new char[0]) = ""
* </pre>
*
* @param str the String to get initials from, may be null
* @param delimiters set of characters to determine words, null means whitespace
* @return String of initial characters, {@code null} if null String input
* @see #initials(String)
* @since 2.2
*/
public static String initials(final String str, final char... delimiters) {
if (StringUtils.isEmpty(str)) {
return str;
}
if (delimiters != null && delimiters.length == 0) {
return StringUtils.EMPTY;
}
final int strLen = str.length();
final char[] buf = new char[strLen / 2 + 1];
int count = 0;
boolean lastWasGap = true;
for (int i = 0; i < strLen; i++) {
final char ch = str.charAt(i);
if (isDelimiter(ch, delimiters)) {
lastWasGap = true;
} else if (lastWasGap) {
buf[count++] = ch;
lastWasGap = false;
} else {
continue; // ignore ch
}
}
return new String(buf, 0, count);
}
// -----------------------------------------------------------------------
/**
* <p>
* Checks if the String contains all words in the given array.
* </p>
*
* <p>
* A {@code null} String will return {@code false}. A {@code null}, zero length
* search array or if one element of array is null will return {@code false}.
* </p>
*
* <pre>
* WordUtils.containsAllWords(null, *) = false
* WordUtils.containsAllWords("", *) = false
* WordUtils.containsAllWords(*, null) = false
* WordUtils.containsAllWords(*, []) = false
* WordUtils.containsAllWords("abcd", "ab", "cd") = false
* WordUtils.containsAllWords("abc def", "def", "abc") = true
* </pre>
*
*
* @param word The CharSequence to check, may be null
* @param words The array of String words to search for, may be null
* @return {@code true} if all search words are found, {@code false} otherwise
* @since 3.5
*/
public static boolean containsAllWords(final CharSequence word, final CharSequence... words) {
if (StringUtils.isEmpty(word) || words.length == 0) {
return false;
}
for (final CharSequence w : words) {
if (StringUtils.isBlank(w)) {
return false;
}
final Pattern p = Pattern.compile(".*\\b" + w + "\\b.*");
if (!p.matcher(word).matches()) {
return false;
}
}
return true;
}
// -----------------------------------------------------------------------
/**
* Is the character a delimiter.
*
* @param ch the character to check
* @param delimiters the delimiters
* @return true if it is a delimiter
*/
private static boolean isDelimiter(final char ch, final char[] delimiters) {
if (delimiters == null) {
return Character.isWhitespace(ch);
}
for (final char delimiter : delimiters) {
if (ch == delimiter) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <p>
* Provides classes for handling and manipulating text, partly as an extension
* to {@link java.text}. The classes in this package are, for the most part,
* intended to be instantiated (i.e. they are not utility classes with lots of
* static methods).
* </p>
*
* <p>
* Amongst other classes, the text package provides a replacement for
* {@link java.lang.StringBuffer} named
* {@link org.apache.commons.lang3.text.StrBuilder}, a class for substituting
* variables within a String named
* {@link org.apache.commons.lang3.text.StrSubstitutor} and a replacement for
* {@link java.util.StringTokenizer} named
* {@link org.apache.commons.lang3.text.StrTokenizer}. While somewhat ungainly,
* the {@code Str} prefix has been used to ensure we don't clash with any
* current or future standard Java classes.
* </p>
*
* @since 2.1
*/
package org.apache.commons.lang3.text;