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.formats;
    21
    22  import java.net.URI;
    23  import java.util.ArrayList;
    24  import java.util.Collection;
    25  import java.util.Collections;
    26  import java.util.HashMap;
    27  import java.util.LinkedHashMap;
    28  import java.util.List;
    29  import java.util.Map;
    30
    31  import javax.ws.rs.WebApplicationException;
    32  import javax.ws.rs.core.MediaType;
    33  import javax.ws.rs.core.Response;
    34
    35  import org.neo4j.server.database.DatabaseBlockedException;
    36  import org.neo4j.server.rest.domain.HtmlHelper;
    37  import org.neo4j.server.rest.repr.BadInputException;
    38  import org.neo4j.server.rest.repr.ListWriter;
    39  import org.neo4j.server.rest.repr.MappingWriter;
    40  import org.neo4j.server.rest.repr.Representation;
    41  import org.neo4j.server.rest.repr.RepresentationFormat;
    42
    43  public class HtmlFormat extends RepresentationFormat
    44  {
    45      public HtmlFormat()
    46      {
    47          super( MediaType.TEXT_HTML_TYPE );
    48      }
    49
    50      private enum MappingTemplate
    51      {
    52          NODE( Representation.NODE )
    53          {
    54              @Override
    55              String render( Map<String, Object> serialized )
    56              {
    57                  String javascript = "";
    58                  StringBuilder builder = HtmlHelper.start( HtmlHelper.ObjectType.NODE,
    59                          javascript );
    60                  HtmlHelper.append(
    61                          builder,
    62                          Collections.singletonMap( "data", serialized.get( "data" ) ),
    63                          HtmlHelper.ObjectType.NODE );
    64                  builder.append( "<form action='javascript:neo4jHtmlBrowse.getRelationships();'><fieldset><legend>Get relationships</legend>\n" );
    65                  builder.append( "<label for='direction'>with direction</label>\n"
    66                          + "<select id='direction'>" );
    67                  builder.append( "<option value='" ).append( serialized.get( "all_typed_relationships" ) ).append( "'>all</option>" );
    68                  builder.append( "<option value='" ).append( serialized.get( "incoming_typed_relationships" ) ).append( "'>in</option>" );
    69                  builder.append( "<option value='" ).append( serialized.get( "outgoing_typed_relationships" ) ).append( "'>out</option>" );
    70                  builder.append( "</select>\n" );
    71                  builder.append( "<label for='types'>for type(s)</label><select id='types' multiple='multiple'>" );
    72
    73                  try
    74                  {
    75                      for ( String relationshipType : (List<String>) serialized.get( "relationship_types" ) )
    76                      {
    77                          builder.append( "<option selected='selected' value='" ).append( relationshipType ).append( "'>" );
    78                          builder.append( relationshipType ).append( "</option>" );
    79                      }
    80                  }
    81                  catch ( DatabaseBlockedException e )
    82                  {
    83                      throw new RuntimeException(
    84                              "Unable to render, database is blocked, see nested exception.",
    85                              e );
    86                  }
    87                  builder.append( "</select>\n" );
    88                  builder.append( "<button>Get</button>\n" );
    89                  builder.append( "</fieldset></form>\n" );
    90
    91                  return HtmlHelper.end( builder );
    92              }
    93          },
    94          RELATIONSHIP( Representation.RELATIONSHIP )
    95          {
    96              @Override
    97              String render( Map<String, Object> serialized )
    98              {
    99                  Map<Object, Object> map = new LinkedHashMap<Object, Object>();
    100                 transfer( serialized, map, "type", "data", "start", "end" );
    101                 return HtmlHelper.from( map, HtmlHelper.ObjectType.RELATIONSHIP );
    102             }
    103         },
    104         NODE_INDEXES( Representation.NODE_INDEXES )
    105         {
    106             @Override
    107             String render( Map<String, Object> serialized )
    108             {
    109                 return renderIndex( serialized );
    110             }
    111         },
    112         RELATIONSHIP_INDEXES( Representation.RELATIONSHIP_INDEXES )
    113         {
    114             @Override
    115             String render( Map<String, Object> serialized )
    116             {
    117                 return renderIndex( serialized );
    118             }
    119         },
    120         GRAPHDB( Representation.GRAPHDB )
    121         {
    122             @Override
    123             String render( Map<String, Object> serialized )
    124             {
    125                 Map<Object, Object> map = new HashMap<Object, Object>();
    126                 transfer( serialized, map, "index", "reference_node", "node_index",
    127                         "relationship_index"/*, "extensions_info"*/);
    128                 return HtmlHelper.from( map, HtmlHelper.ObjectType.ROOT );
    129             }
    130         },
    131         EXCEPTION( Representation.EXCEPTION )
    132         {
    133             @Override
    134             String render( Map<String, Object> serialized )
    135             {
    136                 StringBuilder entity = new StringBuilder( "<html>" );
    137                 entity.append( "<head><title>Error</title></head><body>" );
    138                 Object subjectOrNull = serialized.get( "message" );
    139                 if ( subjectOrNull != null )
    140                 {
    141                     entity.append( "<p><pre>" ).append( subjectOrNull ).append( "</pre></p>" );
    142                 }
    143                 entity.append( "<p><pre>" ).append( serialized.get( "exception" ) );
    144                 List<Object> tb = (List<Object>) serialized.get( "stacktrace" );
    145                 if ( tb != null )
    146                 {
    147                     for ( Object el : tb )
    148                         entity.append( "\n\tat " + el );
    149                 }
    150                 entity.append( "</pre></p>" ).append( "</body></html>" );
    151                 return entity.toString();
    152             }
    153         };
    154         private final String key;
    155         private MappingTemplate( String key )
    156         {
    157             this.key = key;
    158         }
    159
    160         static final Map<String, MappingTemplate> TEMPLATES = new HashMap<String, MappingTemplate>();
    161         static
    162         {
    163             for ( MappingTemplate template : values() )
    164                 TEMPLATES.put( template.key, template );
    165         }
    166
    167         abstract String render( Map<String, Object> data );
    168     }
    169
    170
    171     private enum ListTemplate
    172     {
    173         NODES
    174         {
    175             @Override
    176             String render( List<Object> data )
    177             {
    178                 StringBuilder builder = HtmlHelper.start( "Index hits", null );
    179                 if ( data.isEmpty() )
    180                 {
    181                     HtmlHelper.appendMessage( builder, "No index hits" );
    182                     return HtmlHelper.end( builder );
    183                 }
    184                 else
    185                 {
    186                     for ( Map<?, ?> serialized : (List<Map<?, ?>>) (List<?>) data )
    187                     {
    188                         Map<Object, Object> map = new LinkedHashMap<Object, Object>();
    189                         transfer( serialized, map, "self", "data" );
    190                         HtmlHelper.append( builder, map, HtmlHelper.ObjectType.NODE );
    191                     }
    192                     return HtmlHelper.end( builder );
    193                 }
    194             }
    195         },
    196         RELATIONSHIPS
    197         {
    198             @Override
    199             String render( List<Object> data )
    200             {
    201                 if ( data.isEmpty() )
    202                 {
    203                     StringBuilder builder = HtmlHelper.start( HtmlHelper.ObjectType.RELATIONSHIP,
    204                             null );
    205                     HtmlHelper.appendMessage( builder, "No relationships found" );
    206                     return HtmlHelper.end( builder );
    207                 }
    208                 else
    209                 {
    210                     Collection<Object> list = new ArrayList<Object>();
    211                     for ( Map<?, ?> serialized : (List<Map<?, ?>>) (List<?>) data )
    212                     {
    213                         Map<Object, Object> map = new LinkedHashMap<Object, Object>();
    214                         transfer( serialized, map, "self", "type", "data", "start", "end" );
    215                         list.add( map );
    216                     }
    217                     return HtmlHelper.from( list, HtmlHelper.ObjectType.RELATIONSHIP );
    218                 }
    219             }
    220         };
    221
    222         abstract String render( List<Object> data );
    223     }
    224
    225     private static void transfer( Map<?, ?> from, Map<Object, Object> to, String... keys )
    226     {
    227         for ( String key : keys )
    228         {
    229             Object value = from.get( key );
    230             if ( value != null )
    231             {
    232                 to.put( key, value );
    233             }
    234         }
    235     }
    236
    237     private static String renderIndex( Map<String, Object> serialized )
    238     {
    239         String javascript = "";
    240         StringBuilder builder = HtmlHelper.start( HtmlHelper.ObjectType.INDEX_ROOT, javascript );
    241         int counter = 0;
    242         for ( String indexName : serialized.keySet() )
    243         {
    244             Map<?, ?> indexMapObject = (Map<?, ?>) serialized.get( indexName );
    245             builder.append( "<ul>" );
    246             {
    247                 builder.append( "<li>" );
    248                 Map<?, ?> indexMap = (Map<?, ?>) indexMapObject;
    249                 String keyId = "key_" + counter;
    250                 String valueId = "value_" + counter;
    251                 builder.append( "<form action='javascript:neo4jHtmlBrowse.search(\"" ).append(
    252                         indexMap.get( "template" ) ).append( "\",\"" ).append( keyId ).append(
    253                         "\",\"" ).append( valueId ).append( "\");'><fieldset><legend> name: " ).append( indexName ).append(" (configuration: ").append(
    254                         indexMap.get( "type" ) ).append( ")</legend>\n" );
    255                 builder.append( "<label for='" ).append( keyId ).append( "'>Key</label><input id='" ).append(
    256                         keyId ).append( "'>\n" );
    257                 builder.append( "<label for='" ).append( valueId ).append(
    258                         "'>Value</label><input id='" ).append( valueId ).append( "'>\n" );
    259                 builder.append( "<button>Search</button>\n" );
    260                 builder.append( "</fieldset></form>\n" );
    261                 builder.append( "</li>\n" );
    262                 counter++;
    263             }
    264             builder.append( "</ul>" );
    265         }
    266         return HtmlHelper.end( builder );
    267     }
    268
    269     private static class HtmlMap extends MapWrappingWriter
    270     {
    271         private final MappingTemplate template;
    272
    273         public HtmlMap( MappingTemplate template )
    274         {
    275             super( new HashMap<String, Object>(), true );
    276             this.template = template;
    277         }
    278
    279         String complete()
    280         {
    281             return template.render( this.data );
    282         }
    283     }
    284
    285     private static class HtmlList extends ListWrappingWriter
    286     {
    287         private final ListTemplate template;
    288
    289         public HtmlList( ListTemplate template )
    290         {
    291             super( new ArrayList<Object>(), true );
    292             this.template = template;
    293         }
    294
    295         String complete()
    296         {
    297             return template.render( this.data );
    298         }
    299     }
    300
    301     @Override
    302     protected String complete( ListWriter serializer )
    303     {
    304         return ( (HtmlList) serializer ).complete();
    305     }
    306
    307     @Override
    308     protected String complete( MappingWriter serializer )
    309     {
    310         return ( (HtmlMap) serializer ).complete();
    311     }
    312
    313     @Override
    314     protected ListWriter serializeList( String type )
    315     {
    316         if ( Representation.NODE_LIST.equals( type ) )
    317         {
    318             return new HtmlList( ListTemplate.NODES );
    319         }
    320         else if ( Representation.RELATIONSHIP_LIST.equals( type ) )
    321         {
    322             return new HtmlList( ListTemplate.RELATIONSHIPS );
    323         }
    324         else
    325         {
    326             throw new WebApplicationException(
    327                     Response.status( Response.Status.NOT_ACCEPTABLE ).entity(
    328                             "Cannot represent \"" + type + "\" as html" ).build() );
    329         }
    330     }
    331
    332     @Override
    333     protected MappingWriter serializeMapping( String type )
    334     {
    335         MappingTemplate template = MappingTemplate.TEMPLATES.get( type );
    336         if ( template == null )
    337         {
    338             throw new WebApplicationException(
    339                     Response.status( Response.Status.NOT_ACCEPTABLE ).entity(
    340                             "Cannot represent \"" + type + "\" as html" ).build() );
    341         }
    342         return new HtmlMap( template );
    343     }
    344
    345     @Override
    346     protected String serializeValue( String type, Object value )
    347     {
    348         throw new WebApplicationException(
    349                 Response.status( Response.Status.NOT_ACCEPTABLE ).entity(
    350                         "Cannot represent \"" + type + "\" as html" ).build() );
    351     }
    352
    353     @Override
    354     public List<Object> readList( String input ) throws BadInputException
    355     {
    356         throw new WebApplicationException(
    357                 Response.status( Response.Status.UNSUPPORTED_MEDIA_TYPE ).entity(
    358                         "Cannot read html" ).build() );
    359     }
    360
    361     @Override
    362     public Map<String, Object> readMap( String input ) throws BadInputException
    363     {
    364         throw new WebApplicationException(
    365                 Response.status( Response.Status.UNSUPPORTED_MEDIA_TYPE ).entity(
    366                         "Cannot read html" ).build() );
    367     }
    368
    369     @Override
    370     public URI readUri( String input ) throws BadInputException
    371     {
    372         throw new WebApplicationException(
    373                 Response.status( Response.Status.UNSUPPORTED_MEDIA_TYPE ).entity(
    374                         "Cannot read html" ).build() );
    375     }
    376
    377     @Override
    378     public Object readValue( String input ) throws BadInputException
    379     {
    380         throw new WebApplicationException(
    381                 Response.status( Response.Status.UNSUPPORTED_MEDIA_TYPE ).entity(
    382                         "Cannot read html" ).build() );
    383     }
    384 }