diff --git a/src/java/org/apache/commons/lang/text/ChoiceMetaFormat.java b/src/java/org/apache/commons/lang/text/ChoiceMetaFormat.java
deleted file mode 100644
index ed8cf3054..000000000
--- a/src/java/org/apache/commons/lang/text/ChoiceMetaFormat.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.lang.text;
-
-import java.text.ChoiceFormat;
-import java.text.FieldPosition;
-import java.text.ParsePosition;
-
-/**
- * Stock "choice" MetaFormat.
- *
- * @see ExtendedMessageFormat
- * @author Matt Benson
- * @since 2.4
- * @version $Id$
- */
-public class ChoiceMetaFormat extends MetaFormatSupport {
- private static final long serialVersionUID = 3802197832963795129L;
-
- /**
- * Singleton-usable instance.
- */
- public static final ChoiceMetaFormat INSTANCE = new ChoiceMetaFormat();
-
- /**
- * Create a new ChoiceMetaFormat.
- */
- public ChoiceMetaFormat() {
- super();
- }
-
- /**
- * {@inheritDoc}
- */
- public StringBuffer format(Object obj, StringBuffer toAppendTo,
- FieldPosition pos) {
- if (obj instanceof ChoiceFormat) {
- return toAppendTo.append(((ChoiceFormat) obj).toPattern());
- }
- throw new IllegalArgumentException(String.valueOf(obj));
- }
-
- /**
- * {@inheritDoc}
- */
- public Object parseObject(String source, ParsePosition pos) {
- int start = pos.getIndex();
- seekFormatElementEnd(source, pos);
- return new ChoiceFormat(source.substring(start, pos.getIndex()));
- }
-
-}
diff --git a/src/java/org/apache/commons/lang/text/DateMetaFormat.java b/src/java/org/apache/commons/lang/text/DateMetaFormat.java
deleted file mode 100644
index c7dcdfc97..000000000
--- a/src/java/org/apache/commons/lang/text/DateMetaFormat.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.lang.text;
-
-import java.text.DateFormat;
-import java.util.Locale;
-
-/**
- * Stock "date" MetaFormat.
- *
- * @see ExtendedMessageFormat
- * @author Matt Benson
- * @since 2.4
- * @version $Id$
- */
-public class DateMetaFormat extends DateMetaFormatSupport {
- private static final long serialVersionUID = -4732179430347600208L;
-
- /**
- * Create a new DateMetaFormat.
- */
- public DateMetaFormat() {
- super();
- }
-
- /**
- * Create a new DateMetaFormat.
- *
- * @param locale the Locale to use
- */
- public DateMetaFormat(Locale locale) {
- super(locale);
- }
-
- /** {@inheritDoc} */
- protected DateFormat createSubformatInstance(int style) {
- return DateFormat.getDateInstance(style, getLocale());
- }
-}
diff --git a/src/java/org/apache/commons/lang/text/DateMetaFormatSupport.java b/src/java/org/apache/commons/lang/text/DateMetaFormatSupport.java
deleted file mode 100644
index d7de5268b..000000000
--- a/src/java/org/apache/commons/lang/text/DateMetaFormatSupport.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * 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.lang.text;
-
-import java.text.DateFormat;
-import java.text.DateFormatSymbols;
-import java.text.FieldPosition;
-import java.text.Format;
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * date/time metaFormat support.
- *
- * @see ExtendedMessageFormat
- * @author Matt Benson
- * @since 2.4
- * @version $Id$
- */
-public abstract class DateMetaFormatSupport extends MetaFormatSupport {
- /** "Default" subformat name */
- protected static final String DEFAULT = "";
-
- /** "Short" subformat name */
- protected static final String SHORT = "short";
-
- /** "Medium" subformat name */
- protected static final String MEDIUM = "medium";
-
- /** "Long" subformat name */
- protected static final String LONG = "long";
-
- /** "Full" subformat name */
- protected static final String FULL = "full";
-
- private Locale locale;
- private boolean handlePatterns = true;
-
- private transient boolean initialized;
- private transient Map styleMap;
- private transient Map inverseStyleMap;
- private transient Map subformats;
- private transient Map reverseSubformats;
- private transient DateFormatSymbols dateFormatSymbols;
-
- /**
- * Create a new AbstractDateMetaFormat.
- */
- public DateMetaFormatSupport() {
- this(Locale.getDefault());
- }
-
- /**
- * Create a new AbstractDateMetaFormat.
- *
- * @param locale Locale
- */
- public DateMetaFormatSupport(Locale locale) {
- super();
- this.locale = locale;
- }
-
- /**
- * {@inheritDoc}
- */
- public StringBuffer format(Object obj, StringBuffer toAppendTo,
- FieldPosition pos) {
- String subformat = getSubformatName(obj);
- if (subformat != null) {
- return toAppendTo.append(subformat);
- }
- if (isHandlePatterns() && obj instanceof SimpleDateFormat) {
- SimpleDateFormat sdf = (SimpleDateFormat) obj;
- if (sdf.getDateFormatSymbols().equals(dateFormatSymbols)) {
- return toAppendTo.append(sdf.toPattern());
- }
- }
- throw new IllegalArgumentException(String.valueOf(obj));
- }
-
- /**
- * Get the subformat name for the given object.
- *
- * @param subformat Object
- * @return subformat name.
- */
- private String getSubformatName(Object subformat) {
- initialize();
- if (reverseSubformats.containsKey(subformat)) {
- return (String) inverseStyleMap.get(reverseSubformats
- .get(subformat));
- }
- return null;
- }
-
- /**
- * {@inheritDoc}
- */
- public Object parseObject(String source, ParsePosition pos) {
- int start = pos.getIndex();
- seekFormatElementEnd(source, pos);
- if (pos.getErrorIndex() >= 0) {
- return null;
- }
- String subformat = source.substring(start, pos.getIndex()).trim();
- Object result = getSubformat(subformat);
- if (result != null) {
- return result;
- }
- if (isHandlePatterns()) {
- return new SimpleDateFormat(subformat, getLocale());
- }
- pos.setErrorIndex(start);
- return null;
- }
-
- /**
- * Get the named subformat.
- *
- * @param subformat name
- * @return Format designated by name
, if any
- */
- private Format getSubformat(String subformat) {
- initialize();
- if (!styleMap.containsKey(subformat)) {
- return null;
- }
- initialize();
- return (Format) subformats.get(styleMap.get(subformat));
- }
-
- /**
- * Get the locale in use by this DateMetaFormatSupport.
- *
- * @return Locale
- */
- public Locale getLocale() {
- return locale;
- }
-
- /**
- * Initialize this DateMetaFormatSupport.
- */
- private synchronized void initialize() {
- if (!initialized) {
- styleMap = createStyleMap();
- inverseStyleMap = createInverseStyleMap();
- subformats = new HashMap();
- reverseSubformats = new HashMap();
- for (Iterator iter = styleMap.values().iterator(); iter.hasNext();) {
- Integer style = (Integer) iter.next();
- if (subformats.containsKey(style)) {
- continue;
- }
- Format sf = createSubformatInstance(style.intValue());
- subformats.put(style, sf);
- if (inverseStyleMap.containsKey(style)) {
- reverseSubformats.put(sf, style);
- }
- }
- dateFormatSymbols = new DateFormatSymbols(getLocale());
- }
- initialized = true;
- }
-
- /**
- * Create a subformat for the given DateFormat
style
- * constant.
- *
- * @param style DateFormat style constant
- * @return a DateFormat instance.
- */
- protected abstract DateFormat createSubformatInstance(int style);
-
- /**
- * Get whether this metaformat can parse date/time pattern formats in
- * addition to named formats.
- *
- * @return boolean.
- */
- public boolean isHandlePatterns() {
- return handlePatterns;
- }
-
- /**
- * Set whether this metaformat can parse date/time pattern formats in
- * addition to named formats.
- *
- * @param handlePatterns the boolean handlePatterns to set.
- * @return this
for fluent usage.
- */
- public DateMetaFormatSupport setHandlePatterns(boolean handlePatterns) {
- this.handlePatterns = handlePatterns;
- return this;
- }
-
- /**
- * Create the style map.
- *
- * @return Map
- */
- protected Map createStyleMap() {
- HashMap result = new HashMap();
- result.put(SHORT, new Integer(DateFormat.SHORT));
- result.put(MEDIUM, new Integer(DateFormat.MEDIUM));
- result.put(LONG, new Integer(DateFormat.LONG));
- result.put(FULL, new Integer(DateFormat.FULL));
- result.put(DEFAULT, new Integer(DateFormat.DEFAULT));
- return result;
- }
-
- /**
- * Create the inverse style map.
- *
- * @return Map
- */
- protected Map createInverseStyleMap() {
- Map invertMe = createStyleMap();
- invertMe.remove(DEFAULT);
- return invert(invertMe);
- }
-}
diff --git a/src/java/org/apache/commons/lang/text/DefaultMetaFormatFactory.java b/src/java/org/apache/commons/lang/text/DefaultMetaFormatFactory.java
deleted file mode 100644
index c834bf11d..000000000
--- a/src/java/org/apache/commons/lang/text/DefaultMetaFormatFactory.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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.lang.text;
-
-import java.text.Format;
-import java.text.ParsePosition;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import org.apache.commons.lang.ArrayUtils;
-import org.apache.commons.lang.Validate;
-
-/**
- * Factory methods to produce metaformat instances that behave like
- * java.text.MessageFormat.
- *
- * @author Matt Benson
- * @since 2.4
- * @version $Id: DefaultMetaFormatFactory.java 592077 2007-11-05 16:47:10Z
- * mbenson $
- */
-class DefaultMetaFormatFactory {
-
- /** Number key */
- public static final String NUMBER_KEY = "number";
-
- /** Date key */
- public static final String DATE_KEY = "date";
-
- /** Time key */
- public static final String TIME_KEY = "time";
-
- /** Choice key */
- public static final String CHOICE_KEY = "choice";
-
- private static final String[] NO_SUBFORMAT_KEYS = new String[] {
- NUMBER_KEY, DATE_KEY, TIME_KEY };
-
- private static final String[] NO_PATTERN_KEYS = new String[] { NUMBER_KEY,
- DATE_KEY, TIME_KEY, CHOICE_KEY };
-
- private static final String[] PATTERN_KEYS = new String[] { DATE_KEY,
- TIME_KEY };
-
- /**
- * Ordered NameKeyedMetaFormat
- */
- private static class OrderedNameKeyedMetaFormat extends NameKeyedMetaFormat {
- private static final long serialVersionUID = -7688772075239431055L;
-
- private List keys;
-
- /**
- * Construct a new OrderedNameKeyedMetaFormat.
- *
- * @param names String[]
- * @param formats Format[]
- */
- private OrderedNameKeyedMetaFormat(String[] names, Format[] formats) {
- super(createMap(names, formats));
- this.keys = Arrays.asList(names);
- }
-
- /**
- * Create a map from the specified key/value parameters.
- *
- * @param names keys
- * @param formats values
- * @return Map
- */
- private static Map createMap(String[] names, Format[] formats) {
- Validate.isTrue(ArrayUtils.isSameLength(names, formats));
- HashMap result = new HashMap(names.length);
- for (int i = 0; i < names.length; i++) {
- result.put(names[i], formats[i]);
- }
- return result;
- }
-
- /**
- * {@inheritDoc}
- */
- protected Iterator iterateKeys() {
- return keys.iterator();
- }
- }
-
- /**
- * Get a default metaformat for the specified Locale.
- *
- * @param locale the Locale for the resulting Format instance.
- * @return Format
- */
- public static Format getFormat(final Locale locale) {
- Format nmf = new NumberMetaFormat(locale);
- Format dmf = new DateMetaFormat(locale).setHandlePatterns(false);
- Format tmf = new TimeMetaFormat(locale).setHandlePatterns(false);
-
- return new MultiFormat(new Format[] {
- new OrderedNameKeyedMetaFormat(NO_SUBFORMAT_KEYS, new Format[] {
- getDefaultFormat(nmf), getDefaultFormat(dmf),
- getDefaultFormat(tmf) }),
- new OrderedNameKeyedMetaFormat(NO_PATTERN_KEYS, new Format[] {
- nmf, dmf, tmf, ChoiceMetaFormat.INSTANCE }),
- new OrderedNameKeyedMetaFormat(PATTERN_KEYS,
- new Format[] { new DateMetaFormat(locale),
- new TimeMetaFormat(locale) }) });
- }
-
- /**
- * Get the default format supported by a given metaformat.
- *
- * @param metaformat Format to handle parsing.
- * @return the default format, if any.
- */
- private static Format getDefaultFormat(Format metaformat) {
- ParsePosition pos = new ParsePosition(0);
- Object o = metaformat.parseObject("", pos);
- return pos.getErrorIndex() < 0 ? (Format) o : null;
- }
-}
diff --git a/src/java/org/apache/commons/lang/text/ExtendedMessageFormat.java b/src/java/org/apache/commons/lang/text/ExtendedMessageFormat.java
index 4c75c75c5..d0cf6edb8 100644
--- a/src/java/org/apache/commons/lang/text/ExtendedMessageFormat.java
+++ b/src/java/org/apache/commons/lang/text/ExtendedMessageFormat.java
@@ -20,25 +20,19 @@
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 org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
/**
* Extends MessageFormat
to allow pluggable/additional formatting
- * options for embedded format elements; requires a "meta-format", that is a
- * Format
capable of parsing and formatting other
- * Format
s.
- *
- * Limitations:
- *
toPattern()
results are tailored to JDK 1.4+ output and
- * will produce fairly drastically different results on earlier JDKs.java.text.MessageFormat
.java.lang.MessageFormat
using the
- * default locale.
- *
- * @return Format
- */
- public static Format createDefaultMetaFormat() {
- return createDefaultMetaFormat(Locale.getDefault());
- }
+ private static final String DUMMY_PATTERN = "";
+ private static final String ESCAPED_QUOTE = "''";
+ private static final char START_FMT = ',';
+ private static final char END_FE = '}';
+ private static final char START_FE = '{';
+ private static final char QUOTE = '\'';
- /**
- * Get a default meta-format for the specified Locale. This will produce
- * behavior identical to a java.lang.MessageFormat
using
- * locale
.
- *
- * @param locale the Locale for the resulting Format instance.
- * @return Format
- */
- public static Format createDefaultMetaFormat(Locale locale) {
- return DefaultMetaFormatFactory.getFormat(locale);
- }
-
- /**
- * Conceptual demarcation of methods to parse the pattern.
- */
- private static class Parser {
- private static final String ESCAPED_QUOTE = "''";
- private static final char START_FMT = ',';
- private static final char END_FE = '}';
- private static final char START_FE = '{';
- private static final char QUOTE = '\'';
-
- /**
- * Strip all formats from the pattern.
- *
- * @param pattern String to strip
- * @return stripped pattern
- */
- private String stripFormats(String pattern) {
- StringBuffer sb = new StringBuffer(pattern.length());
- ParsePosition pos = new ParsePosition(0);
- char[] c = pattern.toCharArray();
- while (pos.getIndex() < pattern.length()) {
- switch (c[pos.getIndex()]) {
- case QUOTE:
- appendQuotedString(pattern, pos, sb, true);
- break;
- case START_FE:
- int start = pos.getIndex();
- readArgumentIndex(pattern, next(pos));
- sb.append(c, start, pos.getIndex() - start);
- if (c[pos.getIndex()] == START_FMT) {
- eatFormat(pattern, next(pos));
- }
- if (c[pos.getIndex()] != END_FE) {
- throw new IllegalArgumentException(
- "Unreadable format element at position "
- + start);
- }
- // fall through
- default:
- sb.append(c[pos.getIndex()]);
- next(pos);
- }
- }
- return sb.toString();
- }
-
- /**
- * Insert formats back into the pattern for toPattern() support.
- *
- * @param pattern source
- * @param formats the Formats to insert
- * @param metaFormat Format to format the Formats
- * @return full pattern
- */
- private String insertFormats(String pattern, Format[] formats,
- Format metaFormat) {
- if (formats == null || formats.length == 0) {
- return pattern;
- }
- StringBuffer sb = new StringBuffer(pattern.length() * 2);
- ParsePosition pos = new ParsePosition(0);
- int fe = -1;
- while (pos.getIndex() < pattern.length()) {
- char c = pattern.charAt(pos.getIndex());
- switch (c) {
- case QUOTE:
- appendQuotedString(pattern, pos, sb, false);
- break;
- case START_FE:
- fe++;
- sb.append(START_FE).append(
- readArgumentIndex(pattern, next(pos)));
- if (formats[fe] != null) {
- String formatName = metaFormat.format(formats[fe]);
- if (StringUtils.isNotEmpty(formatName)) {
- sb.append(START_FMT).append(formatName);
- }
- }
- break;
- default:
- sb.append(pattern.charAt(pos.getIndex()));
- next(pos);
- }
- }
- return sb.toString();
- }
-
- /**
- * Parse the formats from the given pattern.
- *
- * @param pattern String to parse
- * @param metaFormat Format to parse the Formats
- * @return array of parsed Formats
- */
- private Format[] parseFormats(String pattern, Format metaFormat) {
- ArrayList result = new ArrayList();
- ParsePosition pos = new ParsePosition(0);
- while (pos.getIndex() < pattern.length()) {
- switch (pattern.charAt(pos.getIndex())) {
- case QUOTE:
- getQuotedString(pattern, next(pos), true);
- break;
- case START_FE:
- int start = pos.getIndex();
- readArgumentIndex(pattern, next(pos));
- if (pattern.charAt(pos.getIndex()) == START_FMT) {
- seekNonWs(pattern, next(pos));
- }
- result.add(metaFormat.parseObject(pattern, pos));
- seekNonWs(pattern, pos);
- if (pattern.charAt(pos.getIndex()) != END_FE) {
- throw new IllegalArgumentException(
- "Unreadable format element at position "
- + start);
- }
- // fall through
- default:
- next(pos);
- }
- }
- return (Format[]) result.toArray(new Format[result.size()]);
- }
-
- /**
- * Consume whitespace from the current parse position.
- *
- * @param pattern String to read
- * @param pos current position
- */
- private void seekNonWs(String pattern, ParsePosition pos) {
- int len = 0;
- 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 pos
- */
- private ParsePosition next(ParsePosition pos) {
- pos.setIndex(pos.getIndex() + 1);
- return pos;
- }
-
- /**
- * Read the argument index from the current format element
- *
- * @param pattern pattern to parse
- * @param pos current parse position
- * @return argument index as string
- */
- private String readArgumentIndex(String pattern, ParsePosition pos) {
- int start = pos.getIndex();
- for (; pos.getIndex() < pattern.length(); next(pos)) {
- char c = pattern.charAt(pos.getIndex());
- if (c == START_FMT || c == END_FE) {
- return pattern.substring(start, pos.getIndex());
- }
- if (!Character.isDigit(c)) {
- throw new IllegalArgumentException(
- "Invalid format argument index at position "
- + start);
- }
- }
- throw new IllegalArgumentException(
- "Unterminated format element at position " + start);
- }
-
- /**
- * Consume a quoted string, adding it to appendTo
if
- * specified.
- *
- * @param pattern pattern to parse
- * @param pos current parse position
- * @param appendTo optional StringBuffer to append
- * @param escapingOn whether to process escaped quotes
- * @return appendTo
- */
- private StringBuffer appendQuotedString(String pattern,
- ParsePosition pos, StringBuffer appendTo, boolean escapingOn) {
- int start = pos.getIndex();
- char[] c = pattern.toCharArray();
- if (escapingOn && c[start] == QUOTE) {
- return appendTo == null ? null : appendTo.append(QUOTE);
- }
- int lastHold = start;
- for (int i = pos.getIndex(); i < pattern.length(); i++) {
- if (escapingOn
- && pattern.substring(i).startsWith(ESCAPED_QUOTE)) {
- appendTo.append(c, lastHold, pos.getIndex() - lastHold)
- .append(QUOTE);
- pos.setIndex(i + ESCAPED_QUOTE.length());
- lastHold = pos.getIndex();
- continue;
- }
- 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
- * @param escapingOn whether to process escaped quotes
- */
- private void getQuotedString(String pattern, ParsePosition pos,
- boolean escapingOn) {
- appendQuotedString(pattern, pos, null, escapingOn);
- }
-
- /**
- * Consume the entire format found at the current position.
- *
- * @param pattern string to parse
- * @param pos current parse position
- */
- private void eatFormat(String pattern, ParsePosition pos) {
- int start = 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;
- }
- break;
- case QUOTE:
- getQuotedString(pattern, pos, false);
- break;
- }
- }
- throw new IllegalArgumentException(
- "Unterminated format element at position " + start);
- }
- }
-
- private static final Parser PARSER = new Parser();
-
- private Format metaFormat;
- private String strippedPattern;
+ private String toPattern;
+ private Map registry;
/**
* Create a new ExtendedMessageFormat for the default locale.
*
* @param pattern String
- * @param metaFormat Format
- * @throws IllegalArgumentException if metaFormat
is
- * null
or in case of a bad pattern.
+ * @throws IllegalArgumentException in case of a bad pattern.
*/
- public ExtendedMessageFormat(String pattern, Format metaFormat) {
- this(pattern, Locale.getDefault(), metaFormat);
+ public ExtendedMessageFormat(String pattern) {
+ this(pattern, Locale.getDefault());
}
/**
@@ -347,89 +66,376 @@ public ExtendedMessageFormat(String pattern, Format metaFormat) {
*
* @param pattern String
* @param locale Locale
- * @param metaFormat Format
- * @throws IllegalArgumentException if metaFormat
is
- * null
or in case of a bad pattern.
+ * @throws IllegalArgumentException in case of a bad pattern.
*/
- public ExtendedMessageFormat(String pattern, Locale locale,
- Format metaFormat) {
- /*
- * We have to do some acrobatics here: the call to the super constructor
- * will invoke applyPattern(), but we don't want to apply the pattern
- * until we've installed our custom metaformat. So we check for that in
- * our (final) applyPattern implementation, and re-call at the end of
- * this constructor.
- */
- super(pattern);
- setLocale(locale);
- setMetaFormat(metaFormat);
+ public ExtendedMessageFormat(String pattern, Locale locale) {
+ this(pattern, locale, null);
+ }
+
+ /**
+ * Create a new ExtendedMessageFormat for the default locale.
+ *
+ * @param pattern String
+ * @param registry Registry of format factories: MapExtendedMessageFormat
.
- *
- * @return pattern String
+ * {@inheritDoc}
+ * UNSUPPORTED
*/
- public String toPattern() {
- return PARSER.insertFormats(strippedPattern, getFormats(), metaFormat);
+ public void setFormats(Format[] newFormats) {
+ throw new UnsupportedOperationException();
}
/**
- * Get the meta-format currently configured.
- *
- * @return Format.
+ * {@inheritDoc}
+ * UNSUPPORTED
*/
- public synchronized Format getMetaFormat() {
- return metaFormat;
+ public void setFormatsByArgumentIndex(Format[] newFormats) {
+ throw new UnsupportedOperationException();
}
/**
- * Set the meta-format. Has no effect until a subsequent call to
- * {@link #applyPattern(String)}.
+ * Get a custom format from a format description.
*
- * @param metaFormat the Format metaFormat to set.
+ * @param desc String
+ * @return Format
*/
- public synchronized void setMetaFormat(Format metaFormat) {
- Validate.notNull(metaFormat, "metaFormat is null");
- this.metaFormat = metaFormat;
+ private Format getFormat(String desc) {
+ if (registry != null) {
+ String name = desc;
+ String args = null;
+ int i = desc.indexOf(START_FMT);
+ if (i > 0) {
+ name = desc.substring(0, i).trim();
+ args = desc.substring(i + 1).trim();
+ }
+ FormatFactory factory = (FormatFactory) 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(String pattern, ParsePosition pos) {
+ int start = pos.getIndex();
+ seekNonWs(pattern, pos);
+ StringBuffer result = new StringBuffer();
+ 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 (NumberFormatException e) {
+ //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(String pattern, ParsePosition pos) {
+ int start = pos.getIndex();
+ seekNonWs(pattern, pos);
+ 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, false);
+ break;
+ }
+ }
+ throw new IllegalArgumentException(
+ "Unterminated format element at position " + start);
+ }
+
+ /**
+ * Insert formats back into the pattern for toPattern() support.
+ *
+ * @param pattern source
+ * @param formats the Formats to insert
+ * @param metaFormat Format to format the Formats
+ * @return full pattern
+ */
+ private String insertFormats(String pattern, ArrayList customPatterns) {
+ if (!containsElements(customPatterns)) {
+ return pattern;
+ }
+ StringBuffer sb = new StringBuffer(pattern.length() * 2);
+ ParsePosition pos = new ParsePosition(0);
+ int fe = -1;
+ int depth = 0;
+ while (pos.getIndex() < pattern.length()) {
+ char c = pattern.charAt(pos.getIndex());
+ switch (c) {
+ case QUOTE:
+ appendQuotedString(pattern, pos, sb, false);
+ break;
+ case START_FE:
+ depth++;
+ if (depth == 1) {
+ fe++;
+ sb.append(START_FE).append(
+ readArgumentIndex(pattern, next(pos)));
+ String customPattern = (String) 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(String pattern, ParsePosition pos) {
+ int len = 0;
+ 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 pos
+ */
+ private ParsePosition next(ParsePosition pos) {
+ pos.setIndex(pos.getIndex() + 1);
+ return pos;
+ }
+
+ /**
+ * Consume a quoted string, adding it to appendTo
if
+ * specified.
+ *
+ * @param pattern pattern to parse
+ * @param pos current parse position
+ * @param appendTo optional StringBuffer to append
+ * @param escapingOn whether to process escaped quotes
+ * @return appendTo
+ */
+ private StringBuffer appendQuotedString(String pattern, ParsePosition pos,
+ StringBuffer appendTo, boolean escapingOn) {
+ int start = pos.getIndex();
+ char[] c = pattern.toCharArray();
+ if (escapingOn && c[start] == QUOTE) {
+ return appendTo == null ? null : appendTo.append(QUOTE);
+ }
+ int lastHold = start;
+ for (int i = pos.getIndex(); i < pattern.length(); i++) {
+ if (escapingOn && pattern.substring(i).startsWith(ESCAPED_QUOTE)) {
+ appendTo.append(c, lastHold, pos.getIndex() - lastHold).append(
+ QUOTE);
+ pos.setIndex(i + ESCAPED_QUOTE.length());
+ lastHold = pos.getIndex();
+ continue;
+ }
+ 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
+ * @param escapingOn whether to process escaped quotes
+ */
+ private void getQuotedString(String pattern, ParsePosition pos,
+ boolean escapingOn) {
+ appendQuotedString(pattern, pos, null, escapingOn);
+ }
+
+ /**
+ * Learn whether the specified Collection contains non-null elements.
+ * @param coll to check
+ * @return true
if some Object was found, false
otherwise.
+ */
+ private boolean containsElements(Collection coll) {
+ if (coll == null || coll.size() == 0) {
+ return false;
+ }
+ for (Iterator iter = coll.iterator(); iter.hasNext();) {
+ if (iter.next() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/java/org/apache/commons/lang/text/FormatFactory.java b/src/java/org/apache/commons/lang/text/FormatFactory.java
new file mode 100644
index 000000000..b0ae7447d
--- /dev/null
+++ b/src/java/org/apache/commons/lang/text/FormatFactory.java
@@ -0,0 +1,24 @@
+package org.apache.commons.lang.text;
+
+import java.text.Format;
+import java.util.Locale;
+
+/**
+ * Format factory.
+ * @since 2.4
+ * @author Niall Pemberton
+ * @version $Id$
+ */
+public interface FormatFactory {
+
+ /**
+ * Create or retrieve a format instance.
+ *
+ * @param name The format type name
+ * @param arguments Arguments used to create the format instance
+ * @param locale The locale, may be null
+ * @return The format instance
+ */
+ Format getFormat(String name, String arguments, Locale locale);
+
+}
diff --git a/src/java/org/apache/commons/lang/text/MetaFormatSupport.java b/src/java/org/apache/commons/lang/text/MetaFormatSupport.java
deleted file mode 100644
index a7574577c..000000000
--- a/src/java/org/apache/commons/lang/text/MetaFormatSupport.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.lang.text;
-
-import java.text.FieldPosition;
-import java.text.Format;
-import java.text.ParsePosition;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * Support class for implementing Formats that parse/format other Formats, with
- * specific support for interoperability with ExtendedMessageFormat.
- *
- * @see ExtendedMessageFormat
- * @author Matt Benson
- * @since 2.4
- * @version $Id$
- */
-public abstract class MetaFormatSupport extends Format {
-
- private static final char END_FE = '}';
- private static final char START_FE = '{';
- private static final char QUOTE = '\'';
-
- /**
- * Invert the specified Map.
- *
- * @param map the Map to invert.
- * @return a new Map instance.
- * @throws NullPointerException if map
is null
.
- */
- protected Map invert(Map map) {
- Map result = new HashMap(map.size());
- for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
- Map.Entry entry = (Map.Entry) iter.next();
- result.put(entry.getValue(), entry.getKey());
- }
- return result;
- }
-
- /**
- * Find the end of the subformat.
- *
- * @param source String
- * @param pos current parse position
- */
- protected void seekFormatElementEnd(String source, ParsePosition pos) {
- int depth = 1;
- boolean quote = false;
- for (; pos.getIndex() < source.length(); next(pos)) {
- switch (source.charAt(pos.getIndex())) {
- case QUOTE:
- quote ^= true;
- break;
- case START_FE:
- depth += quote ? 0 : 1;
- break;
- case END_FE:
- depth -= quote ? 0 : 1;
- if (depth == 0) {
- return;
- }
- break;
- }
- }
- }
-
- /**
- * Advance the parse index by 1.
- *
- * @param pos the ParsePosition to advance.
- * @return pos
- */
- protected ParsePosition next(ParsePosition pos) {
- pos.setIndex(pos.getIndex() + 1);
- return pos;
- }
-
- // provide default javadoc >;)
- /**
- * Parse an object from the specified String and ParsePosition. If an error
- * occurs pos.getErrorIndex()
will contain a value >= zero,
- * indicating the index at which the parse error occurred.
- *
- * @param source String to parse
- * @param pos ParsePosition marking index into source
- * @return Object parsed
- */
- public abstract Object parseObject(String source, ParsePosition pos);
-
- /**
- * Format the specified object, appending to the given StringBuffer, and
- * optionally respecting the specified FieldPosition.
- *
- * @param obj the object to format
- * @param toAppendTo the StringBuffer to which the formatted object should
- * be appended
- * @param pos FieldPosition associated with obj
- * @return toAppendTo
- * @throws NullPointerException if toAppendTo
or
- * pos
is null
- * @throws IllegalArgumentException if unable to format obj
- */
- public abstract StringBuffer format(Object obj, StringBuffer toAppendTo,
- FieldPosition pos);
-}
diff --git a/src/java/org/apache/commons/lang/text/NameKeyedMetaFormat.java b/src/java/org/apache/commons/lang/text/NameKeyedMetaFormat.java
deleted file mode 100644
index b22401c8c..000000000
--- a/src/java/org/apache/commons/lang/text/NameKeyedMetaFormat.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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.lang.text;
-
-import java.text.FieldPosition;
-import java.text.Format;
-import java.text.ParsePosition;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.apache.commons.lang.ObjectUtils;
-
-/**
- * Basic metaFormat that requires enough configuration information to
- * parse/format other Formats for use by ExtendedMessageFormat.
- *
- * @see ExtendedMessageFormat
- * @author Matt Benson
- * @since 2.4
- * @version $Id$
- */
-public class NameKeyedMetaFormat extends MetaFormatSupport {
- private static final long serialVersionUID = 5963121202601122213L;
-
- private static final char TRIGGER_END = '}';
- private static final char TRIGGER_SUBFORMAT = ',';
-
- /**
- * Provides a builder with a fluent interface. Example:
- *
- *
- *
- *
- * NameKeyedMetaFormat nkmf = new NameKeyedMetaFormat.Builder().put("foo",
- * new FooFormat()).put("bar", new BarFormat())
- * .put("baz", new BazFormat()).toNameKeyedMetaFormat();
- *
NumberMetaFormat
.
- *
- * @return Locale
- */
- public Locale getLocale() {
- return locale;
- }
-
- /**
- * Initialize this NumberMetaFormat.
- */
- private synchronized void initialize() {
- if (subformats == null) {
- subformats = new HashMap();
- subformats.put(DEFAULT, NumberFormat.getInstance(getLocale()));
- subformats.put(INTEGER, createIntegerInstance(getLocale()));
- subformats.put(CURRENCY, NumberFormat
- .getCurrencyInstance(getLocale()));
- subformats.put(PERCENT, NumberFormat
- .getPercentInstance(getLocale()));
-
- reverseSubformats = invert(subformats);
- decimalFormatSymbols = new DecimalFormatSymbols(getLocale());
- }
- }
-
- /**
- * Create the "integer" NumberFormat instance for the specified Locale.
- *
- * @param locale the Locale to use
- * @return integer NumberFormat
- */
- private static NumberFormat createIntegerInstance(Locale locale) {
- if (GET_INTEGER_INSTANCE != null) {
- try {
- return (NumberFormat) GET_INTEGER_INSTANCE.invoke(null, new Object[] { locale });
- } catch (IllegalAccessException e) {
- //fall through
- } catch (InvocationTargetException e) {
- //fall through
- }
- }
- NumberFormat result = NumberFormat.getInstance(locale);
- result.setMaximumFractionDigits(0);
- result.setParseIntegerOnly(true);
- return result;
- }
-}
diff --git a/src/java/org/apache/commons/lang/text/TimeMetaFormat.java b/src/java/org/apache/commons/lang/text/TimeMetaFormat.java
deleted file mode 100644
index a0091dda3..000000000
--- a/src/java/org/apache/commons/lang/text/TimeMetaFormat.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.lang.text;
-
-import java.text.DateFormat;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * Stock "time" MetaFormat.
- *
- * @see ExtendedMessageFormat
- * @author Matt Benson
- * @since 2.4
- * @version $Id$
- */
-public class TimeMetaFormat extends DateMetaFormatSupport {
- private static final long serialVersionUID = -4959095416302142342L;
-
- /**
- * Create a new TimeMetaFormat.
- */
- public TimeMetaFormat() {
- super();
- }
-
- /**
- * Create a new NumberMetaFormat.
- *
- * @param locale Locale
- */
- public TimeMetaFormat(Locale locale) {
- super(locale);
- }
-
- /** {@inheritDoc} */
- protected DateFormat createSubformatInstance(int style) {
- return DateFormat.getTimeInstance(style, getLocale());
- }
-
- /** {@inheritDoc} */
- protected Map createInverseStyleMap() {
- Map invertMe = createStyleMap();
- invertMe.remove(DEFAULT);
- DateFormat longDf = DateFormat.getTimeInstance(DateFormat.LONG,
- getLocale());
- DateFormat fullDf = DateFormat.getTimeInstance(DateFormat.FULL,
- getLocale());
- if (fullDf.equals(longDf)) {
- invertMe.remove(FULL);
- }
- return invert(invertMe);
- }
-}
diff --git a/src/test/org/apache/commons/lang/text/ExtendedMessageFormatBaselineTest.java b/src/test/org/apache/commons/lang/text/ExtendedMessageFormatBaselineTest.java
index d52c595bf..d252b29b3 100644
--- a/src/test/org/apache/commons/lang/text/ExtendedMessageFormatBaselineTest.java
+++ b/src/test/org/apache/commons/lang/text/ExtendedMessageFormatBaselineTest.java
@@ -173,8 +173,7 @@ protected Locale getLocale() {
* {@inheritDoc}
*/
protected MessageFormat createMessageFormat(String pattern, Locale locale) {
- return new ExtendedMessageFormat(pattern, locale, ExtendedMessageFormat
- .createDefaultMetaFormat(locale));
+ return new ExtendedMessageFormat(pattern, locale);
}
}
diff --git a/src/test/org/apache/commons/lang/text/MessageFormatExtensionTest.java b/src/test/org/apache/commons/lang/text/MessageFormatExtensionTest.java
index 6512ba3e2..b1b012124 100644
--- a/src/test/org/apache/commons/lang/text/MessageFormatExtensionTest.java
+++ b/src/test/org/apache/commons/lang/text/MessageFormatExtensionTest.java
@@ -24,6 +24,7 @@
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.HashMap;
import java.util.Locale;
/**
@@ -237,13 +238,13 @@ private static void next(ParsePosition pos) {
* {@inheritDoc}
*/
protected MessageFormat createMessageFormat(String pattern, Locale locale) {
- return new ExtendedMessageFormat(pattern, locale,
- new MultiFormat.Builder().add(
- new NameKeyedMetaFormat.Builder().put("properName",
- new ProperNameCapitalizationFormat())
- .toNameKeyedMetaFormat()).add(
- ExtendedMessageFormat.createDefaultMetaFormat(locale))
- .toMultiFormat());
+ final ProperNameCapitalizationFormat properNameCapitalizationFormat = new ProperNameCapitalizationFormat();
+ final FormatFactory ff = new FormatFactory() {
+ public Format getFormat(String name, String arguments, Locale locale) {
+ return "properName".equals(name) ? properNameCapitalizationFormat : null;
+ }
+ };
+ return new ExtendedMessageFormat(pattern, locale, new HashMap() { { put("properName", ff); }});
}
public void testProperName() {