You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by da...@apache.org on 2016/08/22 21:12:22 UTC
svn commit: r1757276 - in /felix/trunk/converter/src:
main/java/org/apache/felix/converter/impl/
main/java/org/osgi/service/converter/
test/java/org/apache/felix/converter/impl/
Author: davidb
Date: Mon Aug 22 21:12:22 2016
New Revision: 1757276
URL: http://svn.apache.org/viewvc?rev=1757276&view=rev
Log:
Felix converter: more flexible adapters.
Added:
felix/trunk/converter/src/main/java/org/osgi/service/converter/ConvertFunction.java
felix/trunk/converter/src/main/java/org/osgi/service/converter/SimpleConvertFunction.java
Removed:
felix/trunk/converter/src/main/java/org/osgi/service/converter/FunctionThrowsException.java
Modified:
felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java
felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterService.java
felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java
felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java
felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/AdapterTest.java
Modified: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java?rev=1757276&r1=1757275&r2=1757276&view=diff
==============================================================================
--- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java (original)
+++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java Mon Aug 22 21:12:22 2016
@@ -17,21 +17,29 @@
package org.apache.felix.converter.impl;
import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.osgi.service.converter.Adapter;
import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.ConvertFunction;
import org.osgi.service.converter.Converter;
import org.osgi.service.converter.Converting;
-import org.osgi.service.converter.FunctionThrowsException;
import org.osgi.service.converter.Rule;
+import org.osgi.service.converter.SimpleConvertFunction;
import org.osgi.service.converter.TypeReference;
public class AdapterImpl implements Adapter, InternalConverter {
private final InternalConverter delegate;
- private final Map<TypePair, FunctionThrowsException<Object, Object>> classRules =
+ private final Map<TypePair, ConvertFunction<Object, Object>> classRules =
new ConcurrentHashMap<>();
AdapterImpl(InternalConverter converter) {
@@ -50,42 +58,46 @@ public class AdapterImpl implements Adap
return new AdapterImpl(this);
}
- @SuppressWarnings("unchecked")
@Override
+ @SuppressWarnings("unchecked")
public <F, T> Adapter rule(Class<F> fromCls, Class<T> toCls,
- FunctionThrowsException<F, T> toFun, FunctionThrowsException<T, F> fromFun) {
+ SimpleConvertFunction<F, T> toFun, SimpleConvertFunction<T, F> fromFun) {
if (fromCls.equals(toCls))
throw new IllegalArgumentException();
- classRules.put(new TypePair(fromCls, toCls), (FunctionThrowsException<Object, Object>) toFun);
- classRules.put(new TypePair(toCls, fromCls), (FunctionThrowsException<Object, Object>) fromFun);
+ classRules.put(new TypePair(fromCls, toCls), (ConvertFunction<Object, Object>) toFun);
+ classRules.put(new TypePair(toCls, fromCls), (ConvertFunction<Object, Object>) fromFun);
return this;
}
@Override
public <F, T> Adapter rule(TypeReference<F> fromRef, TypeReference<T> toRef,
- FunctionThrowsException<F, T> toFun, FunctionThrowsException<T, F> fromFun) {
+ SimpleConvertFunction<F, T> toFun, SimpleConvertFunction<T, F> fromFun) {
// TODO Auto-generated method stub
return null;
}
@Override
public <F, T> Adapter rule(Type fromType, Type toType,
- FunctionThrowsException<F, T> toFun, FunctionThrowsException<T, F> fromFun) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public <F, T> Adapter rule(FunctionThrowsException<F, T> toFun, FunctionThrowsException<T, F> fromFun) {
+ SimpleConvertFunction<F, T> toFun, SimpleConvertFunction<T, F> fromFun) {
// TODO Auto-generated method stub
return null;
}
@Override
+ @SuppressWarnings("unchecked")
public <F, T> Adapter rule(Rule<F, T> rule) {
- // TODO Auto-generated method stub
- return null;
+ ConvertFunction<F, T> toFun = rule.getToFunction();
+ if (toFun != null)
+ classRules.put(new TypePair(rule.getFromClass(), rule.getToClass()),
+ (ConvertFunction<Object, Object>) toFun);
+
+
+ ConvertFunction<T, F> fromFun = rule.getFromFunction();
+ if (fromFun != null)
+ classRules.put(new TypePair(rule.getToClass(), rule.getFromClass()),
+ (ConvertFunction<Object, Object>) fromFun);
+ return this;
}
private class ConvertingWrapper implements InternalConverting {
@@ -128,11 +140,33 @@ public class AdapterImpl implements Adap
@Override
public Object to(Type type) {
if (object != null) {
- FunctionThrowsException<Object, Object> f = classRules.get(
- new TypePair(object.getClass(), Util.primitiveToBoxed(type)));
- if (f != null) {
+ Set<Type> fromTypes = assignableTypes(object.getClass());
+ Set<Type> toTypes = assignableTypes(type);
+
+ List<ConvertFunction<Object, Object>> converters = new ArrayList<>();
+ for (Type fromType : fromTypes) {
+ for (Type toType : toTypes) {
+ // TODO what exactly do we use as order here?
+ converters.add(classRules.get(new TypePair(fromType, Util.primitiveToBoxed(toType))));
+ }
+ }
+ for (Type fromType : fromTypes) {
+ converters.add(classRules.get(new TypePair(fromType, Object.class)));
+ }
+ for (Type toType : toTypes) {
+ converters.add(classRules.get(new TypePair(Object.class, Util.primitiveToBoxed(toType))));
+ }
+
+ for (Iterator<ConvertFunction<Object, Object>> it = converters.iterator(); it.hasNext(); ) {
+ ConvertFunction<Object, Object> func = it.next();
+ it.remove();
+ if (func == null)
+ continue;
+
try {
- return f.apply(object);
+ Object res = func.convert(object, type);
+ if (res != ConvertFunction.CANNOT_CONVERT)
+ return res;
} catch (Exception ex) {
if (hasDefault)
return defaultValue;
@@ -151,6 +185,21 @@ public class AdapterImpl implements Adap
}
}
+ private static Set<Type> assignableTypes(Type mostSpecialized) {
+ if (!(mostSpecialized instanceof Class))
+ return Collections.singleton(mostSpecialized);
+
+ Class<?> curClass = (Class<?>) mostSpecialized;
+ Set<Type> lookupTypes = new LinkedHashSet<>(); // Iteration order matters!
+ while((curClass != null) && (!(Object.class.equals(curClass)))) {
+ lookupTypes.add(curClass);
+ lookupTypes.addAll(Arrays.asList(curClass.getInterfaces()));
+ curClass = curClass.getSuperclass();
+ }
+ lookupTypes.add(Object.class); // Object is the superclass of any type
+ return lookupTypes;
+ }
+
static class TypePair {
private final Type from;
private final Type to;
Modified: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterService.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterService.java?rev=1757276&r1=1757275&r2=1757276&view=diff
==============================================================================
--- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterService.java (original)
+++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterService.java Mon Aug 22 21:12:22 2016
@@ -34,19 +34,24 @@ public class ConverterService implements
public ConverterService() {
Adapter a = new ConverterImpl().getAdapter();
+ a.rule(Byte.class, String.class, v -> v.toString(), Byte::parseByte); // TODO test
a.rule(Character.class, Boolean.class, v -> v.charValue() != 0,
v -> v.booleanValue() ? (char) 1 : (char) 0);
a.rule(Character.class, String.class, v -> v.toString(),
v -> v.length() > 0 ? v.charAt(0) : 0);
a.rule(Class.class, String.class, Class::toString,
v -> getClass().getClassLoader().loadClass(v));
+ a.rule(Double.class, String.class, v -> v.toString(), Double::parseDouble); // TODO test
+ a.rule(Float.class, String.class, v -> v.toString(), Float::parseFloat); // TODO test
a.rule(Integer.class, String.class, v -> v.toString(), Integer::parseInt);
a.rule(LocalDateTime.class, String.class, LocalDateTime::toString, LocalDateTime::parse);
a.rule(LocalDate.class, String.class, LocalDate::toString, LocalDate::parse);
a.rule(LocalTime.class, String.class, LocalTime::toString, LocalTime::parse);
+ a.rule(Long.class, String.class, v -> v.toString(), Long::parseLong); // TODO test
a.rule(OffsetDateTime.class, String.class, OffsetDateTime::toString, OffsetDateTime::parse);
a.rule(OffsetTime.class, String.class, OffsetTime::toString, OffsetTime::parse);
a.rule(Pattern.class, String.class, Pattern::toString, Pattern::compile);
+ a.rule(Short.class, String.class, v -> v.toString(), Short::parseShort); // TODO test
a.rule(UUID.class, String.class, UUID::toString, UUID::fromString);
a.rule(ZonedDateTime.class, String.class, ZonedDateTime::toString, ZonedDateTime::parse);
adapter = a;
Modified: felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java?rev=1757276&r1=1757275&r2=1757276&view=diff
==============================================================================
--- felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java (original)
+++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java Mon Aug 22 21:12:22 2016
@@ -58,24 +58,11 @@ public interface Adapter extends Convert
* @param fromFun the function to perform the reverse conversion.
* @return The current adapter, can be used to chain invocations.
*/
- <F, T> Adapter rule(Class<F> fromCls, Class<T> toCls, FunctionThrowsException<F,T> toFun,
- FunctionThrowsException<T,F> fromFun);
+ <F, T> Adapter rule(Class<F> fromCls, Class<T> toCls, SimpleConvertFunction<F, T> toFun,
+ SimpleConvertFunction<T, F> fromFun);
- /**
- * Specify a rule for the conversion to and from two classes. The rule
- * specifies the conversion in both directions. This overload makes it easy
- * to provide the conversions as method references.
- *
- * @param <F> the type to convert from.
- * @param <T> the type to convert to.
- * @param toFun the function to perform the conversion.
- * @param fromFun the function to perform the reverse conversion.
- * @return The current adapter, can be used to chain invocations.
- */
- <F, T> Adapter rule(FunctionThrowsException<F,T> toFun, FunctionThrowsException<T,F> fromFun);
-
- <F, T> Adapter rule(TypeReference<F> fromRef, TypeReference<T> toRef, FunctionThrowsException<F, T> toFun,
- FunctionThrowsException<T, F> fromFun);
+ <F, T> Adapter rule(TypeReference<F> fromRef, TypeReference<T> toRef, SimpleConvertFunction<F, T> toFun,
+ SimpleConvertFunction<T, F> fromFun);
- <F, T> Adapter rule(Type fromType, Type toType, FunctionThrowsException<F, T> toFun, FunctionThrowsException<T, F> fromFun);
+ <F, T> Adapter rule(Type fromType, Type toType, SimpleConvertFunction<F, T> toFun, SimpleConvertFunction<T, F> fromFun);
}
Added: felix/trunk/converter/src/main/java/org/osgi/service/converter/ConvertFunction.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/ConvertFunction.java?rev=1757276&view=auto
==============================================================================
--- felix/trunk/converter/src/main/java/org/osgi/service/converter/ConvertFunction.java (added)
+++ felix/trunk/converter/src/main/java/org/osgi/service/converter/ConvertFunction.java Mon Aug 22 21:12:22 2016
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) OSGi Alliance (2016). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.converter;
+
+import java.lang.reflect.Type;
+
+/**
+ * An functional interface with a single convert method that is passed
+ * the original object and the target type.
+ * @param <F> Type parameter for the source object.
+ * @param <T> Type parameter for the converted object.
+ */
+@FunctionalInterface
+public interface ConvertFunction<F, T> {
+ /**
+ * Used to indicate that the default converter
+ * should be used for this value.
+ */
+ public static final Object CANNOT_CONVERT = new Object();
+
+ /**
+ * Convert the object into the target type.
+ * @param obj The object to be converted.
+ * @param targetType The target type.
+ * @return The converted object or {@link #CANNOT_CONVERT} to indicate
+ * that this converter cannot handle the conversion.
+ */
+ T convert(F obj, Type targetType) throws Exception;
+}
Modified: felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java?rev=1757276&r1=1757275&r2=1757276&view=diff
==============================================================================
--- felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java (original)
+++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java Mon Aug 22 21:12:22 2016
@@ -25,26 +25,71 @@ package org.osgi.service.converter;
* @Immutable
*/
public class Rule<F, T> {
- private final FunctionThrowsException<F,T> toFun;
- private final FunctionThrowsException<T,F> fromFun;
+ private final Class<F> fromClass;
+ private final Class<T> toClass;
+ private final ConvertFunction<T,F> fromFun;
+ private final ConvertFunction<F,T> toFun;
- /**
- * Specify the functions to do the conversions in both directions.
- *
- * @param to The function that performs the conversion.
- * @param from The function that performs the reverse conversion.
- */
- public Rule(FunctionThrowsException<F,T> to, FunctionThrowsException<T,F> from) {
+ /**
+ * Create a bidirectional rule.
+ * @param fromCls The class from which to convert. If {@link Object} is specified then this
+ * functions as a wildcard for generic conversions.
+ * @param toCls The class to which to convert. If {@link Object} is specified then this
+ * functions as a wildcard for generic conversions.
+ * @param to The conversion function for this rule.
+ * @param from The reverse conversion for this rule.
+ */
+ public Rule(Class<F> fromCls, Class<T> toCls, ConvertFunction<F,T> to, ConvertFunction<T,F> from) {
+ if (fromCls.equals(toCls)) {
+ if (fromCls.equals(Object.class)) {
+ if (from != null) {
+ throw new IllegalStateException("Can only register one catchall converter");
+ }
+ } else {
+ throw new IllegalStateException("Cannot register a convert to itself");
+ }
+ }
+
+ fromClass = fromCls;
+ toClass = toCls;
toFun = to;
fromFun = from;
}
+ /**
+ * Create a single-direction rule.
+ * @param fromCls The class from which to convert. If {@link Object} is specified then this
+ * functions as a wildcard for generic conversions.
+ * @param toCls The class to which to convert. If {@link Object} is specified then this
+ * functions as a wildcard for generic conversions.
+ * @param to The conversion function for this rule.
+ */
+ public Rule(Class<F> fromCls, Class<T> toCls, ConvertFunction<F,T> to) {
+ this(fromCls, toCls, to, null);
+ }
+
+ /**
+ * Accessor for the class to convert from.
+ * @return The class to convert from.
+ */
+ public Class<F> getFromClass() {
+ return fromClass;
+ }
+
+ /**
+ * Accessor for the class to convert to.
+ * @return The class to convert to.
+ */
+ public Class<T> getToClass() {
+ return toClass;
+ }
+
/**
* Obtain the conversion function.
*
* @return The conversion function.
*/
- public FunctionThrowsException<F,T> getToFunction() {
+ public ConvertFunction<F,T> getToFunction() {
return toFun;
}
@@ -53,7 +98,7 @@ public class Rule<F, T> {
*
* @return The reverse conversion function.
*/
- public FunctionThrowsException<T,F> getFromFunction() {
+ public ConvertFunction<T,F> getFromFunction() {
return fromFun;
}
}
Added: felix/trunk/converter/src/main/java/org/osgi/service/converter/SimpleConvertFunction.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/SimpleConvertFunction.java?rev=1757276&view=auto
==============================================================================
--- felix/trunk/converter/src/main/java/org/osgi/service/converter/SimpleConvertFunction.java (added)
+++ felix/trunk/converter/src/main/java/org/osgi/service/converter/SimpleConvertFunction.java Mon Aug 22 21:12:22 2016
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) OSGi Alliance (2016). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.converter;
+
+import java.lang.reflect.Type;
+
+/**
+ * An functional interface with a single convert method that is passed
+ * the object to convert. This interface implements the
+ * {@link ConvertFunction} interface via a default method and can be used
+ * in cases where the target type does not need to be passed to the
+ * {@link #convert} method, for example in Lambda expressions.
+ * @param <F> Type parameter for the source object.
+ * @param <T> Type parameter for the converted object.
+ */
+@FunctionalInterface
+public interface SimpleConvertFunction<F, T> extends ConvertFunction<F, T> {
+ default T convert(F t, Type type) throws Exception {
+ return convert(t);
+ }
+
+ /**
+ * Convert the object into the target type.
+ * @param obj The object to be converted.
+ * @return The converted object or {@link #CANNOT_CONVERT} to indicate
+ * that this converter cannot handle the conversion.
+ */
+ T convert(F t) throws Exception;
+}
Modified: felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/AdapterTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/AdapterTest.java?rev=1757276&r1=1757275&r2=1757276&view=diff
==============================================================================
--- felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/AdapterTest.java (original)
+++ felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/AdapterTest.java Mon Aug 22 21:12:22 2016
@@ -16,6 +16,14 @@
*/
package org.apache.felix.converter.impl;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -23,7 +31,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.osgi.service.converter.Adapter;
+import org.osgi.service.converter.ConvertFunction;
import org.osgi.service.converter.Converter;
+import org.osgi.service.converter.Rule;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -56,4 +66,59 @@ public class AdapterTest {
assertArrayEquals(new String [] {"A","B"},
ca.convert("A,B").to(String[].class));
}
+
+ @Test @SuppressWarnings("rawtypes")
+ public void testWildcardAdapter() {
+ ConvertFunction<List, Object> foo = new ConvertFunction<List, Object>() {
+ @Override
+ public Object convert(List t, Type type) throws Exception {
+ if (type instanceof Class) {
+ if (Number.class.isAssignableFrom((Class<?>) type))
+ return converter.convert(t.size()).to(type);
+ }
+ return ConvertFunction.CANNOT_CONVERT;
+ }
+ };
+
+ Rule<List, Object> r = new Rule<>(List.class, Object.class, foo);
+ Rule<Object, Object> allCatch = new Rule<>(Object.class, Object.class,
+ (v,t) -> v.toString());
+
+ Adapter ca = converter.getAdapter();
+ ca.rule(r);
+ ca.rule(allCatch);
+
+ assertEquals(3L, (long) ca.convert(Arrays.asList("a", "b", "c")).to(Long.class));
+ assertEquals(3, (long) ca.convert(Arrays.asList("a", "b", "c")).to(Integer.class));
+ assertEquals("[a, b, c]", ca.convert(Arrays.asList("a", "b", "c")).to(String.class));
+ }
+
+ @Test @SuppressWarnings("rawtypes")
+ public void testWildcardAdapter2() {
+ Map<Object, Object> snooped = new HashMap<>();
+ Rule<Object, ArrayList> r = new Rule<>(Object.class, ArrayList.class,
+ (v,t) -> null,
+ (v,t) -> "arraylist");
+ Rule<Object, List> r2 = new Rule<>(Object.class, List.class,
+ (v,t) -> null,
+ (v,t) -> "list");
+ Rule<Object, Object> allCatch = new Rule<>(Object.class, Object.class,
+ (v,t) -> {snooped.put(v,t); return ConvertFunction.CANNOT_CONVERT;}, null);
+
+ Adapter ca = converter.getAdapter();
+ ca.rule(r);
+ ca.rule(r2);
+ ca.rule(allCatch);
+
+ assertEquals("Precondition", 0, snooped.size());
+ assertEquals("arraylist", ca.convert(
+ new ArrayList<String>(Arrays.asList("a", "b", "c"))).to(String.class));
+ assertEquals("Precondition", 0, snooped.size());
+ assertEquals("list",ca.convert(
+ new LinkedList<String>(Arrays.asList("a", "b", "c"))).to(String.class));
+ assertEquals("Precondition", 0, snooped.size());
+ assertEquals("a", ca.convert(
+ new HashSet<String>(Arrays.asList("a", "b", "c"))).to(String.class));
+ assertEquals(String.class, snooped.get(new HashSet<String>(Arrays.asList("a", "b", "c"))));
+ }
}