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 }