Author: desruisseaux
Date: Thu Mar 14 21:53:41 2013
New Revision: 1456692
URL: http://svn.apache.org/r1456692
Log:
Almost done with the internal.converter package:
- Implemented ConverterRegistry.toString()
- More internal classes extends SystemConverter
(when we will be done, all internal classes should extend it).
- Ported HeuristicRegistry, which encapsulate the knownledge about
special cases: CodeLists, InternationalString, Numbers.
Added:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/HeuristicRegistry.java
(with props)
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/CharSequenceConverter.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/ConverterRegistry.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/FallbackConverter.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/IdentityConverter.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/NumberConverter.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemConverter.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ObjectConverters.java
sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/internal/converter/NumberConverterTest.java
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/CharSequenceConverter.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/CharSequenceConverter.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/CharSequenceConverter.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/CharSequenceConverter.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -16,9 +16,11 @@
*/
package org.apache.sis.internal.converter;
-import java.io.Serializable;
+import java.util.EnumSet;
+import java.util.Set;
import net.jcip.annotations.Immutable;
import org.apache.sis.util.ObjectConverter;
+import org.apache.sis.math.FunctionProperty;
import org.apache.sis.util.UnconvertibleObjectException;
@@ -37,26 +39,13 @@ import org.apache.sis.util.Unconvertible
* @module
*/
@Immutable
-final class CharSequenceConverter<T> extends
SurjectiveConverter<CharSequence,T> implements Serializable {
+final class CharSequenceConverter<T> extends SystemConverter<CharSequence,T> {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 2591675151163578878L;
/**
- * A converter from {@link CharSequence} to {@link String}.
- */
- static final CharSequenceConverter<String> STRING =
- new CharSequenceConverter<>(String.class,
IdentityConverter.create(String.class));
-
- /**
- * The target type requested by the user. We retain this type explicitly
instead
- * than querying {@code next.getTargetType()} because it may be a
super-class of
- * the later.
- */
- private final Class<T> targetType;
-
- /**
* The converter to apply after this one.
*/
private final ObjectConverter<? super String, ? extends T> next;
@@ -64,54 +53,34 @@ final class CharSequenceConverter<T> ext
/**
* Creates a new converter from {@link CharSequence} to the given target
type.
*
- * @param targetType The target type requested by the user.
+ * @param targetClass The target class requested by the user.
* @param next The converter to apply after this one.
*/
- private CharSequenceConverter(final Class<T> targetType, final
ObjectConverter<? super String, ? extends T> next) {
- this.targetType = targetType;
+ CharSequenceConverter(final Class<T> targetClass, final ObjectConverter<?
super String, ? extends T> next) {
+ super(CharSequence.class, targetClass);
this.next = next;
}
/**
- * Creates a new converter from {@link CharSequence} to the given target
type.
- *
- * @param targetType The target type requested by the user.
- * @param next The converter to apply after this one.
- */
- @SuppressWarnings("unchecked")
- public static <T> ObjectConverter<? super CharSequence, ? extends T>
create(
- final Class<T> targetType, final ObjectConverter<? super String, ?
extends T> next)
- {
- if (next.getSourceClass().isAssignableFrom(CharSequence.class)) {
- return (ObjectConverter<? super CharSequence, ? extends T>) next;
- }
- return new CharSequenceConverter<>(targetType, next);
- }
-
- /**
- * Returns the source class, which is always {@link CharSequence}.
- */
- @Override
- public final Class<CharSequence> getSourceClass() {
- return CharSequence.class;
- }
-
- /**
- * Returns the target class.
+ * Converts an object to an object of the target type.
*/
@Override
- public final Class<T> getTargetClass() {
- return targetType;
+ public T convert(final CharSequence source) throws
UnconvertibleObjectException {
+ if (targetClass.isInstance(source)) {
+ return targetClass.cast(source);
+ }
+ return next.convert(source != null ? source.toString() : null);
}
/**
- * Converts an object to an object of the target type.
+ * Returns the properties of the converter given at construction time minus
+ * {@link FunctionProperty#INJECTIVE}, because we don't know how many
source
+ * {@code CharSequence}s can produce the same {@code String}.
*/
@Override
- public T convert(final CharSequence source) throws
UnconvertibleObjectException {
- if (targetType.isInstance(source)) {
- return targetType.cast(source);
- }
- return next.convert(source != null ? source.toString() : null);
+ public Set<FunctionProperty> properties() {
+ final EnumSet<FunctionProperty> properties =
EnumSet.copyOf(next.properties());
+ properties.remove(FunctionProperty.INJECTIVE);
+ return properties;
}
}
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/Column.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -25,6 +25,7 @@ import org.apache.sis.util.resources.Voc
import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.collection.TreeTableFormat;
+import org.apache.sis.util.collection.DefaultTreeTable;
/**
@@ -82,19 +83,24 @@ final class Column extends TableColumn<C
}
/**
+ * Creates a table.
+ */
+ static TreeTable createTable() {
+ return new DefaultTreeTable(Column.SOURCE, Column.TARGET);
+ }
+
+ /**
* Creates a node for the given converter and adds it to the given tree.
* Used by {@link FallbackConverter} and {@link ConverterRegistry} for
* implementing their {@code toString()} method.
*
* @param converter The converter for which to create a tree.
* @param addTo The node in which to add the converter.
- * @return The child node created by this method.
*/
- static TreeTable.Node toTree(final ObjectConverter<?,?> converter, final
TreeTable.Node addTo) {
+ static void toTree(final ObjectConverter<?,?> converter, final
TreeTable.Node addTo) {
final TreeTable.Node node = addTo.newChild();
node.setValue(SOURCE, converter.getSourceClass());
node.setValue(TARGET, converter.getTargetClass());
- return node;
}
/**
@@ -106,7 +112,7 @@ final class Column extends TableColumn<C
@Debug
static String format(final TreeTable table) {
final TreeTableFormat format = new TreeTableFormat(null, null);
- format.setColumnSeparatorPattern("[ ] ⇨ ");
+ format.setColumnSeparatorPattern("?[ ] ⇨ ");
return format.format(table);
}
}
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/ConverterRegistry.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/ConverterRegistry.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/ConverterRegistry.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/ConverterRegistry.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -19,11 +19,12 @@ package org.apache.sis.internal.converte
import java.util.Map;
import java.util.LinkedHashMap;
import net.jcip.annotations.ThreadSafe;
-import org.apache.sis.internal.util.SystemListener;
+import org.apache.sis.util.Debug;
import org.apache.sis.util.Classes;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.util.UnconvertibleObjectException;
+import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.resources.Errors;
@@ -36,7 +37,7 @@ import org.apache.sis.util.resources.Err
*
* <p>New instances of {@code ConverterRegistry} are initially empty. Custom
converters must be
* explicitly {@linkplain #register(ObjectConverter) registered}. However a
system-wide registry
- * initialized with default converters is provided by the {@link #SYSTEM}
constant.</p>
+ * initialized with default converters is provided by the {@link
HeuristicRegistry#SYSTEM} constant.</p>
*
* {@section Note about conversions from interfaces}
* {@code ConverterRegistry} is primarily designed for handling converters
from classes to
@@ -45,40 +46,13 @@ import org.apache.sis.util.resources.Err
* multi-inheritance in interface hierarchy.
*
* @author Martin Desruisseaux (Geomatys)
- * @since 0.3 (derived from geotk-3.20)
+ * @since 0.3 (derived from geotk-3.00)
* @version 0.3
* @module
*/
@ThreadSafe
public class ConverterRegistry {
/**
- * The default system-wide instance. This register is initialized with
conversions between
- * some basic Java and SIS objects, like conversions between {@link
java.util.Date} and
- * {@link java.lang.Long}. Those conversions are defined for the lifetime
of the JVM.
- *
- * <p>If a temporary set of converters is desired, a new instance of
{@code ConverterRegistry}
- * should be created explicitly instead.</p>
- *
- * {@section Adding system-wide converters}
- * Applications can add system-wide custom providers either by explicit
call to the
- * {@link #register(ObjectConverter)} method on the system converter, or
by listing
- * the fully qualified classnames of their {@link ObjectConverter}
instances in the
- * following file (see {@link ServiceLoader} for more info about services
loading):
- *
- * {@preformat text
- * META-INF/services/org.apache.sis.util.converter.ObjectConverter
- * }
- */
- public static final ConverterRegistry SYSTEM = new ConverterRegistry();
- static {
- SystemListener.add(new SystemListener() {
- @Override protected void classpathChanged() {
- SYSTEM.clear();
- }
- });
- }
-
- /**
* The map of converters of any kind. For any key of type {@code
ClassPair<S,T>},
* the value shall be of type {@code ObjectConverter<? super S, ? extends
T>}.
* To ensure this constraint, values should be read and written using only
the
@@ -193,27 +167,6 @@ public class ConverterRegistry {
}
/**
- * Returns an unique instance of the given converter. If a converter
already exists for the
- * same source an target classes, then that converter is returned.
Otherwise that converter
- * is cached and returned.
- *
- * @param converter The converter to look for a unique instance.
- * @return A previously existing instance if one exists, or the given
converter otherwise.
- */
- @SuppressWarnings("unchecked")
- final <S,T> ObjectConverter<S,T> unique(final SystemConverter<S,T>
converter) {
- ObjectConverter<S,T> existing = findEquals(converter);
- if (existing == null) {
- register(converter);
- existing = findEquals(converter);
- if (existing == null) {
- return converter;
- }
- }
- return existing;
- }
-
- /**
* Registers a new converter. This method should be invoked only once for
a given converter,
* for example in class static initializer. For example if a {@code Angle}
class is defined,
* the static initializer of that class could register a converter from
{@code Angle} to
@@ -379,19 +332,47 @@ public class ConverterRegistry {
}
/**
- * Returns a converter for the specified source and target classes.
+ * Returns a converter for exactly the given source and target classes.
+ * The default implementation invokes {@link #find(Class, Class)}, then
+ * ensures that the converter source and target classes are the same ones
+ * than the classes given in argument to this method.
*
* @param <S> The source class.
* @param <T> The target class.
- * @param source The source class.
- * @param target The target class, or {@code Object.class} for any.
+ * @param sourceClass The source class.
+ * @param targetClass The target class, or {@code Object.class} for any.
* @return The converter from the specified source class to the target
class.
- * @throws UnconvertibleObjectException if no converter is found.
+ * @throws UnconvertibleObjectException if no converter is found for the
given classes.
*/
- public <S,T> ObjectConverter<? super S, ? extends T> find(final Class<S>
source, final Class<T> target)
+ @SuppressWarnings("unchecked")
+ public <S,T> ObjectConverter<S,T> findExact(final Class<S> sourceClass,
final Class<T> targetClass)
throws UnconvertibleObjectException
{
- final ClassPair<S,T> key = new ClassPair<>(source, target);
+ final ObjectConverter<? super S, ? extends T> candidate =
find(sourceClass, targetClass);
+ if (candidate.getSourceClass() == sourceClass &&
+ candidate.getTargetClass() == targetClass)
+ {
+ return (ObjectConverter<S,T>) candidate;
+ }
+ throw new
UnconvertibleObjectException(Errors.format(Errors.Keys.CanNotConvertFromType_2,
sourceClass, targetClass));
+ }
+
+ /**
+ * Returns a converter suitable for the given source and target classes.
+ * This method may return a converter accepting more generic sources or
+ * converting to more specific targets.
+ *
+ * @param <S> The source class.
+ * @param <T> The target class.
+ * @param sourceClass The source class.
+ * @param targetClass The target class, or {@code Object.class} for any.
+ * @return The converter from the specified source class to the target
class.
+ * @throws UnconvertibleObjectException if no converter is found for the
given classes.
+ */
+ public <S,T> ObjectConverter<? super S, ? extends T> find(final Class<S>
sourceClass, final Class<T> targetClass)
+ throws UnconvertibleObjectException
+ {
+ final ClassPair<S,T> key = new ClassPair<>(sourceClass, targetClass);
synchronized (converters) {
ObjectConverter<? super S, ? extends T> converter = get(key);
if (converter != null) {
@@ -414,23 +395,15 @@ public class ConverterRegistry {
}
/*
* No converter found. Gives a chance to subclasses to provide
dynamically-generated
- * converter. The default implementation does not provide any.
+ * converter.
*/
- converter = createConverter(source, target);
+ converter = createConverter(sourceClass, targetClass);
if (converter != null) {
put(key, converter);
return converter;
}
}
- /*
- * No explicit converter were found. Checks for the trivial case where
an identity
- * converter would fit. We perform this operation last in order to
give a chance to
- * register an explicit converter if we need to.
- */
- if (target.isAssignableFrom(source)) {
- return key.cast(IdentityConverter.create(source));
- }
- throw new
UnconvertibleObjectException(Errors.format(Errors.Keys.CanNotConvertFromType_2,
source, target));
+ throw new
UnconvertibleObjectException(Errors.format(Errors.Keys.CanNotConvertFromType_2,
sourceClass, targetClass));
}
/**
@@ -438,14 +411,72 @@ public class ConverterRegistry {
* This method is invoked by <code>{@linkplain #find find}(source,
target)</code> when no
* registered converter were found for the given types.
*
+ * <p>The default implementation checks for the trivial case where an
identity converter
+ * would fit, and returns {@code null} in all other cases.
+ * Subclasses can override this method in order to generate some
converters dynamically.</p>
+ *
* @param <S> The source class.
* @param <T> The target class.
- * @param source The source class.
- * @param target The target class, or {@code Object.class} for any.
+ * @param sourceClass The source class.
+ * @param targetClass The target class, or {@code Object.class} for any.
* @return A newly generated converter from the specified source class to
the target class,
* or {@code null} if none.
*/
- protected <S,T> ObjectConverter<S,T> createConverter(final Class<S>
source, final Class<T> target) {
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ protected <S,T> ObjectConverter<S,T> createConverter(final Class<S>
sourceClass, final Class<T> targetClass) {
+ if (targetClass.isAssignableFrom(sourceClass)) {
+ return new IdentityConverter(sourceClass, targetClass);
+ }
return null;
}
+
+ /**
+ * Returns a string representation of registered converters for debugging
purpose.
+ * The converters are show in a tree where all real converters are leafs.
Parents
+ * of those leafs are {@link FallbackConverter}s which delegate their work
to the
+ * leafs.
+ *
+ * @return A string representation of registered converters.
+ */
+ @Debug
+ @Override
+ public String toString() {
+ final TreeTable table = Column.createTable();
+ final TreeTable.Node root = table.getRoot();
+ root.setValue(Column.SOURCE, getClass());
+ synchronized (converters) {
+ for (final Map.Entry<ClassPair<?,?>, ObjectConverter<?,?>> entry :
converters.entrySet()) {
+ TreeTable.Node addTo = root;
+ final ClassPair<?,?> key = entry.getKey();
+ final ObjectConverter<?,?> converter = entry.getValue();
+ if (converter.getSourceClass() != key.sourceClass ||
+ converter.getTargetClass() != key.targetClass)
+ {
+ /*
+ * If we enter this block, then the converter is not
really for this
+ * (source, target) classes pair. Instead, we are
leveraging a converter
+ * which was defined for an other ClassPair. We show this
fact be first
+ * showing this ClassPair, then the actual converter
(source, target) as
+ * below:
+ *
+ * String ⇨ Number (the ClassPair key)
+ * └─String ⇨ Integer (the
ObjectConverter value)
+ *
+ * This is the same idea than the formatting done by
FallbackConverter,
+ * except that there is only one child. Actually this can
be though as
+ * a lightweight fallback converter.
+ */
+ addTo = addTo.newChild();
+ addTo.setValue(Column.SOURCE, key.sourceClass);
+ addTo.setValue(Column.TARGET, key.targetClass);
+ }
+ if (converter instanceof FallbackConverter<?,?>) {
+ ((FallbackConverter<?,?>)
converter).toTree(addTo.newChild(), true);
+ } else {
+ Column.toTree(converter, addTo);
+ }
+ }
+ }
+ return Column.format(table);
+ }
}
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/FallbackConverter.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/FallbackConverter.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/FallbackConverter.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/FallbackConverter.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -25,7 +25,6 @@ import org.apache.sis.util.Classes;
import org.apache.sis.util.ObjectConverter;
import org.apache.sis.math.FunctionProperty;
import org.apache.sis.util.UnconvertibleObjectException;
-import org.apache.sis.util.collection.DefaultTreeTable;
import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.Debug;
@@ -331,10 +330,11 @@ final class FallbackConverter<S,T> exten
*/
private void toTree(final ObjectConverter<?,?> converter, TreeTable.Node
addTo) {
if (converter instanceof FallbackConverter<?,?>) {
- if (converter.getTargetClass() != targetClass) {
- addTo = Column.toTree(converter, addTo);
+ final boolean isNew = converter.getTargetClass() != targetClass;
+ if (isNew) {
+ addTo = addTo.newChild();
}
- ((FallbackConverter<?,?>) converter).toTree(addTo);
+ ((FallbackConverter<?,?>) converter).toTree(addTo, isNew);
} else {
Column.toTree(converter, addTo);
}
@@ -345,8 +345,13 @@ final class FallbackConverter<S,T> exten
* to the given node.
*
* @param addTo The node in which to add the converter.
+ * @param isNew {@code true} if {@code addTo} is a newly created node.
*/
- final void toTree(final TreeTable.Node addTo) {
+ final void toTree(final TreeTable.Node addTo, final boolean isNew) {
+ if (isNew) {
+ addTo.setValue(Column.SOURCE, sourceClass);
+ addTo.setValue(Column.TARGET, targetClass);
+ }
toTree(primary, addTo);
toTree(fallback, addTo);
}
@@ -358,12 +363,8 @@ final class FallbackConverter<S,T> exten
@Debug
@Override
public String toString() {
- final DefaultTreeTable table = new DefaultTreeTable(Column.SOURCE,
Column.TARGET);
- final DefaultTreeTable.Node root = new DefaultTreeTable.Node(table);
- root.setValue(Column.SOURCE, sourceClass);
- root.setValue(Column.TARGET, targetClass);
- table.setRoot(root);
- toTree(root);
+ final TreeTable table = Column.createTable();
+ toTree(table.getRoot(), true);
return Column.format(table);
}
}
Added:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/HeuristicRegistry.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/HeuristicRegistry.java?rev=1456692&view=auto
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/HeuristicRegistry.java
(added)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/HeuristicRegistry.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -0,0 +1,135 @@
+/*
+ * 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.internal.converter;
+
+import org.opengis.util.CodeList;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.sis.util.Numbers;
+import org.apache.sis.util.ObjectConverter;
+import org.apache.sis.internal.util.SystemListener;
+
+
+/**
+ * A {@link ConverterRegistry} which applies heuristic rules in addition of
the explicitly
+ * registered converters. Those heuristic rules are provided in a separated
class in order
+ * to keep the {@link ConverterRegistry} class "pure", and concentrate all
arbitrary
+ * decisions in this single class.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-3.02)
+ * @version 0.3
+ * @module
+ */
+@ThreadSafe
+public final class HeuristicRegistry extends ConverterRegistry {
+ /**
+ * The default system-wide instance. This register is initialized with
conversions between
+ * some basic Java and SIS objects, like conversions between {@link
java.util.Date} and
+ * {@link java.lang.Long}. Those conversions are defined for the lifetime
of the JVM.
+ *
+ * <p>If a temporary set of converters is desired, a new instance of
{@code ConverterRegistry}
+ * should be created explicitly instead.</p>
+ *
+ * {@section Adding system-wide converters}
+ * Applications can add system-wide custom providers either by explicit
call to the
+ * {@link #register(ObjectConverter)} method on the system converter, or
by listing
+ * the fully qualified classnames of their {@link ObjectConverter}
instances in the
+ * following file (see {@link ServiceLoader} for more info about services
loading):
+ *
+ * {@preformat text
+ * META-INF/services/org.apache.sis.util.converter.ObjectConverter
+ * }
+ */
+ public static final ConverterRegistry SYSTEM = new HeuristicRegistry();
+ static {
+ SystemListener.add(new SystemListener() {
+ @Override protected void classpathChanged() {
+ SYSTEM.clear();
+ }
+ });
+ }
+
+ /**
+ * Creates an initially empty set of object converters. The heuristic
+ * rules apply right away, even if no converter have been registered yet.
+ */
+ private HeuristicRegistry() {
+ }
+
+ /**
+ * Create dynamically the converters for a few special cases.
+ *
+ * <ul>
+ * <li>If the source class is {@link CharSequence}, tries to delegate to
an other
+ * converter accepting {@link String} sources.</li>
+ * <li>If the source and target types are numbers, generates a {@link
NumberConverter}
+ * on the fly.</li>
+ * <li>If the target type is a code list, generate the converter
on-the-fly.
+ * We do not register every code lists in advance because there is
too
+ * many of them, and a generic code is available for all of
them.</li>
+ * </ul>
+ */
+ @Override
+ @SuppressWarnings({"unchecked","rawtypes"})
+ protected <S,T> ObjectConverter<S,T> createConverter(final Class<S>
sourceClass, final Class<T> targetClass) {
+ /*
+ * Before to try any heuristic rule, check for the identity converter.
+ */
+ final ObjectConverter<S,T> identity =
super.createConverter(sourceClass, targetClass);
+ if (identity != null) {
+ return identity;
+ }
+ /*
+ * From CharSequence to anything.
+ */
+ if (sourceClass == CharSequence.class) {
+ return (ObjectConverter<S,T>) new CharSequenceConverter<>(
+ targetClass, find(String.class, targetClass));
+ }
+ /*
+ * From String to various kind of CodeList.
+ */
+ if (sourceClass == String.class &&
CodeList.class.isAssignableFrom(targetClass)) {
+ return (ObjectConverter<S,T>) new StringConverter.CodeList<>(
+ targetClass.asSubclass(CodeList.class));
+ }
+ /*
+ * From Number to other kinds of Number.
+ */
+ if (sourceClass == Number.class || isSupportedNumber(sourceClass)) {
+ if (isSupportedNumber(targetClass)) {
+ return (ObjectConverter<S,T>) new NumberConverter<>(
+ sourceClass.asSubclass(Number.class),
+ targetClass.asSubclass(Number.class));
+ }
+ if (targetClass == Comparable.class) {
+ return (ObjectConverter<S,T>) new NumberConverter.Comparable<>(
+ sourceClass.asSubclass(Number.class));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns {@code true} if the given type is one of the types supported
+ * by {@link NumberConverter}.
+ */
+ private static boolean isSupportedNumber(final Class<?> type) {
+ final int code = Numbers.getEnumConstant(type);
+ return (code >= Numbers.BYTE && code <= Numbers.BIG_DECIMAL);
+ }
+}
Propchange:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/HeuristicRegistry.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/HeuristicRegistry.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/IdentityConverter.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/IdentityConverter.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/IdentityConverter.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/IdentityConverter.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -18,87 +18,38 @@ package org.apache.sis.internal.converte
import java.util.Set;
import java.util.EnumSet;
-import java.util.Map;
-import java.util.IdentityHashMap;
-import java.io.ObjectStreamException;
-import java.io.Serializable;
import net.jcip.annotations.Immutable;
-import org.apache.sis.internal.util.SystemListener;
import org.apache.sis.math.FunctionProperty;
-import org.apache.sis.util.ObjectConverter;
/**
* An object converter which returns the source unchanged.
*
- * @param <T> The base type of source and converted objects.
+ * @param <S> The base type of source objects.
+ * @param <T> The base type of converted objects.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.3 (derived from geotk-3.01)
* @version 0.3
* @module
+ *
+ * @see org.apache.sis.util.ObjectConverters#identity(Class)
*/
@Immutable
-public final class IdentityConverter<T> implements ObjectConverter<T,T>,
Serializable {
+public final class IdentityConverter<S extends T, T> extends
SystemConverter<S,T> {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = -7203549932226245206L;
/**
- * Identity converters created in the JVM, for sharing unique instances.
- */
- private static final Map<Class<?>, IdentityConverter<?>> CACHE = new
IdentityHashMap<>();
- static {
- SystemListener.add(new SystemListener() {
- @Override protected void classpathChanged() {
- clearCache();
- }
- });
- }
-
- /**
- * Returns an identity converter for the given type.
- *
- * @param <T> The compile-time type.
- * @param type The type of the desired converter.
- * @return The identity converter for the given type.
- */
- public static <T> IdentityConverter<T> create(final Class<T> type) {
- synchronized (CACHE) {
- @SuppressWarnings("unchecked")
- IdentityConverter<T> converter = (IdentityConverter<T>)
CACHE.get(type);
- if (converter == null) {
- converter = new IdentityConverter<>(type);
- CACHE.put(type, converter);
- }
- return converter;
- }
- }
-
- /**
- * Invoked when the cache needs to be cleared because the classpath
changed.
- * Some cached type may no longer be on the classpath, so we need to
release
- * references in order to allow the garbage collector to unload them.
- */
- static void clearCache() {
- synchronized (CACHE) {
- CACHE.clear();
- }
- }
-
- /**
- * The type of source and converted objects.
- */
- private final Class<T> type;
-
- /**
* Creates a new identity converter.
*
- * @param type The type of source and converted objects.
+ * @param sourceClass The {@linkplain #getSourceClass() source class}.
+ * @param targetClass The {@linkplain #getTargetClass() target class}.
*/
- private IdentityConverter(final Class<T> type) {
- this.type = type;
+ public IdentityConverter(final Class<S> sourceClass, final Class<T>
targetClass) {
+ super(sourceClass, targetClass);
}
/**
@@ -111,23 +62,12 @@ public final class IdentityConverter<T>
*/
@Override
public Set<FunctionProperty> properties() {
- return EnumSet.allOf(FunctionProperty.class);
- }
-
- /**
- * Returns the type for source objects.
- */
- @Override
- public Class<T> getSourceClass() {
- return type;
- }
-
- /**
- * Returns the type of converted objects.
- */
- @Override
- public Class<T> getTargetClass() {
- return type;
+ final EnumSet<FunctionProperty> properties =
EnumSet.allOf(FunctionProperty.class);
+ if (sourceClass != targetClass) {
+ // Conservative choice (actually we don't really know).
+ properties.remove(FunctionProperty.INVERTIBLE);
+ }
+ return properties;
}
/**
@@ -136,30 +76,7 @@ public final class IdentityConverter<T>
* @param source The value to convert.
*/
@Override
- public T convert(final T source) {
+ public T convert(final S source) {
return source;
}
-
- /**
- * Returns {@code this}, since this converter is its own inverse.
- */
- @Override
- public ObjectConverter<T,T> inverse() {
- return this;
- }
-
- /**
- * Invoked on deserialization for resolving to a unique instance.
- */
- private Object readResolve() throws ObjectStreamException {
- synchronized (CACHE) {
- @SuppressWarnings("unchecked")
- final IdentityConverter<T> converter = (IdentityConverter<T>)
CACHE.get(type);
- if (converter != null) {
- return converter;
- }
- CACHE.put(type, this);
- }
- return this;
- }
}
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/NumberConverter.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/NumberConverter.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/NumberConverter.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/NumberConverter.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -20,7 +20,6 @@ import java.util.Set;
import java.util.EnumSet;
import net.jcip.annotations.Immutable;
import org.apache.sis.util.Numbers;
-import org.apache.sis.util.ObjectConverter;
import org.apache.sis.math.FunctionProperty;
import org.apache.sis.util.UnconvertibleObjectException;
@@ -94,14 +93,6 @@ final class NumberConverter<S extends Nu
}
/**
- * Returns the inverse of this converter.
- */
- @Override
- public ObjectConverter<T,S> inverse() {
- return new NumberConverter<>(targetClass, sourceClass).unique();
- }
-
- /**
* Converter from numbers to comparables. This special case exists because
{@link Number}
* does not implement {@link java.lang.Comparable} directly, but all known
subclasses do.
*/
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemConverter.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemConverter.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemConverter.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/converter/SystemConverter.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -18,6 +18,7 @@ package org.apache.sis.internal.converte
import java.io.ObjectStreamException;
import org.apache.sis.util.ObjectConverter;
+import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.util.resources.Errors;
@@ -41,10 +42,15 @@ abstract class SystemConverter<S,T> exte
private static final long serialVersionUID = 885663610056067478L;
/**
+ * The inverse converter, created when first needed.
+ */
+ private transient volatile ObjectConverter<T,S> inverse;
+
+ /**
* Creates a new converter for the given source and target classes.
*
- * @param sourceClass The {@linkplain ObjectConverter#getSourceClass()
source class}.
- * @param targetClass The {@linkplain ObjectConverter#getTargetClass()
target class}.
+ * @param sourceClass The {@linkplain #getSourceClass() source class}.
+ * @param targetClass The {@linkplain #getTargetClass() target class}.
*/
SystemConverter(final Class<S> sourceClass, final Class<T> targetClass) {
super(sourceClass, targetClass);
@@ -67,11 +73,19 @@ abstract class SystemConverter<S,T> exte
}
/**
- * Unsupported by default. To be overridden by subclasses that support
this operation.
+ * Returns the inverse converter, creating it when first needed.
*/
@Override
- public ObjectConverter<T, S> inverse() throws
UnsupportedOperationException {
- throw new
UnsupportedOperationException(Errors.format(Errors.Keys.NonInvertibleConversion));
+ public final ObjectConverter<T,S> inverse() throws
UnsupportedOperationException {
+ // No need to synchronize. This is not a big deal if the same object
is fetched twice.
+ // The ConverterRegistry clas provides the required synchronization.
+ ObjectConverter<T,S> candidate = inverse;
+ if (candidate == null) try {
+ inverse = candidate =
HeuristicRegistry.SYSTEM.findExact(targetClass, sourceClass);
+ } catch (UnconvertibleObjectException e) {
+ throw new
UnsupportedOperationException(Errors.format(Errors.Keys.NonInvertibleConversion),
e);
+ }
+ return candidate;
}
/**
@@ -110,12 +124,13 @@ abstract class SystemConverter<S,T> exte
}
/**
- * Returns an unique instance of this converter. If a converter already
exists for the same
- * source an target classes, then this converter is returned. Otherwise
this converter is
- * cached and returned.
+ * Returns an unique instance of this converter if one exists. If a
converter already
+ * exists for the same source an target classes, then this converter is
returned.
+ * Otherwise this converter is returned <strong>without</strong> being
cached.
*/
- final ObjectConverter<S,T> unique() {
- return ConverterRegistry.SYSTEM.unique(this);
+ public final ObjectConverter<S,T> unique() {
+ final ObjectConverter<S,T> existing =
HeuristicRegistry.SYSTEM.findEquals(this);
+ return (existing != null) ? existing : this;
}
/**
@@ -123,8 +138,7 @@ abstract class SystemConverter<S,T> exte
* in the virtual machine, we do not cache the instance (for now) for
security reasons.
*/
protected final Object readResolve() throws ObjectStreamException {
- final ObjectConverter<S,T> existing =
ConverterRegistry.SYSTEM.findEquals(this);
- return (existing != null) ? existing : this;
+ return unique();
}
/**
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ObjectConverters.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ObjectConverters.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ObjectConverters.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/ObjectConverters.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -18,9 +18,9 @@ package org.apache.sis.util;
import java.util.Map;
import java.util.Set;
-import org.apache.sis.internal.converter.IdentityConverter;
import org.apache.sis.util.collection.CollectionsExt;
-import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.converter.IdentityConverter;
+import org.apache.sis.internal.converter.HeuristicRegistry;
/**
@@ -78,7 +78,7 @@ public final class ObjectConverters exte
*/
public static <T> ObjectConverter<T,T> identity(final Class<T> type) {
ArgumentChecks.ensureNonNull("type", type);
- return IdentityConverter.create(type);
+ return new IdentityConverter<>(type, type).unique();
}
/**
@@ -94,8 +94,7 @@ public final class ObjectConverters exte
public static <S,T> ObjectConverter<? super S, ? extends T> find(final
Class<S> source, final Class<T> target)
throws UnconvertibleObjectException
{
- // TODO: port the implementation from Geotk
- throw new
UnconvertibleObjectException(Errors.format(Errors.Keys.CanNotConvertFromType_2,
source, target));
+ return HeuristicRegistry.SYSTEM.find(source, target);
}
/**
@@ -174,7 +173,7 @@ public final class ObjectConverters exte
final Class<V> valueType)
{
ArgumentChecks.ensureNonNull("valueType", valueType);
- return CollectionsExt.derivedMap(storage, keyConverter,
IdentityConverter.create(valueType));
+ return CollectionsExt.derivedMap(storage, keyConverter,
identity(valueType));
}
/**
@@ -202,6 +201,6 @@ public final class ObjectConverters exte
final ObjectConverter<SV,V>
valueConverter)
{
ArgumentChecks.ensureNonNull("keyType", keyType);
- return CollectionsExt.derivedMap(storage,
IdentityConverter.create(keyType), valueConverter);
+ return CollectionsExt.derivedMap(storage, identity(keyType),
valueConverter);
}
}
Modified:
sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/internal/converter/NumberConverterTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/internal/converter/NumberConverterTest.java?rev=1456692&r1=1456691&r2=1456692&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/internal/converter/NumberConverterTest.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/internal/converter/NumberConverterTest.java
[UTF-8] Thu Mar 14 21:53:41 2013
@@ -37,6 +37,21 @@ import static org.apache.sis.test.Assert
*/
public final strictfp class NumberConverterTest extends TestCase {
/**
+ * Creates a {@link NumberConverter} for the given source and target
classes.
+ * We have to use the {@link ConverterRegistry} instead than instantiating
the
+ * converters directly because some tests are going to verify that the
converter
+ * has been properly cached.
+ */
+ private static <S extends Number, T> ObjectConverter<S,T> create(
+ final Class<S> sourceClass, final Class<T> targetClass)
+ {
+ final ObjectConverter<S,T> converter =
HeuristicRegistry.SYSTEM.findExact(sourceClass, targetClass);
+ assertInstanceOf("ConverterRegistry.find(" +
sourceClass.getSimpleName() + ", " + targetClass.getSimpleName() + ')',
+ (targetClass == Comparable.class) ?
NumberConverter.Comparable.class : NumberConverter.class, converter);
+ return converter;
+ }
+
+ /**
* Asserts that conversion of the given {@code source} value produces
* the given {@code target} value, and tests the inverse conversion.
*/
@@ -69,8 +84,7 @@ public final strictfp class NumberConver
*/
@Test
public void testByte() {
- final ObjectConverter<Integer, Byte> c =
- new NumberConverter<>(Integer.class, Byte.class).unique();
+ final ObjectConverter<Integer, Byte> c = create(Integer.class,
Byte.class);
runInvertibleConversion(c, Integer.valueOf(-8), Byte.valueOf((byte)
-8));
runInvertibleConversion(c, Integer.valueOf(Byte.MIN_VALUE),
Byte.valueOf(Byte.MIN_VALUE));
runInvertibleConversion(c, Integer.valueOf(Byte.MAX_VALUE),
Byte.valueOf(Byte.MAX_VALUE));
@@ -84,8 +98,7 @@ public final strictfp class NumberConver
*/
@Test
public void testShort() {
- final ObjectConverter<Integer, Short> c =
- new NumberConverter<>(Integer.class, Short.class).unique();
+ final ObjectConverter<Integer, Short> c = create(Integer.class,
Short.class);
runInvertibleConversion(c, Integer.valueOf(-8), Short.valueOf((short)
-8));
runInvertibleConversion(c, Integer.valueOf(Short.MIN_VALUE),
Short.valueOf(Short.MIN_VALUE));
runInvertibleConversion(c, Integer.valueOf(Short.MAX_VALUE),
Short.valueOf(Short.MAX_VALUE));
@@ -99,8 +112,7 @@ public final strictfp class NumberConver
*/
@Test
public void testInteger() {
- final ObjectConverter<Float, Integer> c =
- new NumberConverter<>(Float.class, Integer.class).unique();
+ final ObjectConverter<Float, Integer> c = create(Float.class,
Integer.class);
runInvertibleConversion(c, Float.valueOf(-8), Integer.valueOf(-8));
// Can not easily tests the values around Integer.MIN/MAX_VALUE
because of rounding errors in float.
assertSame("Deserialization shall resolves to the singleton
instance.", c, assertSerializedEquals(c));
@@ -111,8 +123,7 @@ public final strictfp class NumberConver
*/
@Test
public void testLong() {
- final ObjectConverter<Float, Long> c =
- new NumberConverter<>(Float.class, Long.class).unique();
+ final ObjectConverter<Float, Long> c = create(Float.class, Long.class);
runInvertibleConversion(c, Float.valueOf(-8), Long.valueOf(-8));
assertSame("Deserialization shall resolves to the singleton
instance.", c, assertSerializedEquals(c));
}
@@ -122,8 +133,7 @@ public final strictfp class NumberConver
*/
@Test
public void testFloat() {
- final ObjectConverter<Double, Float> c =
- new NumberConverter<>(Double.class, Float.class).unique();
+ final ObjectConverter<Double, Float> c = create(Double.class,
Float.class);
runInvertibleConversion(c, Double.valueOf(2.5), Float.valueOf(2.5f));
tryUnconvertibleValue (c, Double.valueOf(1E+40));
assertSame("Deserialization shall resolves to the singleton
instance.", c, assertSerializedEquals(c));
@@ -134,8 +144,7 @@ public final strictfp class NumberConver
*/
@Test
public void testDouble() {
- final ObjectConverter<BigDecimal, Double> c =
- new NumberConverter<>(BigDecimal.class, Double.class).unique();
+ final ObjectConverter<BigDecimal, Double> c = create(BigDecimal.class,
Double.class);
runInvertibleConversion(c, BigDecimal.valueOf(2.5),
Double.valueOf(2.5));
assertSame("Deserialization shall resolves to the singleton
instance.", c, assertSerializedEquals(c));
}
@@ -145,8 +154,7 @@ public final strictfp class NumberConver
*/
@Test
public void testBigInteger() {
- final ObjectConverter<Double, BigInteger> c =
- new NumberConverter<>(Double.class, BigInteger.class).unique();
+ final ObjectConverter<Double, BigInteger> c = create(Double.class,
BigInteger.class);
runInvertibleConversion(c, Double.valueOf(1000),
BigInteger.valueOf(1000));
assertSame("Deserialization shall resolves to the singleton
instance.", c, assertSerializedEquals(c));
}
@@ -156,8 +164,7 @@ public final strictfp class NumberConver
*/
@Test
public void testBigDecimal() {
- final ObjectConverter<Double, BigDecimal> c =
- new NumberConverter<>(Double.class, BigDecimal.class).unique();
+ final ObjectConverter<Double, BigDecimal> c = create(Double.class,
BigDecimal.class);
runInvertibleConversion(c, Double.valueOf(2.5),
BigDecimal.valueOf(2.5));
assertSame("Deserialization shall resolves to the singleton
instance.", c, assertSerializedEquals(c));
}
@@ -168,8 +175,8 @@ public final strictfp class NumberConver
*/
@Test
public void testComparable() {
- final ObjectConverter<Number,Comparable<?>> c =
- new NumberConverter.Comparable<>(Number.class).unique();
+ @SuppressWarnings("unchecked")
+ final ObjectConverter<Number,Comparable<?>> c = create(Number.class,
(Class) Comparable.class);
final Integer value = 8;
assertSame(value, c.convert(value));
assertSame("Deserialization shall resolves to the singleton
instance.", c, assertSerializedEquals(c));