Java tutorial
/* * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is JSeance code, released * May 6, 2009. * * The Initial Developer of the Original Code is * Andres Murillo. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. */ package com.CodeSeance.JSeance2.CodeGenXML; import com.CodeSeance.JSeance2.CodeGenXML.DependencyTracking.TemplateDependencies; import org.apache.commons.logging.Log; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.xml.XMLObject; import org.w3c.dom.Document; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.*; import java.util.Stack; /** This is class serves a dual purpose, it encapsulates the functionality of the JavaScript engine (Rhino) and manages the stack of Context objets that become active as Template scopes execute. @author Andres Murillo @version 1.0 */ public class Context { /* * Constructor initialized the JavaScript engine and pushes a new context on the stack, * note that the thread that created the context manager should be the same calling any * action on it or its children (rhino js engine requirement). * dispose needs to be called when the Context is no longer needed to release * resources */ public Context(File includesDir, File modelsDir, File targetDir, boolean ignoreReadOnlyOuputFiles, TemplateDependencies templateDependencies) { this.includesDir = includesDir; this.modelsDir = modelsDir; this.targetDir = targetDir; this.ignoreReadOnlyOuputFiles = ignoreReadOnlyOuputFiles; this.templateDependencies = templateDependencies; initializeJavaScriptEngine(); } // The working directories and runtime configuration public final File includesDir; public final File modelsDir; public final File targetDir; public final boolean ignoreReadOnlyOuputFiles; // Dependency manager, readers and writers need to use it public final TemplateDependencies templateDependencies; // The context factory to be used across all instances final static ContextFactory factory = new ContextFactory(); public static final String JAVASCRIPT_UTILS_FILE = "JSeanceUtils.js"; private final static String XML_CREATE_FN = "JSeanceUtils_CreateXML"; private final static String XML_EVAL_PATH_FN = "JSeanceUtils_EvalXMLPath"; private final static String XML_LENGTH_FN = "JSeanceUtils_XMLLength"; private final static String XML_GET_NODE_AT_FN = "JSeanceUtils_XMLGetNodeAt"; private final static String XML_NODE_TO_STRING = "JSeanceUtils_XMLNodeToString"; //Initializes the JavaScript engine (Rhino) with the required context objects and instances private void initializeJavaScriptEngine() { jsContext = factory.enterContext(); try { if (ExecutionError.simulate_CONTEXTMANAGER_INITIALIZE_ERROR) { ExecutionError.simulate_CONTEXTMANAGER_INITIALIZE_ERROR = false; throw new Exception("Simulated exception for log testing"); } jsScope = jsContext.initStandardObjects(); // Declare the java classes that implement models ScriptableObject.defineClass(jsScope, JSModels.class); ScriptableObject.defineClass(jsScope, JSModel.class); ScriptableObject.putProperty(jsScope, "Models", jsModels); // Load the utilities JavaScript file InputStream jsFile = XMLLoader.class.getClassLoader().getResourceAsStream(JAVASCRIPT_UTILS_FILE); Reader jsReader = new BufferedReader(new InputStreamReader(jsFile)); jsContext.evaluateReader(jsScope, jsReader, JAVASCRIPT_UTILS_FILE, 0, null); } catch (Exception ex) { // Wrap Exception with RuntimeException since caller won't be able to handle it throw new RuntimeException(ExecutionError.CONTEXTMANAGER_INITIALIZE_ERROR.getMessage(ex.getMessage())); } } public void addModel(String name, JSModel model) { name = setDefaultModelNameIfEmpty(name); jsModels.setModel(name, model); // Added for supporting a default model referenced by the word "Model" if (name.equals(UNNAMED_MODEL_NAME)) { ScriptableObject.putProperty(jsScope, "Model", model); } } private final static String UNNAMED_MODEL_NAME = "default"; // Returns the default model name if the specified string is empty private String setDefaultModelNameIfEmpty(String name) { return ("".equals(name) || name == null) ? UNNAMED_MODEL_NAME : name; } private JSModels jsModels = new JSModels(); /* * Returns the specified JavaScript representation of a model */ public JSModel getModel(String name) { name = setDefaultModelNameIfEmpty(name); // Obtain the model locally, a deep copy is performend on new context creation return jsModels.getModel(name); } public void pushTextSink(StringBuffer textSink) { textSinkStack.push(textSink); } public void popTextSink() { textSinkStack.pop(); } private final Stack<StringBuffer> textSinkStack = new Stack<StringBuffer>(); /* * writes text to the closes textSink, transversing parents until a textSink is found. Note that the Template node * defines the default textSink */ public void writeText(String text) { textSinkStack.peek().append(text); } // Class logger private Log log = Runtime.CreateLogger(this.getClass()); public void LogInfoMessage(Log log, String tagName, String message) { if (log.isInfoEnabled()) { log.info(String.format("<%s> - %s", tagName, message)); } } /* * Transforms a Java XML Document into a JavaScript XMLObject that can be used within a script context */ public XMLObject createXMLObject(Document document) { //Transform to String Transformer transformer; StreamResult result; try { if (ExecutionError.simulate_CONTEXTMANAGER_CREATEXMLOBJECT_ERROR) { ExecutionError.simulate_CONTEXTMANAGER_CREATEXMLOBJECT_ERROR = false; throw new TransformerException("Simulated exception for log testing"); } transformer = TransformerFactory.newInstance().newTransformer(); // The next section is required in order for the JS engine to parse the XML transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); result = new StreamResult(new StringWriter()); DOMSource source = new DOMSource(document); transformer.transform(source, result); } catch (TransformerException ex) { // Wrap Exception with RuntimeException since caller won't be able to handle it throw new RuntimeException( ExecutionError.CONTEXTMANAGER_CREATEXMLOBJECT_ERROR.getMessage(ex.getMessage())); } String xmlString = result.getWriter().toString(); // Create the JS Objects Object[] args = { xmlString }; Object jsXMLObj = callJSFunction(XML_CREATE_FN, args); assert jsXMLObj instanceof XMLObject; return (XMLObject) jsXMLObj; } /* * Evaluates the specified E4X Path on a JavaScript XML Node */ public Object evaluateE4XPath(XMLObject xmlObj, String e4XPath) { Object result = xmlObj; if (!"".equals(e4XPath) && e4XPath != null) { Object[] args = { xmlObj, e4XPath }; result = callJSFunction(XML_EVAL_PATH_FN, args); } return result; } /* * Evaluates the specified E4X Path on a JavaScript XML Node */ public String xmlNodeToString(XMLObject xmlObj) { Object[] args = { xmlObj }; return callJSFunction(XML_NODE_TO_STRING, args).toString(); } /* * returns the collection size of a Javascript NodeList */ public int getXMLCollectionSize(XMLObject xmlObj) { Object[] args = { xmlObj }; Object result = callJSFunction(XML_LENGTH_FN, args); assert result instanceof Integer; return (Integer) result; } /* * Returns a JavaScript XML Node at the specified place in the NodeList */ public XMLObject getXMLItemAt(XMLObject xmlObj, int index) { Object[] args = { xmlObj, index }; Object result = callJSFunction(XML_GET_NODE_AT_FN, args); assert result instanceof XMLObject; return (XMLObject) result; } // JSEngine context variables org.mozilla.javascript.Context jsContext = null; org.mozilla.javascript.Scriptable jsScope = null; // Releases the resources assciated with the JSEngine attached to the current thread public void dispose() { org.mozilla.javascript.Context.exit(); } // Evaluates the specified JavaScript text - Public version useful for logging & breakpoints public Object evaluateJS(String script, String sourceName, int lineNo) { if (log.isDebugEnabled()) { log.debug(String.format("Evaluating JavaScript:[%s]", script)); } return evaluateJSPrivate(script, sourceName, lineNo); } // Evaluates the specified JavaScript text - Private version, not logged private Object evaluateJSPrivate(String script, String sourceName, int lineNo) { try { return jsContext.evaluateString(jsScope, script, sourceName, lineNo, null); } catch (Exception ex) { throw new RuntimeException(ExecutionError.JAVASCRIPT_EVAL_ERROR.getMessage(script, ex.getMessage())); } } // Calls the specified Javascript function public Object callJSFunction(String functionName, Object[] fxArgs) { Object fxObj = jsScope.get(functionName, jsScope); assert fxObj instanceof org.mozilla.javascript.Function; org.mozilla.javascript.Function fx = (org.mozilla.javascript.Function) fxObj; return fx.call(jsContext, jsScope, jsScope, fxArgs); } public void loadJavaScriptInclude(File file) throws IOException { // Add the dependency templateDependencies.addInputFile(file); InputStream inputStream = new FileInputStream(file); Reader jsReader = new BufferedReader(new InputStreamReader(inputStream)); jsContext.evaluateReader(jsScope, jsReader, file.getName(), 0, null); } }