1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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"
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 }