001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: MultiDOM.java 468651 2006-10-28 07:04:25Z minchau $
020 */
021
022 package org.apache.xalan.xsltc.dom;
023
024 import org.apache.xalan.xsltc.DOM;
025 import org.apache.xalan.xsltc.StripFilter;
026 import org.apache.xml.serializer.SerializationHandler;
027 import org.apache.xalan.xsltc.TransletException;
028 import org.apache.xalan.xsltc.runtime.BasisLibrary;
029 import org.apache.xalan.xsltc.runtime.Hashtable;
030 import org.apache.xml.dtm.DTM;
031 import org.apache.xml.dtm.Axis;
032 import org.apache.xml.dtm.DTMAxisIterator;
033 import org.apache.xml.dtm.DTMManager;
034 import org.apache.xml.dtm.ref.DTMAxisIteratorBase;
035 import org.apache.xml.dtm.ref.DTMDefaultBase;
036 import org.apache.xml.utils.SuballocatedIntVector;
037
038 import org.w3c.dom.Node;
039 import org.w3c.dom.NodeList;
040
041 /**
042 * @author Jacek Ambroziak
043 * @author Morten Jorgensen
044 * @author Erwin Bolwidt <ejb@klomp.org>
045 */
046 public final class MultiDOM implements DOM {
047
048 private static final int NO_TYPE = DOM.FIRST_TYPE - 2;
049 private static final int INITIAL_SIZE = 4;
050
051 private DOM[] _adapters;
052 private DOMAdapter _main;
053 private DTMManager _dtmManager;
054 private int _free;
055 private int _size;
056
057 private Hashtable _documents = new Hashtable();
058
059 private final class AxisIterator extends DTMAxisIteratorBase {
060 // constitutive data
061 private final int _axis;
062 private final int _type;
063 // implementation mechanism
064 private DTMAxisIterator _source;
065 private int _dtmId = -1;
066
067 public AxisIterator(final int axis, final int type) {
068 _axis = axis;
069 _type = type;
070 }
071
072 public int next() {
073 if (_source == null) {
074 return(END);
075 }
076 return _source.next();
077 }
078
079
080 public void setRestartable(boolean flag) {
081 if (_source != null) {
082 _source.setRestartable(flag);
083 }
084 }
085
086 public DTMAxisIterator setStartNode(final int node) {
087 if (node == DTM.NULL) {
088 return this;
089 }
090
091 int dom = node >>> DTMManager.IDENT_DTM_NODE_BITS;
092
093 // Get a new source first time and when mask changes
094 if (_source == null || _dtmId != dom) {
095 if (_type == NO_TYPE) {
096 _source = _adapters[dom].getAxisIterator(_axis);
097 } else if (_axis == Axis.CHILD) {
098 _source = _adapters[dom].getTypedChildren(_type);
099 } else {
100 _source = _adapters[dom].getTypedAxisIterator(_axis, _type);
101 }
102 }
103
104 _dtmId = dom;
105 _source.setStartNode(node);
106 return this;
107 }
108
109 public DTMAxisIterator reset() {
110 if (_source != null) {
111 _source.reset();
112 }
113 return this;
114 }
115
116 public int getLast() {
117 if (_source != null) {
118 return _source.getLast();
119 }
120 else {
121 return END;
122 }
123 }
124
125 public int getPosition() {
126 if (_source != null) {
127 return _source.getPosition();
128 }
129 else {
130 return END;
131 }
132 }
133
134 public boolean isReverse() {
135 return Axis.isReverse(_axis);
136 }
137
138 public void setMark() {
139 if (_source != null) {
140 _source.setMark();
141 }
142 }
143
144 public void gotoMark() {
145 if (_source != null) {
146 _source.gotoMark();
147 }
148 }
149
150 public DTMAxisIterator cloneIterator() {
151 final AxisIterator clone = new AxisIterator(_axis, _type);
152 if (_source != null) {
153 clone._source = _source.cloneIterator();
154 }
155 clone._dtmId = _dtmId;
156 return clone;
157 }
158 } // end of AxisIterator
159
160
161 /**************************************************************
162 * This is a specialised iterator for predicates comparing node or
163 * attribute values to variable or parameter values.
164 */
165 private final class NodeValueIterator extends DTMAxisIteratorBase {
166
167 private DTMAxisIterator _source;
168 private String _value;
169 private boolean _op;
170 private final boolean _isReverse;
171 private int _returnType = RETURN_PARENT;
172
173 public NodeValueIterator(DTMAxisIterator source, int returnType,
174 String value, boolean op) {
175 _source = source;
176 _returnType = returnType;
177 _value = value;
178 _op = op;
179 _isReverse = source.isReverse();
180 }
181
182 public boolean isReverse() {
183 return _isReverse;
184 }
185
186 public DTMAxisIterator cloneIterator() {
187 try {
188 NodeValueIterator clone = (NodeValueIterator)super.clone();
189 clone._source = _source.cloneIterator();
190 clone.setRestartable(false);
191 return clone.reset();
192 }
193 catch (CloneNotSupportedException e) {
194 BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
195 e.toString());
196 return null;
197 }
198 }
199
200
201 public void setRestartable(boolean isRestartable) {
202 _isRestartable = isRestartable;
203 _source.setRestartable(isRestartable);
204 }
205
206 public DTMAxisIterator reset() {
207 _source.reset();
208 return resetPosition();
209 }
210
211 public int next() {
212
213 int node;
214 while ((node = _source.next()) != END) {
215 String val = getStringValueX(node);
216 if (_value.equals(val) == _op) {
217 if (_returnType == RETURN_CURRENT)
218 return returnNode(node);
219 else
220 return returnNode(getParent(node));
221 }
222 }
223 return END;
224 }
225
226 public DTMAxisIterator setStartNode(int node) {
227 if (_isRestartable) {
228 _source.setStartNode(_startNode = node);
229 return resetPosition();
230 }
231 return this;
232 }
233
234 public void setMark() {
235 _source.setMark();
236 }
237
238 public void gotoMark() {
239 _source.gotoMark();
240 }
241 }
242
243 public MultiDOM(DOM main) {
244 _size = INITIAL_SIZE;
245 _free = 1;
246 _adapters = new DOM[INITIAL_SIZE];
247 DOMAdapter adapter = (DOMAdapter)main;
248 _adapters[0] = adapter;
249 _main = adapter;
250 DOM dom = adapter.getDOMImpl();
251 if (dom instanceof DTMDefaultBase) {
252 _dtmManager = ((DTMDefaultBase)dom).getManager();
253 }
254
255 // %HZ% %REVISIT% Is this the right thing to do here? In the old
256 // %HZ% %REVISIT% version, the main document did not get added through
257 // %HZ% %REVISIT% a call to addDOMAdapter, which meant it couldn't be
258 // %HZ% %REVISIT% found by a call to getDocumentMask. The problem is
259 // %HZ% %REVISIT% TransformerHandler is typically constructed with a
260 // %HZ% %REVISIT% system ID equal to the stylesheet's URI; with SAX
261 // %HZ% %REVISIT% input, it ends up giving that URI to the document.
262 // %HZ% %REVISIT% Then, any references to document('') are resolved
263 // %HZ% %REVISIT% using the stylesheet's URI.
264 // %HZ% %REVISIT% MultiDOM.getDocumentMask is called to verify that
265 // %HZ% %REVISIT% a document associated with that URI has not been
266 // %HZ% %REVISIT% encountered, and that method ends up returning the
267 // %HZ% %REVISIT% mask of the main document, when what we really what
268 // %HZ% %REVISIT% is to read the stylesheet itself!
269 addDOMAdapter(adapter, false);
270 }
271
272 public int nextMask() {
273 return _free;
274 }
275
276 public void setupMapping(String[] names, String[] uris, int[] types, String[] namespaces) {
277 // This method only has a function in DOM adapters
278 }
279
280 public int addDOMAdapter(DOMAdapter adapter) {
281 return addDOMAdapter(adapter, true);
282 }
283
284 private int addDOMAdapter(DOMAdapter adapter, boolean indexByURI) {
285 // Add the DOM adapter to the array of DOMs
286 DOM dom = adapter.getDOMImpl();
287
288 int domNo = 1;
289 int dtmSize = 1;
290 SuballocatedIntVector dtmIds = null;
291 if (dom instanceof DTMDefaultBase) {
292 DTMDefaultBase dtmdb = (DTMDefaultBase)dom;
293 dtmIds = dtmdb.getDTMIDs();
294 dtmSize = dtmIds.size();
295 domNo = dtmIds.elementAt(dtmSize-1) >>> DTMManager.IDENT_DTM_NODE_BITS;
296 }
297 else if (dom instanceof SimpleResultTreeImpl) {
298 SimpleResultTreeImpl simpleRTF = (SimpleResultTreeImpl)dom;
299 domNo = simpleRTF.getDocument() >>> DTMManager.IDENT_DTM_NODE_BITS;
300 }
301
302 if (domNo >= _size) {
303 int oldSize = _size;
304 do {
305 _size *= 2;
306 } while (_size <= domNo);
307
308 final DOMAdapter[] newArray = new DOMAdapter[_size];
309 System.arraycopy(_adapters, 0, newArray, 0, oldSize);
310 _adapters = newArray;
311 }
312
313 _free = domNo + 1;
314
315 if (dtmSize == 1) {
316 _adapters[domNo] = adapter;
317 }
318 else if (dtmIds != null) {
319 int domPos = 0;
320 for (int i = dtmSize - 1; i >= 0; i--) {
321 domPos = dtmIds.elementAt(i) >>> DTMManager.IDENT_DTM_NODE_BITS;
322 _adapters[domPos] = adapter;
323 }
324 domNo = domPos;
325 }
326
327 // Store reference to document (URI) in hashtable
328 if (indexByURI) {
329 String uri = adapter.getDocumentURI(0);
330 _documents.put(uri, new Integer(domNo));
331 }
332
333 // If the dom is an AdaptiveResultTreeImpl, we need to create a
334 // DOMAdapter around its nested dom object (if it is non-null) and
335 // add the DOMAdapter to the list.
336 if (dom instanceof AdaptiveResultTreeImpl) {
337 AdaptiveResultTreeImpl adaptiveRTF = (AdaptiveResultTreeImpl)dom;
338 DOM nestedDom = adaptiveRTF.getNestedDOM();
339 if (nestedDom != null) {
340 DOMAdapter newAdapter = new DOMAdapter(nestedDom,
341 adapter.getNamesArray(),
342 adapter.getUrisArray(),
343 adapter.getTypesArray(),
344 adapter.getNamespaceArray());
345 addDOMAdapter(newAdapter);
346 }
347 }
348
349 return domNo;
350 }
351
352 public int getDocumentMask(String uri) {
353 Integer domIdx = (Integer)_documents.get(uri);
354 if (domIdx == null) {
355 return(-1);
356 } else {
357 return domIdx.intValue();
358 }
359 }
360
361 public DOM getDOMAdapter(String uri) {
362 Integer domIdx = (Integer)_documents.get(uri);
363 if (domIdx == null) {
364 return(null);
365 } else {
366 return(_adapters[domIdx.intValue()]);
367 }
368 }
369
370 public int getDocument()
371 {
372 return _main.getDocument();
373 }
374
375 public DTMManager getDTMManager() {
376 return _dtmManager;
377 }
378
379 /**
380 * Returns singleton iterator containing the document root
381 */
382 public DTMAxisIterator getIterator() {
383 // main source document @ 0
384 return _main.getIterator();
385 }
386
387 public String getStringValue() {
388 return _main.getStringValue();
389 }
390
391 public DTMAxisIterator getChildren(final int node) {
392 return _adapters[getDTMId(node)].getChildren(node);
393 }
394
395 public DTMAxisIterator getTypedChildren(final int type) {
396 return new AxisIterator(Axis.CHILD, type);
397 }
398
399 public DTMAxisIterator getAxisIterator(final int axis) {
400 return new AxisIterator(axis, NO_TYPE);
401 }
402
403 public DTMAxisIterator getTypedAxisIterator(final int axis, final int type)
404 {
405 return new AxisIterator(axis, type);
406 }
407
408 public DTMAxisIterator getNthDescendant(int node, int n,
409 boolean includeself)
410 {
411 return _adapters[getDTMId(node)].getNthDescendant(node, n, includeself);
412 }
413
414 public DTMAxisIterator getNodeValueIterator(DTMAxisIterator iterator,
415 int type, String value,
416 boolean op)
417 {
418 return(new NodeValueIterator(iterator, type, value, op));
419 }
420
421 public DTMAxisIterator getNamespaceAxisIterator(final int axis,
422 final int ns)
423 {
424 DTMAxisIterator iterator = _main.getNamespaceAxisIterator(axis, ns);
425 return(iterator);
426 }
427
428 public DTMAxisIterator orderNodes(DTMAxisIterator source, int node) {
429 return _adapters[getDTMId(node)].orderNodes(source, node);
430 }
431
432 public int getExpandedTypeID(final int node) {
433 if (node != DTM.NULL) {
434 return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getExpandedTypeID(node);
435 }
436 else {
437 return DTM.NULL;
438 }
439 }
440
441 public int getNamespaceType(final int node) {
442 return _adapters[getDTMId(node)].getNamespaceType(node);
443 }
444
445 public int getNSType(int node)
446 {
447 return _adapters[getDTMId(node)].getNSType(node);
448 }
449
450 public int getParent(final int node) {
451 if (node == DTM.NULL) {
452 return DTM.NULL;
453 }
454 return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getParent(node);
455 }
456
457 public int getAttributeNode(final int type, final int el) {
458 if (el == DTM.NULL) {
459 return DTM.NULL;
460 }
461 return _adapters[el >>> DTMManager.IDENT_DTM_NODE_BITS].getAttributeNode(type, el);
462 }
463
464 public String getNodeName(final int node) {
465 if (node == DTM.NULL) {
466 return "";
467 }
468 return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getNodeName(node);
469 }
470
471 public String getNodeNameX(final int node) {
472 if (node == DTM.NULL) {
473 return "";
474 }
475 return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getNodeNameX(node);
476 }
477
478 public String getNamespaceName(final int node) {
479 if (node == DTM.NULL) {
480 return "";
481 }
482 return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getNamespaceName(node);
483 }
484
485 public String getStringValueX(final int node) {
486 if (node == DTM.NULL) {
487 return "";
488 }
489 return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getStringValueX(node);
490 }
491
492 public void copy(final int node, SerializationHandler handler)
493 throws TransletException
494 {
495 if (node != DTM.NULL) {
496 _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].copy(node, handler);
497 }
498 }
499
500 public void copy(DTMAxisIterator nodes, SerializationHandler handler)
501 throws TransletException
502 {
503 int node;
504 while ((node = nodes.next()) != DTM.NULL) {
505 _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].copy(node, handler);
506 }
507 }
508
509
510 public String shallowCopy(final int node, SerializationHandler handler)
511 throws TransletException
512 {
513 if (node == DTM.NULL) {
514 return "";
515 }
516 return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].shallowCopy(node, handler);
517 }
518
519 public boolean lessThan(final int node1, final int node2) {
520 if (node1 == DTM.NULL) {
521 return true;
522 }
523 if (node2 == DTM.NULL) {
524 return false;
525 }
526 final int dom1 = getDTMId(node1);
527 final int dom2 = getDTMId(node2);
528 return dom1 == dom2 ? _adapters[dom1].lessThan(node1, node2)
529 : dom1 < dom2;
530 }
531
532 public void characters(final int textNode, SerializationHandler handler)
533 throws TransletException
534 {
535 if (textNode != DTM.NULL) {
536 _adapters[textNode >>> DTMManager.IDENT_DTM_NODE_BITS].characters(textNode, handler);
537 }
538 }
539
540 public void setFilter(StripFilter filter) {
541 for (int dom=0; dom<_free; dom++) {
542 if (_adapters[dom] != null) {
543 _adapters[dom].setFilter(filter);
544 }
545 }
546 }
547
548 public Node makeNode(int index) {
549 if (index == DTM.NULL) {
550 return null;
551 }
552 return _adapters[getDTMId(index)].makeNode(index);
553 }
554
555 public Node makeNode(DTMAxisIterator iter) {
556 // TODO: gather nodes from all DOMs ?
557 return _main.makeNode(iter);
558 }
559
560 public NodeList makeNodeList(int index) {
561 if (index == DTM.NULL) {
562 return null;
563 }
564 return _adapters[getDTMId(index)].makeNodeList(index);
565 }
566
567 public NodeList makeNodeList(DTMAxisIterator iter) {
568 // TODO: gather nodes from all DOMs ?
569 return _main.makeNodeList(iter);
570 }
571
572 public String getLanguage(int node) {
573 return _adapters[getDTMId(node)].getLanguage(node);
574 }
575
576 public int getSize() {
577 int size = 0;
578 for (int i=0; i<_size; i++) {
579 size += _adapters[i].getSize();
580 }
581 return(size);
582 }
583
584 public String getDocumentURI(int node) {
585 if (node == DTM.NULL) {
586 node = DOM.NULL;
587 }
588 return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getDocumentURI(0);
589 }
590
591 public boolean isElement(final int node) {
592 if (node == DTM.NULL) {
593 return false;
594 }
595 return(_adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].isElement(node));
596 }
597
598 public boolean isAttribute(final int node) {
599 if (node == DTM.NULL) {
600 return false;
601 }
602 return(_adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].isAttribute(node));
603 }
604
605 public int getDTMId(int nodeHandle)
606 {
607 if (nodeHandle == DTM.NULL)
608 return 0;
609
610 int id = nodeHandle >>> DTMManager.IDENT_DTM_NODE_BITS;
611 while (id >= 2 && _adapters[id] == _adapters[id-1]) {
612 id--;
613 }
614 return id;
615 }
616
617 public int getNodeIdent(int nodeHandle)
618 {
619 return _adapters[nodeHandle >>> DTMManager.IDENT_DTM_NODE_BITS].getNodeIdent(nodeHandle);
620 }
621
622 public int getNodeHandle(int nodeId)
623 {
624 return _main.getNodeHandle(nodeId);
625 }
626
627 public DOM getResultTreeFrag(int initSize, int rtfType)
628 {
629 return _main.getResultTreeFrag(initSize, rtfType);
630 }
631
632 public DOM getResultTreeFrag(int initSize, int rtfType, boolean addToManager)
633 {
634 return _main.getResultTreeFrag(initSize, rtfType, addToManager);
635 }
636
637 public DOM getMain()
638 {
639 return _main;
640 }
641
642 /**
643 * Returns a DOMBuilder class wrapped in a SAX adapter.
644 */
645 public SerializationHandler getOutputDomBuilder()
646 {
647 return _main.getOutputDomBuilder();
648 }
649
650 public String lookupNamespace(int node, String prefix)
651 throws TransletException
652 {
653 return _main.lookupNamespace(node, prefix);
654 }
655
656 // %HZ% Does this method make any sense here???
657 public String getUnparsedEntityURI(String entity) {
658 return _main.getUnparsedEntityURI(entity);
659 }
660
661 // %HZ% Does this method make any sense here???
662 public Hashtable getElementsWithIDs() {
663 return _main.getElementsWithIDs();
664 }
665 }