Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }