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.domain;
21
22 import java.util.Map;
23
24 import javax.script.Compilable;
25 import javax.script.CompiledScript;
26 import javax.script.ScriptContext;
27 import javax.script.ScriptEngine;
28 import javax.script.ScriptEngineManager;
29 import javax.script.ScriptException;
30 import javax.script.SimpleScriptContext;
31
32 import org.neo4j.graphdb.Path;
33 import org.neo4j.graphdb.traversal.PruneEvaluator;
34 import org.neo4j.helpers.Predicate;
35 import org.neo4j.kernel.Traversal;
36
37
38
39
40
41
42
43 abstract class EvaluatorFactory
44 {
45 private static final String BUILTIN = "builtin";
46 private static final String KEY_LANGUAGE = "language";
47 private static final String KEY_BODY = "body";
48 private static final String KEY_NAME = "name";
49
50 public static PruneEvaluator pruneEvaluator( Map<String, Object> description )
51 {
52 if ( refersToBuiltInEvaluator( description ) )
53 {
54 return builtInPruneEvaluator( description );
55 }
56 else
57 {
58 return new ScriptedPruneEvaluator( scriptEngine( description ),
59 (String) description.get( KEY_BODY ) );
60 }
61 }
62
63 public static Predicate<Path> returnFilter( Map<String, Object> description )
64 {
65 if ( refersToBuiltInEvaluator( description ) )
66 {
67 return builtInReturnFilter( description );
68 }
69 else
70 {
71 return new ScriptedReturnEvaluator( scriptEngine( description ),
72 (String) description.get( KEY_BODY ) );
73 }
74 }
75
76 private static boolean refersToBuiltInEvaluator( Map<String, Object> description )
77 {
78 String language = (String) description.get( KEY_LANGUAGE );
79 return language.equals( BUILTIN );
80 }
81
82 private static PruneEvaluator builtInPruneEvaluator(
83 Map<String, Object> description )
84 {
85 String name = (String) description.get( KEY_NAME );
86
87 if ( name.equalsIgnoreCase( "none" ) )
88 {
89 return PruneEvaluator.NONE;
90 }
91 else
92 {
93 throw new EvaluationException( "Unrecognized prune evaluator name '" + name + "'" );
94 }
95 }
96
97 private static Predicate<Path> builtInReturnFilter( Map<String, Object> description )
98 {
99 String name = (String) description.get( KEY_NAME );
100
101 if ( name.equalsIgnoreCase( "all" ) )
102 {
103 return Traversal.returnAll();
104 }
105 else if ( name.equalsIgnoreCase( "all but start node" ) )
106 {
107 return Traversal.returnAllButStartNode();
108 }
109 else
110 {
111 throw new EvaluationException( "Unrecognized return evaluator name '" + name + "'" );
112 }
113 }
114
115 private static ScriptEngine scriptEngine( Map<String, Object> description )
116 {
117 String language = (String) description.get( KEY_LANGUAGE );
118 ScriptEngine engine = new ScriptEngineManager().getEngineByName( language );
119 if ( engine == null )
120 {
121 throw new EvaluationException( "Unknown script language '" + language + "'" );
122 }
123 return engine;
124 }
125
126
127
128
129
130 private static abstract class ScriptExecutor
131 {
132 abstract Object eval( Path position );
133 }
134
135 private static class EvalScriptExecutor extends ScriptExecutor
136 {
137 private final ScriptEngine script;
138 private final String body;
139
140 EvalScriptExecutor( ScriptEngine script, String body )
141 {
142 this.script = script;
143 this.body = body;
144 }
145
146 @Override
147 Object eval( Path position )
148 {
149 try
150 {
151 this.script.getContext().setAttribute( "position", position,
152 ScriptContext.ENGINE_SCOPE );
153 return this.script.eval( body );
154 }
155 catch ( ScriptException e )
156 {
157 throw new EvaluationException( e );
158 }
159 }
160 }
161
162 private static class CompiledScriptExecutor extends ScriptExecutor
163 {
164 private final CompiledScript script;
165 private final ScriptContext context;
166
167 CompiledScriptExecutor( CompiledScript script, ScriptContext context )
168 {
169 this.script = script;
170 this.context = context;
171 }
172
173 @Override
174 Object eval( Path position )
175 {
176 try
177 {
178 this.context.setAttribute( "position", position, ScriptContext.ENGINE_SCOPE );
179 return this.script.eval( this.context );
180 }
181 catch ( ScriptException e )
182 {
183 throw new EvaluationException( e );
184 }
185 }
186 }
187
188 private static abstract class ScriptedEvaluator
189 {
190 private final ScriptEngine engine;
191 private final String body;
192 private ScriptExecutor executor;
193
194 ScriptedEvaluator( ScriptEngine engine, String body )
195 {
196 this.engine = engine;
197 this.body = body;
198 }
199
200 protected ScriptExecutor executor( Path position )
201 {
202
203
204
205 if ( this.executor == null )
206 {
207 try
208 {
209 ScriptContext context = new SimpleScriptContext();
210 context.setAttribute( "position", position, ScriptContext.ENGINE_SCOPE );
211 this.engine.setContext( context );
212 if ( this.engine instanceof Compilable )
213 {
214 this.executor = new CompiledScriptExecutor(
215 ((Compilable) engine).compile( body ), context );
216 }
217 else
218 {
219 this.executor = new EvalScriptExecutor( engine, body );
220 }
221 return executor;
222 }
223 catch ( ScriptException e )
224 {
225 throw new EvaluationException( e );
226 }
227 }
228 return this.executor;
229 }
230 }
231
232 private static class ScriptedPruneEvaluator extends ScriptedEvaluator implements PruneEvaluator
233 {
234 ScriptedPruneEvaluator( ScriptEngine engine, String body )
235 {
236 super( engine, body );
237 }
238
239 public boolean pruneAfter( Path position )
240 {
241 return (Boolean) executor( position ).eval( position );
242 }
243 }
244
245 private static class ScriptedReturnEvaluator extends ScriptedEvaluator implements
246 Predicate<Path>
247 {
248 ScriptedReturnEvaluator( ScriptEngine engine, String body )
249 {
250 super( engine, body );
251 }
252
253 public boolean accept( Path position )
254 {
255 return (Boolean) this.executor( position ).eval( position );
256 }
257 }
258 }