1
2
3
4
5
6
7
8
9
10 package org.dom4j.io;
11
12 import org.dom4j.Element;
13 import org.dom4j.ElementHandler;
14
15 /*** <p><code>PruningElementStack</code> is a stack of {@link Element}
16 * instances which will prune the tree when a path expression is reached.
17 * This is useful for parsing very large documents where children of the
18 * root element can be processed individually rather than keeping them all
19 * in memory at the same time.</p>
20 *
21 * @author <a href="mailto:james.strachan@metastuff.com">James Strachan</a>
22 * @version $Revision: 1.9 $
23 */
24 class PruningElementStack extends ElementStack {
25
26 /*** ElementHandler to call when pruning occurs */
27 private ElementHandler elementHandler;
28
29 /*** the element name path which denotes the node to remove from its parent
30 * when it is complete (i.e. when it is popped from the stack).
31 * The first entry in the path will be a child of the root node
32 */
33 private String[] path;
34
35 /*** The level at which a path match can occur.
36 * We match when we have popped the selected node so the
37 * and the lastElementIndex points to its parent so this
38 * value should be path.length - 2
39 */
40 private int matchingElementIndex;
41
42
43
44 public PruningElementStack(String[] path, ElementHandler elementHandler) {
45 this.path = path;
46 this.elementHandler = elementHandler;
47 checkPath();
48 }
49
50 public PruningElementStack(String[] path, ElementHandler elementHandler, int defaultCapacity) {
51 super(defaultCapacity);
52 this.path = path;
53 this.elementHandler = elementHandler;
54 checkPath();
55 }
56
57 public Element popElement() {
58 Element answer = super.popElement();
59
60 if ( lastElementIndex == matchingElementIndex && lastElementIndex >= 0 ) {
61
62
63
64
65
66 if ( validElement( answer, lastElementIndex + 1 ) ) {
67 Element parent = null;
68 for ( int i = 0; i <= lastElementIndex; i++ ) {
69 parent = stack[i];
70 if ( ! validElement( parent, i ) ) {
71 parent = null;
72 break;
73 }
74 }
75 if ( parent != null ) {
76 pathMatches(parent, answer);
77 }
78 }
79 }
80 return answer;
81 }
82
83 protected void pathMatches(Element parent, Element selectedNode) {
84
85
86
87 elementHandler.onEnd(this);
88
89
90 parent.remove( selectedNode );
91 }
92
93 protected boolean validElement(Element element, int index) {
94 String requiredName = path[index];
95 String name = element.getName();
96 if (requiredName == name) {
97 return true;
98 }
99 if (requiredName != null && name != null ) {
100 return requiredName.equals( name );
101 }
102 return false;
103 }
104
105
106 private void checkPath() {
107 if ( path.length < 2 ) {
108 throw new RuntimeException( "Invalid path of length: " + path.length + " it must be greater than 2" );
109 }
110 matchingElementIndex = path.length - 2;
111 }
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160