Java tutorial
/** * Copyright 2011 meltmedia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.xchain.namespaces.jsl; import java.util.LinkedList; import org.xchain.Command; import org.xchain.Filter; import org.xchain.Locatable; import org.xchain.Registerable; import org.xchain.framework.lifecycle.Execution; import org.xchain.framework.sax.CommandXmlReader; import org.xchain.framework.sax.CommandHandler; import org.xchain.impl.ChainImpl; import static org.xchain.namespaces.jsl.CommandExecutionState.*; import org.xchain.namespaces.sax.PipelineCommand; import org.xchain.framework.jxpath.ScopedJXPathContextImpl; import org.apache.commons.jxpath.JXPathContext; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.Locator; import javax.xml.namespace.QName; /** * The base class for generated jsl template commands. * * @author Christian Trimble * @author Jason Rose */ public abstract class AbstractTemplateCommand extends ChainImpl implements Locatable, Registerable { /** The thread local stack of command execution state arrays. */ public static final ThreadLocal<LinkedList<CommandExecutionState[]>> commandExecutionStateStackTL = new ThreadLocal<LinkedList<CommandExecutionState[]>>(); /** The thread local stack of element output state arrays. */ public static final ThreadLocal<LinkedList<ElementOutputState[]>> elementOutputStateStackTL = new ThreadLocal<LinkedList<ElementOutputState[]>>(); public static final ThreadLocal<LinkedList<QName>> dynamicElementStackTL = new ThreadLocal<LinkedList<QName>>(); public static final ThreadLocal<SAXException> saxExceptionTl = new ThreadLocal<SAXException>(); public static final ThreadLocal<Integer> depthTl = new ThreadLocal<Integer>(); /** * Returns the command execute state array for the current thread. */ protected static CommandExecutionState[] getCommandExecutionState() { LinkedList<CommandExecutionState[]> stack = commandExecutionStateStackTL.get(); if (stack == null || stack.isEmpty()) { throw new IllegalStateException("getCommandExecutionState() called outside of execute method."); } return stack.getFirst(); } protected static ElementOutputState[] getElementOutputState() { LinkedList<ElementOutputState[]> stack = elementOutputStateStackTL.get(); if (stack == null || stack.isEmpty()) { throw new IllegalStateException("getElementOutputState() called outside of execute method."); } return stack.getFirst(); } protected static LinkedList<QName> getDynamicElementStack() { LinkedList<QName> stack = dynamicElementStackTL.get(); if (stack == null) { throw new IllegalStateException("getDynamicElementStack() called outside of execute method."); } return stack; } protected Locator locator; private int elementCount; protected String systemId = null; protected QName qName = null; protected int templateDepth = 0; public AbstractTemplateCommand(int elementCount) { this.elementCount = elementCount; } public boolean isRegistered() { return qName != null && systemId != null; } public void setQName(QName qName) { this.qName = qName; } public QName getQName() { return this.qName; } public void setSystemId(String systemId) { this.systemId = systemId; } public String getSystemId() { return this.systemId; } /** * Pushes a new command execution state array onto the stack. The new state array has its values initialized to PRE_EXECUTE. */ private final void pushCommandExecutionState() { // create the state array. int childCount = getCommandList().size(); CommandExecutionState[] state = new CommandExecutionState[childCount]; for (int i = 0; i < childCount; i++) { state[i] = PRE_EXECUTE; } // get the stack, creating it if it is missing. LinkedList<CommandExecutionState[]> stack = commandExecutionStateStackTL.get(); if (stack == null) { stack = new LinkedList<CommandExecutionState[]>(); commandExecutionStateStackTL.set(stack); } // push the state array. stack.addFirst(state); } /** * Pops the current command execution state array from the stack. */ private final void popCommandExecutionState() { // get the stack for the current thread. LinkedList<CommandExecutionState[]> stack = commandExecutionStateStackTL.get(); // if there was not a stack, then we are in an illegal state. if (stack == null) { throw new IllegalStateException( "popCommandExecutionState() called when there was not a current stack."); } // remove the state array from the stack. stack.removeFirst(); // if the stack is now empty, clean the thread local up. if (stack.isEmpty()) { commandExecutionStateStackTL.remove(); } } private final void pushElementOutputState() { // create the state array. ElementOutputState[] state = new ElementOutputState[elementCount]; for (int i = 0; i < elementCount; i++) { state[i] = ElementOutputState.PRE_START; } // get the stack, creating it if it is missing. LinkedList<ElementOutputState[]> stack = elementOutputStateStackTL.get(); if (stack == null) { stack = new LinkedList<ElementOutputState[]>(); elementOutputStateStackTL.set(stack); } // push the state array. stack.addFirst(state); } private final void popElementOutputState() { // get the stack for the current thread. LinkedList<ElementOutputState[]> stack = elementOutputStateStackTL.get(); // if there was not a stack, then we are in an illegal state. if (stack == null) { throw new IllegalStateException("popElementOutputState() called when there was not a current stack."); } // remove the state array from the stack. stack.removeFirst(); // if the stack is now empty, clean the thread local up. if (stack.isEmpty()) { elementOutputStateStackTL.remove(); } } private final void pushHandlerInfo(JXPathContext context) { } private final void popHandlerInfo() { } /** * Uses the specified nameXPath and namespaceXPath to create a dynamic qName. */ protected QName dynamicQName(JXPathContext context, String nameXPath, String namespaceXPath, boolean includeDefaultPrefix) throws SAXException { String name = (String) context.getValue(nameXPath, String.class); if (name == null) { throw new SAXException("QNames cannot have null names."); } String namespace = null; if (namespaceXPath != null) { namespace = (String) context.getValue(namespaceXPath, String.class); if (namespace == null) { throw new SAXException("Namespace uris cannot be null."); } } String[] parts = name.split(":", 2); String prefixPart = parts.length == 2 ? parts[0] : ""; String localPart = parts.length == 2 ? parts[1] : parts[0]; // if the prefix is not "", then it must be defined in the context. String prefixPartNamespace = (includeDefaultPrefix || !"".equals(prefixPart)) ? context.getNamespaceURI(prefixPart) : ""; if (!"".equals(prefixPart) && prefixPartNamespace == null) { throw new SAXException("The prefix '" + prefixPart + "' is not defined in the context."); } // ASSERT: if the prefix is not "", then it is defined in the context. // if the namespace is null, then the prefix defines the namespace. if (namespace == null) { return new QName(prefixPartNamespace != null ? prefixPartNamespace : "", localPart, prefixPart); } // ASSERT: the namespace was specified. // if the prefix is "", then we can lookup the proper namespace. if ("".equals(prefixPart)) { if (!namespace.equals(prefixPartNamespace)) { String namespacePrefix = ((ScopedJXPathContextImpl) context).getPrefix(namespace); if (namespacePrefix == null) { throw new SAXException("The namespace '" + namespace + "' is not bound to a prefix."); } if (!includeDefaultPrefix && "".equals(namespacePrefix)) { throw new SAXException("The namespace '" + namespace + "' cannot be used for the attribute '" + name + "', because it maps to the default namespace."); } return new QName(namespace, localPart, namespacePrefix); } else { return new QName(namespace, localPart, prefixPart); } } // ASSERT: the prefix is defined and the namespace is defined, if they do not match, we must fail. if (!namespace.equals(prefixPartNamespace)) { throw new SAXException("The prefix '" + prefixPart + "' is bound to '" + prefixPartNamespace + "', but the namespace '" + namespace + "' is required."); } // ASSERT: the namespace and prefix will work together. return new QName(namespace, localPart, prefixPart); } protected void trackStartElement(int elementIndex) { getElementOutputState()[elementIndex] = ElementOutputState.STARTED; } protected void trackEndElement(int elementIndex) { getElementOutputState()[elementIndex] = ElementOutputState.ENDED; } public boolean isElementStarted(int elementIndex) { return getElementOutputState()[elementIndex] == ElementOutputState.STARTED; } public void setLocator(Locator locator) { this.locator = locator; } public Locator getLocator() { return locator; } /** * Initializes the internal state of this command and then calls executeTemplate( JXPathContext ). */ public final boolean execute(JXPathContext context) throws Exception { boolean inExecution = Execution.inExecution(); boolean createDynamicElementStack = dynamicElementStackTL.get() == null; boolean result = false; if (!inExecution) { Execution.startExecution(context); } context = Execution.startCommandExecute(this, context); try { if (depthTl.get() == null) { depthTl.set(new Integer(0)); } else { depthTl.set(depthTl.get() + 1); } if (createDynamicElementStack) { dynamicElementStackTL.set(new LinkedList<QName>()); } // push a new command execution state array onto the stack. pushCommandExecutionState(); pushElementOutputState(); // push the information about the current output document. pushHandlerInfo(context); result = executeTemplate(context); // if there was a sax exception registered and this is the outer most template, then we need to pass it up. if (depthTl.get().equals(new Integer(0)) && hasSaxExceptionFired()) { SAXException saxException = saxExceptionTl.get(); saxExceptionTl.remove(); throw saxException; } } catch (Exception e) { Execution.exceptionThrown(this, e); throw e; } finally { // pop the information about the current output document. popHandlerInfo(); popElementOutputState(); // pop the command execution state array off of the stack. popCommandExecutionState(); if (createDynamicElementStack) { dynamicElementStackTL.remove(); } if (depthTl.get().equals(new Integer(0))) { depthTl.remove(); } else { depthTl.set(depthTl.get() - 1); } context = Execution.endCommandExecute(this, context); if (!inExecution) { Execution.endExecution(); } } return result; } /** * Generated templates implement this method to provide sax output. */ public abstract boolean executeTemplate(JXPathContext context) throws Exception; /** * Calls the execute method of the children specified by childIndecies, passing it the supplied context. * If the execute call completes without failing, then the command execution state for that * child command is updated to EXECUTED. */ protected final boolean executeChildren(JXPathContext context, int[] childIndecies) throws Exception { boolean result = false; for (int i = 0; !result && i < childIndecies.length; i++) { getCommandExecutionState()[childIndecies[i]] = EXECUTED; result = getCommandList().get(childIndecies[i]).execute(context); } return result; } /** * The post process command for virtual chains. */ protected final boolean virtualPostProcess(JXPathContext context, Exception exception, boolean result, int[] childIndecies) throws Exception { boolean handled = postProcessChildren(context, exception, childIndecies); if (exception != null && !handled) { throw exception; } else if (hasSaxExceptionFired()) { return true; } else { return result; } } /** * Calls post process of the specified indices. */ protected final boolean postProcessChildren(JXPathContext context, Exception exception, int[] childIndecies) { boolean handled = false; for (int i = childIndecies.length - 1; i >= 0; i--) { if (getCommandExecutionState()[childIndecies[i]] == EXECUTED) { try { Command command = getCommandList().get(childIndecies[i]); if (command instanceof Filter) { handled = ((Filter) command).postProcess(context, exception) || handled; } } catch (Exception e) { // ignore this exception. } getCommandExecutionState()[childIndecies[i]] = POST_PROCESSED; } } return handled; } /** * Turns a QName into its prefixed string representation. */ protected static final String toPrefixedQName(QName qName) { if (qName.getPrefix() == null || "".equals(qName.getPrefix())) { return qName.getLocalPart(); } else { return qName.getPrefix() + ":" + qName.getLocalPart(); } } protected CommandHandler getContentHandler() { return ((CommandXmlReader) PipelineCommand.getPipelineConfig().getXmlReader()).getCommandHandler(); } protected boolean hasSaxExceptionFired() { return saxExceptionTl.get() != null; } protected void registerSaxException(SAXException saxException) { saxExceptionTl.set(saxException); } }