NSStack.java |
/* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Axis" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.jboss.axis.utils; import org.jboss.logging.Logger; import java.util.ArrayList; /** * The abstraction this class provides is a push down stack of variable * length frames of prefix to namespace mappings. Used for keeping track * of what namespaces are active at any given point as an XML document is * traversed or produced. * <p/> * From a performance point of view, this data will both be modified frequently * (at a minimum, there will be one push and pop per XML element processed), * and scanned frequently (many of the "good" mappings will be at the bottom * of the stack). The one saving grace is that the expected maximum * cardinalities of the number of frames and the number of total mappings * is only in the dozens, representing the nesting depth of an XML document * and the number of active namespaces at any point in the processing. * <p/> * Accordingly, this stack is implemented as a single array, will null * values used to indicate frame boundaries. * * @author James Snell * @author Glen Daniels (gdaniels@macromedia.com) * @author Sam Ruby (rubys@us.ibm.com) */ public class NSStack { private static Logger log = Logger.getLogger(NSStack.class.getName()); private Mapping[] stack; private int top = 0; private int iterator = 0; private int currentDefaultNS = -1; public NSStack() { stack = new Mapping[32]; stack[0] = null; } /** * Create a new frame at the top of the stack. */ public void push() { top++; if (top >= stack.length) { Mapping newstack[] = new Mapping[stack.length * 2]; System.arraycopy(stack, 0, newstack, 0, stack.length); stack = newstack; } // if (log.isTraceEnabled()) // log.trace("NSPush (" + stack.length + ")"); stack[top] = null; } /** * Remove the top frame from the stack. */ public void pop() { clearFrame(); top--; // If we've moved below the current default NS, figure out the new // default (if any) if (top < currentDefaultNS) { while (currentDefaultNS > 0) { if (stack[currentDefaultNS] != null && stack[currentDefaultNS].getPrefix().length() == 0) break; currentDefaultNS--; } } if (top == 0) { // if (log.isTraceEnabled()) // log.trace("NSPop (" + Messages.getMessage("empty00") + ")"); return; } // if (log.isTraceEnabled()) // log.trace("NSPop (" + stack.length + ")"); } /** * Return a copy of the current frame. Returns null if none are present. */ public ArrayList cloneFrame() { if (stack[top] == null) return null; ArrayList clone = new ArrayList(); for (Mapping map = topOfFrame(); map != null; map = next()) { clone.add(map); } return clone; } /** * Remove all mappings from the current frame. */ private void clearFrame() { while (stack[top] != null) top--; } /** * Reset the embedded iterator in this class to the top of the current * (i.e., last) frame. Note that this is not threadsafe, nor does it * provide multiple iterators, so don't use this recursively. Nor * should you modify the stack while iterating over it. */ public Mapping topOfFrame() { iterator = top; while (stack[iterator] != null) iterator--; iterator++; return next(); } /** * Return the next namespace mapping in the top frame. */ public Mapping next() { if (iterator > top) { return null; } else { return stack[iterator++]; } } /** * Add a mapping for a namespaceURI to the specified prefix to the top * frame in the stack. If the prefix is already mapped in that frame, * remap it to the (possibly different) namespaceURI. */ public void add(String namespaceURI, String prefix) { int idx = top; try { // Replace duplicate prefixes (last wins - this could also fault) for (int cursor = top; stack[cursor] != null; cursor--) { if (stack[cursor].getPrefix().equals(prefix)) { stack[cursor].setNamespaceURI(namespaceURI); idx = cursor; return; } } push(); stack[top] = new Mapping(namespaceURI, prefix); idx = top; } finally { // If this is the default namespace, note the new in-scope // default is here. if (prefix.length() == 0) { currentDefaultNS = idx; } } } /** * Return an active prefix for the given namespaceURI. NOTE : This * may return null even if the namespaceURI was actually mapped further * up the stack IF the prefix which was used has been repeated further * down the stack. I.e.: * <p/> * <pre:outer xmlns:pre="namespace"> * <pre:inner xmlns:pre="otherNamespace"> * *here's where we're looking* * </pre:inner> * </pre:outer> * <p/> * If we look for a prefix for "namespace" at the indicated spot, we won't * find one because "pre" is actually mapped to "otherNamespace" */ public String getPrefix(String namespaceURI, boolean noDefault) { if ((namespaceURI == null) || (namespaceURI.equals(""))) return null; int hash = namespaceURI.hashCode(); // If defaults are OK, and the given NS is the current default, // return "" as the prefix to favor defaults where possible. if (!noDefault && currentDefaultNS > 0 && stack[currentDefaultNS] != null && namespaceURI.equals(stack[currentDefaultNS].getNamespaceURI())) return ""; for (int cursor = top; cursor > 0; cursor--) { Mapping map = stack[cursor]; if (map == null) continue; if (map.getNamespaceHash() == hash && map.getNamespaceURI().equals(namespaceURI)) { String possiblePrefix = map.getPrefix(); if (noDefault && possiblePrefix.length() == 0) continue; // now make sure that this is the first occurance of this // particular prefix int ppHash = possiblePrefix.hashCode(); for (int cursor2 = top; true; cursor2--) { if (cursor2 == cursor) return possiblePrefix; map = stack[cursor2]; if (map == null) continue; if (ppHash == map.getPrefixHash() && possiblePrefix.equals(map.getPrefix())) break; } } } return null; } /** * Return an active prefix for the given namespaceURI, including * the default prefix (""). */ public String getPrefix(String namespaceURI) { return getPrefix(namespaceURI, false); } /** * Given a prefix, return the associated namespace (if any). */ public String getNamespaceURI(String prefix) { if (prefix == null) prefix = ""; int hash = prefix.hashCode(); for (int cursor = top; cursor > 0; cursor--) { Mapping map = stack[cursor]; if (map == null) continue; if (map.getPrefixHash() == hash && map.getPrefix().equals(prefix)) return map.getNamespaceURI(); } return null; } /** * Produce a trace dump of the entire stack, starting from the top and * including frame markers. */ public void dump(String dumpPrefix) { for (int cursor = top; cursor > 0; cursor--) { Mapping map = stack[cursor]; if (map == null) { log.trace(dumpPrefix + Messages.getMessage("stackFrame00")); } else { log.trace(dumpPrefix + map.getNamespaceURI() + " -> " + map.getPrefix()); } } } }
NSStack.java |