Author: desruisseaux Date: Wed Nov 14 17:18:51 2012 New Revision: 1409261 URL: http://svn.apache.org/viewvc?rev=1409261&view=rev Log: First draft of CompoundFormat, which will be the base class of CoordinateFormat, WKTFormat and TreeTableFormat.
Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java (with props) Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Localized.java Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java?rev=1409261&view=auto ============================================================================== --- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java (added) +++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java Wed Nov 14 17:18:51 2012 @@ -0,0 +1,307 @@ +/* + * 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.sis.io; + +import java.util.Map; +import java.util.HashMap; +import java.util.Locale; +import java.util.TimeZone; +import java.util.Date; +import java.io.IOException; +import java.text.Format; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.ParsePosition; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import net.jcip.annotations.NotThreadSafe; +import org.opengis.util.InternationalString; + +import org.apache.sis.measure.Angle; +import org.apache.sis.measure.AngleFormat; +import org.apache.sis.util.Localized; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.collection.BackingStoreException; +import org.apache.sis.internal.util.LocalizedParseException; + + +/** + * Base class of {@link Format} implementations which delegate part of their work to other + * {@code Format} instances. {@code CompoundFormat} subclasses typically work on relatively + * large blocks of data, for example a metadata tree or a <cite>Well Known Text</cite> (WKT). + * Those blocks of data usually contain smaller information units like numbers and dates, + * whose parsing and formatting can be delegated to {@link NumberFormat} and {@link DateFormat} + * respectively. + * + * <p>Since this subclasses may work on larger texts than the usual {@code Format} classes, + * they will work with {@link CharSequence} and {@link Appendable} as much as possible. + * The abstract methods to be defined by subclasses are:</p> + * + * <ul> + * <li>{@link #getValueType()} : return the {@code <T>} class</li> + * <li>{@link #parse(CharSequence, ParsePosition)}</li> + * <li>{@link #format(Object, Appendable)} throws {@link IOException}</li> + * </ul> + * + * @param <T> The type of objects parsed and formatted by this class. + * + * @author Martin Desruisseaux (Geomatys) + * @since 0.3 + * @version 0.3 + * @module + */ +@NotThreadSafe +public abstract class CompoundFormat<T> extends Format implements Localized { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = -7094915750367581487L; + + /** + * The locale given at construction time, or {@code null} for unlocalized format. + * + * @see #getLocale() + */ + protected final Locale locale; + + /** + * The timezone given at construction time, or {@code null} for UTC. + */ + protected final TimeZone timezone; + + /** + * The formats for smaller unit of information. + * Will be created only when first needed. + */ + private transient Map<Class<?>,Format> formats; + + /** + * Creates a new format for the given locale. The given locale can be {@code null} if this + * format shall parse and format "unlocalized" strings. See {@link #getLocale()} for more + * information on {@code null} locale. + * + * @param locale The locale, or {@code null} for unlocalized format. + * @param timezone The timezone, or {@code null} for UTC. + */ + protected CompoundFormat(final Locale locale, final TimeZone timezone) { + this.locale = locale; + this.timezone = timezone; + } + + /** + * Returns the locale given at construction time. The returned locale may be {@code null} + * if this format does not apply any localization. The definition of "unlocalized string" + * is implementation-dependent, but some typical examples are: + * + * <ul> + * <li>Format {@link Number}s using {@code toString()} instead than {@code NumberFormat}.</li> + * <li>Format {@link InternationalString}s using {@code toString(null)}. This has the desired + * behavior at least with {@linkplain org.apache.sis.util.Type.DefaultInternationalString + * SIS implementation}.</li> + * </ul> + * + * @return The locale used for this format, or {@code null} for unlocalized format. + */ + @Override + public Locale getLocale() { + return locale; + } + + /** + * Returns the type of values formatted by this {@code Format} instance. + * + * @return The type of values formatted by this {@code Format} instance. + */ + public abstract Class<T> getValueType(); + + /** + * Creates an object from the given character sequence, or returns {@code null} if an error + * occurred while parsing the characters. + * + * @param text The character sequence for the object to parse. + * @param pos The position where to start the parsing. + * @return The parsed object, or {@code null} if the given character sequence can not be parsed. + */ + public abstract T parse(CharSequence text, ParsePosition pos); + + /** + * Creates an object from the given string representation, or returns {@code null} if an error + * occurred while parsing the string. The default implementation delegates to + * {@link #parse(CharSequence, ParsePosition)}. + * + * @param text The string representation of the object to parse. + * @param pos The position where to start the parsing. + * @return The parsed object, or {@code null} if the given string can not be parsed. + */ + @Override + public T parseObject(final String text, final ParsePosition pos) { + return parse(text, pos); + } + + /** + * Creates an object from the given string representation. + * The default implementation delegates to {@link #parseObject(String, ParsePosition)}. + * + * @param text The string representation of the object to parse. + * @return The parsed object. + * @throws ParseException If an error occurred while parsing the tree. + */ + @Override + public T parseObject(final String text) throws ParseException { + final ParsePosition pos = new ParsePosition(0); + final T table = parseObject(text, pos); + if (table != null) { + return table; + } + throw new LocalizedParseException(locale, getValueType(), text, pos); + } + + /** + * Writes a textual representation of the given object in the given stream or buffer. + * + * @param object The object to format. + * @param toAppendTo Where to format the object. + * @throws IOException If an error occurred while writing in the given appender. + */ + public abstract void format(T object, Appendable toAppendTo) throws IOException; + + /** + * Writes a textual representation of the specified object in the given buffer. + * This method delegates its work to {@link #format(Object, Appendable)}, but + * without propagating {@link IOException}. The I/O exception should never + * occur since we are writing in a {@link StringBuffer}. + * + * {@note Strictly speaking, an <code>IOException</code> could still occur if the user + * overrides the above <code>format</code> method and performs some I/O operation outside + * the given <code>StringBuffer</code>. However this is not the intended usage of this + * class and implementors should avoid such unexpected I/O operation.} + * + * @param object The object to format. + * @param toAppendTo Where to format the object. + * @param pos Ignored in current implementation. + * @return The given buffer, returned for convenience. + */ + @Override + public StringBuffer format(final Object object, final StringBuffer toAppendTo, final FieldPosition pos) { + final Class<T> valueType = getValueType(); + ArgumentChecks.ensureCanCast("tree", valueType, object); + try { + format(valueType.cast(object), toAppendTo); + } catch (IOException e) { + /* + * Should never happen when writing into a StringBuffer, unless the user + * override the format(Object, Appendable) method. We do not rethrown an + * AssertionError because of this possibility. + */ + throw new BackingStoreException(e); + } + return toAppendTo; + } + + /** + * Returns the format to use for parsing and formatting values of the given type. + * This method applies the following algorithm: + * + * <ul> + * <li>If a format is cached for the given type, return that format.</li> + * <li>Otherwise if a format can be {@linkplain #createFormat(Class) created} + * for the given type, cache the newly created format and return it.</li> + * <li>Otherwise do again the same checks for the {@linkplain Class#getSuperclass() super class}.</li> + * <li>If no format can be created, returns {@code null}.</li> + * </ul> + * + * See {@link #createFormat(Class)} for the list of value types recognized by the default + * {@code CompoundFormat} implementation. + * + * @param valueType The base type of values to parse or format. + * @return The format to use for parsing and formatting values of the given type or any + * parent type, or {@code null} if none. + */ + protected Format getFormat(final Class<?> valueType) { + Format format = null; + Map<Class<?>,Format> formats = this.formats; + for (Class<?> type=valueType; type!=null; type=type.getSuperclass()) { + if (formats != null) { + format = formats.get(type); + if (format != null) { + if (type != valueType) { + formats.put(valueType, format); + } + break; + } + } + format = createFormat(type); + if (format != null) { + if (formats == null) { + this.formats = formats = new HashMap<>(4); + } + formats.put(type, format); + break; + } + } + return format; + } + + /** + * Creates a new format to use for parsing and formatting values of the given type. + * This method is invoked by {@link #getFormat(Class)} the first time that a format + * is needed for the given type. + * + * <p>The default implementation creates the following formats:</p> + * + * <table class="sis"> + * <tr><th>Value type</th> <th>Format</th></tr> + * <tr><td>{@link Angle}</td> <td>{@link AngleFormat}</td></tr> + * <tr><td>{@link Date}</td> <td>{@link DateFormat}</td></tr> + * <tr><td>{@link Number}</td> <td>{@link NumberFormat}</td></tr> + * </table> + * + * Subclasses can override this method for adding more types, or for configuring the + * newly created {@link Format} instances. Note that implementations shall check the + * type using the {@code expected == type} comparator, not + * <code>expected.{@linkplain Class#isAssignableFrom(Class) isAssignableFrom}(type)</code>, + * because the check for parent types is done by the {@link #getFormat(Class)} method. + * This approach allows sub-classes to create specialized formats for different value + * sub-types. For example a sub-class may choose to format {@link Double} values differently + * than other type of numbers. + * + * @param valueType The base type of values to parse or format. + * @return The format to use for parsing of formatting values of the given type, + * or {@code null} if none. + */ + protected Format createFormat(final Class<?> valueType) { + if (valueType == Number.class) { + if (locale == null) return null; + return NumberFormat.getInstance(locale); + } + if (valueType == Date.class) { + final DateFormat format; + if (locale != null) { + format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, locale); + } else { + format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US); + } + format.setTimeZone(timezone != null ? timezone : TimeZone.getTimeZone("UTC")); + } + if (valueType == Angle.class) { + if (locale == null) return null; + return AngleFormat.getInstance(locale); + } + return null; + } +} Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Localized.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Localized.java?rev=1409261&r1=1409260&r2=1409261&view=diff ============================================================================== --- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Localized.java (original) +++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/Localized.java Wed Nov 14 17:18:51 2012 @@ -30,10 +30,18 @@ import java.util.Locale; public interface Localized { /** * Returns the locale of the implemented service. Some implementations may return - * {@code null} if no locale is explicitly defined. In such case, the locale to use - * is typically the {@linkplain Locale#getDefault() default} locale. + * {@code null} if no locale is explicitly defined. The meaning of null locale is + * implementation-dependent, but typical interpretations are: + * + * <ul> + * <li>A synonymous of the {@linkplain Locale#getDefault() system default locale};</li> + * <li>or an "unlocalized" service, for example formatting numbers using + * {@link Double#toString(double)} instead than {@link java.text.NumberFormat}.</li> + * </ul> * * @return The locale, or {@code null} if not explicitly defined. + * + * @see org.apache.sis.io.CompoundFormat#getLocale() */ Locale getLocale(); }