All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wss4j.common.util.NSStack Maven / Gradle / Ivy

There is a newer version: 3.0.4
Show newest version
/**
 * 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.wss4j.common.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. * */ public class NSStack { private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(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) { String pfix = prefix; if (pfix == null) { pfix = ""; } int hash = pfix.hashCode(); for (int cursor = top; cursor > 0; cursor--) { Mapping map = stack[cursor]; if (map == null) { continue; } if (map.getPrefixHash() == hash && map.getPrefix().equals(pfix)) { 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()); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy