Source code: org/apache/axis/utils/NSStack.java
1 /*
2 * Copyright 2001-2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.apache.axis.utils;
17
18 import org.apache.axis.components.logger.LogFactory;
19 import org.apache.axis.AxisProperties;
20 import org.apache.axis.Constants;
21 import org.apache.commons.logging.Log;
22
23 import java.util.ArrayList;
24
25 /**
26 * The abstraction this class provides is a push down stack of variable
27 * length frames of prefix to namespace mappings. Used for keeping track
28 * of what namespaces are active at any given point as an XML document is
29 * traversed or produced.
30 *
31 * From a performance point of view, this data will both be modified frequently
32 * (at a minimum, there will be one push and pop per XML element processed),
33 * and scanned frequently (many of the "good" mappings will be at the bottom
34 * of the stack). The one saving grace is that the expected maximum
35 * cardinalities of the number of frames and the number of total mappings
36 * is only in the dozens, representing the nesting depth of an XML document
37 * and the number of active namespaces at any point in the processing.
38 *
39 * Accordingly, this stack is implemented as a single array, will null
40 * values used to indicate frame boundaries.
41 *
42 * @author James Snell
43 * @author Glen Daniels (gdaniels@apache.org)
44 * @author Sam Ruby (rubys@us.ibm.com)
45 */
46 public class NSStack {
47 protected static Log log =
48 LogFactory.getLog(NSStack.class.getName());
49
50 private Mapping[] stack;
51 private int top = 0;
52 private int iterator = 0;
53 private int currentDefaultNS = -1;
54 private boolean optimizePrefixes = true;
55
56 // invariant member variable to track low-level logging requirements
57 // we cache this once per instance lifecycle to avoid repeated lookups
58 // in heavily used code.
59 private final boolean traceEnabled = log.isTraceEnabled();
60
61 public NSStack(boolean optimizePrefixes) {
62 this.optimizePrefixes = optimizePrefixes;
63 stack = new Mapping[32];
64 stack[0] = null;
65 }
66
67 public NSStack() {
68 stack = new Mapping[32];
69 stack[0] = null;
70 }
71
72 /**
73 * Create a new frame at the top of the stack.
74 */
75 public void push() {
76 top ++;
77
78 if (top >= stack.length) {
79 Mapping newstack[] = new Mapping[stack.length*2];
80 System.arraycopy (stack, 0, newstack, 0, stack.length);
81 stack = newstack;
82 }
83
84 if (traceEnabled)
85 log.trace("NSPush (" + stack.length + ")");
86
87 stack[top] = null;
88 }
89
90 /**
91 * Remove the top frame from the stack.
92 */
93 public void pop() {
94 clearFrame();
95
96 top--;
97
98 // If we've moved below the current default NS, figure out the new
99 // default (if any)
100 if (top < currentDefaultNS) {
101 // Reset the currentDefaultNS to ignore the frame just removed.
102 currentDefaultNS = top;
103 while (currentDefaultNS > 0) {
104 if (stack[currentDefaultNS] != null &&
105 stack[currentDefaultNS].getPrefix().length() == 0)
106 break;
107 currentDefaultNS--;
108 }
109 }
110
111 if (top == 0) {
112 if (traceEnabled)
113 log.trace("NSPop (" + Messages.getMessage("empty00") + ")");
114
115 return;
116 }
117
118 if (traceEnabled){
119 log.trace("NSPop (" + stack.length + ")");
120 }
121 }
122
123 /**
124 * Return a copy of the current frame. Returns null if none are present.
125 */
126 public ArrayList cloneFrame() {
127 if (stack[top] == null) return null;
128
129 ArrayList clone = new ArrayList();
130
131 for (Mapping map=topOfFrame(); map!=null; map=next()) {
132 clone.add(map);
133 }
134
135 return clone;
136 }
137
138 /**
139 * Remove all mappings from the current frame.
140 */
141 private void clearFrame() {
142 while (stack[top] != null) top--;
143 }
144
145 /**
146 * Reset the embedded iterator in this class to the top of the current
147 * (i.e., last) frame. Note that this is not threadsafe, nor does it
148 * provide multiple iterators, so don't use this recursively. Nor
149 * should you modify the stack while iterating over it.
150 */
151 public Mapping topOfFrame() {
152 iterator = top;
153 while (stack[iterator] != null) iterator--;
154 iterator++;
155 return next();
156 }
157
158 /**
159 * Return the next namespace mapping in the top frame.
160 */
161 public Mapping next() {
162 if (iterator > top) {
163 return null;
164 } else {
165 return stack[iterator++];
166 }
167 }
168
169 /**
170 * Add a mapping for a namespaceURI to the specified prefix to the top
171 * frame in the stack. If the prefix is already mapped in that frame,
172 * remap it to the (possibly different) namespaceURI.
173 */
174 public void add(String namespaceURI, String prefix) {
175 int idx = top;
176 prefix = prefix.intern();
177 try {
178 // Replace duplicate prefixes (last wins - this could also fault)
179 for (int cursor=top; stack[cursor]!=null; cursor--) {
180 if (stack[cursor].getPrefix() == prefix) {
181 stack[cursor].setNamespaceURI(namespaceURI);
182 idx = cursor;
183 return;
184 }
185 }
186
187 push();
188 stack[top] = new Mapping(namespaceURI, prefix);
189 idx = top;
190 } finally {
191 // If this is the default namespace, note the new in-scope
192 // default is here.
193 if (prefix.length() == 0) {
194 currentDefaultNS = idx;
195 }
196 }
197 }
198
199 /**
200 * Return an active prefix for the given namespaceURI. NOTE : This
201 * may return null even if the namespaceURI was actually mapped further
202 * up the stack IF the prefix which was used has been repeated further
203 * down the stack. I.e.:
204 *
205 * <pre:outer xmlns:pre="namespace">
206 * <pre:inner xmlns:pre="otherNamespace">
207 * *here's where we're looking*
208 * </pre:inner>
209 * </pre:outer>
210 *
211 * If we look for a prefix for "namespace" at the indicated spot, we won't
212 * find one because "pre" is actually mapped to "otherNamespace"
213 */
214 public String getPrefix(String namespaceURI, boolean noDefault) {
215 if ((namespaceURI == null) || (namespaceURI.length()==0))
216 return null;
217
218 if(optimizePrefixes) {
219 // If defaults are OK, and the given NS is the current default,
220 // return "" as the prefix to favor defaults where possible.
221 if (!noDefault && currentDefaultNS > 0 && stack[currentDefaultNS] != null &&
222 namespaceURI == stack[currentDefaultNS].getNamespaceURI())
223 return "";
224 }
225 namespaceURI = namespaceURI.intern();
226
227 for (int cursor=top; cursor>0; cursor--) {
228 Mapping map = stack[cursor];
229 if (map == null)
230 continue;
231
232 if (map.getNamespaceURI() == namespaceURI) {
233 String possiblePrefix = map.getPrefix();
234 if (noDefault && possiblePrefix.length() == 0)
235 continue;
236
237 // now make sure that this is the first occurance of this
238 // particular prefix
239 for (int cursor2 = top; true; cursor2--) {
240 if (cursor2 == cursor)
241 return possiblePrefix;
242 map = stack[cursor2];
243 if (map == null)
244 continue;
245 if (possiblePrefix == map.getPrefix())
246 break;
247 }
248 }
249 }
250
251 return null;
252 }
253
254 /**
255 * Return an active prefix for the given namespaceURI, including
256 * the default prefix ("").
257 */
258 public String getPrefix(String namespaceURI) {
259 return getPrefix(namespaceURI, false);
260 }
261
262 /**
263 * Given a prefix, return the associated namespace (if any).
264 */
265 public String getNamespaceURI(String prefix) {
266 if (prefix == null)
267 prefix = "";
268
269 prefix = prefix.intern();
270
271 for (int cursor=top; cursor>0; cursor--) {
272 Mapping map = stack[cursor];
273 if (map == null) continue;
274
275 if (map.getPrefix() == prefix)
276 return map.getNamespaceURI();
277 }
278
279 return null;
280 }
281
282 /**
283 * Produce a trace dump of the entire stack, starting from the top and
284 * including frame markers.
285 */
286 public void dump(String dumpPrefix)
287 {
288 for (int cursor=top; cursor>0; cursor--) {
289 Mapping map = stack[cursor];
290
291 if (map == null) {
292 log.trace(dumpPrefix + Messages.getMessage("stackFrame00"));
293 } else {
294 log.trace(dumpPrefix + map.getNamespaceURI() + " -> " + map.getPrefix());
295 }
296 }
297 }
298 }