diff --git a/src/java/org/apache/commons/lang/text/MultiFormat.java b/src/java/org/apache/commons/lang/text/MultiFormat.java new file mode 100644 index 000000000..fb102a45f --- /dev/null +++ b/src/java/org/apache/commons/lang/text/MultiFormat.java @@ -0,0 +1,159 @@ +/* + * 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.ArrayList; + +import org.apache.commons.lang.Validate; + +/** + * Format that tries a number of delegates in turn until one is successful. + * Contrast to {@link CompositeFormat}. + * + * @author Matt Benson + * @since 2.4 + * @version $Id$ + */ +public class MultiFormat extends Format { + private static final long serialVersionUID = -6128683973856547540L; + + /** + * Provides a builder with a fluent interface. Example: + *

+ * + *

+     * MultiFormat mf = new MultiFormat.Builder().add(new FooFormat()).add(
+     *         new BarFormat()).add(new BazFormat()).toMultiFormat();
+     * 
+ *

+ */ + public static class Builder { + private ArrayList delegates = new ArrayList(); + + /** + * Add a delegate format. + * + * @param delegate + * @return the builder + */ + public Builder add(Format delegate) { + Validate.notNull(delegate, "delegate format is null"); + delegates.add(delegate); + return this; + } + + /** + * Render the {@link MultiFormat} instance from this Builder. + * + * @return MultiFormat + */ + public MultiFormat toMultiFormat() { + return new MultiFormat((Format[]) delegates + .toArray(new Format[delegates.size()])); + } + + } + + private Format[] delegates; + + /** + * Create a new MultiFormat. + */ + public MultiFormat() { + } + + /** + * Create a new MultiFormat. + * + * @param delegates + */ + public MultiFormat(Format[] delegates) { + setDelegates(delegates); + } + + /* + * (non-Javadoc) + * + * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, + * java.text.FieldPosition) + */ + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + Format[] d = getValidDelegates(); + for (int i = 0; i < d.length; i++) { + try { + return d[i].format(obj, toAppendTo, pos); + } catch (IllegalArgumentException e) { + continue; + } + } + throw new IllegalArgumentException("No delegate Format can parse " + + obj); + } + + /* + * (non-Javadoc) + * + * @see java.text.Format#parseObject(java.lang.String, + * java.text.ParsePosition) + */ + public Object parseObject(String source, ParsePosition pos) { + int start = pos.getIndex(); + Format[] d = getDelegates(); + for (int i = 0; i < d.length; i++) { + Object o = d[i].parseObject(source, pos); + if (pos.getErrorIndex() < 0) { + return o; + } + // set up for next attempt: + pos.setIndex(start); + pos.setErrorIndex(-1); + } + pos.setErrorIndex(start); + return null; + } + + /** + * Set the delegates. + * + * @param delegates + * the Format[] delegates to set. + */ + public void setDelegates(Format[] delegates) { + Validate.noNullElements(delegates, + "Null elements present in delegates Format[]"); + this.delegates = delegates; + } + + /** + * Get the delegates. + * + * @return Format[]. + */ + public Format[] getDelegates() { + return delegates; + } + + private Format[] getValidDelegates() { + Format[] result = getDelegates(); + Validate.notEmpty(result, "No delegate Formats configured"); + return result; + } +} diff --git a/src/test/org/apache/commons/lang/text/MultiFormatTest.java b/src/test/org/apache/commons/lang/text/MultiFormatTest.java new file mode 100644 index 000000000..c77dac507 --- /dev/null +++ b/src/test/org/apache/commons/lang/text/MultiFormatTest.java @@ -0,0 +1,133 @@ +/* + * 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.FieldPosition; +import java.text.Format; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; + +import org.apache.commons.lang.ClassUtils; +import org.apache.commons.lang.Validate; + +import junit.framework.TestCase; + +/** + * Test MultiFormat + * + * @author Matt Benson + * @since 2.4 + * @version $Id$ + */ +public class MultiFormatTest extends TestCase { + private class GuardedFormat extends Format { + private static final long serialVersionUID = 1L; + + Format delegate; + Class[] allowableTypes; + + /** + * Create a new MultiFormatTest.GuardedFormat. + */ + public GuardedFormat(Format delegate, Class[] allowableTypes) { + Validate.notNull(delegate); + this.delegate = delegate; + Validate.notNull(allowableTypes); + this.allowableTypes = allowableTypes; + } + + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + Class c = obj == null ? null : obj.getClass(); + for (int i = 0; i < allowableTypes.length; i++) { + if (ClassUtils.isAssignable(c, allowableTypes[i])) { + return delegate.format(obj, toAppendTo, pos); + } + } + throw new IllegalArgumentException(); + } + + public Object parseObject(String source, ParsePosition pos) { + return delegate.parseObject(source, pos); + } + } + + private Format format; + + /* + * (non-Javadoc) + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + // silliness to avoid the DateFormat grabbing the Integer, or the + // integer parsing the first (month) date component: + format = new MultiFormat.Builder().add( + new GuardedFormat(DateFormat.getDateInstance(DateFormat.SHORT, + Locale.US), new Class[] { Date.class })).add( + NumberFormat.getIntegerInstance(Locale.US)).toMultiFormat(); + } + + public void testWTF() { + System.out.println(DateFormat.getDateInstance(DateFormat.SHORT, + Locale.US).format(new Integer(1000))); + } + + public void testFormatNumber() { + assertEquals("1,000", format.format(new Integer(1000))); + } + + public void testParseNumber() throws ParseException { + assertEquals(new Integer(-1000).intValue(), ((Number) format + .parseObject("-1,000")).intValue()); + } + + public void testFormatDate() { + assertEquals("1/1/70", format.format(new GregorianCalendar(1970, + Calendar.JANUARY, 01).getTime())); + } + + public void testParseDate() throws ParseException { + assertEquals(new GregorianCalendar(1970, Calendar.JANUARY, 01) + .getTime(), format.parseObject("1/1/70")); + } + + public void testFormatObject() { + try { + format.format(new Object()); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // okay + } + } + + public void testParseGarbage() { + try { + format.parseObject("garbage"); + fail("expected ParseException"); + } catch (ParseException e) { + //okay + } + } +} diff --git a/src/test/org/apache/commons/lang/text/TextTestSuite.java b/src/test/org/apache/commons/lang/text/TextTestSuite.java index 59f2953c3..cbcb1b126 100644 --- a/src/test/org/apache/commons/lang/text/TextTestSuite.java +++ b/src/test/org/apache/commons/lang/text/TextTestSuite.java @@ -56,6 +56,7 @@ public static Test suite() { suite.addTest(StrMatcherTest.suite()); suite.addTest(StrSubstitutorTest.suite()); suite.addTest(StrTokenizerTest.suite()); + suite.addTestSuite(MultiFormatTest.class); return suite; }