001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.Controller;
004: import net.sf.saxon.OutputURIResolver;
005: import net.sf.saxon.Err;
006: import net.sf.saxon.Configuration;
007: import net.sf.saxon.functions.EscapeURI;
008: import net.sf.saxon.sort.IntHashMap;
009: import net.sf.saxon.sort.IntIterator;
010: import net.sf.saxon.event.SaxonOutputKeys;
011: import net.sf.saxon.event.SequenceReceiver;
012: import net.sf.saxon.event.StandardOutputResolver;
013: import net.sf.saxon.expr.*;
014: import net.sf.saxon.om.*;
015: import net.sf.saxon.pattern.NoNodeTest;
016: import net.sf.saxon.style.StandardNames;
017: import net.sf.saxon.trans.DynamicError;
018: import net.sf.saxon.trans.XPathException;
019: import net.sf.saxon.trans.SaxonErrorCode;
020: import net.sf.saxon.type.ItemType;
021: import net.sf.saxon.type.SchemaType;
022: import net.sf.saxon.type.TypeHierarchy;
023: import net.sf.saxon.value.Value;
024:
025: import javax.xml.transform.OutputKeys;
026: import javax.xml.transform.Result;
027: import javax.xml.transform.TransformerException;
028: import javax.xml.transform.dom.DOMResult;
029: import javax.xml.transform.sax.SAXResult;
030: import javax.xml.transform.stream.StreamResult;
031: import java.io.PrintStream;
032: import java.io.File;
033: import java.util.*;
034:
035: /**
036: * The compiled form of an xsl:result-document element in the stylesheet.
037: * <p>
038: * The xsl:result-document element takes an attribute href="filename". The filename will
039: * often contain parameters, e.g. {position()} to ensure that a different file is produced
040: * for each element instance.
041: * <p>
042: * There is a further attribute "format" which determines the format of the
043: * output file, it identifies the name of an xsl:output element containing the output
044: * format details. In addition, individual serialization properties may be specified as attributes.
045: * These are attribute value templates, so they may need to be computed at run-time.
046: */
047:
048: public class ResultDocument extends Instruction {
049:
050: private Expression href;
051: private Expression formatExpression; // null if format was known at compile time
052: private Expression content;
053: private Properties globalProperties;
054: private Properties localProperties;
055: private String baseURI; // needed only for saxon:next-in-chain
056: private int validationAction;
057: private SchemaType schemaType;
058: private IntHashMap serializationAttributes;
059: private NamespaceResolver nsResolver;
060:
061: public ResultDocument(
062: Properties globalProperties, // properties defined on static xsl:output
063: Properties localProperties, // non-AVT properties defined on result-document element
064: Expression href,
065: Expression formatExpression, // AVT defining the output format
066: String baseURI, int validationAction,
067: SchemaType schemaType, IntHashMap serializationAttributes, // computed local properties only
068: NamespaceResolver nsResolver) {
069: this .globalProperties = globalProperties;
070: this .localProperties = localProperties;
071: this .href = href;
072: this .formatExpression = formatExpression;
073: this .baseURI = baseURI;
074: this .validationAction = validationAction;
075: this .schemaType = schemaType;
076: this .serializationAttributes = serializationAttributes;
077: this .nsResolver = nsResolver;
078: adoptChildExpression(href);
079: for (Iterator it = serializationAttributes.valueIterator(); it
080: .hasNext();) {
081: adoptChildExpression((Expression) it.next());
082: }
083: }
084:
085: /**
086: * Set the expression that constructs the content
087: */
088:
089: public void setContent(Expression content) {
090: this .content = content;
091: adoptChildExpression(content);
092: }
093:
094: /**
095: * Simplify an expression. This performs any static optimization (by rewriting the expression
096: * as a different expression). The default implementation does nothing.
097: * @return the simplified expression
098: * @throws net.sf.saxon.trans.XPathException
099: * if an error is discovered during expression rewriting
100: */
101:
102: public Expression simplify(StaticContext env) throws XPathException {
103: content = content.simplify(env);
104: if (href != null) {
105: href = href.simplify(env);
106: }
107: for (IntIterator it = serializationAttributes.keyIterator(); it
108: .hasNext();) {
109: int key = it.next();
110: Expression value = (Expression) serializationAttributes
111: .get(key);
112: if (!(value instanceof Value)) {
113: value = value.simplify(env);
114: serializationAttributes.put(key, value);
115: }
116: }
117: return this ;
118: }
119:
120: public Expression typeCheck(StaticContext env,
121: ItemType contextItemType) throws XPathException {
122: content = content.typeCheck(env, contextItemType);
123: adoptChildExpression(content);
124: if (href != null) {
125: href = href.typeCheck(env, contextItemType);
126: adoptChildExpression(href);
127: }
128: if (formatExpression != null) {
129: formatExpression = formatExpression.typeCheck(env,
130: contextItemType);
131: adoptChildExpression(formatExpression);
132: }
133: for (IntIterator it = serializationAttributes.keyIterator(); it
134: .hasNext();) {
135: int key = it.next();
136: Expression value = (Expression) serializationAttributes
137: .get(key);
138: if (!(value instanceof Value)) {
139: value = value.typeCheck(env, contextItemType);
140: adoptChildExpression(value);
141: serializationAttributes.put(key, value);
142: }
143: }
144: return this ;
145: }
146:
147: public Expression optimize(Optimizer opt, StaticContext env,
148: ItemType contextItemType) throws XPathException {
149: content = content.optimize(opt, env, contextItemType);
150: adoptChildExpression(content);
151: if (href != null) {
152: href = href.optimize(opt, env, contextItemType);
153: adoptChildExpression(href);
154: }
155: if (formatExpression != null) {
156: formatExpression = formatExpression.optimize(opt, env,
157: contextItemType);
158: adoptChildExpression(formatExpression);
159: // TODO: if the formatExpression is now a constant, could get the output properties now
160: }
161: for (IntIterator it = serializationAttributes.keyIterator(); it
162: .hasNext();) {
163: int key = it.next();
164: Expression value = (Expression) serializationAttributes
165: .get(key);
166: if (!(value instanceof Value)) {
167: value = value.optimize(opt, env, contextItemType);
168: adoptChildExpression(value);
169: serializationAttributes.put(key, value);
170: }
171: }
172: return this ;
173: }
174:
175: /**
176: * Handle promotion offers, that is, non-local tree rewrites.
177: * @param offer The type of rewrite being offered
178: * @throws XPathException
179: */
180:
181: protected void promoteInst(PromotionOffer offer)
182: throws XPathException {
183: content = doPromotion(content, offer);
184: if (href != null) {
185: href = doPromotion(href, offer);
186: }
187: for (IntIterator it = serializationAttributes.keyIterator(); it
188: .hasNext();) {
189: int key = it.next();
190: Expression value = (Expression) serializationAttributes
191: .get(key);
192: if (!(value instanceof Value)) {
193: value = doPromotion(value, offer);
194: serializationAttributes.put(key, value);
195: }
196: }
197: }
198:
199: /**
200: * Get the name of this instruction for diagnostic and tracing purposes
201: * (the string "xsl:result-document")
202: */
203:
204: public int getInstructionNameCode() {
205: return StandardNames.XSL_RESULT_DOCUMENT;
206: }
207:
208: /**
209: * Get the item type of the items returned by evaluating this instruction
210: * @return the static item type of the instruction. This is empty: the result-document instruction
211: * returns nothing.
212: * @param th
213: */
214:
215: public ItemType getItemType(TypeHierarchy th) {
216: return NoNodeTest.getInstance();
217: }
218:
219: /**
220: * Get all the XPath expressions associated with this instruction
221: * (in XSLT terms, the expression present on attributes of the instruction,
222: * as distinct from the child instructions in a sequence construction)
223: */
224:
225: public Iterator iterateSubExpressions() {
226: ArrayList list = new ArrayList(6);
227: list.add(content);
228: if (href != null) {
229: list.add(href);
230: }
231: for (Iterator it = serializationAttributes.valueIterator(); it
232: .hasNext();) {
233: list.add(it.next());
234: }
235: return list.iterator();
236: }
237:
238: public TailCall processLeavingTail(XPathContext context)
239: throws XPathException {
240: final Controller controller = context.getController();
241: final Configuration config = controller.getConfiguration();
242: final NamePool namePool = config.getNamePool();
243: XPathContext c2 = context.newMinorContext();
244: c2.setOrigin(this );
245:
246: Result result;
247: OutputURIResolver resolver = null;
248:
249: if (href == null) {
250: result = controller.getPrincipalResult();
251: } else {
252: try {
253: String base = controller.getBaseOutputURI();
254: if (base == null && config.isAllowExternalFunctions()) {
255: // if calling external functions is allowed, then the stylesheet is trusted, so
256: // we allow it to write to files relative to the current directory
257: base = new File(System.getProperty("user.dir"))
258: .toURI().toString();
259: }
260: if (base != null) {
261: base = EscapeURI.escape(base, false).toString();
262: }
263:
264: resolver = controller.getOutputURIResolver();
265:
266: String hrefValue = EscapeURI.escape(
267: href.evaluateAsString(context), false)
268: .toString();
269: result = resolver.resolve(hrefValue, base);
270: if (result == null) {
271: resolver = StandardOutputResolver.getInstance();
272: result = resolver.resolve(hrefValue, base);
273: }
274: } catch (TransformerException e) {
275: throw DynamicError.makeDynamicError(e);
276: }
277: }
278:
279: if (!controller.checkUniqueOutputDestination(result
280: .getSystemId())) {
281: DynamicError err = new DynamicError(
282: "Cannot write more than one result document to the same URI: "
283: + result.getSystemId());
284: err.setXPathContext(context);
285: err.setErrorCode("XTDE1490");
286: throw err;
287: }
288:
289: boolean timing = controller.getConfiguration().isTiming();
290: if (timing) {
291: String dest = result.getSystemId();
292: if (dest == null) {
293: if (result instanceof StreamResult) {
294: dest = "anonymous output stream";
295: } else if (result instanceof SAXResult) {
296: dest = "SAX2 ContentHandler";
297: } else if (result instanceof DOMResult) {
298: dest = "DOM tree";
299: } else {
300: dest = result.getClass().getName();
301: }
302: }
303: System.err.println("Writing to " + dest);
304: }
305:
306: Properties computedGlobalProps = globalProperties;
307:
308: if (formatExpression != null) {
309: // format was an AVT and now needs to be computed
310: String format = formatExpression.evaluateAsString(context);
311: String[] parts;
312: try {
313: parts = controller.getConfiguration().getNameChecker()
314: .getQNameParts(format);
315: } catch (QNameException e) {
316: DynamicError err = new DynamicError(
317: "The requested output format "
318: + Err.wrap(format)
319: + " is not a valid QName");
320: err.setErrorCode("XTDE1460");
321: err.setXPathContext(context);
322: throw err;
323: }
324: String uri = nsResolver.getURIForPrefix(parts[0], false);
325: if (uri == null) {
326: DynamicError err = new DynamicError(
327: "The namespace prefix in the format name "
328: + format + " is undeclared");
329: err.setErrorCode("XTDE1460");
330: err.setXPathContext(context);
331: throw err;
332: }
333: int fp = namePool.allocate(parts[0], uri, parts[1])
334: & NamePool.FP_MASK;
335: computedGlobalProps = getExecutable().getOutputProperties(
336: fp);
337: if (computedGlobalProps == null) {
338: DynamicError err = new DynamicError(
339: "There is no xsl:output format named " + format);
340: err.setErrorCode("XTDE1460");
341: err.setXPathContext(context);
342: throw err;
343: }
344:
345: }
346:
347: // Now combine the properties specified on xsl:result-document with those specified on xsl:output
348:
349: Properties computedLocalProps = new Properties(
350: computedGlobalProps);
351:
352: // First handle the properties with fixed values on xsl:result-document
353:
354: final NameChecker checker = config.getNameChecker();
355: for (Iterator citer = localProperties.keySet().iterator(); citer
356: .hasNext();) {
357: String key = (String) citer.next();
358: String[] parts = NamePool.parseClarkName(key);
359: setSerializationProperty(computedLocalProps, parts[0],
360: parts[1], localProperties.getProperty(key),
361: nsResolver, true, checker);
362: }
363:
364: // Now add the properties that were specified as AVTs
365:
366: if (serializationAttributes.size() > 0) {
367: for (IntIterator it = serializationAttributes.keyIterator(); it
368: .hasNext();) {
369: int key = it.next();
370: Expression exp = (Expression) serializationAttributes
371: .get(key);
372: String value = exp.evaluateAsString(context);
373: String lname = namePool.getLocalName(key);
374: String uri = namePool.getURI(key);
375: try {
376: setSerializationProperty(computedLocalProps, uri,
377: lname, value, nsResolver, false, checker);
378: } catch (DynamicError e) {
379: if (NamespaceConstant.SAXON.equals(e
380: .getErrorCodeNamespace())
381: && "warning".equals(e
382: .getErrorCodeLocalPart())) {
383: try {
384: context.getController().getErrorListener()
385: .warning(e);
386: } catch (TransformerException e2) {
387: throw DynamicError.makeDynamicError(e2);
388: }
389: } else {
390: e.setXPathContext(context);
391: e.setLocator(getSourceLocator());
392: throw e;
393: }
394: }
395: }
396: }
397:
398: String nextInChain = computedLocalProps
399: .getProperty(SaxonOutputKeys.NEXT_IN_CHAIN);
400: if (nextInChain != null) {
401: try {
402: result = controller.prepareNextStylesheet(nextInChain,
403: baseURI, result);
404: } catch (TransformerException e) {
405: throw DynamicError.makeDynamicError(e);
406: }
407: }
408:
409: // TODO: cache the serializer and reuse it if the serialization properties are fixed at
410: // compile time (that is, if serializationAttributes.isEmpty). Need to save the serializer
411: // in a form where the final output destination can be changed.
412:
413: c2.changeOutputDestination(computedLocalProps, result, true,
414: validationAction, schemaType);
415: SequenceReceiver out = c2.getReceiver();
416:
417: out.startDocument(0);
418: content.process(c2);
419: out.endDocument();
420: out.close();
421: if (resolver != null) {
422: try {
423: resolver.close(result);
424: } catch (TransformerException e) {
425: throw DynamicError.makeDynamicError(e);
426: }
427: }
428: return null;
429: }
430:
431: /**
432: * Validate a serialization property and add its value to a Properties collection
433: * @param details the properties to be updated
434: * @param uri the uri of the property name
435: * @param lname the local part of the property name
436: * @param value the value of the serialization property. In the case of QName-valued values,
437: * this will use lexical QNames if prevalidated is false, Clark-format names otherwise
438: * @param nsResolver resolver for lexical QNames; not needed if prevalidated
439: * @param prevalidated true if values are already known to be valid and lexical QNames have been
440: * expanded into Clark notation
441: * @param checker
442: */
443:
444: public static void setSerializationProperty(Properties details,
445: String uri, String lname, String value,
446: NamespaceResolver nsResolver, boolean prevalidated,
447: NameChecker checker) throws XPathException {
448: if (uri.equals("")) {
449: if (lname.equals(StandardNames.METHOD)) {
450: if (value.equals("xml") || value.equals("html")
451: || value.equals("text")
452: || value.equals("xhtml") || prevalidated) {
453: details.put(OutputKeys.METHOD, value);
454: } else {
455: String[] parts;
456: try {
457: parts = checker.getQNameParts(value);
458: String prefix = parts[0];
459: if (prefix.equals("")) {
460: DynamicError err = new DynamicError(
461: "method must be xml, html, xhtml, or text, or a prefixed name");
462: err.setErrorCode("XTSE1570");
463: throw err;
464: } else {
465: String muri = nsResolver.getURIForPrefix(
466: prefix, false);
467: if (muri == null) {
468: DynamicError err = new DynamicError(
469: "Namespace prefix '"
470: + prefix
471: + "' has not been declared");
472: err.setErrorCode("XTSE1570");
473: throw err;
474: }
475: details.put(OutputKeys.METHOD, '{' + muri
476: + '}' + parts[1]);
477: }
478: } catch (QNameException e) {
479: DynamicError err = new DynamicError(
480: "Invalid method name. "
481: + e.getMessage());
482: err.setErrorCode("XTSE1570");
483: throw err;
484: }
485: }
486: } else
487:
488: if (lname.equals(StandardNames.OUTPUT_VERSION)) {
489: details.put(OutputKeys.VERSION, value);
490: } else
491:
492: if (lname.equals("byte-order-mark")) {
493: if (prevalidated || value.equals("yes")
494: || value.equals("no")) {
495: details.put(SaxonOutputKeys.BYTE_ORDER_MARK, value);
496: } else {
497: DynamicError err = new DynamicError(
498: "byte-order-mark value must be 'yes' or 'no'");
499: err.setErrorCode("XTDE0030");
500: throw err;
501: }
502: } else
503:
504: if (lname.equals(StandardNames.INDENT)) {
505: if (prevalidated || value.equals("yes")
506: || value.equals("no")) {
507: details.put(OutputKeys.INDENT, value);
508: } else {
509: DynamicError err = new DynamicError(
510: "indent must be 'yes' or 'no'");
511: err.setErrorCode("XTDE0030");
512: throw err;
513: }
514: } else
515:
516: if (lname.equals(StandardNames.ENCODING)) {
517: details.put(OutputKeys.ENCODING, value);
518: } else
519:
520: if (lname.equals(StandardNames.MEDIA_TYPE)) {
521: details.put(OutputKeys.MEDIA_TYPE, value);
522: } else
523:
524: if (lname.equals(StandardNames.DOCTYPE_SYSTEM)) {
525: details.put(OutputKeys.DOCTYPE_SYSTEM, value);
526: } else
527:
528: if (lname.equals(StandardNames.DOCTYPE_PUBLIC)) {
529: details.put(OutputKeys.DOCTYPE_PUBLIC, value);
530: } else
531:
532: if (lname.equals(StandardNames.OMIT_XML_DECLARATION)) {
533: if (prevalidated || value.equals("yes")
534: || value.equals("no")) {
535: details.put(OutputKeys.OMIT_XML_DECLARATION, value);
536: } else {
537: DynamicError err = new DynamicError(
538: "omit-xml-declaration attribute must be 'yes' or 'no'");
539: err.setErrorCode("XTDE0030");
540: throw err;
541: }
542: } else
543:
544: if (lname.equals(StandardNames.STANDALONE)) {
545: if (prevalidated || value.equals("yes")
546: || value.equals("no") || value.equals("omit")) {
547: details.put(OutputKeys.STANDALONE, value);
548: DynamicError err = new DynamicError(
549: "standalone attribute must be 'yes' or 'no' or 'omit'");
550: err.setErrorCode("XTDE0030");
551: throw err;
552: }
553: } else
554:
555: if (lname.equals(StandardNames.CDATA_SECTION_ELEMENTS)) {
556: String existing = details
557: .getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
558: if (existing == null) {
559: existing = "";
560: }
561: String s = "";
562: StringTokenizer st = new StringTokenizer(value);
563: while (st.hasMoreTokens()) {
564: String displayname = st.nextToken();
565: if (prevalidated) {
566: s += ' ' + displayname;
567: } else {
568: try {
569: String[] parts = checker
570: .getQNameParts(displayname);
571: String muri = nsResolver.getURIForPrefix(
572: parts[0], true);
573: if (muri == null) {
574: DynamicError err = new DynamicError(
575: "Namespace prefix '"
576: + parts[0]
577: + "' has not been declared");
578: err.setErrorCode("XTDE0030");
579: throw err;
580: }
581: s += " {" + muri + '}' + parts[1];
582: } catch (QNameException err) {
583: DynamicError e = new DynamicError(
584: "Invalid CDATA element name. "
585: + err.getMessage());
586: e.setErrorCode("XTDE0030");
587: throw e;
588: }
589: }
590:
591: details.put(OutputKeys.CDATA_SECTION_ELEMENTS,
592: existing + s);
593: }
594: } else
595:
596: if (lname.equals(StandardNames.USE_CHARACTER_MAPS)) {
597: // The use-character-maps attribute is always turned into a Clark-format name at compile time
598: String existing = details
599: .getProperty(SaxonOutputKeys.USE_CHARACTER_MAPS);
600: if (existing == null) {
601: existing = "";
602: }
603: details.put(SaxonOutputKeys.USE_CHARACTER_MAPS,
604: existing + value);
605: } else
606:
607: if (lname.equals(StandardNames.UNDECLARE_PREFIXES)) {
608: if (prevalidated || value.equals("yes")
609: || value.equals("no")) {
610: details.put(SaxonOutputKeys.UNDECLARE_PREFIXES,
611: value);
612: } else {
613: DynamicError err = new DynamicError(
614: "undeclare-namespaces value must be 'yes' or 'no'");
615: err.setErrorCode("XTDE0030");
616: throw err;
617: }
618: } else
619:
620: if (lname.equals(StandardNames.INCLUDE_CONTENT_TYPE)) {
621: if (prevalidated || value.equals("yes")
622: || value.equals("no")) {
623: details.put(SaxonOutputKeys.INCLUDE_CONTENT_TYPE,
624: value);
625: } else {
626: DynamicError err = new DynamicError(
627: "include-content-type attribute must be 'yes' or 'no'");
628: err.setErrorCode("XTDE0030");
629: throw err;
630: }
631: } else
632:
633: if (lname.equals(StandardNames.ESCAPE_URI_ATTRIBUTES)) {
634: if (prevalidated || value.equals("yes")
635: || value.equals("no")) {
636: details.put(SaxonOutputKeys.ESCAPE_URI_ATTRIBUTES,
637: value);
638: } else {
639: DynamicError err = new DynamicError(
640: "escape-uri-attributes value must be 'yes' or 'no'");
641: err.setErrorCode("XTDE0030");
642: throw err;
643: }
644: } else
645:
646: if (lname.equals(StandardNames.NORMALIZATION_FORM)) {
647: if (XML11Char.isXML11ValidNmtoken(value)) {
648: // if (prevalidated || value.equals("NFC") || value.equals("NFD") ||
649: // value.equals("NFKC") || value.equals("NFKD")) {
650: details.put(SaxonOutputKeys.NORMALIZATION_FORM,
651: value);
652: } else if (value.equals("none")) {
653: // do nothing
654: } else {
655: DynamicError err = new DynamicError(
656: "normalization-form must be a valid NMTOKEN");
657: err.setErrorCode("XTDE0030");
658: throw err;
659: }
660: }
661:
662: } else if (uri.equals(NamespaceConstant.SAXON)) {
663:
664: if (lname.equals("character-representation")) {
665: details.put(SaxonOutputKeys.CHARACTER_REPRESENTATION,
666: value);
667: } else
668:
669: if (lname.equals("indent-spaces")) {
670: try {
671: Integer.parseInt(value);
672: details.put(OutputKeys.INDENT, "yes");
673: details.put(SaxonOutputKeys.INDENT_SPACES, value);
674: } catch (NumberFormatException err) {
675: DynamicError e = new DynamicError(
676: "saxon:indent-spaces must be an integer");
677: e.setErrorCode(NamespaceConstant.SAXON,
678: SaxonErrorCode.SXWN9002);
679: throw e;
680: }
681: } else
682:
683: if (lname.equals("next-in-chain")) {
684: DynamicError e = new DynamicError(
685: "saxon:next-in-chain value cannot be specified dynamically");
686: e.setErrorCode(NamespaceConstant.SAXON,
687: SaxonErrorCode.SXWN9004);
688: throw e;
689: } else
690:
691: if (lname.equals("require-well-formed")) {
692: if (prevalidated || value.equals("yes")
693: || value.equals("no")) {
694: details.put(SaxonOutputKeys.REQUIRE_WELL_FORMED,
695: value);
696: } else {
697: DynamicError e = new DynamicError(
698: "saxon:require-well-formed value must be 'yes' or 'no'");
699: e.setErrorCode(NamespaceConstant.SAXON,
700: SaxonErrorCode.SXWN9003);
701: throw e;
702: }
703: }
704:
705: } else {
706:
707: // deal with user-defined attributes
708: details.put('{' + uri + '}' + lname, value);
709: }
710:
711: }
712:
713: /**
714: * Diagnostic print of expression structure. The expression is written to the System.err
715: * output stream
716: *
717: * @param level indentation level for this expression
718: * @param out
719: */
720:
721: public void display(int level, NamePool pool, PrintStream out) {
722: out.println(ExpressionTool.indent(level) + "result-document");
723: content.display(level + 1, pool, out);
724: }
725: }
726:
727: //
728: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
729: // you may not use this file except in compliance with the License. You may obtain a copy of the
730: // License at http://www.mozilla.org/MPL/
731: //
732: // Software distributed under the License is distributed on an "AS IS" basis,
733: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
734: // See the License for the specific language governing rights and limitations under the License.
735: //
736: // The Original Code is: all this file.
737: //
738: // The Initial Developer of the Original Code is Michael H. Kay.
739: //
740: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
741: //
742: // Additional Contributor(s): Brett Knights [brett@knightsofthenet.com]
743: //
|