]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/reflection/ClassInfo.java
Improved Bindings.getBinding(Class) caching for Datatype.class
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / reflection / ClassInfo.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
3  * Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.binding.reflection;
13
14 import java.lang.reflect.Constructor;
15 import java.lang.reflect.Field;
16 import java.lang.reflect.Method;
17 import java.lang.reflect.Modifier;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.LinkedList;
21
22 import org.simantics.databoard.binding.error.BindingConstructionException;
23
24 public class ClassInfo {
25
26         public Constructor<?> argsConstructor, noArgsConstructor, beanConstructor;
27         public Class<?> clazz;
28         public Field[] fields;
29         public Method[] getters, setters;
30         public boolean[] writable;
31         public boolean partialConstructionPossible; // set to true, if partial construction is possible
32         
33         public static ClassInfo getInfo(Class<?> clazz)
34         throws BindingConstructionException
35         {
36                 boolean isAbstract = (clazz.getModifiers() & Modifier.ABSTRACT) != 0; 
37 //              if ( isAbstract )
38 //                      throw new BindingConstructionException("Cannot create reflection binding to abstract class "+clazz);
39                 
40                 ClassInfo ci = new ClassInfo();
41                 ci.clazz = clazz;
42                 try {
43                         ci.fields = getFields(clazz);
44                         for (Field f : ci.fields)
45                                 f.setAccessible(true);
46
47                         int c = ci.fields.length;
48                         ci.getters = new Method[ c ];
49                         ci.setters = new Method[ c ];
50                         ci.writable = new boolean[ c ];
51                         for (int i=0; i<ci.fields.length; i++)
52                         {
53                                 Field f = ci.fields[i];
54                                 
55                                 boolean isPublic = (f.getModifiers() & (Modifier.PUBLIC)) > 0;
56                                 boolean isFinal = (f.getModifiers() & (Modifier.FINAL)) > 0;
57                                 boolean isPrimitive = (f.getType().getName().length()=='1');
58                                 
59                                 ci.writable[i] = isPublic && (!isFinal || isPrimitive); 
60                                 String name = f.getName();
61                                 try {
62                                         String getterName = "get"+name.substring(0, 1).toUpperCase()+name.substring(1, name.length());
63                                         Method getter = clazz.getMethod(getterName);
64                                         if (getter.getReturnType().equals(f.getType()))
65                                                 ci.getters[i] = getter;
66                                 } catch (NoSuchMethodException e) {
67                                 }
68                                 
69                                 try {
70                                         String setterName = "set"+name.substring(0, 1).toUpperCase()+name.substring(1, name.length());
71                                         Method setter = clazz.getMethod(setterName, f.getType());
72                                         if (setter.getReturnType().equals(void.class)) {
73                                                 ci.setters[i] = setter;
74                                                 ci.writable[i] = true;
75                                         }
76                                         
77                                 } catch (NoSuchMethodException e) {                                     
78                                 }
79                                 
80                         }
81                         
82                         // Prepare constuctor for case 2)
83                         if (!isAbstract) {
84                                 Class<?>[] constructorArgs = new Class<?>[ci.fields.length];
85                                 for (int i=0; i<ci.fields.length; i++) {
86                                         constructorArgs[i] = ci.fields[i].getType();
87                                 }
88                                 
89                                 try {
90                                         ci.argsConstructor = clazz.getDeclaredConstructor(constructorArgs);
91                                         ci.argsConstructor.setAccessible(true);
92                                 } catch (NoSuchMethodException e) {}
93         
94                                 try {
95                                         ci.noArgsConstructor = clazz.getDeclaredConstructor();
96                                         ci.noArgsConstructor.setAccessible(true);                               
97                                 } catch (NoSuchMethodException e) {}
98         
99                                 
100                                 try {
101                                         ci.beanConstructor = clazz.getDeclaredConstructor(org.simantics.databoard.binding.Binding.class);
102                                         ci.beanConstructor.setAccessible(true);
103                                 } catch (NoSuchMethodException e) {}
104                         }
105                                                 
106                         boolean allWritable = true;
107                         for (boolean b : ci.writable) allWritable &= b;
108                         
109                         ci.partialConstructionPossible = allWritable;
110                         
111                         return ci;
112                 } catch (SecurityException e1) {
113                         throw new BindingConstructionException(e1);
114                 }
115                 
116         }
117         
118         /**
119          * Get all, including inherited, public, protected and default 
120          * non-transient, non-static, non-final fields. All returned fields have access.
121          * 
122          * @param clazz
123          * @return
124          */
125         static Field[] getFields(Class<?> clazz)
126         {
127                 // TODO Sort as order is not guaranteed
128                 // Sort priority 1-class, 2-modifiers, 3-name, 4-signature
129                 Field[] fields = getAllFields(clazz);
130                 
131                 ArrayList<Field> result = new ArrayList<Field>(fields.length);
132                 for (Field f : fields) {
133                         if (Modifier.isStatic(f.getModifiers())) continue;
134 //                      if (Modifier.isFinal(f.getModifiers())) continue;
135             if (Modifier.isTransient(f.getModifiers())) continue;           
136             if (Modifier.isVolatile(f.getModifiers())) continue;           
137                         f.setAccessible(true);
138                         result.add(f);
139                 }
140                 return result.toArray( new Field[result.size()] );
141         }
142         
143         // Get all fields except fields of Throwable
144         static Field[] getAllFields(Class<?> clazz)
145         {
146                 LinkedList<Class<?>> classes = new LinkedList<Class<?>>();
147                 while (clazz!=null) {
148                         classes.addFirst(clazz);
149                         clazz = clazz.getSuperclass();
150                         if (clazz==Throwable.class) clazz=null;
151                 }
152                 
153                 ArrayList<Field> result = new ArrayList<Field>();
154                 for (Class<?> _class : classes) {
155                         _getAllFields(_class, result);
156                 }
157                 
158                 return result.toArray(new Field[result.size()]);
159         }
160         
161         public static void _getAllFields(Class<?> clazz, Collection<Field> result)
162         {
163                 for (Field m : clazz.getDeclaredFields())
164                         result.add(m);
165         }           
166
167         @Override
168         public boolean equals(Object arg0) {
169                 return this == arg0 || (arg0.getClass().equals(this.getClass()) && ((ClassInfo)arg0).clazz.equals(clazz));
170         }
171
172         @Override
173         public int hashCode() {
174                 return clazz.hashCode();
175         };
176 }