1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.neo4j.server.plugins;
21
22 import org.neo4j.graphdb.Node;
23 import org.neo4j.graphdb.Relationship;
24 import org.neo4j.graphdb.RelationshipType;
25
26 import java.lang.annotation.Annotation;
27 import java.lang.reflect.GenericArrayType;
28 import java.lang.reflect.Method;
29 import java.lang.reflect.ParameterizedType;
30 import java.lang.reflect.Type;
31 import java.net.URI;
32 import java.net.URL;
33 import java.util.Arrays;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40
41 class PluginPointFactoryImpl implements PluginPointFactory
42 {
43
44 public PluginPoint createFrom( ServerPlugin plugin, Method method,
45 Class<?> discovery )
46 {
47 ResultConverter result = ResultConverter.get( method.getGenericReturnType() );
48 Type[] types = method.getGenericParameterTypes();
49 Annotation[][] annotations = method.getParameterAnnotations();
50 SourceExtractor sourceExtractor = null;
51 DataExtractor[] extractors = new DataExtractor[ types.length ];
52 for ( int i = 0; i < types.length; i++ )
53 {
54 Description description = null;
55 Parameter param = null;
56 Source source = null;
57 for ( Annotation annotation : annotations[ i ] )
58 {
59 if ( annotation instanceof Description )
60 {
61 description = (Description)annotation;
62 } else if ( annotation instanceof Parameter )
63 {
64 param = (Parameter)annotation;
65 } else if ( annotation instanceof Source )
66 {
67 source = (Source)annotation;
68 }
69 }
70 if ( param != null && source != null )
71 {
72 throw new IllegalStateException(
73 String.format(
74 "Method parameter %d of %s cannot be retrieved as both Parameter and Source",
75 Integer.valueOf( i ), method ) );
76 } else if ( source != null )
77 {
78 if ( types[ i ] != discovery )
79 {
80 throw new IllegalStateException(
81 "Source parameter type must equal the discovery type." );
82 }
83 if ( sourceExtractor != null )
84 {
85 throw new IllegalStateException(
86 "Server Extension methods may have at most one Source parameter." );
87 }
88 extractors[ i ] = sourceExtractor = new SourceExtractor( source, description );
89 } else if ( param != null )
90 {
91 extractors[ i ] = parameterExtractor( types[ i ], param, description );
92 } else
93 {
94 throw new IllegalStateException(
95 "Parameters of Server Extension methods must be annotated as either Source or Parameter." );
96 }
97 }
98 return new PluginMethod( nameOf( method ), discovery, plugin, result, method,
99 extractors, method.getAnnotation( Description.class ) );
100 }
101
102 private static ParameterExtractor parameterExtractor( Type type, Parameter parameter,
103 Description description )
104 {
105 if ( type instanceof ParameterizedType )
106 {
107 ParameterizedType paramType = (ParameterizedType)type;
108 Class<?> raw = (Class<?>)paramType.getRawType();
109 Type componentType = paramType.getActualTypeArguments()[ 0 ];
110 Class<?> component = null;
111 if ( componentType instanceof Class<?> )
112 {
113 component = (Class<?>)componentType;
114 }
115 if ( Set.class == raw )
116 {
117 TypeCaster caster = TYPES.get( component );
118 if ( caster != null )
119 {
120 return new ListParameterExtractor( caster, component, parameter, description )
121 {
122 @Override
123 Object convert( Object[] result )
124 {
125 return new HashSet<Object>( Arrays.asList( result ) );
126 }
127 };
128 }
129 } else if ( List.class == raw || Collection.class == raw || Iterable.class == raw )
130 {
131 TypeCaster caster = TYPES.get( component );
132 if ( caster != null )
133 {
134 return new ListParameterExtractor( caster, component, parameter, description )
135 {
136 @Override
137 Object convert( Object[] result )
138 {
139 return Arrays.asList( result );
140 }
141 };
142 }
143 }
144 } else if ( type instanceof Class<?> )
145 {
146 Class<?> raw = (Class<?>)type;
147 if ( raw.isArray() )
148 {
149 TypeCaster caster = TYPES.get( raw.getComponentType() );
150 if ( caster != null )
151 {
152 return new ListParameterExtractor( caster, raw.getComponentType(), parameter,
153 description )
154 {
155 @Override
156 Object convert( Object[] result )
157 {
158 return result;
159 }
160 };
161 }
162 } else
163 {
164 TypeCaster caster = TYPES.get( raw );
165 if ( caster != null )
166 {
167 return new ParameterExtractor( caster, raw, parameter, description );
168 }
169 }
170 } else if ( type instanceof GenericArrayType )
171 {
172 GenericArrayType array = (GenericArrayType)type;
173 Type component = array.getGenericComponentType();
174 if ( component instanceof Class<?> )
175 {
176 TypeCaster caster = TYPES.get( component );
177 if ( caster != null )
178 {
179 return new ListParameterExtractor( caster, (Class<?>)component, parameter,
180 description )
181 {
182 @Override
183 Object convert( Object[] result )
184 {
185 return result;
186 }
187 };
188 }
189 }
190 }
191 throw new IllegalStateException( "Unsupported parameter type: " + type );
192 }
193
194 private static void put( Map<Class<?>, TypeCaster> types, TypeCaster caster, Class<?>... keys )
195 {
196 for ( Class<?> key : keys )
197 {
198 types.put( key, caster );
199 }
200 }
201
202 private static final Map<Class<?>, TypeCaster> TYPES = new HashMap<Class<?>, TypeCaster>();
203 static
204 {
205 put( TYPES, new StringTypeCaster(), String.class );
206 put( TYPES, new ByteTypeCaster(), byte.class, Byte.class );
207 put( TYPES, new ShortTypeCaster(), short.class, Short.class );
208 put( TYPES, new IntegerTypeCaster(), int.class, Integer.class );
209 put( TYPES, new LongTypeCaster(), long.class, Long.class );
210 put( TYPES, new CharacterTypeCaster(), char.class, Character.class );
211 put( TYPES, new BooleanTypeCaster(), boolean.class, Boolean.class );
212 put( TYPES, new FloatTypeCaster(), float.class, Float.class );
213 put( TYPES, new DoubleTypeCaster(), double.class, Double.class );
214 put( TYPES, new NodeTypeCaster(), Node.class );
215 put( TYPES, new RelationshipTypeCaster(), Relationship.class );
216 put( TYPES, new RelationshipTypeTypeCaster(), RelationshipType.class );
217 put( TYPES, new UriTypeCaster(), URI.class );
218 put( TYPES, new URLTypeCaster(), URL.class );
219 }
220
221 private static String nameOf( Method method )
222 {
223 Name name = method.getAnnotation( Name.class );
224 if ( name != null )
225 {
226 return name.value();
227 }
228 return method.getName();
229 }
230
231 }