+/*******************************************************************************\r
+ * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.binding.reflection;\r
+\r
+import java.lang.reflect.Constructor;\r
+import java.lang.reflect.Field;\r
+import java.lang.reflect.Method;\r
+import java.lang.reflect.Modifier;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.LinkedList;\r
+\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+\r
+public class ClassInfo {\r
+\r
+ public Constructor<?> argsConstructor, noArgsConstructor, beanConstructor;\r
+ public Class<?> clazz;\r
+ public Field[] fields;\r
+ public Method[] getters, setters;\r
+ public boolean[] writable;\r
+ public boolean partialConstructionPossible; // set to true, if partial construction is possible\r
+ \r
+ public static ClassInfo getInfo(Class<?> clazz)\r
+ throws BindingConstructionException\r
+ {\r
+ boolean isAbstract = (clazz.getModifiers() & Modifier.ABSTRACT) != 0; \r
+// if ( isAbstract )\r
+// throw new BindingConstructionException("Cannot create reflection binding to abstract class "+clazz);\r
+ \r
+ ClassInfo ci = new ClassInfo();\r
+ ci.clazz = clazz;\r
+ try {\r
+ ci.fields = getFields(clazz);\r
+ for (Field f : ci.fields)\r
+ f.setAccessible(true);\r
+\r
+ int c = ci.fields.length;\r
+ ci.getters = new Method[ c ];\r
+ ci.setters = new Method[ c ];\r
+ ci.writable = new boolean[ c ];\r
+ for (int i=0; i<ci.fields.length; i++)\r
+ {\r
+ Field f = ci.fields[i];\r
+ \r
+ boolean isPublic = (f.getModifiers() & (Modifier.PUBLIC)) > 0;\r
+ boolean isFinal = (f.getModifiers() & (Modifier.FINAL)) > 0;\r
+ boolean isPrimitive = (f.getType().getName().length()=='1');\r
+ \r
+ ci.writable[i] = isPublic && (!isFinal || isPrimitive); \r
+ String name = f.getName();\r
+ try {\r
+ String getterName = "get"+name.substring(0, 1).toUpperCase()+name.substring(1, name.length());\r
+ Method getter = clazz.getMethod(getterName);\r
+ if (getter.getReturnType().equals(f.getType()))\r
+ ci.getters[i] = getter;\r
+ } catch (NoSuchMethodException e) {\r
+ }\r
+ \r
+ try {\r
+ String setterName = "set"+name.substring(0, 1).toUpperCase()+name.substring(1, name.length());\r
+ Method setter = clazz.getMethod(setterName, f.getType());\r
+ if (setter.getReturnType().equals(void.class)) {\r
+ ci.setters[i] = setter;\r
+ ci.writable[i] = true;\r
+ }\r
+ \r
+ } catch (NoSuchMethodException e) { \r
+ }\r
+ \r
+ }\r
+ \r
+ // Prepare constuctor for case 2)\r
+ if (!isAbstract) {\r
+ Class<?>[] constructorArgs = new Class<?>[ci.fields.length];\r
+ for (int i=0; i<ci.fields.length; i++) {\r
+ constructorArgs[i] = ci.fields[i].getType();\r
+ }\r
+ \r
+ try {\r
+ ci.argsConstructor = clazz.getDeclaredConstructor(constructorArgs);\r
+ ci.argsConstructor.setAccessible(true);\r
+ } catch (NoSuchMethodException e) {}\r
+ \r
+ try {\r
+ ci.noArgsConstructor = clazz.getDeclaredConstructor();\r
+ ci.noArgsConstructor.setAccessible(true); \r
+ } catch (NoSuchMethodException e) {}\r
+ \r
+ \r
+ try {\r
+ ci.beanConstructor = clazz.getDeclaredConstructor(org.simantics.databoard.binding.Binding.class);\r
+ ci.beanConstructor.setAccessible(true);\r
+ } catch (NoSuchMethodException e) {}\r
+ }\r
+ \r
+ boolean allWritable = true;\r
+ for (boolean b : ci.writable) allWritable &= b;\r
+ \r
+ ci.partialConstructionPossible = allWritable;\r
+ \r
+ return ci;\r
+ } catch (SecurityException e1) {\r
+ throw new BindingConstructionException(e1);\r
+ }\r
+ \r
+ }\r
+ \r
+ /**\r
+ * Get all, including inherited, public, protected and default \r
+ * non-transient, non-static, non-final fields. All returned fields have access.\r
+ * \r
+ * @param clazz\r
+ * @return\r
+ */\r
+ static Field[] getFields(Class<?> clazz)\r
+ {\r
+ // TODO Sort as order is not guaranteed\r
+ // Sort priority 1-class, 2-modifiers, 3-name, 4-signature\r
+ Field[] fields = getAllFields(clazz);\r
+ \r
+ ArrayList<Field> result = new ArrayList<Field>(fields.length);\r
+ for (Field f : fields) {\r
+ if (Modifier.isStatic(f.getModifiers())) continue;\r
+// if (Modifier.isFinal(f.getModifiers())) continue;\r
+ if (Modifier.isTransient(f.getModifiers())) continue; \r
+ if (Modifier.isVolatile(f.getModifiers())) continue; \r
+ f.setAccessible(true);\r
+ result.add(f);\r
+ }\r
+ return result.toArray( new Field[result.size()] );\r
+ }\r
+ \r
+ // Get all fields except fields of Throwable\r
+ static Field[] getAllFields(Class<?> clazz)\r
+ {\r
+ LinkedList<Class<?>> classes = new LinkedList<Class<?>>();\r
+ while (clazz!=null) {\r
+ classes.addFirst(clazz);\r
+ clazz = clazz.getSuperclass();\r
+ if (clazz==Throwable.class) clazz=null;\r
+ }\r
+ \r
+ ArrayList<Field> result = new ArrayList<Field>();\r
+ for (Class<?> _class : classes) {\r
+ _getAllFields(_class, result);\r
+ }\r
+ \r
+ return result.toArray(new Field[result.size()]);\r
+ }\r
+ \r
+ public static void _getAllFields(Class<?> clazz, Collection<Field> result)\r
+ {\r
+ for (Field m : clazz.getDeclaredFields())\r
+ result.add(m);\r
+ } \r
+\r
+ @Override\r
+ public boolean equals(Object arg0) {\r
+ return this == arg0 || (arg0.getClass().equals(this.getClass()) && ((ClassInfo)arg0).clazz.equals(clazz));\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return clazz.hashCode();\r
+ };\r
+}\r