org.apache.ws.security.util.NSStack Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wss4j Show documentation
Show all versions of wss4j Show documentation
The Apache WSS4J project provides a Java implementation of the primary security standards
for Web Services, namely the OASIS Web Services Security (WS-Security) specifications
from the OASIS Web Services Security TC.
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.ws.security.util;
import java.util.ArrayList;
import java.util.List;
/**
* 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.
*
* 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.
*
* Accordingly, this stack is implemented as a single array, will null
* values used to indicate frame boundaries.
*
* @author James Snell
* @author Glen Daniels ([email protected])
* @author Sam Ruby ([email protected])
*/
public class NSStack {
protected static final org.apache.commons.logging.Log log =
org.apache.commons.logging.LogFactory.getLog(NSStack.class);
private Mapping[] stack;
private int top = 0;
private int iterator = 0;
private int currentDefaultNS = -1;
// invariant member variable to track low-level logging requirements
// we cache this once per instance lifecycle to avoid repeated lookups
// in heavily used code.
private final boolean traceEnabled = log.isTraceEnabled();
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 (traceEnabled) {
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) {
// Reset the currentDefaultNS to ignore the frame just removed.
currentDefaultNS = top;
while (currentDefaultNS > 0) {
if (stack[currentDefaultNS] != null &&
stack[currentDefaultNS].getPrefix().length() == 0) {
break;
}
currentDefaultNS--;
}
}
if (top == 0) {
if (traceEnabled) {
log.trace("NSPop (empty)");
}
return;
}
if (traceEnabled) {
log.trace("NSPop (" + stack.length + ")");
}
}
/**
* Return a copy of the current frame. Returns null if none are present.
*/
public List cloneFrame() {
if (stack[top] == null) {
return null;
}
List 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.:
*
*
*
* *here's where we're looking*
*
*
*
* 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 + "stackFrame00");
} else {
log.trace(dumpPrefix + map.getNamespaceURI() + " -> " + map.getPrefix());
}
}
}
}