Improve Databoard's dynamically typed data capabilities.
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / reflection / BindingRequest.java
1 package org.simantics.databoard.binding.reflection;\r
2 \r
3 import java.lang.annotation.Annotation;\r
4 import java.lang.reflect.Field;\r
5 import java.util.ArrayList;\r
6 import java.util.Arrays;\r
7 import java.util.Collections;\r
8 import java.util.List;\r
9 import org.simantics.databoard.annotations.ArgumentImpl;\r
10 import org.simantics.databoard.annotations.Arguments;\r
11 import org.simantics.databoard.binding.Binding;\r
12 import org.simantics.databoard.primitives.MutableInteger;\r
13 \r
14 public class BindingRequest {\r
15         \r
16         public static BindingRequest create( Field field )\r
17         {\r
18         Annotation[] annotations = ClassBindingFactory.getFieldAnnotations(field);\r
19         Class<?> fieldClass = field.getType(); \r
20         return new BindingRequest(fieldClass, annotations);\r
21         }\r
22         \r
23         /** Requested class */\r
24     private Class<?> clazz;\r
25     private ClassLoader cl;\r
26     \r
27     /** Annotations */\r
28     public final Annotation[] annotations;\r
29     \r
30     public final Annotation[] NO_ANNOTATIONS = new Annotation[0];\r
31     \r
32     public final String className; // eg. java.util.Map\r
33     public final String signature; // eg. Ljava/util/Map;\r
34     public final String descriptor; //eg. Ljava/util/Map<I;I>;\r
35     \r
36     public BindingRequest[] componentRequests;\r
37     public Binding[] componentBindings;\r
38     \r
39     transient int hash;\r
40 \r
41     /**\r
42      * Create BindingRequest that creates class lazily. \r
43      * \r
44      * @param cl classloader\r
45      * @param className \r
46      * @param classSignature \r
47      * @param classDescriptor\r
48      * @param annotations \r
49      */\r
50     public BindingRequest(ClassLoader cl, String className, String classSignature, String classDescriptor, Annotation...annotations)\r
51     {\r
52         this.className = className;\r
53         this.cl = cl;    \r
54         this.signature = classSignature;\r
55         this.annotations = annotations;\r
56         this.descriptor = classDescriptor;\r
57         hash = className.hashCode();\r
58         for (Annotation a : annotations) {\r
59             hash = 7*hash + a.hashCode();\r
60         }\r
61     }\r
62     \r
63     /**\r
64      * Create BindingRequest\r
65      * \r
66      * @param clazz\r
67      * @param annotations\r
68      */\r
69     public BindingRequest(Class<?> clazz, Annotation...annotations)\r
70     {\r
71         assert annotations!=null;\r
72         this.clazz = clazz;\r
73         Annotation[] classAnnotations = clazz.getAnnotations();\r
74         if (classAnnotations!=null && classAnnotations.length>0) {\r
75             this.annotations = new Annotation[classAnnotations.length + annotations.length];\r
76             System.arraycopy(annotations, 0, this.annotations, 0, annotations.length);\r
77             System.arraycopy(classAnnotations, 0, this.annotations, annotations.length, classAnnotations.length);\r
78         } else {\r
79                 this.annotations = annotations;\r
80         }\r
81         \r
82         className = clazz.getCanonicalName();\r
83         signature = getSignature(clazz);\r
84         List<Class<?>> args = createArgsList();\r
85         StringBuilder desc = new StringBuilder();\r
86         _buildDescriptor(desc, clazz, args, new MutableInteger(0));\r
87         descriptor = desc.toString();\r
88         hash = clazz.getName().hashCode();\r
89         for (Annotation a : annotations) {\r
90             hash = 7*hash + a.hashCode();\r
91         }\r
92     }\r
93     \r
94     private void _buildDescriptor(StringBuilder sb, Class<?> c, List<Class<?>> classes, MutableInteger pos)\r
95     {\r
96         int genericCount = c.getTypeParameters().length;\r
97         int genericsLeft = classes.size()-pos.value;\r
98         if ( genericCount>0 && genericsLeft >= genericCount ) {\r
99                 sb.append('L');\r
100                 sb.append(c.getName().replaceAll("\\.", "/"));\r
101                 sb.append('<');\r
102                 for (int i=0; i<genericCount; i++) \r
103                 {\r
104                         Class<?> gc = classes.get( pos.value++ );\r
105                         _buildDescriptor(sb, gc, classes, pos);\r
106                 }\r
107                 sb.append('>');                 \r
108                 sb.append(';');\r
109         } else {\r
110                 sb.append( getSignature(c) );\r
111         }\r
112     }\r
113 \r
114     public BindingRequest(Class<?> clazz, List<Annotation> annotations)\r
115     {\r
116         this(clazz, annotations.toArray(new Annotation[annotations.size()]));\r
117     }\r
118     \r
119     public BindingRequest(Class<?> clazz, Class<?>[] parameters)\r
120     {\r
121         this(clazz, new ArgumentImpl(parameters));\r
122     }\r
123     \r
124     public boolean hasAnnotation(Class<?> annotationClass) \r
125     {\r
126         for (Annotation a : annotations)\r
127             if (annotationClass.equals(a.annotationType())) return true;\r
128         return false;\r
129     }\r
130     \r
131     @SuppressWarnings("unchecked")\r
132     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) \r
133     {\r
134         for (Annotation a : annotations)\r
135         {\r
136             if (annotationClass.equals(a.annotationType()))\r
137                 return (A) a;\r
138         }\r
139         return null;\r
140     }\r
141     @Override\r
142     public int hashCode() {\r
143         return hash;\r
144     }\r
145     \r
146     @Override\r
147     public boolean equals(Object obj) {\r
148         if (obj==null) return false;\r
149         if (obj instanceof BindingRequest==false) return false;\r
150         BindingRequest other = (BindingRequest) obj;\r
151         return other.descriptor.equals(descriptor) &&\r
152             Arrays.deepEquals(annotations, other.annotations);\r
153     }\r
154     \r
155     public Class<?> getClazz()\r
156     {\r
157         if ( clazz==null ) {\r
158                 try {\r
159                                 clazz = cl.loadClass( className );\r
160                         } catch (ClassNotFoundException e) {\r
161                                 throw new RuntimeException( e );\r
162                         }\r
163         }\r
164         return clazz;\r
165     }\r
166         \r
167     /**\r
168      * Return a version of annotations list, where given set of annotations and\r
169      * a number of class arguments were dropped. \r
170      * \r
171      * @param argumentsToDrop the number of class arguments to drop\r
172      * @param annotationsToDrop annotation to drop\r
173      * @return request without argument annotation\r
174      */\r
175     public Annotation[] dropAnnotations(int argumentsToDrop, Annotation...annotationsToDrop)\r
176     {\r
177         ArrayList<Annotation> result = new ArrayList<Annotation>( annotations.length );\r
178         nextA:\r
179         for (Annotation a : annotations) {\r
180                 for (Annotation b : annotationsToDrop) \r
181                         if (a==b) continue nextA;\r
182                 if (a instanceof Arguments && argumentsToDrop>0) {\r
183                         Arguments c = ArgumentImpl.dropArguments((Arguments) a, argumentsToDrop);\r
184                         if (c!=null) result.add(c);\r
185                 } else result.add(a);\r
186         }\r
187         Annotation[] newAnnotations = result.toArray( new Annotation[result.size()] );\r
188         return newAnnotations;\r
189     }\r
190     \r
191     \r
192     @Override\r
193     public String toString() {\r
194         StringBuilder sb = new StringBuilder();\r
195         sb.append(clazz.getName());\r
196         if ( annotations!=null && annotations.length>0 ) {\r
197             sb.append('(');\r
198             for (int i=0; i<annotations.length; i++) {\r
199                 Annotation a = annotations[i];\r
200                 if (i>0) sb.append(", ");\r
201                 sb.append(a);\r
202             }           \r
203             sb.append(')');\r
204         }\r
205         \r
206         return sb.toString();\r
207     }\r
208     \r
209     /**\r
210      * Get signature, e.g. Ljava/util/Map;\r
211      * \r
212      * @return singature string\r
213      */\r
214     public static String getSignature(Class<?> clazz) {\r
215                 if (clazz==void.class) return "V";\r
216                 if (clazz==boolean.class) return "Z";\r
217                 if (clazz==char.class) return "C";\r
218                 if (clazz==byte.class) return "B";\r
219                 if (clazz==short.class) return "S";\r
220                 if (clazz==int.class) return "I";\r
221                 if (clazz==float.class) return "F";\r
222                 if (clazz==long.class) return "J";\r
223                 if (clazz==double.class) return "D";\r
224                 if (clazz.isArray()) return clazz.getName().replaceAll("\\.", "/");             \r
225                 return "L"+clazz.getName().replaceAll("\\.", "/")+";";\r
226     }\r
227     \r
228     @SuppressWarnings("unchecked")\r
229         List<Class<?>> createArgsList()\r
230     {\r
231         if (annotations==null || !hasAnnotation(Arguments.class)) return Collections.EMPTY_LIST;\r
232         List<Class<?>> result = new ArrayList<Class<?>>();\r
233         for (Annotation a : annotations) {\r
234                 if ( a instanceof Arguments ) {\r
235                         Arguments args = (Arguments) a;\r
236                         for (Class<?> clazz : args.value()) result.add( clazz );\r
237                 }\r
238         }\r
239         return result;\r
240     }\r
241         \r
242 }\r