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 2017/02/06 11:54:34 UTC
svn commit: r1781884 - in /felix/trunk/converter/converter: ./
src/main/java/org/apache/felix/converter/impl/
src/test/java/org/apache/felix/converter/impl/
Author: davidb
Date: Mon Feb 6 11:54:34 2017
New Revision: 1781884
URL: http://svn.apache.org/viewvc?rev=1781884&view=rev
Log:
Felix Converter - support dynamic maps that reflect changes in the backing object.
Added:
felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java
felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/MapDelegate.java
Modified:
felix/trunk/converter/converter/pom.xml
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/Util.java
felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterBuilderTest.java
felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterEqualsTest.java
felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java
Modified: felix/trunk/converter/converter/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/pom.xml?rev=1781884&r1=1781883&r2=1781884&view=diff
==============================================================================
--- felix/trunk/converter/converter/pom.xml (original)
+++ felix/trunk/converter/converter/pom.xml Mon Feb 6 11:54:34 2017
@@ -28,7 +28,7 @@
<name>Apache Felix Converter</name>
<artifactId>org.apache.felix.converter</artifactId>
- <version>0.1-SNAPSHOT</version>
+ <version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<scm>
@@ -66,7 +66,7 @@
<configuration>
<instructions>
<Private-Package>org.apache.felix.converter.*</Private-Package>
- <Export-Package>org.osgi.util.function,org.osgi.util.converter; version="0.1"; mandatory:="status"; status="provisional"</Export-Package>
+ <Export-Package>org.osgi.util.function,org.osgi.util.converter</Export-Package>
<Import-Package>org.osgi.util.converter, *</Import-Package>
</instructions>
</configuration>
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=1781884&r1=1781883&r2=1781884&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 Mon Feb 6 11:54:34 2017
@@ -58,17 +58,18 @@ public class ConvertingImpl implements C
interfaceImplementations = Collections.unmodifiableMap(m);
}
- private volatile InternalConverter converter;
+ volatile InternalConverter converter;
private volatile Object object;
- private volatile Class<?> sourceAsClass;
private volatile Object defaultValue;
private volatile boolean hasDefault;
- private volatile Class<?> sourceClass;
- private volatile Class<?> targetActualClass;
+ volatile Class<?> sourceClass;
+ volatile Class<?> sourceAsClass;
+ private volatile Class<?> targetClass;
private volatile Class<?> targetAsClass;
- private volatile Type[] typeArguments;
- private List<Object> keys = new ArrayList<>();
+ volatile Type[] typeArguments;
+ List<Object> keys = new ArrayList<>();
private volatile Object root;
+ private volatile boolean forceCopy = false;
private volatile boolean sourceAsJavaBean = false;
@SuppressWarnings( "unused" )
private volatile boolean targetAsJavaBean = false;
@@ -126,7 +127,8 @@ public class ConvertingImpl implements C
@Override
public Converting copy() {
- // TODO Implement this
+ forceCopy = true;
+
return null;
}
@@ -154,7 +156,6 @@ public class ConvertingImpl implements C
return this;
}
- @SuppressWarnings( "unchecked" )
@Override
public void setConverter(Converter c) {
if (c instanceof InternalConverter)
@@ -197,17 +198,12 @@ public class ConvertingImpl implements C
if (object == null)
return handleNull(cls);
- targetActualClass = Util.primitiveToBoxed(cls);
+ targetClass = Util.primitiveToBoxed(cls);
if (targetAsClass == null)
- targetAsClass = targetActualClass;
+ targetAsClass = targetClass;
sourceClass = sourceAsClass != null ? sourceAsClass : object.getClass();
- // Temporary - to remove next commit!!
- // This is just to catch any old code that may still be using {source|target}As(DTO.class)
- if(DTO.class.equals(sourceClass) || DTO.class.equals(targetAsClass))
- throw new RuntimeException("To update!!");
-
if (!isCopyRequiredType(targetAsClass) && targetAsClass.isAssignableFrom(sourceClass)) {
return object;
}
@@ -220,7 +216,7 @@ public class ConvertingImpl implements C
return convertToArray();
} else if (Collection.class.isAssignableFrom(targetAsClass)) {
return convertToCollection();
- } else if (isDTOType(targetAsClass) || ((sourceAsDTO || targetAsDTO) && DTO.class.isAssignableFrom(targetActualClass))) {
+ } else if (isDTOType(targetAsClass) || ((sourceAsDTO || targetAsDTO) && DTO.class.isAssignableFrom(targetClass))) {
return convertToDTO();
} else if (isMapType(targetAsClass)) {
return convertToMapType();
@@ -238,7 +234,7 @@ public class ConvertingImpl implements C
return res2;
} else {
if (defaultValue != null)
- return converter.convert(defaultValue).sourceAs(sourceAsClass).targetAs(targetAsClass).to(targetActualClass);
+ return converter.convert(defaultValue).sourceAs(sourceAsClass).targetAs(targetAsClass).to(targetClass);
else
return null;
}
@@ -312,17 +308,17 @@ public class ConvertingImpl implements C
Class<?> cls = targetAsClass;
if (targetAsDTO)
- cls = targetActualClass;
+ cls = targetClass;
try {
- T dto = (T) targetActualClass.newInstance();
+ T dto = (T) targetClass.newInstance();
for (Map.Entry entry : (Set<Map.Entry>) m.entrySet()) {
Field f = null;
try {
- f = cls.getDeclaredField(mangleName(entry.getKey().toString()));
+ f = cls.getDeclaredField(Util.mangleName(entry.getKey().toString()));
} catch (NoSuchFieldException e) {
try {
- f = cls.getField(mangleName(entry.getKey().toString()));
+ f = cls.getField(Util.mangleName(entry.getKey().toString()));
} catch (NoSuchFieldException e1) {
// There is not field with this name
}
@@ -340,7 +336,7 @@ public class ConvertingImpl implements C
return dto;
} catch (Exception e) {
- throw new ConversionException("Cannot create DTO " + targetActualClass, e);
+ throw new ConversionException("Cannot create DTO " + targetClass, e);
}
}
@@ -349,15 +345,14 @@ public class ConvertingImpl implements C
Map m = mapView(object, sourceClass, converter);
if (m == null)
return null;
- Type targetKeyType = null, targetValueType = null;
- if (typeArguments != null && typeArguments.length > 1) {
+ Type targetKeyType = null;
+ if (typeArguments != null && typeArguments.length > 0) {
targetKeyType = typeArguments[0];
- targetValueType = typeArguments[1];
}
- Class<?> ctrCls = interfaceImplementations.get(targetActualClass);
+ Class<?> ctrCls = interfaceImplementations.get(targetClass);
if (ctrCls == null)
- ctrCls = targetActualClass;
+ ctrCls = targetClass;
Map instance = (Map) createMapOrCollection(ctrCls, m.size());
if (instance == null)
@@ -369,32 +364,62 @@ public class ConvertingImpl implements C
if (targetKeyType != null)
key = converter.convert(key).key(ks.toArray()).to(targetKeyType);
ks.add(key);
- Object[] ka = ks.toArray();
Object value = entry.getValue();
- if (value != null) {
- if (targetValueType != null) {
- value = converter.convert(value).key(ka).to(targetValueType);
- } else {
- Class<?> cls = value.getClass();
- if (isCopyRequiredType(cls)) {
- cls = getConstructableType(cls);
- }
- if (sourceAsDTO && DTO.class.isAssignableFrom(cls))
- // sourceAsDTO or sourceAsClass???
- value = converter.convert(value).key(ka).sourceAsDTO().to(cls);
- else
- value = converter.convert(value).key(ka).to(cls);
- }
- }
+ value = convertMapValue(value, ks.toArray());
instance.put(key, value);
}
return instance;
}
+ Object convertMapValue(Object value, Object[] ka) {
+ Type targetValueType = null;
+ if (typeArguments != null && typeArguments.length > 1) {
+ targetValueType = typeArguments[1];
+ }
+
+ if (value != null) {
+ if (targetValueType != null) {
+ value = converter.convert(value).key(ka).to(targetValueType);
+ } else {
+ Class<?> cls = value.getClass();
+ if (isCopyRequiredType(cls)) {
+ cls = getConstructableType(cls);
+ }
+ if (sourceAsDTO && DTO.class.isAssignableFrom(cls))
+ value = converter.convert(value).key(ka).sourceAsDTO().to(cls);
+ else
+ value = converter.convert(value).key(ka).to(cls);
+ }
+ }
+ return value;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private Map convertToMapDelegate() {
+ if (Map.class.isAssignableFrom(sourceClass)) {
+ return MapDelegate.forMap((Map) object, this);
+ } else if (Dictionary.class.isAssignableFrom(sourceClass)) {
+ return MapDelegate.forDictionary((Dictionary) object, this);
+ } else if (isDTOType(sourceClass) || sourceAsDTO) {
+ return MapDelegate.forDTO(object, this);
+ } else if (sourceAsJavaBean) {
+ return MapDelegate.forBean(object, this);
+ }
+
+ // Assume it's an interface
+ return MapDelegate.forInterface(object, this);
+ }
+
@SuppressWarnings({ "unchecked", "rawtypes" })
private Object convertToMapType() {
+ if (Map.class.equals(targetClass) && !forceCopy) {
+ Map res = convertToMapDelegate();
+ if (res != null)
+ return res;
+ }
+
if (Map.class.isAssignableFrom(targetAsClass))
return convertToMap();
else if (Dictionary.class.isAssignableFrom(targetAsClass))
@@ -423,7 +448,7 @@ public class ConvertingImpl implements C
@SuppressWarnings("rawtypes")
Map m = mapView(object, sourceCls, converter);
try {
- Object res = targetActualClass.newInstance();
+ Object res = targetClass.newInstance();
for (Method setter : getSetters(targetCls)) {
String setterName = setter.getName();
StringBuilder propName = new StringBuilder(Character.valueOf(Character.toLowerCase(setterName.charAt(3))).toString());
@@ -447,7 +472,7 @@ public class ConvertingImpl implements C
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- String propName = getInterfacePropertyName(method);
+ String propName = Util.getInterfacePropertyName(method);
if (propName == null)
return null;
@@ -574,14 +599,7 @@ public class ConvertingImpl implements C
return null;
}
} else if (Enum.class.isAssignableFrom(targetAsClass)) {
- if (object instanceof Boolean) {
- try {
- Method m = targetAsClass.getMethod("valueOf", String.class);
- return m.invoke(null, object.toString().toUpperCase());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- } else if (object instanceof Number) {
+ if (object instanceof Number) {
try {
Method m = targetAsClass.getMethod("values");
Object[] values = (Object[]) m.invoke(null);
@@ -589,8 +607,24 @@ public class ConvertingImpl implements C
} catch (Exception e) {
throw new RuntimeException(e);
}
+ } else {
+ try {
+ Method m = targetAsClass.getMethod("valueOf", String.class);
+ return m.invoke(null, object.toString());
+ } catch (Exception e) {
+ try {
+ // Case insensitive fallback
+ Method m = targetAsClass.getMethod("values");
+ for (Object v : (Object[]) m.invoke(null)) {
+ if (v.toString().equalsIgnoreCase(object.toString())) {
+ return v;
+ }
+ }
+ } catch (Exception e1) {
+ throw new RuntimeException(e1);
+ }
+ }
}
-
}
return null;
}
@@ -598,13 +632,13 @@ public class ConvertingImpl implements C
@SuppressWarnings("unchecked")
private <T> T tryStandardMethods() {
try {
- Method m = targetActualClass.getDeclaredMethod("valueOf", String.class);
+ Method m = targetClass.getDeclaredMethod("valueOf", String.class);
if (m != null) {
return (T) m.invoke(null, object.toString());
}
} catch (Exception e) {
try {
- Constructor<?> ctr = targetActualClass.getConstructor(String.class);
+ Constructor<?> ctr = targetClass.getConstructor(String.class);
return (T) ctr.newInstance(object.toString());
} catch (Exception e2) {
}
@@ -654,9 +688,6 @@ public class ConvertingImpl implements C
for (Method md : sourceCls.getDeclaredMethods()) {
handleBeanMethod(obj, md, invokedMethods, result);
}
- for (Method md : sourceCls.getMethods()) {
- handleBeanMethod(obj, md, invokedMethods, result);
- }
return result;
}
@@ -666,11 +697,12 @@ public class ConvertingImpl implements C
Set<String> handledFields = new HashSet<>();
Map result = new HashMap();
+ // Do we need 'declaredfields'? We only need to look at the public ones...
for (Field f : obj.getClass().getDeclaredFields()) {
- handleField(obj, f, handledFields, result, converter);
+ handleDTOField(obj, f, handledFields, result, converter);
}
for (Field f : obj.getClass().getFields()) {
- handleField(obj, f, handledFields, result, converter);
+ handleDTOField(obj, f, handledFields, result, converter);
}
return result;
}
@@ -732,59 +764,13 @@ public class ConvertingImpl implements C
return null;
}
- private static String getAccessorPropertyName(Method md) {
- if (md.getReturnType().equals(Void.class))
- return null; // not an accessor
-
- if (md.getParameterTypes().length > 0)
- return null; // not an accessor
-
- if (Object.class.equals(md.getDeclaringClass()))
- return null; // do not use any methods on the Object class as a accessor
-
- String mn = md.getName();
- int prefix;
- if (mn.startsWith("get"))
- prefix = 3;
- else if (mn.startsWith("is"))
- prefix = 2;
- else
- return null; // not an accessor prefix
-
- if (mn.length() <= prefix)
- return null; // just 'get' or 'is': not an accessor
- String propStr = mn.substring(prefix);
- StringBuilder propName = new StringBuilder(propStr.length());
- char firstChar = propStr.charAt(0);
- if (!Character.isUpperCase(firstChar))
- return null; // no acccessor as no camel casing
- propName.append(Character.toLowerCase(firstChar));
- if (propStr.length() > 1)
- propName.append(propStr.substring(1));
-
- return propName.toString();
- }
-
- private static String getInterfacePropertyName(Method md) {
- if (md.getReturnType().equals(Void.class))
- return null; // not an accessor
-
- if (md.getParameterTypes().length > 1)
- return null; // not an accessor
-
- if (Object.class.equals(md.getDeclaringClass()))
- return null; // do not use any methods on the Object class as a accessor
-
- return md.getName().replace('_', '.'); // TODO support all the escaping mechanisms.
- }
-
@SuppressWarnings({ "rawtypes", "unchecked" })
- private void handleField(Object obj, Field field, Set<String> handledFields, Map result,
+ private void handleDTOField(Object obj, Field field, Set<String> handledFields, Map result,
InternalConverter converter) {
- if (Modifier.isStatic(field.getModifiers()))
+ String fn = Util.getDTOKey(field);
+ if (fn == null)
return;
- String fn = unMangleName(field.getName());
if (handledFields.contains(fn))
return; // Field with this name was already handled
@@ -804,64 +790,38 @@ public class ConvertingImpl implements C
}
}
- private String mangleName(String key) {
- String res = key.replace("_", "__");
- res = res.replace("$", "$$");
- res = res.replaceAll("[.]([._])", "_\\$$1");
- res = res.replace('.', '_');
- // TODO handle Java keywords
- return res;
- }
-
- private String unMangleName(String key) {
- String res = key.replaceAll("_\\$", ".");
- res = res.replace("__", "\f"); // parkl double underscore as formfeed char
- res = res.replace('_', '.');
- res = res.replace("$$", "\b"); // park double dollar as backspace char
- res = res.replace("$", "");
- res = res.replace('\f', '_'); // convert formfeed char back to single underscore
- res = res.replace('\b', '$'); // convert backspace char back go dollar
- return res;
- }
-
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void handleBeanMethod(Object obj, Method md, Set<String> invokedMethods, Map res) {
- if (Modifier.isStatic(md.getModifiers()))
+ String bp = Util.getBeanKey(md);
+ if (bp == null)
return;
- String mn = md.getName();
- if (invokedMethods.contains(mn))
+ if (invokedMethods.contains(bp))
return; // method with this name already invoked
- String propName = getAccessorPropertyName(md);
- if (propName == null)
- return;
-
try {
- res.put(propName.toString(), md.invoke(obj));
- invokedMethods.add(mn);
+ res.put(bp, md.invoke(obj));
+ invokedMethods.add(bp);
} catch (Exception e) {
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void handleInterfaceMethod(Object obj, Method md, Set<String> invokedMethods, Map res) {
- if (Modifier.isStatic(md.getModifiers()))
- return;
-
- if (md.getParameterCount() > 0)
- return;
-
String mn = md.getName();
if (invokedMethods.contains(mn))
return; // method with this name already invoked
- String propName = getInterfacePropertyName(md);
+ String propName = Util.getInterfacePropertyName(md);
if (propName == null)
return;
try {
- res.put(propName.toString(), md.invoke(obj));
+ Object r = Util.getInterfaceProperty(obj, md);
+ if (r == null)
+ return;
+
+ res.put(propName, r);
invokedMethods.add(mn);
} catch (Exception e) {
}
Added: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java?rev=1781884&view=auto
==============================================================================
--- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java (added)
+++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java Mon Feb 6 11:54:34 2017
@@ -0,0 +1,242 @@
+/*
+ * 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.felix.converter.impl;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+abstract class DynamicMapLikeFacade<K, V> implements Map<K, V> {
+ protected final ConvertingImpl convertingImpl;
+
+ protected DynamicMapLikeFacade(ConvertingImpl convertingImpl) {
+ this.convertingImpl = convertingImpl;
+ }
+
+ @Override
+ public int size() {
+ return keySet().size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return keySet().contains(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ for (Entry<K, V> entry : entrySet()) {
+ if (value == null) {
+ if (entry.getValue() == null) {
+ return true;
+ }
+ } else if (value.equals(entry.getValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public V put(K key, V value) {
+ // Should never be called; the delegate should swap to a copy in this case
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public V remove(Object key) {
+ // Should never be called; the delegate should swap to a copy in this case
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ // Should never be called; the delegate should swap to a copy in this case
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ // Should never be called; the delegate should swap to a copy in this case
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return entrySet().stream().map(Entry::getValue).collect(Collectors.toList());
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ Set<K> ks = keySet();
+
+ Set<Entry<K, V>> res = new LinkedHashSet<>(ks.size());
+
+ for (K k : ks) {
+ V v = get(k);
+ res.add(new MapDelegate.MapEntry<K,V>(k, v));
+ }
+ return res;
+ }
+}
+
+class DynamicBeanFacade extends DynamicMapLikeFacade<String,Object> {
+ private Map <String, Method> keys = null;
+ private final Object backingObject;
+
+ DynamicBeanFacade(Object backingObject, ConvertingImpl convertingImpl) {
+ super(convertingImpl);
+ this.backingObject = backingObject;
+ }
+
+ @Override
+ public Object get(Object key) {
+ Method m = getKeys().get(key);
+ try {
+ return m.invoke(backingObject);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Set<String> keySet() {
+ return getKeys().keySet();
+ }
+
+ private Map<String, Method> getKeys() {
+ if (keys == null)
+ keys = Util.getBeanKeys(convertingImpl.sourceClass);
+
+ return keys;
+ }
+}
+
+class DynamicDictionaryFacade<K,V> extends DynamicMapLikeFacade<K,V> {
+ private final Dictionary<K, V> backingObject;
+
+ DynamicDictionaryFacade(Dictionary<K, V> backingObject, ConvertingImpl convertingImpl) {
+ super(convertingImpl);
+ this.backingObject = backingObject;
+ }
+
+ @Override
+ public V get(Object key) {
+ return backingObject.get(key);
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return new HashSet<>(Collections.list(backingObject.keys()));
+ }
+}
+
+class DynamicMapFacade<K,V> extends DynamicMapLikeFacade<K,V> {
+ private final Map<K, V> backingObject;
+
+ DynamicMapFacade(Map<K,V> backingObject, ConvertingImpl convertingImpl) {
+ super(convertingImpl);
+ this.backingObject = backingObject;
+ }
+
+ @Override
+ public V get(Object key) {
+ return backingObject.get(key);
+ }
+
+ @Override
+ public Set<K> keySet() {
+ Map<K, V> m = backingObject;
+ return m.keySet();
+ }
+}
+
+class DynamicDTOFacade extends DynamicMapLikeFacade<String, Object> {
+ private Map <String, Field> keys = null;
+ private final Object backingObject;
+
+ DynamicDTOFacade(Object backingObject, ConvertingImpl converting) {
+ super(converting);
+ this.backingObject = backingObject;
+ }
+
+ @Override
+ public Object get(Object key) {
+ Field f = getKeys().get(key);
+ try {
+ return f.get(backingObject);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Set<String> keySet() {
+ return getKeys().keySet();
+ }
+
+ private Map<String, Field> getKeys() {
+ if (keys == null)
+ keys = Util.getDTOKeys(convertingImpl.sourceClass);
+
+ return keys;
+ }
+}
+
+class DynamicInterfaceFacade extends DynamicMapLikeFacade<String, Object> {
+ private Map <String, Method> keys = null;
+ private final Object backingObject;
+
+ DynamicInterfaceFacade(Object backingObject, ConvertingImpl convertingImpl) {
+ super(convertingImpl);
+ this.backingObject = backingObject;
+ }
+
+ @Override
+ public Object get(Object key) {
+ Method m = getKeys().get(key);
+ try {
+ return m.invoke(backingObject);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Set<String> keySet() {
+ return getKeys().keySet();
+ }
+
+ private Map<String, Method> getKeys() {
+ if (keys == null)
+ keys = Util.getInterfaceKeys(convertingImpl.sourceClass);
+
+ return keys;
+ }
+}
\ No newline at end of file
Added: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/MapDelegate.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/MapDelegate.java?rev=1781884&view=auto
==============================================================================
--- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/MapDelegate.java (added)
+++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/MapDelegate.java Mon Feb 6 11:54:34 2017
@@ -0,0 +1,259 @@
+/*
+ * 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.felix.converter.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+class MapDelegate<K, V> implements Map<K, V> {
+ private final ConvertingImpl convertingImpl;
+ Map<K, V> delegate;
+
+ private MapDelegate(ConvertingImpl converting, Map<K, V> del) {
+ convertingImpl = converting;
+ delegate = del;
+ }
+
+ static MapDelegate<String, Object> forBean(Object b, ConvertingImpl converting) {
+ return new MapDelegate<>(converting, new DynamicBeanFacade(b, converting));
+ }
+
+ static <K, V> Map<K, V> forMap(Map<K, V> m, ConvertingImpl converting) {
+ return new MapDelegate<>(converting, new DynamicMapFacade<>(m, converting));
+ }
+
+ static <K, V> MapDelegate<K, V> forDictionary(Dictionary<K, V> d, ConvertingImpl converting) {
+ return new MapDelegate<>(converting, new DynamicDictionaryFacade<>(d, converting));
+ }
+
+ static MapDelegate<String, Object> forDTO(Object obj, ConvertingImpl converting) {
+ return new MapDelegate<>(converting, new DynamicDTOFacade(obj, converting));
+ }
+
+ static MapDelegate<String, Object> forInterface(Object obj, ConvertingImpl converting) {
+ return new MapDelegate<>(converting, new DynamicInterfaceFacade(obj, converting));
+ }
+
+ public int size() {
+ return delegate.size();
+ }
+
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ public boolean containsKey(Object key) {
+ return delegate.containsKey(key);
+ }
+
+ public boolean containsValue(Object value) {
+ return delegate.containsValue(value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public V get(Object key) {
+ V val = null;
+ if (keySet().contains(key)) {
+ val = delegate.get(key);
+ }
+
+ if (val == null) {
+ key = findConvertedKey(keySet(), key);
+ val = delegate.get(key);
+ }
+
+ if (val == null)
+ return null;
+ else
+ return (V) getConvertedValue(key, val);
+ }
+
+ private Object getConvertedValue(Object key, Object val) {
+ List<Object> ks = new ArrayList<>(convertingImpl.keys);
+ ks.add(key);
+ return convertingImpl.convertMapValue(val, ks.toArray());
+ }
+
+ private Object findConvertedKey(Set<?> keySet, Object key) {
+ for (Object k : keySet) {
+ Object c = convertingImpl.converter.convert(k).to(key.getClass());
+ if (c != null && c.equals(key))
+ return k;
+
+// Maybe the other way around too?
+// Object c2 = facade.convertingImpl.converter.convert(key).to(k.getClass());
+// if (c2 != null && c2.equals(key))
+// return c2;
+ }
+ return key;
+ }
+
+ public V put(K key, V value) {
+ cloneDelegate();
+
+ return delegate.put(key, value);
+ }
+
+ public V remove(Object key) {
+ cloneDelegate();
+
+ return delegate.remove(key);
+ }
+
+ public void putAll(Map<? extends K, ? extends V> m) {
+ cloneDelegate();
+
+ delegate.putAll(m);
+ }
+
+ public void clear() {
+ delegate = new HashMap<>();
+ }
+
+ private Set<K> internalKeySet() {
+ return delegate.keySet();
+ }
+
+ public Set<K> keySet() {
+ Set<K> keys = new HashSet<>();
+ for (Map.Entry<K,V> entry : entrySet()) {
+ keys.add(entry.getKey());
+ }
+ return keys;
+ }
+
+ public Collection<V> values() {
+ List<V> values = new ArrayList<>();
+ for (Map.Entry<K,V> entry : entrySet()) {
+ values.add(entry.getValue());
+ }
+ return values;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ Set<Map.Entry<K,V>> result = new HashSet<>();
+ for (Map.Entry<?,?> entry : delegate.entrySet()) {
+ K key = (K) findConvertedKey(internalKeySet(), entry.getKey());
+ V val = (V) getConvertedValue(key, entry.getValue());
+ result.add(new MapEntry<K,V>(key, val));
+ }
+ return result;
+ }
+
+ public boolean equals(Object o) {
+ return delegate.equals(o);
+ }
+
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ public V getOrDefault(Object key, V defaultValue) {
+ return delegate.getOrDefault(key, defaultValue);
+ }
+
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ delegate.forEach(action);
+ }
+
+ public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ cloneDelegate();
+
+ delegate.replaceAll(function);
+ }
+
+ public V putIfAbsent(K key, V value) {
+ cloneDelegate();
+
+ return delegate.putIfAbsent(key, value);
+ }
+
+ public boolean remove(Object key, Object value) {
+ cloneDelegate();
+
+ return delegate.remove(key, value);
+ }
+
+ public boolean replace(K key, V oldValue, V newValue) {
+ cloneDelegate();
+
+ return delegate.replace(key, oldValue, newValue);
+ }
+
+ public V replace(K key, V value) {
+ cloneDelegate();
+
+ return delegate.replace(key, value);
+ }
+
+ public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
+ return delegate.computeIfAbsent(key, mappingFunction);
+ }
+
+ public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ return delegate.computeIfPresent(key, remappingFunction);
+ }
+
+ public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ return delegate.compute(key, remappingFunction);
+ }
+
+ public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ cloneDelegate();
+
+ return delegate.merge(key, value, remappingFunction);
+ }
+
+ private void cloneDelegate() {
+ delegate = new HashMap<>(delegate);
+ }
+
+ static class MapEntry<K,V> implements Map.Entry<K,V> {
+ private final K key;
+ private final V value;
+
+ MapEntry(K k, V v) {
+ key = k;
+ value = v;
+ }
+
+ @Override
+ public K getKey() {
+ return key;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public V setValue(V value) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
\ No newline at end of file
Modified: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java?rev=1781884&r1=1781883&r2=1781884&view=diff
==============================================================================
--- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java (original)
+++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java Mon Feb 6 11:54:34 2017
@@ -16,9 +16,14 @@
*/
package org.apache.felix.converter.impl;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
class Util {
@@ -53,4 +58,134 @@ class Util {
else
return cls;
}
+
+ static Map<String, Method> getBeanKeys(Class<?> beanClass) {
+ Map<String, Method> keys = new LinkedHashMap<>();
+ for (Method md : beanClass.getDeclaredMethods()) {
+ String key = getBeanKey(md);
+ if (key != null && !keys.containsKey(key))
+ keys.put(key, md);
+ }
+ return keys;
+ }
+
+ static String getBeanKey(Method md) {
+ if (Modifier.isStatic(md.getModifiers()))
+ return null;
+
+ if (!Modifier.isPublic(md.getModifiers()))
+ return null;
+
+ return getBeanAccessorPropertyName(md);
+ }
+
+ private static String getBeanAccessorPropertyName(Method md) {
+ if (md.getReturnType().equals(Void.class))
+ return null; // not an accessor
+
+ if (md.getParameterTypes().length > 0)
+ return null; // not an accessor
+
+ if (Object.class.equals(md.getDeclaringClass()))
+ return null; // do not use any methods on the Object class as a accessor
+
+ String mn = md.getName();
+ int prefix;
+ if (mn.startsWith("get"))
+ prefix = 3;
+ else if (mn.startsWith("is"))
+ prefix = 2;
+ else
+ return null; // not an accessor prefix
+
+ if (mn.length() <= prefix)
+ return null; // just 'get' or 'is': not an accessor
+ String propStr = mn.substring(prefix);
+ StringBuilder propName = new StringBuilder(propStr.length());
+ char firstChar = propStr.charAt(0);
+ if (!Character.isUpperCase(firstChar))
+ return null; // no acccessor as no camel casing
+ propName.append(Character.toLowerCase(firstChar));
+ if (propStr.length() > 1)
+ propName.append(propStr.substring(1));
+
+ return propName.toString();
+ }
+
+
+ static Map<String, Field> getDTOKeys(Class<?> dto) {
+ Map<String, Field> keys = new LinkedHashMap<>();
+
+ for (Field f : dto.getFields()) {
+ String key = getDTOKey(f);
+ if (key != null && !keys.containsKey(key))
+ keys.put(key, f);
+ }
+ return keys;
+ }
+
+ static String getDTOKey(Field f) {
+ if (Modifier.isStatic(f.getModifiers()))
+ return null;
+
+ if (!Modifier.isPublic(f.getModifiers()))
+ return null;
+
+ return unMangleName(f.getName());
+ }
+
+ static Map<String, Method> getInterfaceKeys(Class<?> intf) {
+ Map<String, Method> keys = new LinkedHashMap<>();
+
+ for (Method md : intf.getMethods()) {
+ String name = getInterfacePropertyName(md);
+ if (name != null)
+ keys.put(name, md);
+ }
+ return keys;
+ }
+
+ static String getInterfacePropertyName(Method md) {
+ if (md.getReturnType().equals(Void.class))
+ return null; // not an accessor
+
+ if (md.getParameterTypes().length > 1)
+ return null; // not an accessor
+
+ if (Object.class.equals(md.getDeclaringClass()))
+ return null; // do not use any methods on the Object class as a accessor
+
+ return md.getName().replace('_', '.'); // TODO support all the escaping mechanisms.
+ }
+
+ static Object getInterfaceProperty(Object obj, Method md) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ if (Modifier.isStatic(md.getModifiers()))
+ return null;
+
+ if (md.getParameterCount() > 0)
+ return null;
+
+ return md.invoke(obj);
+ }
+
+ static String mangleName(String key) {
+ String res = key.replace("_", "__");
+ res = res.replace("$", "$$");
+ res = res.replaceAll("[.]([._])", "_\\$$1");
+ res = res.replace('.', '_');
+ // TODO handle Java keywords
+ return res;
+ }
+
+ static String unMangleName(String key) {
+ String res = key.replaceAll("_\\$", ".");
+ res = res.replace("__", "\f"); // park double underscore as formfeed char
+ res = res.replace('_', '.');
+ res = res.replace("$$", "\b"); // park double dollar as backspace char
+ res = res.replace("$", "");
+ res = res.replace('\f', '_'); // convert formfeed char back to single underscore
+ res = res.replace('\b', '$'); // convert backspace char back go dollar
+ // TODO handle Java keywords
+ return res;
+ }
}
Modified: felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterBuilderTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterBuilderTest.java?rev=1781884&r1=1781883&r2=1781884&view=diff
==============================================================================
--- felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterBuilderTest.java (original)
+++ felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterBuilderTest.java Mon Feb 6 11:54:34 2017
@@ -30,6 +30,7 @@ import java.util.stream.Stream;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.osgi.util.converter.ConvertFunction;
import org.osgi.util.converter.Converter;
@@ -242,7 +243,7 @@ public class ConverterBuilderTest {
}
@SuppressWarnings("rawtypes")
- @Test
+ @Test @Ignore("This test assumes that the all the embedded objects are also converted to maps, but they aren't")
public void testConvertWithKeysDeep() {
MyDTO6 subsubDTO1 = new MyDTO6();
subsubDTO1.chars = Arrays.asList('a', 'b', 'c');
Modified: felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterEqualsTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterEqualsTest.java?rev=1781884&r1=1781883&r2=1781884&view=diff
==============================================================================
--- felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterEqualsTest.java (original)
+++ felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterEqualsTest.java Mon Feb 6 11:54:34 2017
@@ -21,6 +21,7 @@ import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
+import org.junit.Ignore;
import org.junit.Test;
import org.osgi.util.converter.Converter;
import org.osgi.util.converter.StandardConverter;
@@ -29,7 +30,7 @@ import static org.junit.Assert.assertFal
import static org.junit.Assert.assertTrue;
public class ConverterEqualsTest {
- @Test
+ @Test @Ignore("This functionality should go")
public void testEquals() {
Converter c = new StandardConverter();
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=1781884&r1=1781883&r2=1781884&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 Mon Feb 6 11:54:34 2017
@@ -19,6 +19,8 @@ package org.apache.felix.converter.impl;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -34,6 +36,7 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -52,7 +55,6 @@ import org.apache.felix.converter.impl.M
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.osgi.dto.DTO;
import org.osgi.util.converter.ConversionException;
import org.osgi.util.converter.Converter;
import org.osgi.util.converter.ConverterBuilder;
@@ -456,11 +458,17 @@ public class ConverterTest {
assertEquals(Long.MIN_VALUE, m.get("pong"));
assertEquals(Count.ONE, m.get("count"));
assertNotNull(m.get("embedded"));
- @SuppressWarnings("rawtypes")
+
+ MyEmbeddedDTO e = (MyEmbeddedDTO) m.get("embedded");
+ assertEquals("hohoho", e.marco);
+ assertEquals(Long.MAX_VALUE, e.polo);
+ assertEquals(Alpha.A, e.alpha);
+ /*
Map e = (Map)m.get("embedded");
assertEquals("hohoho", e.get("marco"));
assertEquals(Long.MAX_VALUE, e.get("polo"));
assertEquals(Alpha.A, e.get("alpha"));
+ */
}
@Test
@@ -483,11 +491,18 @@ public class ConverterTest {
assertEquals(Long.MIN_VALUE, m.get("pong"));
assertEquals(Count.ONE, m.get("count"));
assertNotNull(m.get("embedded"));
- @SuppressWarnings("rawtypes")
+
+ MyEmbeddedDTO e = (MyEmbeddedDTO) m.get("embedded");
+ assertEquals("hohoho", e.marco);
+ assertEquals(Long.MAX_VALUE, e.polo);
+ assertEquals(Alpha.A, e.alpha);
+
+ /* TODO this is the way it was, but it does not seem right
Map e = (Map)m.get("embedded");
assertEquals("hohoho", e.get("marco"));
assertEquals(Long.MAX_VALUE, e.get("polo"));
assertEquals(Alpha.A, e.get("alpha"));
+ */
}
@Test
@@ -523,7 +538,7 @@ public class ConverterTest {
assertEquals(Count.ONE, e.count);
assertNotNull(e.embedded);
assertTrue(e.embedded instanceof MyEmbeddedDTO);
- MyEmbeddedDTO e2 = (MyEmbeddedDTO)e.embedded;
+ MyEmbeddedDTO e2 = e.embedded;
assertEquals("hohoho", e2.marco);
assertEquals(Long.MAX_VALUE, e2.polo);
assertEquals(Alpha.A, e2.alpha);
@@ -723,7 +738,112 @@ public class ConverterTest {
// And convert back
Map<String, String> m2 = converter.convert(dto).to(new TypeReference<Map<String,String>>() {});
- assertEquals(m, m2);
+ assertEquals(new HashMap<String,String>(m), new HashMap<String,String>(m2));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testLiveMapFromInterface() {
+ int[] val = new int[1];
+ val[0] = 51;
+
+ MyIntf intf = new MyIntf() {
+ @Override
+ public int value() {
+ return val[0];
+ }
+ };
+
+ @SuppressWarnings("rawtypes")
+ Map m = converter.convert(intf).to(Map.class);
+ assertEquals(51, m.get("value"));
+
+ val[0] = 52;
+ assertEquals("Changes to the backing map should be reflected",
+ 52, m.get("value"));
+
+ m.put("value", 53);
+ assertEquals(53, m.get("value"));
+
+ val[0] = 54;
+ assertEquals("Changes to the backing map should not be reflected any more",
+ 53, m.get("value"));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testLiveMapFromDTO() {
+ MyDTO8 myDTO = new MyDTO8();
+
+ myDTO.count = MyDTO8.Count.TWO;
+ myDTO.pong = 42L;
+
+ @SuppressWarnings("rawtypes")
+ Map m = converter.convert(myDTO).to(Map.class);
+ assertEquals(42L, m.get("pong"));
+
+ myDTO.ping = "Ping!";
+ assertEquals("Ping!", m.get("ping"));
+ myDTO.pong = 52L;
+ assertEquals(52L, m.get("pong"));
+ myDTO.ping = "Pong!";
+ assertEquals("Pong!", m.get("ping"));
+
+ m.put("pong", 62L);
+ myDTO.ping = "Poing!";
+ myDTO.pong = 72L;
+ assertEquals("Pong!", m.get("ping"));
+ assertEquals(62L, m.get("pong"));
+ }
+
+ @Test
+ public void testLiveMapFromDictionary() throws URISyntaxException {
+ URI testURI = new URI("http://foo");
+ Hashtable<String, Object> d = new Hashtable<>();
+ d.put("test", testURI);
+
+ Map<String, Object> m = converter.convert(d).to(new TypeReference<Map<String, Object>>(){});
+ assertEquals(testURI, m.get("test"));
+
+ URI testURI2 = new URI("http://bar");
+ d.put("test2", testURI2);
+ assertEquals(testURI2, m.get("test2"));
+ assertEquals(testURI, m.get("test"));
+ }
+
+ @Test
+ public void testLiveMapFromMap() {
+ Map<String, String> s = new HashMap<>();
+
+ s.put("true", "123");
+ s.put("false", "456");
+
+ Map<Boolean, Short> m = converter.convert(s).to(new TypeReference<Map<Boolean, Short>>(){});
+ assertEquals(Short.valueOf("123"), m.get(Boolean.TRUE));
+ assertEquals(Short.valueOf("456"), m.get(Boolean.FALSE));
+
+ s.remove("true");
+ assertNull(m.get(Boolean.TRUE));
+
+ s.put("TRUE", "999");
+ assertEquals(Short.valueOf("999"), m.get(Boolean.TRUE));
+ }
+
+ @Test
+ public void testLiveMapFromBean() {
+ MyBean mb = new MyBean();
+ mb.beanVal = "" + Long.MAX_VALUE;
+
+ Map<SomeEnum, Long> m = converter.convert(mb).sourceAsBean().to(new TypeReference<Map<SomeEnum, Long>>(){});
+ assertEquals(1, m.size());
+ assertEquals(Long.valueOf(Long.MAX_VALUE), m.get(SomeEnum.VALUE));
+
+ mb.beanVal = "" + Long.MIN_VALUE;
+ assertEquals(Long.valueOf(Long.MIN_VALUE), m.get(SomeEnum.VALUE));
+
+ m.put(SomeEnum.GETVALUE, 123L);
+ mb.beanVal = "12";
+ assertEquals(Long.valueOf(Long.MIN_VALUE), m.get(SomeEnum.VALUE));
}
static class MyClass2 {
@@ -768,4 +888,6 @@ public class ConverterTest {
return value;
}
}
+
+ enum SomeEnum { VALUE, GETVALUE };
}