| /* |
| * 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.cocoon.components.serializers.util; |
| |
| import org.xml.sax.SAXException; |
| |
| /** |
| * The <code>Namespaces</code> class is an utility class implementing a |
| * stack for XML namespaces declarations. |
| * |
| * @author <a href="mailto:pier@apache.org">Pier Fumagalli</a>, February 2003 |
| * @version CVS $Id$ |
| */ |
| public class Namespaces { |
| /** The array of all URIs in this stack. */ |
| private String uri[] = new String[512]; |
| /** The array of all prefixes in this stack. */ |
| private String pre[] = new String[512]; |
| /** The number of URI/prefix mappings in this stack. */ |
| private int depth = 0; |
| /** The last "committed" namespace. */ |
| private int last = 0; |
| |
| /** The index of the namespace prefix in for <code>commit()</code>. */ |
| public static final int NAMESPACE_PREFIX = 0; |
| /** The index of the namespace uri in for <code>commit()</code>. */ |
| public static final int NAMESPACE_URI = 1; |
| |
| /** |
| * Create a new <code>Namespaces</code> instance. |
| */ |
| public Namespaces() { |
| super(); |
| |
| this.push("", ""); |
| this.push("xml", "http://www.w3.org/XML/1998/namespace"); |
| this.last = this.depth; |
| } |
| |
| /** |
| * Push a new namespace declaration into this stack. |
| * |
| * @param prefix The prefix to associate with the specified URI. |
| * @param uri The URI associated with the namespace. |
| */ |
| public synchronized void push(String prefix, String uri) { |
| if (this.depth == this.uri.length) { |
| int newDepth = this.uri.length + (this.uri.length >> 1); |
| String newUri[] = new String[newDepth]; |
| String newPre[] = new String[newDepth]; |
| System.arraycopy(this.uri, 0, newUri, 0, this.depth); |
| System.arraycopy(this.pre, 0, newPre, 0, this.depth); |
| this.uri = newUri; |
| this.pre = newPre; |
| } |
| |
| this.uri[this.depth] = uri; |
| this.pre[this.depth] = prefix; |
| this.depth ++; |
| } |
| |
| /** |
| * Pop a new namespace declaration out of this stack. |
| * <br /> |
| * If more than one namespace is associated with the specified namespace, |
| * only the last pushed namespace will be popped out. |
| * |
| * @param prefix The prefix to associate with the specified URI. |
| * @throws SAXException If the prefix was not mapped in this stack. |
| */ |
| public synchronized void pop(String prefix) |
| throws SAXException { |
| for (int x = this.position(prefix, pre); x < this.depth; x++) { |
| int k = (x + 1); |
| this.pre[x] = this.pre[k]; |
| this.uri[x] = this.uri[k]; |
| } |
| //this.pre[this.depth] = null; |
| //this.uri[this.depth] = null; |
| this.last--; |
| this.depth--; |
| } |
| |
| /** |
| * Qualify an XML name. |
| * <br /> |
| * Given a URI, local name and qualified name as passed to the SAX |
| * <code>ContentHandler</code> interface in the <code>startElement()</code> |
| * method, this method will always return a valid XML name token usable |
| * for serialization (checking namespaces URIs and prefixes). |
| * |
| * @param nsuri The Namespace URI, or the empty string if the element has |
| * no namespace URI or if namespace processing is not being |
| * performed. |
| * @param local The local name (without prefix), or the empty string if |
| * namespace processing is not being performed. |
| * @param qualified The qualified name (with prefix), or the empty string |
| * if qualified names are not available. |
| * @throws SAXException If the specified URI is not mapped with a prefix. |
| */ |
| public String qualify(String nsuri, String local, String qualified) |
| throws SAXException { |
| if (nsuri == null) nsuri = ""; |
| if (local == null) local = ""; |
| if (qualified == null) qualified = ""; |
| |
| /** No namespaces processing. */ |
| if ((nsuri.length() == 0 ) && (local.length() == 0)) return(qualified); |
| |
| /* |
| * Get the prefix for the given namespace and return the qualified |
| * name: "prefix:local" if prefix is not empty, "local" otherwise. |
| */ |
| int position = position(nsuri, this.uri); |
| if (this.pre[position].length() > 0) { |
| return(this.pre[position] + ':' + local); |
| } |
| return(local); |
| } |
| |
| /** |
| * Checkpoint this stack, returning the list of all namespaces added since |
| * the last <code>commit()</code> or <code>pop(...)</code> call. |
| */ |
| public String[][] commit() { |
| int size = this.depth - this.last; |
| String result[][] = new String[size][2]; |
| int k = 0; |
| for (int x = this.last; x < this.depth; x++) { |
| result[k][NAMESPACE_PREFIX] = this.pre[x]; |
| result[k][NAMESPACE_URI] = this.uri[x]; |
| k++; |
| } |
| this.last = this.depth; |
| return(result); |
| } |
| |
| /** |
| * Return the namespace URI associated with the specified prefix. |
| * |
| * @throws SAXException If the prefix cannot be mapped. |
| */ |
| public String getUri(String prefix) |
| throws SAXException { |
| return(this.uri[this.position(prefix, this.pre)]); |
| } |
| |
| /** |
| * Return the namespace prefix associated with the specified URI. |
| * |
| * @throws SAXException If the URI cannot be mapped. |
| */ |
| public String getPrefix(String nsuri) |
| throws SAXException { |
| return(this.pre[this.position(nsuri, this.uri)]); |
| } |
| |
| /** |
| * Return the position of the given check <code>String</code> in the |
| * specified <code>String</code> array. |
| */ |
| private int position(String check, String array[]) |
| throws SAXException { |
| int x = this.depth; |
| while (true) { |
| if (check.equals(array[--x])) return(x); |
| if (x == 0) break; |
| } |
| throw new SAXException("Unable to map \"" + check + "\""); |
| } |
| |
| } |