| 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 |