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.rest.repr;
    21
    22  import java.lang.annotation.ElementType;
    23  import java.lang.annotation.Retention;
    24  import java.lang.annotation.RetentionPolicy;
    25  import java.lang.annotation.Target;
    26  import java.lang.reflect.InvocationTargetException;
    27  import java.lang.reflect.Method;
    28  import java.util.HashMap;
    29  import java.util.Map;
    30  import java.util.concurrent.ConcurrentHashMap;
    31
    32  public abstract class ObjectRepresentation extends MappingRepresentation
    33  {
    34      @Target( ElementType.METHOD )
    35      @Retention( RetentionPolicy.RUNTIME )
    36      protected @interface Mapping
    37      {
    38          String value();
    39      }
    40
    41      private static Map<Class<?>, Map<String, PropertyGetter>> serializations = new ConcurrentHashMap<Class<?>, Map<String, PropertyGetter>>();
    42
    43      private final Map<String, PropertyGetter> serialization = serialization( getClass() );
    44
    45      ObjectRepresentation( RepresentationType type )
    46      {
    47          super( type );
    48      }
    49
    50      public ObjectRepresentation( String type )
    51      {
    52          super( type );
    53      }
    54
    55      private static Map<String, PropertyGetter> serialization(
    56              Class<? extends ObjectRepresentation> type )
    57      {
    58          Map<String, PropertyGetter> serialization = serializations.get( type );
    59          if ( serialization == null )
    60          {
    61              synchronized ( type )
    62              {
    63                  serialization = serializations.get( type );
    64                  if ( serialization == null )
    65                  {
    66                      serialization = new HashMap<String, PropertyGetter>();
    67                      for ( Method method : type.getMethods() )
    68                      {
    69                          Mapping property = method.getAnnotation( Mapping.class );
    70                          if ( property != null )
    71                          {
    72                              serialization.put( property.value(), getter( method ) );
    73                          }
    74                      }
    75                  }
    76              }
    77          }
    78          return serialization;
    79      }
    80
    81      private static PropertyGetter getter( final Method method )
    82      {
    83          // If this turns out to be a bottle neck we could use a byte code
    84          // generation library, such as ASM, instead of reflection.
    85          return new PropertyGetter( method )
    86          {
    87              @Override
    88              Object get( ObjectRepresentation object )
    89              {
    90                  Throwable e;
    91                  try
    92                  {
    93                      return method.invoke( object );
    94                  }
    95                  catch ( InvocationTargetException ex )
    96                  {
    97                      e = ex.getTargetException();
    98                      if ( e instanceof RuntimeException )
    99                      {
    100                         throw (RuntimeException) e;
    101                     }
    102                     else if ( e instanceof Error )
    103                     {
    104                         throw (Error) e;
    105                     }
    106                 }
    107                 catch ( Exception ex )
    108                 {
    109                     e = ex;
    110                 }
    111                 throw new IllegalStateException( "Serialization failure", e );
    112             }
    113         };
    114     }
    115
    116     private static abstract class PropertyGetter
    117     {
    118         PropertyGetter( Method method )
    119         {
    120             if ( method.getParameterTypes().length != 0 )
    121             {
    122                 throw new IllegalStateException(
    123                         "Property getter method may not have any parameters." );
    124             }
    125             if ( !Representation.class.isAssignableFrom( (Class<?>) method.getReturnType() ) )
    126             {
    127                 throw new IllegalStateException(
    128                         "Property getter must return Representation object." );
    129             }
    130         }
    131
    132         void putTo( MappingSerializer serializer, ObjectRepresentation object, String key )
    133         {
    134             Object value = get( object );
    135             if ( value != null ) ( (Representation) value ).putTo( serializer, key );
    136         }
    137
    138         abstract Object get( ObjectRepresentation object );
    139     }
    140
    141     @Override
    142     protected final void serialize( MappingSerializer serializer )
    143     {
    144         for ( Map.Entry<String, PropertyGetter> property : serialization.entrySet() )
    145         {
    146             property.getValue().putTo( serializer, this, property.getKey() );
    147         }
    148         extraData( serializer );
    149     }
    150
    151     void extraData( MappingSerializer serializer )
    152     {
    153     }
    154 }