Author: davidb Date: Fri Feb 17 16:21:15 2017 New Revision: 1783423 URL: http://svn.apache.org/viewvc?rev=1783423&view=rev Log: Felix Converter: allow the specification of an error handler with a converter Rule
Modified: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/InternalConverter.java felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java Modified: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java?rev=1783423&r1=1783422&r2=1783423&view=diff ============================================================================== --- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java (original) +++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java Fri Feb 17 16:21:15 2017 @@ -180,45 +180,59 @@ public class AdapterImpl implements Inte @SuppressWarnings("unchecked") @Override public Object to(Type type) { - if (object != null) { - Set<Type> fromTypes = assignableTypes(treatAsClass != null ? treatAsClass : object.getClass()); - Set<Type> toTypes = assignableTypes(type); + List<ConvertFunction<Object, Object>> converters = new ArrayList<>(); + try { + if (object != null) { + Set<Type> fromTypes = assignableTypes(treatAsClass != null ? treatAsClass : object.getClass()); + Set<Type> toTypes = assignableTypes(type); - List<ConvertFunction<Object, Object>> converters = new ArrayList<>(); - for (Type fromType : fromTypes) { + 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) { - // TODO what exactly do we use as order here? - converters.add(classRules.get(new TypePair(fromType, Util.primitiveToBoxed(toType)))); + converters.add(classRules.get(new TypePair(Object.class, 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; + for (Iterator<ConvertFunction<Object, Object>> it = converters.iterator(); it.hasNext(); ) { + // remove null values + ConvertFunction<Object, Object> func = it.next(); + if (func == null) + it.remove(); + } - try { - Object res = func.convert(object, type, root, keys.toArray()); - if (res != null) { - return res; + for (ConvertFunction<Object,Object> cf : converters) { + try { + Object res = cf.convert(object, type, root, keys.toArray()); + if (res != null) { + return res; + } + } catch (Exception ex) { + if (hasDefault) + // TODO override this too! + return defaultValue; + else + throw new ConversionException("Cannot convert " + object + " to " + type, ex); } - } catch (Exception ex) { - if (hasDefault) - return defaultValue; - else - throw new ConversionException("Cannot convert " + object + " to " + type, ex); } } - } - return del.to(type); + return del.to(type); + } catch (Exception ex) { + // do custom error handling + for (ConvertFunction<Object, Object> cf : converters) { + Object eh = cf.handleError(object, type, root, keys.toArray()); + if (eh != null) + return eh; + } + // No error handler, throw the original exception + throw ex; + } } @Override Modified: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java?rev=1783423&r1=1783422&r2=1783423&view=diff ============================================================================== --- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java (original) +++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java Fri Feb 17 16:21:15 2017 @@ -481,14 +481,19 @@ public class ConvertingImpl implements C Object val = m.get(propName); // If no value is available take the default if specified + boolean defaultUsed = false; // TODO maybe we don't need this... if (val == null) { if (targetCls.isAnnotation()) { val = method.getDefaultValue(); + defaultUsed = true; } - if (val == null && args != null && args.length == 1) + if (val == null && args != null && args.length == 1) { val = args[0]; + defaultUsed = true; + } } + return converter.convert(val).to(targetType); } }); Modified: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/InternalConverter.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/InternalConverter.java?rev=1783423&r1=1783422&r2=1783423&view=diff ============================================================================== --- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/InternalConverter.java (original) +++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/InternalConverter.java Fri Feb 17 16:21:15 2017 @@ -19,5 +19,5 @@ package org.apache.felix.converter.impl; import org.osgi.util.converter.Converter; public interface InternalConverter extends Converter { - public InternalConverting convert(Object obj); + InternalConverting convert(Object obj); } Modified: felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java?rev=1783423&r1=1783422&r2=1783423&view=diff ============================================================================== --- felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java (original) +++ felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java Fri Feb 17 16:21:15 2017 @@ -56,8 +56,10 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.osgi.util.converter.ConversionException; +import org.osgi.util.converter.ConvertFunction; import org.osgi.util.converter.Converter; import org.osgi.util.converter.ConverterBuilder; +import org.osgi.util.converter.Rule; import org.osgi.util.converter.StandardConverter; import org.osgi.util.converter.TypeReference; @@ -342,9 +344,74 @@ public class ConverterTest { int[] ia = {1, 2}; assertEquals("1,2", adapted.convert(ia).to(String.class)); assertArrayEquals(ia, adapted.convert("1,2").to(int[].class)); + } + + @Test + public void testCustomErrorHandling() { + ConvertFunction<String,Integer> func = new ConvertFunction<String,Integer>() { + @Override + public Integer convert(String obj, Type targetType, Object root, Object[] keyPath) throws Exception { + return null; + } + + @Override + public Integer handleError(String obj, Type targetType, Object root, Object[] keyPath) { + if ("hello".equals(obj)) { + return -1; + } + return null; + } + }; + ConverterBuilder cb = converter.newConverterBuilder(); + cb.rule(new Rule<>(String.class, Integer.class, func)); + Converter adapted = cb.build(); + + assertEquals(new Integer(12), adapted.convert("12").to(Integer.class)); + assertEquals(new Integer(-1), adapted.convert("hello").to(Integer.class)); + + // This is with the non-adapted converter + try { + converter.convert("hello").to(Integer.class); + fail("Should have thrown a Conversion Exception when converting 'hello' to a number"); + } catch (ConversionException ce) { + // good + } } + /* + @Test + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testCustomDefaultHandling() { + // TODO re-enable + Map<String, String> m = new HashMap<>(); +// MyAnnotation ann = converter.convert(m).to(MyAnnotation.class); +// assertEquals(17, ann.value()); + + // Now register a custom default handler + ConvertFunction<Map,MyAnnotation> func = new ConvertFunction<Map,MyAnnotation>() { + @Override + public MyAnnotation convert(Map obj, Type targetType, Object root, Object[] keyPath) throws Exception { + return null; + } + + @Override + public MyAnnotation handleDefault(Map obj, Class<?> targetType, Object root, Object[] keyPath) { + return 42; + } + }; + + ConverterBuilder cb = converter.newConverterBuilder(); + Rule r = new Rule(Map.class, MyAnnotation.class, func); + cb.rule(r); + Converter adapted = cb.build(); + + MyAnnotation ann2 = adapted.convert(m).to(MyAnnotation.class); + assertEquals("The default value from the annotation should have been overridden by the default handler", + 42, ann2.value()); + } + */ + @Test public void testUUIDConversion() { UUID uuid = UUID.randomUUID(); @@ -889,5 +956,9 @@ public class ConverterTest { } } + static @interface MyAnnotation { + int value() default 17; + } + enum SomeEnum { VALUE, GETVALUE }; }