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/05/12 09:04:51 UTC
svn commit: r1743484 - in /felix/trunk/converter/src:
main/java/org/apache/felix/converter/impl/ConvertingImpl.java
test/java/org/apache/felix/converter/impl/ConverterMapTest.java
test/java/org/apache/felix/converter/impl/MyBean.java
Author: davidb
Date: Thu May 12 09:04:51 2016
New Revision: 1743484
URL: http://svn.apache.org/viewvc?rev=1743484&view=rev
Log:
Felix Converter Service: support for conversions to-from Java Beans.
Added:
felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/MyBean.java
Modified:
felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java
felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java
Modified: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java?rev=1743484&r1=1743483&r2=1743484&view=diff
==============================================================================
--- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java (original)
+++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java Thu May 12 09:04:51 2016
@@ -39,6 +39,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import org.osgi.service.converter.ConversionException;
import org.osgi.service.converter.Converter;
import org.osgi.service.converter.Converting;
import org.osgi.service.converter.TypeReference;
@@ -112,12 +113,10 @@ public class ConvertingImpl implements C
if (cls == null)
return null;
- Class<?> targetCls = cls;
-
if (object == null)
return handleNull(cls);
- targetCls = primitiveToBoxed(targetCls);
+ Class<?> targetCls = primitiveToBoxed(cls);
if (!Map.class.isAssignableFrom(targetCls) &&
!Collections.class.isAssignableFrom(targetCls)) {
@@ -256,7 +255,30 @@ public class ConvertingImpl implements C
return convertToMap(targetCls, typeArguments);
else if (Dictionary.class.isAssignableFrom(targetCls))
return new Hashtable(convertToMap(Map.class, typeArguments));
- return createProxy(targetCls);
+ else if (targetCls.isInterface())
+ return createProxy(targetCls);
+ return createJavaBean(targetCls);
+ }
+
+ private Object createJavaBean(Class<?> targetCls) {
+ @SuppressWarnings("rawtypes")
+ Map m = mapView(object);
+ try {
+ Object res = targetCls.getConstructor().newInstance();
+ for (Method setter : getSetters(targetCls)) {
+ String setterName = setter.getName();
+ StringBuilder propName = new StringBuilder(Character.valueOf(Character.toLowerCase(setterName.charAt(3))).toString());
+ if (setterName.length() > 4)
+ propName.append(setterName.substring(4));
+
+ Class<?> setterType = setter.getParameterTypes()[0];
+ setter.invoke(res, converter.convert(m.get(propName.toString())).to(setterType));
+ }
+ return res;
+ } catch (Exception e) {
+ throw new ConversionException("Cannot convert to class: " + targetCls.getName() +
+ ". Not a JavaBean with a Zero-arg Constructor.", e);
+ }
}
@SuppressWarnings("rawtypes")
@@ -294,6 +316,8 @@ public class ConvertingImpl implements C
// All interface types that are not Collections are treated as maps
if (targetCls.isInterface())
return true;
+ else if (isWriteableJavaBean(targetCls))
+ return true;
else
return Dictionary.class.isAssignableFrom(targetCls);
}
@@ -436,7 +460,10 @@ public class ConvertingImpl implements C
return null; // just 'get' or 'is': not an accessor
String propStr = mn.substring(prefix);
StringBuilder propName = new StringBuilder(propStr.length());
- propName.append(Character.toLowerCase(propStr.charAt(0)));
+ 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));
@@ -450,6 +477,8 @@ public class ConvertingImpl implements C
return; // method with this name already invoked
String propName = getAccessorPropertyName(md);
+ if (propName == null)
+ return;
try {
res.put(propName.toString(), md.invoke(obj));
@@ -466,4 +495,38 @@ public class ConvertingImpl implements C
else
return createMapFromBeanAccessors(obj);
}
+
+ private boolean isWriteableJavaBean(Class<?> cls) {
+ boolean hasNoArgCtor = false;
+ for (Constructor<?> ctor : cls.getConstructors()) {
+ if (ctor.getParameterTypes().length == 0)
+ hasNoArgCtor = true;
+ }
+ if (!hasNoArgCtor)
+ return false; // A JavaBean must have a public no-arg constructor
+
+ return getSetters(cls).size() > 0;
+ }
+
+ private Set<Method> getSetters(Class<?> cls) {
+ Set<Method> setters = new HashSet<>();
+ while (!Object.class.equals(cls)) {
+ Set<Method> methods = new HashSet<>();
+ methods.addAll(Arrays.asList(cls.getDeclaredMethods()));
+ methods.addAll(Arrays.asList(cls.getMethods()));
+ for (Method md : methods) {
+ if (md.getParameterTypes().length != 1)
+ continue; // Only setters with a single argument
+ String name = md.getName();
+ if (name.length() < 4)
+ continue;
+ if (name.startsWith("set") &&
+ Character.isUpperCase(name.charAt(3)))
+ setters.add(md);
+ }
+ cls = cls.getSuperclass();
+ }
+ return setters;
+ }
+
}
Modified: felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java?rev=1743484&r1=1743483&r2=1743484&view=diff
==============================================================================
--- felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java (original)
+++ felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java Thu May 12 09:04:51 2016
@@ -29,7 +29,9 @@ import org.junit.Test;
import org.osgi.service.converter.Converter;
import org.osgi.service.converter.TypeReference;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -71,6 +73,49 @@ public class ConverterMapTest {
}
@Test
+ public void testJavaBeanToMap() {
+ MyBean mb = new MyBean();
+ mb.setMe("You");
+ mb.setF(true);
+ mb.setNumbers(new int[] {3,2,1});
+
+ @SuppressWarnings("rawtypes")
+ Map m = converter.convert(mb).to(Map.class);
+ assertEquals(4, m.size());
+ assertEquals("You", m.get("me"));
+ assertTrue((boolean) m.get("f"));
+ assertFalse((boolean) m.get("enabled"));
+ assertArrayEquals(new int [] {3,2,1}, (int[]) m.get("numbers"));
+ }
+
+ @Test
+ public void testMapToJavaBean() {
+ Map<String, String> m = new HashMap<>();
+
+ m.put("me", "Joe");
+ m.put("enabled", "true");
+ m.put("numbers", "42");
+ m.put("s", "will disappear");
+ MyBean mb = converter.convert(m).to(MyBean.class);
+ assertEquals("Joe", mb.getMe());
+ assertTrue(mb.isEnabled());
+ assertNull(mb.getF());
+ assertArrayEquals(new int[] {42}, mb.getNumbers());
+ }
+
+ public void testMapToJavaBean2() {
+ Map<String, String> m = new HashMap<>();
+
+ m.put("blah", "blahblah");
+ m.put("f", "true");
+ MyBean mb = converter.convert(m).to(MyBean.class);
+ assertNull(mb.getMe());
+ assertTrue(mb.getF());
+ assertFalse(mb.isEnabled());
+ assertNull(mb.getNumbers());
+ }
+
+ @Test
public void testInterfaceToMap() {
Object obj = new Object();
TestInterface impl = new TestInterface() {
@@ -80,7 +125,7 @@ public class ConverterMapTest {
}
@Override
- public int getbar() {
+ public int getBar() {
return 76543;
}
@@ -130,7 +175,7 @@ public class ConverterMapTest {
TestInterface ti = converter.convert(m).to(TestInterface.class);
assertEquals("12345", ti.getFoo());
- assertEquals(999, ti.getbar());
+ assertEquals(999, ti.getBar());
}
@SuppressWarnings("rawtypes")
@@ -140,7 +185,7 @@ public class ConverterMapTest {
TestInterface ti = converter.convert(m).to(TestInterface.class);
assertNull(ti.getFoo());
- assertEquals(0, ti.getbar());
+ assertEquals(0, ti.getBar());
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@@ -156,6 +201,6 @@ public class ConverterMapTest {
interface TestInterface {
String getFoo();
- int getbar();
+ int getBar();
}
}
Added: felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/MyBean.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/MyBean.java?rev=1743484&view=auto
==============================================================================
--- felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/MyBean.java (added)
+++ felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/MyBean.java Thu May 12 09:04:51 2016
@@ -0,0 +1,61 @@
+/*
+ * 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;
+
+public class MyBean {
+ String me;
+ boolean enabled;
+ Boolean f;
+ int[] numbers;
+
+ public String get() {
+ return "Not a bean accessor because no camel casing";
+ }
+ public String gettisburgh() {
+ return "Not a bean accessor because no camel casing";
+ }
+ public int issue() {
+ return -1; // not a bean accessor as no camel casing
+ }
+ public void sets(String s) {
+ throw new RuntimeException("Not a bean accessor because no camel casing");
+ }
+ public String getMe() {
+ return me;
+ }
+ public void setMe(String me) {
+ this.me = me;
+ }
+ public boolean isEnabled() {
+ return enabled;
+ }
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+ public Boolean getF() {
+ return f;
+ }
+ public void setF(Boolean f) {
+ this.f = f;
+ }
+ public int[] getNumbers() {
+ return numbers;
+ }
+ public void setNumbers(int[] numbers) {
+ this.numbers = numbers;
+ }
+}