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 }