View Javadoc

1   /**
2    * Copyright (c) 2002-2011 "Neo Technology,"
3    * Network Engine for Objects in Lund AB [http://neotechnology.com]
4    *
5    * This file is part of Neo4j.
6    *
7    * Neo4j is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation, either version 3 of the License, or
10   * (at your option) any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
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 }