1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.xchain.namespaces.jsl;
17
18 import java.util.LinkedList;
19
20 import org.xchain.Command;
21 import org.xchain.Filter;
22 import org.xchain.Locatable;
23 import org.xchain.Registerable;
24 import org.xchain.framework.lifecycle.Execution;
25 import org.xchain.framework.sax.CommandXmlReader;
26 import org.xchain.framework.sax.CommandHandler;
27 import org.xchain.impl.ChainImpl;
28 import static org.xchain.namespaces.jsl.CommandExecutionState.*;
29 import org.xchain.namespaces.sax.PipelineCommand;
30 import org.xchain.framework.jxpath.ScopedJXPathContextImpl;
31
32 import org.apache.commons.jxpath.JXPathContext;
33
34 import org.xml.sax.ContentHandler;
35 import org.xml.sax.SAXException;
36 import org.xml.sax.Locator;
37
38 import javax.xml.namespace.QName;
39
40
41
42
43
44
45
46 public abstract class AbstractTemplateCommand
47 extends ChainImpl
48 implements Locatable, Registerable
49 {
50
51 public static final ThreadLocal<LinkedList<CommandExecutionState[]>> commandExecutionStateStackTL = new ThreadLocal<LinkedList<CommandExecutionState[]>>();
52
53
54 public static final ThreadLocal<LinkedList<ElementOutputState[]>> elementOutputStateStackTL = new ThreadLocal<LinkedList<ElementOutputState[]>>();
55
56 public static final ThreadLocal<LinkedList<QName>> dynamicElementStackTL = new ThreadLocal<LinkedList<QName>>();
57
58 public static final ThreadLocal<SAXException> saxExceptionTl = new ThreadLocal<SAXException>();
59
60 public static final ThreadLocal<Integer> depthTl = new ThreadLocal<Integer>();
61
62
63
64
65 protected static CommandExecutionState[] getCommandExecutionState()
66 {
67 LinkedList<CommandExecutionState[]> stack = commandExecutionStateStackTL.get();
68
69 if( stack == null || stack.isEmpty() ) {
70 throw new IllegalStateException("getCommandExecutionState() called outside of execute method.");
71 }
72
73 return stack.getFirst();
74 }
75
76 protected static ElementOutputState[] getElementOutputState()
77 {
78 LinkedList<ElementOutputState[]> stack = elementOutputStateStackTL.get();
79
80 if( stack == null || stack.isEmpty() ) {
81 throw new IllegalStateException("getElementOutputState() called outside of execute method.");
82 }
83
84 return stack.getFirst();
85 }
86
87 protected static LinkedList<QName> getDynamicElementStack()
88 {
89 LinkedList<QName> stack = dynamicElementStackTL.get();
90 if( stack == null ) {
91 throw new IllegalStateException("getDynamicElementStack() called outside of execute method.");
92 }
93 return stack;
94 }
95
96 protected Locator locator;
97 private int elementCount;
98 protected String systemId = null;
99 protected QName qName = null;
100 protected int templateDepth = 0;
101
102 public AbstractTemplateCommand( int elementCount )
103 {
104 this.elementCount = elementCount;
105 }
106
107 public boolean isRegistered() { return qName != null && systemId != null; }
108 public void setQName( QName qName ) { this.qName = qName; }
109 public QName getQName() { return this.qName; }
110 public void setSystemId( String systemId ) { this.systemId = systemId; }
111 public String getSystemId() { return this.systemId; }
112
113
114
115
116 private final void pushCommandExecutionState()
117 {
118
119 int childCount = getCommandList().size();
120 CommandExecutionState[] state = new CommandExecutionState[childCount];
121 for( int i = 0; i < childCount; i++ ) {
122 state[i] = PRE_EXECUTE;
123 }
124
125
126 LinkedList<CommandExecutionState[]> stack = commandExecutionStateStackTL.get();
127 if( stack == null ) {
128 stack = new LinkedList<CommandExecutionState[]>();
129 commandExecutionStateStackTL.set(stack);
130 }
131
132
133 stack.addFirst(state);
134 }
135
136
137
138
139 private final void popCommandExecutionState()
140 {
141
142 LinkedList<CommandExecutionState[]> stack = commandExecutionStateStackTL.get();
143
144
145 if( stack == null ) {
146 throw new IllegalStateException("popCommandExecutionState() called when there was not a current stack.");
147 }
148
149
150 stack.removeFirst();
151
152
153 if( stack.isEmpty() ) {
154 commandExecutionStateStackTL.remove();
155 }
156 }
157
158 private final void pushElementOutputState()
159 {
160
161 ElementOutputState[] state = new ElementOutputState[elementCount];
162 for( int i = 0; i < elementCount; i++ ) {
163 state[i] = ElementOutputState.PRE_START;
164 }
165
166
167 LinkedList<ElementOutputState[]> stack = elementOutputStateStackTL.get();
168 if( stack == null ) {
169 stack = new LinkedList<ElementOutputState[]>();
170 elementOutputStateStackTL.set(stack);
171 }
172
173
174 stack.addFirst(state);
175 }
176
177 private final void popElementOutputState()
178 {
179
180 LinkedList<ElementOutputState[]> stack = elementOutputStateStackTL.get();
181
182
183 if( stack == null ) {
184 throw new IllegalStateException("popElementOutputState() called when there was not a current stack.");
185 }
186
187
188 stack.removeFirst();
189
190
191 if( stack.isEmpty() ) {
192 elementOutputStateStackTL.remove();
193 }
194
195 }
196
197 private final void pushHandlerInfo( JXPathContext context )
198 {
199
200 }
201
202 private final void popHandlerInfo()
203 {
204
205 }
206
207
208
209
210 protected QName dynamicQName( JXPathContext context, String nameXPath, String namespaceXPath, boolean includeDefaultPrefix )
211 throws SAXException
212 {
213 String name = (String)context.getValue(nameXPath, String.class);
214 if( name == null ) {
215 throw new SAXException("QNames cannot have null names.");
216 }
217 String namespace = null;
218 if( namespaceXPath != null ) {
219 namespace = (String)context.getValue(namespaceXPath, String.class);
220 if( namespace == null ) {
221 throw new SAXException("Namespace uris cannot be null.");
222 }
223 }
224
225 String[] parts = name.split(":", 2);
226 String prefixPart = parts.length == 2 ? parts[0] : "";
227 String localPart = parts.length == 2 ? parts[1] : parts[0];
228
229
230 String prefixPartNamespace = (includeDefaultPrefix || !"".equals(prefixPart)) ? context.getNamespaceURI(prefixPart) : "";
231 if( !"".equals(prefixPart) && prefixPartNamespace == null ) {
232 throw new SAXException("The prefix '"+prefixPart+"' is not defined in the context.");
233 }
234
235
236
237
238 if( namespace == null ) {
239 return new QName( prefixPartNamespace != null ? prefixPartNamespace : "", localPart, prefixPart );
240 }
241
242
243
244
245 if( "".equals(prefixPart) ) {
246 if( !namespace.equals(prefixPartNamespace) ) {
247 String namespacePrefix = ((ScopedJXPathContextImpl)context).getPrefix(namespace);
248 if( namespacePrefix == null ) {
249 throw new SAXException("The namespace '"+namespace+"' is not bound to a prefix.");
250 }
251 if( !includeDefaultPrefix && "".equals(namespacePrefix) ) {
252 throw new SAXException("The namespace '"+namespace+"' cannot be used for the attribute '"+name+"', because it maps to the default namespace.");
253 }
254 return new QName(namespace, localPart, namespacePrefix);
255 }
256 else {
257 return new QName(namespace, localPart, prefixPart);
258 }
259 }
260
261
262 if( !namespace.equals(prefixPartNamespace) ) {
263 throw new SAXException("The prefix '"+prefixPart+"' is bound to '"+prefixPartNamespace+"', but the namespace '"+namespace+"' is required.");
264 }
265
266
267 return new QName(namespace, localPart, prefixPart);
268 }
269
270 protected void trackStartElement( int elementIndex )
271 {
272 getElementOutputState()[elementIndex] = ElementOutputState.STARTED;
273 }
274
275 protected void trackEndElement( int elementIndex )
276 {
277 getElementOutputState()[elementIndex] = ElementOutputState.ENDED;
278 }
279
280 public boolean isElementStarted( int elementIndex )
281 {
282 return getElementOutputState()[elementIndex] == ElementOutputState.STARTED;
283 }
284
285 public void setLocator( Locator locator ) { this.locator = locator; }
286 public Locator getLocator() { return locator; }
287
288
289
290
291 public final boolean execute( JXPathContext context )
292 throws Exception
293 {
294 boolean inExecution = Execution.inExecution();
295 boolean createDynamicElementStack = dynamicElementStackTL.get() == null;
296 boolean result = false;
297
298 if( !inExecution ) {
299 Execution.startExecution(context);
300 }
301 context = Execution.startCommandExecute(this, context);
302 try {
303 if( depthTl.get() == null ) {
304 depthTl.set(new Integer(0));
305 }
306 else {
307 depthTl.set(depthTl.get()+1);
308 }
309 if( createDynamicElementStack ) {
310 dynamicElementStackTL.set(new LinkedList<QName>());
311 }
312
313 pushCommandExecutionState();
314 pushElementOutputState();
315
316
317 pushHandlerInfo( context );
318
319 result = executeTemplate( context );
320
321
322 if( depthTl.get().equals(new Integer(0)) && hasSaxExceptionFired() ) {
323 SAXException saxException = saxExceptionTl.get();
324 saxExceptionTl.remove();
325 throw saxException;
326 }
327 }
328 catch( Exception e ) {
329 Execution.exceptionThrown(this, e);
330 throw e;
331 }
332 finally {
333
334 popHandlerInfo();
335
336 popElementOutputState();
337
338 popCommandExecutionState();
339
340 if( createDynamicElementStack ) {
341 dynamicElementStackTL.remove();
342 }
343
344 if( depthTl.get().equals(new Integer(0)) ) {
345 depthTl.remove();
346 }
347 else {
348 depthTl.set(depthTl.get()-1);
349 }
350
351 context = Execution.endCommandExecute(this, context);
352
353 if( !inExecution ) {
354 Execution.endExecution();
355 }
356 }
357
358 return result;
359 }
360
361
362
363
364 public abstract boolean executeTemplate( JXPathContext context )
365 throws Exception;
366
367
368
369
370
371
372 protected final boolean executeChildren( JXPathContext context, int[] childIndecies )
373 throws Exception
374 {
375 boolean result = false;
376 for( int i = 0; !result && i < childIndecies.length; i ++ ) {
377 getCommandExecutionState()[childIndecies[i]] = EXECUTED;
378 result = getCommandList().get(childIndecies[i]).execute(context);
379 }
380 return result;
381 }
382
383
384
385
386 protected final boolean virtualPostProcess( JXPathContext context, Exception exception, boolean result, int[] childIndecies )
387 throws Exception
388 {
389 boolean handled = postProcessChildren( context, exception, childIndecies );
390
391 if( exception != null && !handled ) {
392 throw exception;
393 }
394 else if( hasSaxExceptionFired() ) {
395 return true;
396 }
397 else {
398 return result;
399 }
400 }
401
402
403
404
405 protected final boolean postProcessChildren( JXPathContext context, Exception exception, int[] childIndecies )
406 {
407 boolean handled = false;
408
409 for( int i = childIndecies.length - 1; i >= 0; i-- ) {
410 if( getCommandExecutionState()[childIndecies[i]] == EXECUTED ) {
411 try {
412 Command command = getCommandList().get(childIndecies[i]);
413 if( command instanceof Filter ) {
414 handled = ((Filter)command).postProcess( context, exception ) || handled;
415 }
416 }
417 catch( Exception e ) {
418
419 }
420 getCommandExecutionState()[childIndecies[i]] = POST_PROCESSED;
421 }
422 }
423
424 return handled;
425 }
426
427
428
429
430 protected static final String toPrefixedQName( QName qName )
431 {
432 if( qName.getPrefix() == null || "".equals(qName.getPrefix()) ) {
433 return qName.getLocalPart();
434 }
435 else {
436 return qName.getPrefix() + ":" + qName.getLocalPart();
437 }
438 }
439
440 protected CommandHandler getContentHandler()
441 {
442 return ((CommandXmlReader)PipelineCommand.getPipelineConfig().getXmlReader()).getCommandHandler();
443 }
444
445 protected boolean hasSaxExceptionFired()
446 {
447 return saxExceptionTl.get() != null;
448 }
449
450 protected void registerSaxException( SAXException saxException )
451 {
452 saxExceptionTl.set(saxException);
453 }
454 }