001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.xpath;
030:
031: import com.caucho.util.FreeList;
032: import com.caucho.xml.XmlUtil;
033: import com.caucho.xpath.expr.ObjectVar;
034: import com.caucho.xpath.expr.Var;
035: import com.caucho.xpath.pattern.AbstractPattern;
036: import com.caucho.xpath.pattern.NodeIterator;
037:
038: import org.w3c.dom.Document;
039: import org.w3c.dom.Element;
040: import org.w3c.dom.Node;
041:
042: import java.util.ArrayList;
043: import java.util.HashMap;
044: import java.util.Iterator;
045:
046: /**
047: * Global and local variable environment. The April XSLT draft introduces
048: * global and local variables. The Env class contains those bindings.
049: *
050: * <p>Because this class exists only to support XSL, it makes a number
051: * of assumptions that would be invalid for a typical API. Specifically,
052: * the variable names <font color='red'>must</font> be interned strings, i.e.
053: * variable matching uses '==', not equals.
054: *
055: * <p>Local variables are handled like a stack. They are pushed and
056: * popped as necessary. The top variables shadow bottom variables.
057: *
058: * <p>In other words, although the API somewhat resembles a HashMap,
059: * it can't be used as a generic hash map.
060: */
061: public class Env implements ExprEnvironment {
062: static FreeList<Env> _freeList = new FreeList<Env>(32);
063:
064: HashMap _ids;
065: HashMap _idCache;
066: Element _lastElement;
067: HashMap<String, Var> _globals; // = new HashMap();
068: HashMap _functions;
069: HashMap _cache;
070:
071: String[] _varKeys;
072: Var[] _varValues;
073: int _varSize;
074:
075: private Node _currentNode;
076: private Node _contextNode;
077:
078: private int _positionIndex;
079: private boolean _hasMorePositions;
080: private int _useCount;
081:
082: private Env _parent;
083: private Env _root;
084:
085: private ExprEnvironment _exprEnv;
086: private AbstractPattern _select;
087:
088: private int _position;
089: private int _size;
090:
091: private StylesheetEnv _stylesheetEnv;
092: private VarEnv _varEnv;
093:
094: static Env create() {
095: Env env = null; // (Env) freeList.allocate();
096: if (env == null)
097: env = new Env();
098:
099: env._root = env;
100:
101: return env;
102: }
103:
104: public void setStylesheetEnv(StylesheetEnv stylesheetEnv) {
105: _stylesheetEnv = stylesheetEnv;
106: }
107:
108: public StylesheetEnv getStylesheetEnv() {
109: for (Env env = this ; env != null; env = env._parent)
110: if (env._stylesheetEnv != null)
111: return env._stylesheetEnv;
112:
113: return null;
114: }
115:
116: /**
117: * Sets the variable environment.
118: */
119: public void setVarEnv(VarEnv varEnv) {
120: _varEnv = varEnv;
121: }
122:
123: /**
124: * Returns the variable environment.
125: */
126: public VarEnv getVarEnv() {
127: return _varEnv;
128: }
129:
130: /**
131: * Initialize the XPath environment with values from the parent.
132: */
133: void init(Env parent) {
134: _parent = parent;
135: _root = parent._root;
136: }
137:
138: /**
139: * Initialize the XPath environment with a context and a select node.
140: */
141: void init(Env parent, AbstractPattern select, Node currentNode) {
142: _parent = parent;
143: _root = parent._root;
144: _select = select;
145: _currentNode = currentNode;
146: }
147:
148: /**
149: * Initialize the XPath environment with values from the parent.
150: */
151: void initMacro(Env parent) {
152: _parent = parent;
153: _root = parent._root;
154:
155: _select = parent._select;
156: _stylesheetEnv = parent._stylesheetEnv;
157: _exprEnv = parent._exprEnv;
158: _currentNode = parent._currentNode;
159: _contextNode = parent._contextNode;
160:
161: _position = parent._position;
162: _size = parent._size;
163:
164: _positionIndex = 0;
165: _hasMorePositions = false;
166: _useCount = 0;
167: }
168:
169: /**
170: * Clears all values in the local environment.
171: */
172: public void clear() {
173: if (true)
174: return;
175:
176: _useCount++;
177: if (_ids != null) {
178: _ids.clear();
179: _idCache.clear();
180: }
181:
182: while (_varSize-- > 0) {
183: _varKeys[_varSize] = null;
184: _varValues[_varSize] = null;
185: }
186: _varSize = 0;
187:
188: _lastElement = null;
189: _globals.clear();
190: _functions = null;
191: _cache = null;
192: _currentNode = null;
193: _contextNode = null;
194: _parent = null;
195: _select = null;
196:
197: _size = 0;
198: _position = 0;
199: _positionIndex = 0;
200: _hasMorePositions = false;
201: }
202:
203: /**
204: * Returns the parent envivonment.
205: */
206: Env getParent() {
207: return _parent;
208: }
209:
210: /**
211: * Returns the current number of local variables.
212: */
213: public int getVarSize() {
214: return _varSize;
215: }
216:
217: /**
218: * Sets the current number of local variables (popping, them).
219: */
220: public void setVarSize(int size) {
221: if (_varKeys == null)
222: return;
223:
224: for (; _varSize > size; _varSize--) {
225: _varSize--;
226: _varKeys[_varSize] = null;
227: _varValues[_varSize] = null;
228: }
229: }
230:
231: /**
232: * Returns the value associated with name.
233: *
234: * <p><em>name must be interned</em>
235: */
236: public Var getVar(String name) {
237: for (int i = _varSize - 1; i >= 0; i--) {
238: if (_varKeys[i] == name)
239: return _varValues[i];
240: }
241:
242: if (_root._globals != null) {
243: Var var = _root._globals.get(name);
244:
245: if (var != null)
246: return var;
247: }
248:
249: if (_root._varEnv != null)
250: return _root._varEnv.getVar(name);
251: else
252: return null;
253: }
254:
255: /**
256: * Adds the value associated with name.
257: *
258: * <p><em>name must be interned</em>
259: */
260: public int addVar(String name, Object value) {
261: _useCount++;
262: if (value instanceof Iterator)
263: value = iteratorToList((Iterator) value);
264:
265: if (!(value instanceof Var))
266: value = new ObjectVar(value);
267:
268: return addVar(name, (Var) value);
269: }
270:
271: /**
272: * Sets the value associated with name.
273: *
274: * <p><em>name must be interned</em>
275: */
276: public void setVar(String name, Object value) {
277: _useCount++;
278: if (value instanceof Iterator)
279: value = iteratorToList((Iterator) value);
280:
281: if (!(value instanceof Var))
282: value = new ObjectVar(value);
283:
284: for (int i = _varSize - 1; i >= 0; i--) {
285: if (_varKeys[i] == name) {
286: _varValues[i] = (Var) value;
287: return;
288: }
289: }
290:
291: addVar(name, (Var) value);
292: }
293:
294: /**
295: * Adds the value associated with name.
296: *
297: * <p><em>name must be interned</em>
298: */
299: public int addVar(String name, Var value) {
300: _useCount++;
301:
302: if (_varKeys == null) {
303: _varKeys = new String[16];
304: _varValues = new Var[16];
305: } else if (_varSize == _varKeys.length) {
306: String[] newKeys = new String[2 * _varKeys.length];
307: Var[] newValues = new Var[2 * _varKeys.length];
308:
309: System.arraycopy(_varKeys, 0, newKeys, 0, _varSize);
310: System.arraycopy(_varValues, 0, newValues, 0, _varSize);
311:
312: _varKeys = newKeys;
313: _varValues = newValues;
314: }
315:
316: _varKeys[_varSize] = name;
317: _varValues[_varSize] = value;
318: _varSize++;
319:
320: return _varSize - 1;
321: }
322:
323: /**
324: * Pops the last count vars from the local stack.
325: */
326: public void popVars(int count) {
327: _useCount++;
328: if (_varKeys == null)
329: return;
330:
331: for (; count > 0 && _varSize > 0; count--) {
332: _varSize--;
333: _varKeys[_varSize] = null;
334: _varValues[_varSize].free();
335: _varValues[_varSize] = null;
336: }
337: }
338:
339: /**
340: * Returns the top of the stack.
341: */
342: public int getTop() {
343: return _varSize;
344: }
345:
346: /**
347: * Pops the last count vars from the local stack.
348: */
349: public void popToTop(int top) {
350: _useCount++;
351: if (_varKeys == null)
352: return;
353:
354: while (top < _varSize) {
355: _varSize--;
356: _varKeys[_varSize] = null;
357: _varValues[_varSize].free();
358: _varValues[_varSize] = null;
359: }
360: }
361:
362: /**
363: * Sets a global variable.
364: */
365: public void setGlobal(String name, Object value) {
366: _useCount++;
367:
368: Var var = null;
369:
370: if (value instanceof Iterator)
371: value = iteratorToList((Iterator) value);
372:
373: if (value instanceof Var)
374: var = (Var) value;
375: else
376: var = new ObjectVar(value);
377:
378: if (_root._globals == null)
379: _root._globals = new HashMap<String, Var>();
380:
381: _root._globals.put(name, var);
382: }
383:
384: /**
385: * Converts an iterator to an array list
386: */
387: private ArrayList iteratorToList(Iterator iter) {
388: ArrayList list = new ArrayList();
389: while (iter.hasNext())
390: list.add(iter.next());
391:
392: return list;
393: }
394:
395: /**
396: * Sets the extension function library
397: *
398: * @param function new function library
399: * @return old function library
400: */
401: public HashMap setFunctions(HashMap functions) {
402: HashMap old = _functions;
403:
404: _functions = functions;
405:
406: return old;
407: }
408:
409: /**
410: * Adds and extension function
411: *
412: * @param function new function library
413: * @return old function library
414: */
415: public void addFunction(String name, Object fun) {
416: if (_functions == null)
417: _functions = new HashMap();
418:
419: _functions.put(name, fun);
420: }
421:
422: /**
423: * Returns the named function.
424: */
425: public XPathFun getFunction(String name) {
426: if (_root._functions == null)
427: return null;
428: else
429: return (XPathFun) _root._functions.get(name);
430: }
431:
432: /**
433: * Returns true if there are more positions() needed to iterate through.
434: */
435: public boolean hasMorePositions() {
436: return _hasMorePositions;
437: }
438:
439: /**
440: * Set true if there are more positions() needed to iterate through.
441: *
442: * @param more if true, there are more positions to iterate through.
443: *
444: * @return the old more-position value.
445: */
446: public boolean setMorePositions(boolean more) {
447: boolean old = _hasMorePositions;
448:
449: _hasMorePositions = more;
450:
451: return old;
452: }
453:
454: /*
455: * The position index is used for patterns which have multiple position()s
456: * for the same node. See FilterPattern for a more detailed description.
457: *
458: * @param index the new position index.
459: *
460: * @return the old position index.
461: */
462: public int setPositionIndex(int index) {
463: int old = _positionIndex;
464:
465: _positionIndex = index;
466:
467: return old;
468: }
469:
470: /*
471: * Returns the position index is used for patterns which have
472: * multiple position()s for the same node. See FilterPattern for a
473: * more detailed description.
474: */
475: public int getPositionIndex() {
476: return _positionIndex;
477: }
478:
479: /**
480: * Gets the current node.
481: */
482: public Node getCurrentNode() {
483: return _currentNode;
484: }
485:
486: /**
487: * Sets the current node.
488: */
489: public void setCurrentNode(Node node) {
490: _currentNode = node;
491: }
492:
493: /**
494: * Sets the selection context
495: */
496: public AbstractPattern setSelect(Node node, AbstractPattern select) {
497: AbstractPattern oldSelect = _select;
498:
499: _contextNode = node;
500: _select = select;
501:
502: _position = 0;
503:
504: return oldSelect;
505: }
506:
507: public AbstractPattern getSelect() {
508: return _select;
509: }
510:
511: /**
512: * Sets the selection context
513: */
514: public ExprEnvironment setExprEnv(ExprEnvironment exprEnv) {
515: ExprEnvironment oldExprEnv = _exprEnv;
516:
517: _exprEnv = exprEnv;
518:
519: return oldExprEnv;
520: }
521:
522: public ExprEnvironment getExprEnv() {
523: return _exprEnv;
524: }
525:
526: /**
527: * Gets the context node.
528: */
529: public Node getContextNode() {
530: return _contextNode;
531: }
532:
533: /**
534: * Sets the context node.
535: */
536: public Node setContextNode(Node contextNode) {
537: Node oldNode = _contextNode;
538: _contextNode = contextNode;
539: return oldNode;
540: }
541:
542: /**
543: * Returns the position of the context node.
544: */
545: public int getContextPosition() {
546: if (_exprEnv != null)
547: return _exprEnv.getContextPosition();
548:
549: if (_position > 0)
550: return _position;
551:
552: if (_contextNode == null || _currentNode == null)
553: return 0;
554:
555: if (_select != null) {
556: try {
557: NodeIterator iter = _select.select(_contextNode, this );
558: Node child;
559:
560: while ((child = iter.nextNode()) != null
561: && child != _currentNode) {
562: }
563:
564: return iter.getContextPosition();
565: } catch (Exception e) {
566: }
567: }
568:
569: Node child = _contextNode.getFirstChild();
570: int pos = 1;
571: for (; child != null && child != _currentNode; child = child
572: .getNextSibling()) {
573: pos++;
574: }
575:
576: return pos;
577: }
578:
579: /**
580: * Returns the number of nodes in the context list.
581: */
582: public int getContextSize() {
583: if (_exprEnv != null)
584: return _exprEnv.getContextSize();
585:
586: if (_size > 0)
587: return _size;
588:
589: if (_contextNode == null || _currentNode == null)
590: return 0;
591:
592: if (_select != null) {
593: try {
594: NodeIterator iter = _select.select(_contextNode, this );
595: Node child;
596:
597: while ((child = iter.nextNode()) != null
598: && child != _currentNode) {
599: }
600:
601: return iter.getContextSize();
602: } catch (Exception e) {
603: }
604: }
605:
606: Node child = _contextNode.getFirstChild();
607: int pos = 0;
608: for (; child != null; child = child.getNextSibling())
609: pos++;
610:
611: return pos;
612: }
613:
614: /**
615: * Returns a document for creating nodes.
616: */
617: public Document getOwnerDocument() {
618: return null;
619: }
620:
621: /**
622: * Returns the given system property.
623: */
624: public Object systemProperty(String namespaceURI, String localName) {
625: return null;
626: }
627:
628: /**
629: * Returns the string-value of the node.
630: */
631: public String stringValue(Node node) {
632: return XmlUtil.textValue(node);
633: }
634:
635: /*
636: * Returns the position() value. Note, this is not the same as
637: * positionIndex.
638: */
639: public void setPosition(int position) {
640: _position = position;
641: }
642:
643: public int setContextPosition(int position) {
644: int oldPosition = _position;
645: _position = position;
646: return oldPosition;
647: }
648:
649: /**
650: * Sets the context size to a know value.
651: */
652: public int setContextSize(int size) {
653: int oldSize = _size;
654: _size = size;
655: return oldSize;
656: }
657:
658: public Object getCache(Object key) {
659: if (_root._cache == null)
660: return null;
661: else
662: return _root._cache.get(key);
663: }
664:
665: public void setCache(Object key, Object value) {
666: if (_root._cache == null)
667: _root._cache = new HashMap();
668:
669: _root._cache.put(key, value);
670: }
671:
672: public int getUseCount() {
673: return _useCount;
674: }
675:
676: public void free() {
677: _root = null;
678: _parent = null;
679: _select = null;
680:
681: _exprEnv = null;
682: _stylesheetEnv = null;
683:
684: if (_ids != null) {
685: _ids.clear();
686: _idCache.clear();
687: }
688:
689: while (_varSize-- > 0) {
690: _varKeys[_varSize] = null;
691: _varValues[_varSize] = null;
692: }
693: _varSize = 0;
694:
695: _lastElement = null;
696: if (_globals != null)
697: _globals.clear();
698: _functions = null;
699: _cache = null;
700: _currentNode = null;
701: _contextNode = null;
702:
703: _size = 0;
704: _position = 0;
705: _positionIndex = 0;
706: _hasMorePositions = false;
707:
708: _freeList.free(this);
709: }
710: }
|