All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.instruct.ResultDocument Maven / Gradle / Ivy

Go to download

Provides a basic XSLT 2.0 and XQuery 1.0 processor (W3C Recommendations, January 2007). Command line interfaces and implementations of several Java APIs (DOM, XPath, s9api) are also included.

The newest version!
package net.sf.saxon.instruct;

import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.Err;
import net.sf.saxon.OutputURIResolver;
import net.sf.saxon.event.SaxonOutputKeys;
import net.sf.saxon.event.SequenceReceiver;
import net.sf.saxon.event.StandardOutputResolver;
import net.sf.saxon.expr.*;
import net.sf.saxon.functions.EscapeURI;
import net.sf.saxon.functions.Serialize;
import net.sf.saxon.om.*;
import net.sf.saxon.pattern.EmptySequenceTest;
import net.sf.saxon.sort.IntHashMap;
import net.sf.saxon.sort.IntIterator;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.SaxonErrorCode;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.SchemaType;
import net.sf.saxon.type.TypeHierarchy;
import net.sf.saxon.type.Type;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import java.util.StringTokenizer;

/**
 * The compiled form of an xsl:result-document element in the stylesheet.
 * 

* The xsl:result-document element takes an attribute href="filename". The filename will * often contain parameters, e.g. {position()} to ensure that a different file is produced * for each element instance. *

* There is a further attribute "format" which determines the format of the * output file, it identifies the name of an xsl:output element containing the output * format details. In addition, individual serialization properties may be specified as attributes. * These are attribute value templates, so they may need to be computed at run-time. */ public class ResultDocument extends Instruction { private Expression href; private Expression formatExpression; // null if format was known at compile time private Expression content; private Properties globalProperties; private Properties localProperties; private String baseURI; // needed only for saxon:next-in-chain private int validationAction; private SchemaType schemaType; private IntHashMap serializationAttributes; private NamespaceResolver nsResolver; private Expression dynamicOutputElement; // used in saxon:result-document() extension function /** * Create a result-document instruction * @param globalProperties properties defined on static xsl:output * @param localProperties non-AVT properties defined on result-document element * @param href href attribute of instruction * @param formatExpression format attribute of instruction * @param baseURI base URI of the instruction * @param validationAction for example {@link Validation#STRICT} * @param schemaType schema type against which output is to be validated * @param serializationAttributes computed local properties * @param nsResolver namespace resolver */ public ResultDocument(Properties globalProperties, // properties defined on static xsl:output Properties localProperties, // non-AVT properties defined on result-document element Expression href, Expression formatExpression, // AVT defining the output format String baseURI, int validationAction, SchemaType schemaType, IntHashMap serializationAttributes, // computed local properties only NamespaceResolver nsResolver) { this.globalProperties = globalProperties; this.localProperties = localProperties; this.href = href; this.formatExpression = formatExpression; this.baseURI = baseURI; this.validationAction = validationAction; this.schemaType = schemaType; this.serializationAttributes = serializationAttributes; this.nsResolver = nsResolver; adoptChildExpression(href); for (Iterator it = serializationAttributes.valueIterator(); it.hasNext();) { adoptChildExpression((Expression) it.next()); } } /** * Set the expression that constructs the content * @param content the expression defining the content of the result document */ public void setContent(Expression content) { this.content = content; adoptChildExpression(content); } /** * Set an expression that evaluates to a run-time xsl:output element, used in the saxon:result-document() * extension function designed for use in XQuery * @param exp the expression whose result should be an xsl:output element */ public void setDynamicOutputElement(Expression exp) { dynamicOutputElement = exp; } /** * Simplify an expression. This performs any static optimization (by rewriting the expression * as a different expression). The default implementation does nothing. * @return the simplified expression * @throws net.sf.saxon.trans.XPathException * if an error is discovered during expression rewriting * @param visitor an expression visitor */ public Expression simplify(ExpressionVisitor visitor) throws XPathException { content = visitor.simplify(content); href = visitor.simplify(href); for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression value = (Expression)serializationAttributes.get(key); if (!(value instanceof Literal)) { value = visitor.simplify(value); serializationAttributes.put(key, value); } } return this; } public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { content = visitor.typeCheck(content, contextItemType); adoptChildExpression(content); if (href != null) { href = visitor.typeCheck(href, contextItemType); adoptChildExpression(href); } if (formatExpression != null) { formatExpression = visitor.typeCheck(formatExpression, contextItemType); adoptChildExpression(formatExpression); } for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression value = (Expression)serializationAttributes.get(key); if (!(value instanceof Literal)) { value = visitor.typeCheck(value, contextItemType); adoptChildExpression(value); serializationAttributes.put(key, value); } } try { DocumentInstr.checkContentSequence(visitor.getStaticContext(), content, validationAction, schemaType); } catch (XPathException err) { err.maybeSetLocation(this); throw err; } return this; } public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { content = visitor.optimize(content, contextItemType); adoptChildExpression(content); if (href != null) { href = visitor.optimize(href, contextItemType); adoptChildExpression(href); } if (formatExpression != null) { formatExpression = visitor.optimize(formatExpression, contextItemType); adoptChildExpression(formatExpression); // TODO: if the formatExpression is now a constant, could get the output properties now } for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression value = (Expression)serializationAttributes.get(key); if (!(value instanceof Literal)) { value = visitor.optimize(value, contextItemType); adoptChildExpression(value); serializationAttributes.put(key, value); } } return this; } public int getIntrinsicDependencies() { return StaticProperty.HAS_SIDE_EFFECTS; } /** * Copy an expression. This makes a deep copy. * * @return the copy of the original expression */ public Expression copy() { throw new UnsupportedOperationException("copy"); } /** * Handle promotion offers, that is, non-local tree rewrites. * @param offer The type of rewrite being offered * @throws XPathException */ protected void promoteInst(PromotionOffer offer) throws XPathException { content = doPromotion(content, offer); if (href != null) { href = doPromotion(href, offer); } for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression value = (Expression)serializationAttributes.get(key); if (!(value instanceof Literal)) { value = doPromotion(value, offer); serializationAttributes.put(key, value); } } } /** * Get the name of this instruction for diagnostic and tracing purposes * (the string "xsl:result-document") */ public int getInstructionNameCode() { return StandardNames.XSL_RESULT_DOCUMENT; } /** * Get the item type of the items returned by evaluating this instruction * @return the static item type of the instruction. This is empty: the result-document instruction * returns nothing. * @param th the type hierarchy cache */ public ItemType getItemType(TypeHierarchy th) { return EmptySequenceTest.getInstance(); } /** * Get all the XPath expressions associated with this instruction * (in XSLT terms, the expression present on attributes of the instruction, * as distinct from the child instructions in a sequence construction) */ public Iterator iterateSubExpressions() { ArrayList list = new ArrayList(6); list.add(content); if (href != null) { list.add(href); } if (formatExpression != null) { list.add(formatExpression); } for (Iterator it = serializationAttributes.valueIterator(); it.hasNext();) { list.add(it.next()); } if (dynamicOutputElement != null) { list.add(dynamicOutputElement); } return list.iterator(); } /** * Replace one subexpression by a replacement subexpression * @param original the original subexpression * @param replacement the replacement subexpression * @return true if the original subexpression is found */ public boolean replaceSubExpression(Expression original, Expression replacement) { boolean found = false; if (content == original) { content = replacement; found = true; } if (href == original) { href = replacement; found = true; } for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int k = it.next(); if (serializationAttributes.get(k) == original) { serializationAttributes.put(k, replacement); found = true; } } if (dynamicOutputElement == original) { dynamicOutputElement = replacement; found = true; } return found; } public TailCall processLeavingTail(XPathContext context) throws XPathException { final Controller controller = context.getController(); final Configuration config = controller.getConfiguration(); final NamePool namePool = config.getNamePool(); XPathContext c2 = context.newMinorContext(); c2.setOrigin(this); Result result; OutputURIResolver resolver = null; if (href == null) { result = controller.getPrincipalResult(); } else { try { String base = controller.getBaseOutputURI(); if (base == null && config.isAllowExternalFunctions()) { // if calling external functions is allowed, then the stylesheet is trusted, so // we allow it to write to files relative to the current directory base = new File(System.getProperty("user.dir")).toURI().toString(); } if (base != null) { base = EscapeURI.iriToUri(base).toString(); //TODO: avoid doing this repeatedly } resolver = controller.getOutputURIResolver(); String hrefValue = EscapeURI.iriToUri(href.evaluateAsString(context)).toString(); try { result = resolver.resolve(hrefValue, base); } catch (Exception err) { throw new XPathException("Exception thrown by OutputURIResolver", err); } if (result == null) { resolver = StandardOutputResolver.getInstance(); result = resolver.resolve(hrefValue, base); } } catch (TransformerException e) { throw XPathException.makeXPathException(e); } } if (!controller.checkUniqueOutputDestination(result.getSystemId())) { XPathException err = new XPathException("Cannot write more than one result document to the same URI, or write to a URI that has been read: " + result.getSystemId()); err.setXPathContext(context); err.setErrorCode("XTDE1490"); throw err; // TODO: writing to a document that has previously been read should be error code XTDE1500 } else { controller.addUnavailableOutputDestination(result.getSystemId()); controller.setThereHasBeenAnExplicitResultDocument(); } boolean timing = controller.getConfiguration().isTiming(); if (timing) { String dest = result.getSystemId(); if (dest == null) { if (result instanceof StreamResult) { dest = "anonymous output stream"; } else if (result instanceof SAXResult) { dest = "SAX2 ContentHandler"; } else if (result instanceof DOMResult) { dest = "DOM tree"; } else { dest = result.getClass().getName(); } } System.err.println("Writing to " + dest); } Properties computedGlobalProps = globalProperties; if (formatExpression != null) { // format was an AVT and now needs to be computed CharSequence format = formatExpression.evaluateAsString(context); String[] parts; try { parts = controller.getConfiguration().getNameChecker().getQNameParts(format); } catch (QNameException e) { XPathException err = new XPathException("The requested output format " + Err.wrap(format) + " is not a valid QName"); err.setErrorCode("XTDE1460"); err.setXPathContext(context); throw err; } String uri = nsResolver.getURIForPrefix(parts[0], false); if (uri == null) { XPathException err = new XPathException("The namespace prefix in the format name " + format + " is undeclared"); err.setErrorCode("XTDE1460"); err.setXPathContext(context); throw err; } StructuredQName qName = new StructuredQName(parts[0], uri, parts[1]); computedGlobalProps = getExecutable().getOutputProperties(qName); if (computedGlobalProps == null) { XPathException err = new XPathException("There is no xsl:output format named " + format); err.setErrorCode("XTDE1460"); err.setXPathContext(context); throw err; } } // Now combine the properties specified on xsl:result-document with those specified on xsl:output Properties computedLocalProps = new Properties(computedGlobalProps); // First handle the properties with fixed values on xsl:result-document final NameChecker checker = config.getNameChecker(); for (Iterator citer=localProperties.keySet().iterator(); citer.hasNext();) { String key = (String)citer.next(); String[] parts = NamePool.parseClarkName(key); try { setSerializationProperty(computedLocalProps, parts[0], parts[1], localProperties.getProperty(key), nsResolver, true, checker); } catch (XPathException e) { e.maybeSetLocation(this); throw e; } } // Now add the properties that were specified as AVTs if (serializationAttributes.size() > 0) { for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) { int key = it.next(); Expression exp = (Expression) serializationAttributes.get(key); String value = exp.evaluateAsString(context).toString(); String lname = namePool.getLocalName(key); String uri = namePool.getURI(key); try { setSerializationProperty(computedLocalProps, uri, lname, value, nsResolver, false, checker); } catch (XPathException e) { e.maybeSetLocation(this); e.maybeSetContext(context); if (NamespaceConstant.SAXON.equals(e.getErrorCodeNamespace()) && "warning".equals(e.getErrorCodeLocalPart())) { try { context.getController().getErrorListener().warning(e); } catch (TransformerException e2) { throw XPathException.makeXPathException(e2); } } else { throw e; } } } } // Handle properties specified using a dynamic xsl:output element // (Used when the instruction is generated from a saxon:result-document extension function call) if (dynamicOutputElement != null) { Item outputArg = dynamicOutputElement.evaluateItem(context); if (!(outputArg instanceof NodeInfo && ((NodeInfo)outputArg).getNodeKind() == Type.ELEMENT && ((NodeInfo)outputArg).getFingerprint() == StandardNames.XSL_OUTPUT)) { XPathException err = new XPathException( "The third argument of saxon:result-document must be an element"); err.setLocator(this); err.setXPathContext(context); throw err; } Properties dynamicProperties = new Properties(); Serialize.processXslOutputElement((NodeInfo)outputArg, dynamicProperties, context); for (Iterator it = dynamicProperties.keySet().iterator(); it.hasNext();) { String key = (String)it.next(); StructuredQName name = StructuredQName.fromClarkName(key); String value = dynamicProperties.getProperty(key); try { setSerializationProperty( computedLocalProps, name.getNamespaceURI(), name.getLocalName(), value, nsResolver, false, checker); } catch (XPathException e) { e.maybeSetLocation(this); e.maybeSetContext(context); throw e; } } } String nextInChain = computedLocalProps.getProperty(SaxonOutputKeys.NEXT_IN_CHAIN); if (nextInChain != null) { try { result = controller.prepareNextStylesheet(nextInChain, baseURI, result); } catch (TransformerException e) { throw XPathException.makeXPathException(e); } } // TODO: cache the serializer and reuse it if the serialization properties are fixed at // compile time (that is, if serializationAttributes.isEmpty). Need to save the serializer // in a form where the final output destination can be changed. c2.changeOutputDestination(computedLocalProps, result, true, Configuration.XSLT, validationAction, schemaType); SequenceReceiver out = c2.getReceiver(); out.open(); out.startDocument(0); content.process(c2); out.endDocument(); out.close(); if (resolver != null) { try { resolver.close(result); } catch (TransformerException e) { throw XPathException.makeXPathException(e); } } return null; } /** * Validate a serialization property and add its value to a Properties collection * @param details the properties to be updated * @param uri the uri of the property name * @param lname the local part of the property name * @param value the value of the serialization property. In the case of QName-valued values, * this will use lexical QNames if prevalidated is false, Clark-format names otherwise * @param nsResolver resolver for lexical QNames; not needed if prevalidated * @param prevalidated true if values are already known to be valid and lexical QNames have been * expanded into Clark notation * @param checker the XML 1.0 or 1.1 name checker */ public static void setSerializationProperty(Properties details, String uri, String lname, String value, NamespaceResolver nsResolver, boolean prevalidated, NameChecker checker) throws XPathException { if (uri.length() == 0) { if (lname.equals(StandardNames.METHOD)) { if (value.equals("xml") || value.equals("html") || value.equals("text") || value.equals("xhtml") || prevalidated) { details.setProperty(OutputKeys.METHOD, value); } else { String[] parts; try { parts = checker.getQNameParts(value); String prefix = parts[0]; if (prefix.length() == 0) { XPathException err = new XPathException("method must be xml, html, xhtml, or text, or a prefixed name"); err.setErrorCode("XTSE1570"); err.setIsStaticError(true); throw err; } else { String muri = nsResolver.getURIForPrefix(prefix, false); if (muri==null) { XPathException err = new XPathException("Namespace prefix '" + prefix + "' has not been declared"); err.setErrorCode("XTSE1570"); err.setIsStaticError(true); throw err; } details.setProperty(OutputKeys.METHOD, '{' + muri + '}' + parts[1]); } } catch (QNameException e) { XPathException err = new XPathException("Invalid method name. " + e.getMessage()); err.setErrorCode("XTSE1570"); err.setIsStaticError(true); throw err; } } } else if (lname.equals(StandardNames.OUTPUT_VERSION) || lname.equals(StandardNames.VERSION)) { details.setProperty(OutputKeys.VERSION, value); } else if (lname.equals("byte-order-mark")) { if (prevalidated || value.equals("yes") || value.equals("no")) { details.setProperty(SaxonOutputKeys.BYTE_ORDER_MARK, value); } else { XPathException err = new XPathException("byte-order-mark value must be 'yes' or 'no'"); err.setErrorCode("XTDE0030"); throw err; } } else if (lname.equals(StandardNames.INDENT)) { if (prevalidated || value.equals("yes") || value.equals("no")) { details.setProperty(OutputKeys.INDENT, value); } else { XPathException err = new XPathException("indent must be 'yes' or 'no'"); err.setErrorCode("XTDE0030"); throw err; } } else if (lname.equals(StandardNames.ENCODING)) { details.setProperty(OutputKeys.ENCODING, value); } else if (lname.equals(StandardNames.MEDIA_TYPE)) { details.setProperty(OutputKeys.MEDIA_TYPE, value); } else if (lname.equals(StandardNames.DOCTYPE_SYSTEM)) { details.setProperty(OutputKeys.DOCTYPE_SYSTEM, value); } else if (lname.equals(StandardNames.DOCTYPE_PUBLIC)) { details.setProperty(OutputKeys.DOCTYPE_PUBLIC, value); } else if (lname.equals(StandardNames.OMIT_XML_DECLARATION)) { if (prevalidated || value.equals("yes") || value.equals("no")) { details.setProperty(OutputKeys.OMIT_XML_DECLARATION, value); } else { XPathException err = new XPathException("omit-xml-declaration attribute must be 'yes' or 'no'"); err.setErrorCode("XTDE0030"); throw err; } } else if (lname.equals(StandardNames.STANDALONE)) { if (prevalidated || value.equals("yes") || value.equals("no") || value.equals("omit")) { details.setProperty(OutputKeys.STANDALONE, value); } else { XPathException err = new XPathException("standalone attribute must be 'yes' or 'no' or 'omit'"); err.setErrorCode("XTDE0030"); throw err; } } else if (lname.equals(StandardNames.CDATA_SECTION_ELEMENTS)) { String existing = details.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS); if (existing == null) { existing = ""; } String s = ""; StringTokenizer st = new StringTokenizer(value, " \t\n\r", false); while (st.hasMoreTokens()) { String displayname = st.nextToken(); if (prevalidated) { s += ' ' + displayname; } else { try { String[] parts = checker.getQNameParts(displayname); String muri = nsResolver.getURIForPrefix(parts[0], true); if (muri==null) { XPathException err = new XPathException("Namespace prefix '" + parts[0] + "' has not been declared"); err.setErrorCode("XTDE0030"); throw err; } s += " {" + muri + '}' + parts[1]; } catch (QNameException err) { XPathException e = new XPathException("Invalid CDATA element name. " + err.getMessage()); e.setErrorCode("XTDE0030"); throw e; } } details.setProperty(OutputKeys.CDATA_SECTION_ELEMENTS, existing + s); } } else if (lname.equals(StandardNames.USE_CHARACTER_MAPS)) { // The use-character-maps attribute is always turned into a Clark-format name at compile time String existing = details.getProperty(SaxonOutputKeys.USE_CHARACTER_MAPS); if (existing == null) { existing = ""; } details.setProperty(SaxonOutputKeys.USE_CHARACTER_MAPS, existing + value); } else if (lname.equals(StandardNames.UNDECLARE_PREFIXES)) { if (prevalidated || value.equals("yes") || value.equals("no")) { details.setProperty(SaxonOutputKeys.UNDECLARE_PREFIXES, value); } else { XPathException err = new XPathException("undeclare-namespaces value must be 'yes' or 'no'"); err.setErrorCode("XTDE0030"); throw err; } } else if (lname.equals(StandardNames.INCLUDE_CONTENT_TYPE)) { if (prevalidated || value.equals("yes") || value.equals("no")) { details.setProperty(SaxonOutputKeys.INCLUDE_CONTENT_TYPE, value); } else { XPathException err = new XPathException("include-content-type attribute must be 'yes' or 'no'"); err.setErrorCode("XTDE0030"); throw err; } } else if (lname.equals(StandardNames.ESCAPE_URI_ATTRIBUTES)) { if (prevalidated || value.equals("yes") || value.equals("no")) { details.setProperty(SaxonOutputKeys.ESCAPE_URI_ATTRIBUTES, value); } else { XPathException err = new XPathException("escape-uri-attributes value must be 'yes' or 'no'"); err.setErrorCode("XTDE0030"); throw err; } } else if (lname.equals(StandardNames.NORMALIZATION_FORM)) { if (Name11Checker.getInstance().isValidNmtoken(value)) { // if (prevalidated || value.equals("NFC") || value.equals("NFD") || // value.equals("NFKC") || value.equals("NFKD")) { details.setProperty(SaxonOutputKeys.NORMALIZATION_FORM, value); } else if (value.equals("none")) { // do nothing } else { XPathException err = new XPathException("normalization-form must be a valid NMTOKEN"); err.setErrorCode("XTDE0030"); throw err; } } else { // Normally detected statically, but not with saxon:serialize XPathException err = new XPathException("Unknown serialization property " + lname); err.setErrorCode("XTDE0030"); throw err; } } else if (uri.equals(NamespaceConstant.SAXON)) { if (lname.equals("character-representation")) { details.setProperty(SaxonOutputKeys.CHARACTER_REPRESENTATION, value); } else if (lname.equals("indent-spaces")) { try { Integer.parseInt(value); details.setProperty(OutputKeys.INDENT, "yes"); details.setProperty(SaxonOutputKeys.INDENT_SPACES, value); } catch (NumberFormatException err) { XPathException e = new XPathException("saxon:indent-spaces must be an integer"); e.setErrorCode(NamespaceConstant.SAXON, SaxonErrorCode.SXWN9002); throw e; } } else if (lname.equals("suppress-indentation")) { String existing = details.getProperty(SaxonOutputKeys.SUPPRESS_INDENTATION); if (existing == null) { existing = ""; } String s = ""; StringTokenizer st = new StringTokenizer(value, " \t\n\r", false); while (st.hasMoreTokens()) { String displayname = st.nextToken(); if (prevalidated) { s += ' ' + displayname; } else { try { String[] parts = checker.getQNameParts(displayname); String muri = nsResolver.getURIForPrefix(parts[0], true); if (muri==null) { XPathException err = new XPathException("Namespace prefix '" + parts[0] + "' has not been declared"); err.setErrorCode("XTDE0030"); throw err; } s += " {" + muri + '}' + parts[1]; } catch (QNameException err) { XPathException e = new XPathException("Invalid element name. " + err.getMessage()); e.setErrorCode("XTDE0030"); throw e; } } details.setProperty(SaxonOutputKeys.SUPPRESS_INDENTATION, existing + s); } } else if (lname.equals("next-in-chain")) { XPathException e = new XPathException("saxon:next-in-chain value cannot be specified dynamically"); e.setErrorCode(NamespaceConstant.SAXON, SaxonErrorCode.SXWN9004); throw e; } else if (lname.equals("require-well-formed")) { if (prevalidated || value.equals("yes") || value.equals("no")) { details.setProperty(SaxonOutputKeys.REQUIRE_WELL_FORMED, value); } else { XPathException e = new XPathException("saxon:require-well-formed value must be 'yes' or 'no'"); e.setErrorCode(NamespaceConstant.SAXON, SaxonErrorCode.SXWN9003); throw e; } } } else { // deal with user-defined attributes details.setProperty('{' + uri + '}' + lname, value); } } /** * Diagnostic print of expression structure. The abstract expression tree * is written to the supplied output destination. */ public void explain(ExpressionPresenter out) { out.startElement("resultDocument"); content.explain(out); out.endElement(); } } // // The contents of this file are subject to the Mozilla Public License Version 1.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.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: all this file. // // The Initial Developer of the Original Code is Michael H. Kay. // // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved. // // Additional Contributor(s): Brett Knights [[email protected]] //





© 2015 - 2025 Weber Informatics LLC | Privacy Policy