TEXT-23: Adding remainder of the o.a.c.l.text package
Project: http://git-wip-us.apache.org/repos/asf/commons-text/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-text/commit/6f24aa45 Tree: http://git-wip-us.apache.org/repos/asf/commons-text/tree/6f24aa45 Diff: http://git-wip-us.apache.org/repos/asf/commons-text/diff/6f24aa45 Branch: refs/heads/master Commit: 6f24aa4568fb62777d515625cd0146d4c36d5a6c Parents: e3c006e Author: Rob Tompkins <christopher.tompk...@capitalone.com> Authored: Wed Nov 9 15:35:38 2016 -0500 Committer: Rob Tompkins <christopher.tompk...@capitalone.com> Committed: Wed Nov 9 15:35:38 2016 -0500 ---------------------------------------------------------------------- .../java/org/apache/commons/text/Builder.java | 87 + .../apache/commons/text/CompositeFormat.java | 114 + .../commons/text/ExtendedMessageFormat.java | 529 +++ .../org/apache/commons/text/FormatFactory.java | 39 + .../apache/commons/text/FormattableUtils.java | 151 + .../org/apache/commons/text/StrBuilder.java | 3117 ++++++++++++++++++ .../java/org/apache/commons/text/StrLookup.java | 178 + .../org/apache/commons/text/StrMatcher.java | 437 +++ .../org/apache/commons/text/StrSubstitutor.java | 1227 +++++++ .../org/apache/commons/text/StrTokenizer.java | 1116 +++++++ .../java/org/apache/commons/text/WordUtils.java | 733 ++++ .../text/translate/AggregateTranslator.java | 2 - .../text/translate/CharSequenceTranslator.java | 6 +- .../text/translate/CodePointTranslator.java | 2 - .../commons/text/translate/EntityArrays.java | 2 - .../text/translate/JavaUnicodeEscaper.java | 14 +- .../text/translate/LookupTranslator.java | 2 - .../text/translate/NumericEntityEscaper.java | 14 +- .../text/translate/NumericEntityUnescaper.java | 2 - .../commons/text/translate/OctalUnescaper.java | 2 - .../commons/text/translate/UnicodeEscaper.java | 14 +- .../text/translate/UnicodeUnescaper.java | 2 - .../commons/text/translate/package-info.java | 2 - .../commons/text/CompositeFormatTest.java | 85 + .../commons/text/ExtendedMessageFormatTest.java | 484 +++ .../commons/text/FormattableUtilsTest.java | 115 + .../text/StrBuilderAppendInsertTest.java | 1607 +++++++++ .../org/apache/commons/text/StrBuilderTest.java | 2009 +++++++++++ .../org/apache/commons/text/StrLookupTest.java | 115 + .../org/apache/commons/text/StrMatcherTest.java | 214 ++ .../apache/commons/text/StrSubstitutorTest.java | 740 +++++ .../apache/commons/text/StrTokenizerTest.java | 879 +++++ .../org/apache/commons/text/WordUtilsTest.java | 421 +++ 33 files changed, 14417 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-text/blob/6f24aa45/src/main/java/org/apache/commons/text/Builder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/text/Builder.java b/src/main/java/org/apache/commons/text/Builder.java new file mode 100644 index 0000000..8d9a34d --- /dev/null +++ b/src/main/java/org/apache/commons/text/Builder.java @@ -0,0 +1,87 @@ +/* + * 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.text; + +/** + * <p> + * The Builder interface is designed to designate a class as a <em>builder</em> + * object in the Builder design pattern. Builders are capable of creating and + * configuring objects or results that normally take multiple steps to construct + * or are very complex to derive. + * </p> + * + * <p> + * The builder interface defines a single method, {@link #build()}, that + * classes must implement. The result of this method should be the final + * configured object or result after all building operations are performed. + * </p> + * + * <p> + * It is a recommended practice that the methods supplied to configure the + * object or result being built return a reference to {@code this} so that + * method calls can be chained together. + * </p> + * + * <p> + * Example Builder: + * <pre><code> + * class FontBuilder implements Builder<Font> { + * private Font font; + * + * public FontBuilder(String fontName) { + * this.font = new Font(fontName, Font.PLAIN, 12); + * } + * + * public FontBuilder bold() { + * this.font = this.font.deriveFont(Font.BOLD); + * return this; // Reference returned so calls can be chained + * } + * + * public FontBuilder size(float pointSize) { + * this.font = this.font.deriveFont(pointSize); + * return this; // Reference returned so calls can be chained + * } + * + * // Other Font construction methods + * + * public Font build() { + * return this.font; + * } + * } + * </code></pre> + * + * Example Builder Usage: + * <pre><code> + * Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold() + * .size(14.0f) + * .build(); + * </code></pre> + * + * + * @param <T> the type of object that the builder will construct or compute. + * + */ +public interface Builder<T> { + + /** + * Returns a reference to the object being constructed or result being + * calculated by the builder. + * + * @return the object constructed or result calculated by the builder. + */ + T build(); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/commons-text/blob/6f24aa45/src/main/java/org/apache/commons/text/CompositeFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/text/CompositeFormat.java b/src/main/java/org/apache/commons/text/CompositeFormat.java new file mode 100644 index 0000000..6239561 --- /dev/null +++ b/src/main/java/org/apache/commons/text/CompositeFormat.java @@ -0,0 +1,114 @@ +/* + * 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.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. + */ +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</code> + * @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)); + } + +} http://git-wip-us.apache.org/repos/asf/commons-text/blob/6f24aa45/src/main/java/org/apache/commons/text/ExtendedMessageFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/text/ExtendedMessageFormat.java b/src/main/java/org/apache/commons/text/ExtendedMessageFormat.java new file mode 100644 index 0000000..c88752a --- /dev/null +++ b/src/main/java/org/apache/commons/text/ExtendedMessageFormat.java @@ -0,0 +1,529 @@ +/* + * 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.text; + +import org.apache.commons.lang3.ObjectUtils; + +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; + +/** + * Extends <code>java.text.MessageFormat</code> to allow pluggable/additional formatting + * options for embedded format elements. Client code should specify a registry + * of <code>FormatFactory</code> instances associated with <code>String</code> + * 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</code> 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>,</code><i>format-name</i><b> + * (</b><code>,</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</code>. If <i>format-name</i> denotes + * <code>FormatFactory formatFactoryInstance</code> in <code>registry</code>, a <code>Format</code> + * matching <i>format-name</i> and <i>format-style</i> is requested from + * <code>formatFactoryInstance</code>. If this is successful, the <code>Format</code> + * 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</code> if called. + * </p> + * + * <p>Limitations inherited from <code>java.text.MessageFormat</code>:</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</code>s, including <code>MessageFormat</code> and thus + * <code>ExtendedMessageFormat</code>, is not guaranteed.</li> + * </ul> + */ +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, 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 Locale locale, final Map<String, ? extends FormatFactory> registry) { + super(DUMMY_PATTERN); + setLocale(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); + if(foundFormats.size() != fmtCount) { + throw new IllegalArgumentException("The validated expression is false"); + } + if (foundDescriptions.size() != fmtCount) { + throw new IllegalArgumentException("The validated expression is false"); + } + 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; + } + if (ObjectUtils.notEqual(registry, rhs.registry)) { + return false; + } + return true; + } + + /** + * {@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; + } + + /** + * Get 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</code> + */ + private ParsePosition next(final ParsePosition pos) { + pos.setIndex(pos.getIndex() + 1); + return pos; + } + + /** + * Consume a quoted string, adding it to <code>appendTo</code> if + * specified. + * + * @param pattern pattern to parse + * @param pos current parse position + * @param appendTo optional StringBuilder to append + * @return <code>appendTo</code> + */ + 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++) { + switch (c[pos.getIndex()]) { + case QUOTE: + next(pos); + return appendTo == null ? null : appendTo.append(c, lastHold, + pos.getIndex() - lastHold); + default: + 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</code> if some Object was found, <code>false</code> 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; + } +} http://git-wip-us.apache.org/repos/asf/commons-text/blob/6f24aa45/src/main/java/org/apache/commons/text/FormatFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/text/FormatFactory.java b/src/main/java/org/apache/commons/text/FormatFactory.java new file mode 100644 index 0000000..1f85b0f --- /dev/null +++ b/src/main/java/org/apache/commons/text/FormatFactory.java @@ -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. + */ +package org.apache.commons.text; + +import java.text.Format; +import java.util.Locale; + +/** + * Format factory. + */ +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</code> to implement the "format style" + * concept from <code>java.text.MessageFormat</code>. + * @param locale The locale, may be null + * @return The format instance + */ + Format getFormat(String name, String arguments, Locale locale); + +} http://git-wip-us.apache.org/repos/asf/commons-text/blob/6f24aa45/src/main/java/org/apache/commons/text/FormattableUtils.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/text/FormattableUtils.java b/src/main/java/org/apache/commons/text/FormattableUtils.java new file mode 100644 index 0000000..887f593 --- /dev/null +++ b/src/main/java/org/apache/commons/text/FormattableUtils.java @@ -0,0 +1,151 @@ +/* + * 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.text; + +import java.util.Formattable; +import java.util.Formatter; + +import static java.util.FormattableFlags.LEFT_JUSTIFY; + +/** + * <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> + * + */ +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() { + super(); + } + + //----------------------------------------------------------------------- + /** + * Get 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 String.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) { + if ( ! (ellipsis == null || precision < 0 || ellipsis.length() <= precision) ) { + throw new IllegalArgumentException(String.format("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; + if (ellipsis == null) { + _ellipsis = ""; + } else { + _ellipsis = ellipsis; + } + 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; + } + +}