001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.Item;
004: import net.sf.saxon.om.NamePool;
005: import net.sf.saxon.om.SequenceIterator;
006: import net.sf.saxon.om.ValueRepresentation;
007: import net.sf.saxon.trace.Location;
008: import net.sf.saxon.trans.XPathException;
009: import net.sf.saxon.type.ItemType;
010: import net.sf.saxon.type.SchemaType;
011: import net.sf.saxon.type.TypeHierarchy;
012: import net.sf.saxon.value.Cardinality;
013: import net.sf.saxon.value.EmptySequence;
014: import net.sf.saxon.value.IntegerValue;
015: import net.sf.saxon.value.SequenceType;
016: import net.sf.saxon.functions.SystemFunction;
017:
018: import java.io.PrintStream;
019: import java.util.ArrayList;
020: import java.util.List;
021:
022: /**
023: * A ForExpression maps an expression over a sequence.
024: * This version works with range variables, it doesn't change the context information
025: */
026:
027: public class ForExpression extends Assignation {
028:
029: private transient RangeVariableDeclaration positionVariable = null;
030: private PositionBinding positionBinding = null;
031:
032: public ForExpression() {
033: }
034:
035: /**
036: * Set the reference to the position variable (XQuery only)
037: */
038:
039: public void setPositionVariable(RangeVariableDeclaration decl) {
040: positionVariable = decl;
041: if (decl != null) {
042: positionBinding = new PositionBinding(decl.getNameCode());
043: }
044: }
045:
046: public int getPositionVariableNameCode() {
047: if (positionBinding == null) {
048: return -1;
049: } else {
050: return positionBinding.getNameCode();
051: }
052: }
053:
054: public void setAction(Expression action) {
055: super .setAction(action);
056: if (positionVariable != null) {
057: positionVariable.fixupReferences(positionBinding);
058: }
059: }
060:
061: /**
062: * Set the slot number for the range variable
063: */
064:
065: public void setSlotNumber(int nr) {
066: super .setSlotNumber(nr);
067: if (positionBinding != null) {
068: positionBinding.setSlotNumber(nr + 1);
069: }
070: }
071:
072: /**
073: * Get the number of slots required. Normally 1, except for a FOR expression with an AT clause, where it is 2.
074: */
075:
076: public int getRequiredSlots() {
077: return (positionBinding == null ? 1 : 2);
078: }
079:
080: /**
081: * Type-check the expression
082: */
083:
084: public Expression typeCheck(StaticContext env,
085: ItemType contextItemType) throws XPathException {
086:
087: // The order of events is critical here. First we ensure that the type of the
088: // sequence expression is established. This is used to establish the type of the variable,
089: // which in turn is required when type-checking the action part.
090:
091: sequence = sequence.typeCheck(env, contextItemType);
092: if (sequence instanceof EmptySequence) {
093: return EmptySequence.getInstance();
094: }
095:
096: if (declaration != null) {
097: // if declaration is null, we've already done the type checking in a previous pass
098: final TypeHierarchy th = env.getNamePool()
099: .getTypeHierarchy();
100: SequenceType decl = declaration.getRequiredType();
101: SequenceType sequenceType = SequenceType.makeSequenceType(
102: decl.getPrimaryType(),
103: StaticProperty.ALLOWS_ZERO_OR_MORE);
104: RoleLocator role = new RoleLocator(RoleLocator.VARIABLE,
105: new Integer(nameCode), 0, env.getNamePool());
106: role.setSourceLocator(this );
107: sequence = TypeChecker.strictTypeCheck(sequence,
108: sequenceType, role, env);
109: ItemType actualItemType = sequence.getItemType(th);
110: declaration.refineTypeInformation(actualItemType,
111: StaticProperty.EXACTLY_ONE, null, sequence
112: .getSpecialProperties(), env);
113: }
114:
115: action = action.typeCheck(env, contextItemType);
116: if (action instanceof EmptySequence) {
117: return EmptySequence.getInstance();
118: }
119:
120: return this ;
121: }
122:
123: /**
124: * Optimize the expression
125: */
126:
127: public Expression optimize(Optimizer opt, StaticContext env,
128: ItemType contextItemType) throws XPathException {
129:
130: // Try to promote any WHERE clause appearing within the FOR expression
131:
132: Expression p = promoteWhereClause(positionBinding);
133: if (p != null) {
134: return p.optimize(opt, env, contextItemType);
135: }
136:
137: // See if there is a simple "where" condition that can be turned into a predicate
138:
139: Expression pred = convertWhereToPredicate(opt, env,
140: contextItemType);
141: if (pred != null && pred != this ) {
142: return pred.optimize(opt, env, contextItemType);
143: }
144:
145: int tries = 0;
146: while (tries++ < 5) {
147: Expression seq2 = sequence.optimize(opt, env,
148: contextItemType);
149: if (seq2 == sequence) {
150: break;
151: }
152: sequence = seq2;
153: adoptChildExpression(sequence);
154: resetStaticProperties();
155: }
156: if (sequence instanceof EmptySequence) {
157: return EmptySequence.getInstance();
158: }
159:
160: tries = 0;
161: while (tries++ < 5) {
162: Expression act2 = action
163: .optimize(opt, env, contextItemType);
164: if (act2 == action) {
165: break;
166: }
167: action = act2;
168: adoptChildExpression(action);
169: resetStaticProperties();
170: }
171: if (action instanceof EmptySequence) {
172: return EmptySequence.getInstance();
173: }
174:
175: Expression e2 = extractLoopInvariants(opt, env, contextItemType);
176: if (e2 != null && e2 != this ) {
177: return e2.optimize(opt, env, contextItemType);
178: }
179:
180: // Simplify an expression of the form "for $b in a/b/c return $b/d".
181: // (XQuery users seem to write these a lot!)
182:
183: if (declaration != null && positionVariable == null
184: && sequence instanceof PathExpression
185: && action instanceof PathExpression) {
186: int count = declaration.getReferenceCount(this , env);
187: PathExpression path2 = (PathExpression) action;
188: Expression s2 = path2.getStartExpression();
189: if (count == 1 && s2 instanceof VariableReference
190: && ((VariableReference) s2).getBinding() == this ) {
191: PathExpression newPath = new PathExpression(sequence,
192: path2.getStepExpression());
193: if ((newPath.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) {
194: // see test qxmp299, where this condition isn't satisfied
195: return newPath.simplify(env).typeCheck(env,
196: contextItemType).optimize(opt, env,
197: contextItemType);
198: }
199: }
200: }
201:
202: // Simplify an expression of the form "for $x in EXPR return $x". These sometimes
203: // arise as a result of previous optimization steps.
204:
205: if (action instanceof VariableReference
206: && ((VariableReference) action).getBinding() == this ) {
207: return sequence;
208: }
209:
210: declaration = null; // let the garbage collector take it
211:
212: return this ;
213: }
214:
215: /**
216: * Extract subexpressions in the action part that don't depend on the range variable
217: */
218:
219: private Expression extractLoopInvariants(Optimizer opt,
220: StaticContext env, ItemType contextItemType)
221: throws XPathException {
222: // Extract subexpressions that don't depend on the range variable.
223: // We don't do this if there is a position variable. Ideally we would
224: // extract subexpressions so long as they don't depend on either variable,
225: // but we don't have the machinery to do that yet.
226: // TODO: add this optimisation: we now have the mechanism in ExpressionTool.dependsOnVariable()
227: // If a subexpression is (or might be) creative, this is, if it creates new nodes, we don't
228: // extract it from the loop, but we do extract its non-creative subexpressions
229:
230: if (positionVariable == null) {
231: PromotionOffer offer = new PromotionOffer(opt);
232: offer.containingExpression = this ;
233: offer.action = PromotionOffer.RANGE_INDEPENDENT;
234: Binding[] bindingList = { this };
235: offer.bindingList = bindingList;
236: Container container = getParentExpression();
237: action = doPromotion(action, offer);
238: if (offer.containingExpression instanceof LetExpression) {
239: // a subexpression has been promoted
240: ((ComputedExpression) offer.containingExpression)
241: .setParentExpression(container);
242: // try again: there may be further subexpressions to promote
243: offer.containingExpression = offer.containingExpression
244: //.simplify(env)
245: //.typeCheck(env, contextItemType)
246: .optimize(opt, env, contextItemType);
247: }
248: return offer.containingExpression;
249: }
250: return null;
251:
252: }
253:
254: /**
255: * Convert where clause, if possible, to a predicate. Returns the converted expression if modified,
256: * or null otherwise
257: */
258:
259: public Expression convertWhereToPredicate(Optimizer opt,
260: StaticContext env, ItemType contextItemType)
261: throws XPathException {
262: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
263: if (action instanceof IfExpression
264: && ((IfExpression) action).getElseExpression() instanceof EmptySequence) {
265: Expression head = null;
266: Expression selection = sequence;
267: if (sequence instanceof PathExpression
268: && ((PathExpression) sequence).isAbsolute(th)) {
269: head = ((PathExpression) sequence).getFirstStep();
270: selection = ((PathExpression) sequence)
271: .getRemainingSteps();
272: }
273:
274: boolean changed = false;
275: IfExpression condAction = (IfExpression) action;
276: List list = new ArrayList(4);
277: BooleanExpression.listAndComponents(condAction
278: .getCondition(), list);
279: for (int t = list.size() - 1; t >= 0; t--) {
280: // Process each term in the where clause independently
281: Expression term = (Expression) list.get(t);
282:
283: if (term instanceof ValueComparison) {
284: ValueComparison comp = (ValueComparison) term;
285: Expression[] operands = comp.getOperands();
286: for (int op = 0; op < 2; op++) {
287:
288: // If the where clause is a simple test on the position variable, for example
289: // for $x at $p in EXPR where $p = 5 return A
290: // then absorb the where condition into a predicate, rewriting it as
291: // for $x in EXPR[position() = 5] return A
292: // This takes advantage of the optimizations applied to positional filter expressions
293: // Only do this if the sequence expression has not yet been changed, because
294: // the position in a predicate after the first is different.
295:
296: if (positionVariable != null
297: && positionVariable.getReferenceList()
298: .size() == 1 && !changed) {
299: if (operands[op] instanceof VariableReference
300: && ((VariableReference) operands[op])
301: .getBinding() == positionBinding
302: && (operands[1 - op]
303: .getDependencies() & StaticProperty.DEPENDS_ON_FOCUS) == 0) {
304: FunctionCall position = SystemFunction
305: .makeSystemFunction("position",
306: 1, env.getNamePool());
307: position
308: .setArguments(SimpleExpression.NO_ARGUMENTS);
309: Expression predicate;
310: if (op == 0) {
311: predicate = new ValueComparison(
312: position, comp
313: .getOperator(),
314: operands[1]);
315: } else {
316: predicate = new ValueComparison(
317: operands[0], comp
318: .getOperator(),
319: position);
320: }
321: selection = new FilterExpression(
322: selection, predicate, env);
323: //action = condAction.getThenExpression();
324: positionVariable = null;
325: positionBinding = null;
326: list.remove(t);
327: changed = true;
328: break;
329: //return simplify(env).typeCheck(env, contextItemType).optimize(opt, env, contextItemType);
330: }
331: }
332:
333: // If the where clause is a simple test on the value of the range variable, or a path
334: // expression starting with the range variable, then rewrite it as a predicate.
335: // For example, rewrite
336: // for $x in EXPR where $x/a/b eq "z" return A
337: // as
338: // for $x in EXPR[a/b eq "z"] return A
339:
340: Binding[] this Var = { this };
341: if (positionVariable == null
342: && ExpressionTool
343: .isVariableReplaceableByDot(
344: term, this Var)
345: && (term.getDependencies() & StaticProperty.DEPENDS_ON_FOCUS) == 0
346: && ExpressionTool.dependsOnVariable(
347: operands[op], this Var)
348: && !ExpressionTool.dependsOnVariable(
349: operands[1 - op], this Var)) {
350: PromotionOffer offer = new PromotionOffer(
351: opt);
352: offer.action = PromotionOffer.INLINE_VARIABLE_REFERENCES;
353: offer.bindingList = this Var;
354: offer.containingExpression = new ContextItemExpression();
355: Expression newOperand = operands[op]
356: .promote(offer);
357: if (newOperand != null && offer.accepted) {
358: Expression predicate;
359: if (op == 0) {
360: predicate = new ValueComparison(
361: newOperand, comp
362: .getOperator(),
363: operands[1]);
364: } else {
365: predicate = new ValueComparison(
366: operands[0], comp
367: .getOperator(),
368: newOperand);
369: }
370: predicate = predicate.typeCheck(env,
371: sequence.getItemType(th));
372: selection = new FilterExpression(
373: selection, predicate, env);
374: changed = true;
375: positionVariable = null;
376: positionBinding = null;
377: list.remove(t);
378: }
379: }
380: }
381: } else if (term instanceof GeneralComparison) {
382: GeneralComparison comp = (GeneralComparison) term;
383: Expression[] operands = comp.getOperands();
384: for (int op = 0; op < 2; op++) {
385:
386: // If the where clause is a simple test on the value of the range variable, or a path
387: // expression starting with the range variable, then rewrite it as a predicate.
388: // For example, rewrite
389: // for $x in EXPR where $x/a/b = "z" return A
390: // as
391: // for $x in EXPR[a/b = "z"] return A
392:
393: Binding[] this Var = { this };
394: if (positionVariable == null
395: && ExpressionTool
396: .isVariableReplaceableByDot(
397: term, this Var)
398: && (term.getDependencies() & StaticProperty.DEPENDS_ON_FOCUS) == 0
399: && ExpressionTool.dependsOnVariable(
400: operands[op], this Var)
401: && !ExpressionTool.dependsOnVariable(
402: operands[1 - op], this Var)) {
403: PromotionOffer offer = new PromotionOffer(
404: opt);
405: offer.action = PromotionOffer.INLINE_VARIABLE_REFERENCES;
406: offer.bindingList = this Var;
407: offer.containingExpression = new ContextItemExpression();
408: Expression newOperand = operands[op]
409: .promote(offer);
410: if (newOperand != null
411: && !ExpressionTool
412: .dependsOnVariable(
413: newOperand, this Var)) {
414: if (newOperand instanceof ComputedExpression) {
415: ((ComputedExpression) newOperand)
416: .resetStaticProperties();
417: }
418: Expression predicate;
419: //newOperand = new Atomizer(newOperand, env.getConfiguration());
420: if (op == 0) {
421: // todo: make GeneralComparisonSA where appropriate
422: predicate = new GeneralComparison(
423: newOperand, comp
424: .getOperator(),
425: operands[1]);
426: } else {
427: predicate = new GeneralComparison(
428: operands[0], comp
429: .getOperator(),
430: newOperand);
431: }
432: selection = new FilterExpression(
433: selection, predicate, env);
434: resetStaticProperties();
435: //action = condAction.getThenExpression();
436: positionVariable = null;
437: positionBinding = null;
438: //return simplify(env).typeCheck(env, contextItemType).optimize(opt, env, contextItemType);
439: list.remove(t);
440: changed = true;
441: break;
442: }
443: }
444:
445: // Expression start = operands[op];
446: // Expression rest = new ContextItemExpression();
447: // Expression ostart = start;
448: // if (start instanceof Atomizer) {
449: // ostart = ((Atomizer)start).getBaseExpression();
450: // }
451: // if (ostart instanceof PathExpression) {
452: // rest = ((PathExpression)ostart).getRemainingSteps();
453: // ostart = ((PathExpression)ostart).getFirstStep();
454: // }
455: // Binding[] thisVar = {this};
456: // if (ostart instanceof VariableReference &&
457: // ((VariableReference)ostart).getBinding() == this &&
458: // positionVariable == null &&
459: // (operands[1-op].getDependencies() & StaticProperty.DEPENDS_ON_FOCUS) == 0 &&
460: // !ExpressionTool.dependsOnVariable(rest, thisVar) &&
461: // !ExpressionTool.dependsOnVariable(operands[1-op], thisVar)) {
462: // Expression predicate;
463: // rest = new Atomizer(rest, env.getConfiguration());
464: // if (op==0) {
465: // predicate = new GeneralComparison(rest, comp.getOperator(), operands[1]);
466: // } else {
467: // predicate = new GeneralComparison(operands[0], comp.getOperator(), rest);
468: // }
469: // selection = new FilterExpression(selection, predicate, env);
470: // //action = condAction.getThenExpression();
471: // positionVariable = null;
472: // positionBinding = null;
473: // //return simplify(env).typeCheck(env, contextItemType).optimize(opt, env, contextItemType);
474: // list.remove(t);
475: // changed = true;
476: // break;
477: // }
478: }
479: }
480: }
481: if (changed) {
482: if (list.isEmpty()) {
483: action = condAction.getThenExpression();
484: adoptChildExpression(action);
485: } else {
486: Expression term = (Expression) list.get(0);
487: for (int t = 1; t < list.size(); t++) {
488: term = new BooleanExpression(term, Token.AND,
489: (Expression) list.get(t));
490: }
491: condAction.setCondition(term);
492: }
493: if (head == null) {
494: sequence = selection;
495: } else {
496: PathExpression path = new PathExpression(head,
497: selection);
498: path.setParentExpression(this );
499: Expression k = opt.convertPathExpressionToKey(path,
500: env);
501: if (k == null) {
502: sequence = path;
503: } else {
504: sequence = k;
505: }
506: sequence = sequence.simplify(env).typeCheck(env,
507: contextItemType).optimize(opt, env,
508: contextItemType);
509: adoptChildExpression(sequence);
510: }
511: return this ;
512: }
513: }
514: return null;
515: }
516:
517: /**
518: * Mark tail function calls: only possible if the for expression iterates zero or one times.
519: * (This arises in XSLT/XPath, which does not have a LET expression, so FOR gets used instead)
520: */
521:
522: public boolean markTailFunctionCalls() {
523: if (!Cardinality.allowsMany(sequence.getCardinality())) {
524: return ExpressionTool.markTailFunctionCalls(action);
525: } else {
526: return false;
527: }
528: }
529:
530: /**
531: * Extend an array of variable bindings to include the binding(s) defined in this expression
532: */
533:
534: protected Binding[] extendBindingList(Binding[] in) {
535: if (positionBinding == null) {
536: return super .extendBindingList(in);
537: }
538: Binding[] newBindingList = new Binding[in.length + 2];
539: System.arraycopy(in, 0, newBindingList, 0, in.length);
540: newBindingList[in.length] = this ;
541: newBindingList[in.length + 1] = positionBinding;
542: return newBindingList;
543: }
544:
545: /**
546: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
547: * This method indicates which of these methods is provided. This implementation provides both iterate() and
548: * process() methods natively.
549: */
550:
551: public int getImplementationMethod() {
552: return ITERATE_METHOD | PROCESS_METHOD;
553: }
554:
555: /**
556: * Check that any elements and attributes constructed or returned by this expression are acceptable
557: * in the content model of a given complex type. It's always OK to say yes, since the check will be
558: * repeated at run-time. The process of checking element and attribute constructors against the content
559: * model of a complex type also registers the type of content expected of those constructors, so the
560: * static validation can continue recursively.
561: */
562:
563: public void checkPermittedContents(SchemaType parentType,
564: StaticContext env, boolean whole) throws XPathException {
565: action.checkPermittedContents(parentType, env, false);
566: }
567:
568: /**
569: * Iterate over the sequence of values
570: */
571:
572: public SequenceIterator iterate(XPathContext context)
573: throws XPathException {
574:
575: // First create an iteration of the base sequence.
576:
577: // Then create a MappingIterator which applies a mapping function to each
578: // item in the base sequence. The mapping function is essentially the "return"
579: // expression, wrapped in a MappingAction object that is responsible also for
580: // setting the range variable at each step.
581:
582: SequenceIterator base = sequence.iterate(context);
583:
584: MappingFunction map = new MappingAction(context, slotNumber,
585: positionBinding, action);
586: return new MappingIterator(base, map, null);
587: }
588:
589: /**
590: * Process this expression as an instruction, writing results to the current
591: * outputter
592: */
593:
594: public void process(XPathContext context) throws XPathException {
595: SequenceIterator iter = sequence.iterate(context);
596: int position = 1;
597: while (true) {
598: Item item = iter.next();
599: if (item == null)
600: break;
601: context.setLocalVariable(slotNumber, item);
602: if (positionBinding != null) {
603: positionBinding.setPosition(position++, context);
604: }
605: action.process(context);
606: }
607: }
608:
609: /**
610: * Determine the data type of the items returned by the expression, if possible
611: * @return one of the values Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
612: * or Type.ITEM (meaning not known in advance)
613: * @param th
614: */
615:
616: public ItemType getItemType(TypeHierarchy th) {
617: return action.getItemType(th);
618: }
619:
620: /**
621: * Determine the static cardinality of the expression
622: */
623:
624: public int computeCardinality() {
625: int c1 = sequence.getCardinality();
626: int c2 = action.getCardinality();
627: return Cardinality.multiply(c1, c2);
628: }
629:
630: /**
631: * Diagnostic print of expression structure
632: */
633:
634: public void display(int level, NamePool pool, PrintStream out) {
635: out.println(ExpressionTool.indent(level) + "for $"
636: + getVariableName(pool)
637: + (positionVariable == null ? "" : " at $?") + " in");
638: sequence.display(level + 1, pool, out);
639: out.println(ExpressionTool.indent(level) + "return");
640: action.display(level + 1, pool, out);
641: }
642:
643: /**
644: * The MappingAction represents the action to be taken for each item in the
645: * source sequence. It acts as the MappingFunction for the mapping iterator, and
646: * also as the Binding of the position variable (at $n) in XQuery, if used.
647: */
648:
649: private static class MappingAction implements MappingFunction {
650:
651: private XPathContext context;
652: private int slotNumber;
653: private Expression action;
654: private PositionBinding positionBinding;
655: private int position = 1;
656:
657: public MappingAction(XPathContext context, int slotNumber,
658: PositionBinding positionBinding, Expression action) {
659: this .context = context;
660: this .slotNumber = slotNumber;
661: this .positionBinding = positionBinding;
662: this .action = action;
663: }
664:
665: public Object map(Item item, XPathContext c)
666: throws XPathException {
667: context.setLocalVariable(slotNumber, item);
668: if (positionBinding != null) {
669: positionBinding.setPosition(position++, context);
670: }
671: return action.iterate(context);
672: }
673: }
674:
675: /**
676: * Get the type of this expression for use in tracing and diagnostics
677: * @return the type of expression, as enumerated in class {@link net.sf.saxon.trace.Location}
678: */
679:
680: protected int getConstructType() {
681: return Location.FOR_EXPRESSION;
682: }
683:
684: /**
685: * This class represents the binding of the position variable ("at $p") in an XQuery FOR clause.
686: * The variable is held in a slot on the stackframe: in 8.4 and earlier it was held as a property
687: * of the iterator, but that let to problems with lazy evaluation because the value wasn't saved as
688: * part of a Closure.
689: */
690:
691: private static class PositionBinding implements Binding {
692:
693: private int slotNumber;
694: private int nameCode;
695:
696: public PositionBinding(int nameCode) {
697: this .nameCode = nameCode;
698: }
699:
700: private void setSlotNumber(int slot) {
701: this .slotNumber = slot;
702: }
703:
704: private void setPosition(int position, XPathContext context) {
705: context.setLocalVariable(slotNumber, new IntegerValue(
706: position));
707: }
708:
709: /**
710: * Indicate whether the binding is local or global. A global binding is one that has a fixed
711: * value for the life of a query or transformation; any other binding is local.
712: */
713:
714: public final boolean isGlobal() {
715: return false;
716: }
717:
718: /**
719: * Test whether it is permitted to assign to the variable using the saxon:assign
720: * extension element. This will only be for an XSLT global variable where the extra
721: * attribute saxon:assignable="yes" is present.
722: */
723:
724: public final boolean isAssignable() {
725: return false;
726: }
727:
728: /**
729: * If this is a local variable held on the local stack frame, return the corresponding slot number.
730: * In other cases, return -1.
731: */
732:
733: public int getLocalSlotNumber() {
734: return slotNumber;
735: }
736:
737: /**
738: * Get the name of the positional variable
739: * @return the namecode of the positional variable
740: */
741:
742: public int getNameCode() {
743: return nameCode;
744: }
745:
746: public ValueRepresentation evaluateVariable(XPathContext context)
747: throws XPathException {
748: return context.evaluateLocalVariable(slotNumber);
749: }
750:
751: }
752:
753: }
754:
755: //
756: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
757: // you may not use this file except in compliance with the License. You may obtain a copy of the
758: // License at http://www.mozilla.org/MPL/
759: //
760: // Software distributed under the License is distributed on an "AS IS" basis,
761: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
762: // See the License for the specific language governing rights and limitations under the License.
763: //
764: // The Original Code is: all this file.
765: //
766: // The Initial Developer of the Original Code is Michael H. Kay
767: //
768: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
769: //
770: // Contributor(s): none.
771: //
|