org.apache.cocoon.xml.sax.NamespacesTable Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* 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.xml.sax;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
/**
* Keeps track of namespaces declarations and resolve namespaces names.
*
* This class also provides a very convenient and safe way of handling
* namespace declarations in SAX pipes. It also allows to filter duplicate namespace
* declarations that too often clutter up XML documents that went through
* several transformations, and avoid useless namespace declarations that aren't followed
* by element events.
*
* Usage example in a SAX pipe:
*
* NamespacesTable namespaces = new NamespacesTable();
* ContentHandler nextHandler;
*
* public void startPrefixMapping(String prefix, String uri) throws SAXException {
* namespaces.addDeclaration(prefix, uri);
* }
*
* public void startElement(...) throws SAXException {
* // automatically start mappings for this scope
* namespaces.enterScope(nextHandler);
* nextHandler.startElement(...);
* }
*
* public void endElement(...) throws SAXException {
* nextHandler.endElement(...);
* // automatically end mappings for this scope
* namespaces.leaveScope(nextHandler);
* }
*
* public void endPrefixMapping(String prefix) throws SAXException {
* // Ignore, it is handled by leaveScope()
* }
*
*
* @version $Id: NamespacesTable.java 729283 2008-12-24 09:25:21Z cziegeler $
*
* @deprecated This API is deprecated, migrate code to the XML APIs provided by the JDK.
*/
@Deprecated(since = "2022-01-27")
public class NamespacesTable {
/**
* The last namespace declaration.
*/
private Entry lastEntry;
/**
* The entry that start the prefix mappings for the scope that's about to be entered
* or was just left.
*/
private Entry lastDeclaredEntry;
private boolean usesScopes = false;
/**
* Construct a new NamespacesTable
instance.
*/
public NamespacesTable() {
clear();
}
/**
* Clear and reinitialize this namespace table before reuse.
*
* @since 2.1.8
*/
public void clear() {
this.lastEntry = Entry.create("", "");
this.addDeclaration("xml", "http://www.w3.org/XML/1998/namespace");
// Lock this scope
this.lastEntry.closedScopes = 1;
}
/**
* Declare a new namespace prefix-uri mapping.
*
* @return The newly added Declaration
.
*/
public Declaration addDeclaration(String prefix, String uri) {
// Find a previous declaration of the same prefix
Entry dup = this.lastEntry;
while (dup != null && !dup.prefix.equals(prefix)) {
dup = dup.previous;
}
if (dup != null) {
if (usesScopes && dup.uri.equals(uri)) {
return dup;
}
dup.overriden = true;
}
Entry e = Entry.create(prefix, uri);
e.previous = this.lastEntry;
e.overrides = dup;
this.lastEntry = e;
// this always starts the declared prefix chain
this.lastDeclaredEntry = e;
return e;
}
/**
* Undeclare a namespace prefix-uri mapping. If the prefix was previously declared
* mapping another URI, its value is restored.
*
* When using {@link #enterScope()}/{@link #leaveScope()}, this method does nothing and always
* returns null
, as declaration removal is handled in {@link #leaveScope()}.
*
* @return the removed Declaration
or null.
*/
public Declaration removeDeclaration(String prefix) {
if (usesScopes) {
// Automatically handled in leaveScope
// or throw and IllegalStateException if enterScope(handler) was used?
return null;
}
Entry current = this.lastEntry;
Entry afterCurrent = null;
while (current != null) {
if (current.closedScopes > 0) {
// Don't undeclare mappings not declared in this scope
return null;
}
if (current.prefix.equals(prefix)) {
// Got it
// Remove it from the chain
if (afterCurrent != null) {
afterCurrent.previous = current.previous;
}
// And report closed scopes on the previous entry
current.previous.closedScopes += current.closedScopes;
Entry overrides = current.overrides;
if (overrides != null) {
// No more overriden
overrides.overriden = false;
}
if (this.lastDeclaredEntry == current) {
if (current.previous.closedScopes == 0) {
this.lastDeclaredEntry = current.previous;
} else {
this.lastDeclaredEntry = null;
}
}
if (this.lastEntry == current) {
this.lastEntry = current.previous;
}
return current;
}
afterCurrent = current;
current = current.previous;
}
// Not found
return null;
}
/**
* Enter a new scope. This starts a new, empty list of declarations for the new scope.
*
* Typically called in a SAX handler before sending a startElement()
* event.
*
* @since 2.1.8
*/
public void enterScope() {
this.usesScopes = true;
this.lastEntry.closedScopes++;
this.lastDeclaredEntry = null;
}
/**
* Start all declared mappings of the current scope and enter a new scope. This starts a new,
* empty list of declarations for the new scope.
*
* Typically called in a SAX handler before sending a startElement()
* event.
*
* @param handler the handler that will receive startPrefixMapping events.
* @throws SAXException
* @since 2.1.8
*/
public void enterScope(ContentHandler handler) throws SAXException {
this.usesScopes = true;
Entry current = this.lastEntry;
while (current != null && current.closedScopes == 0) {
handler.startPrefixMapping(current.prefix, current.uri);
current = current.previous;
}
this.lastEntry.closedScopes++;
this.lastDeclaredEntry = null;
}
/**
* Leave a scope. The namespace declarations that occured before the corresponding
* enterScope()
are no more visible using the resolution methods, but
* still available using {@link #getCurrentScopeDeclarations()} until the next call
* to {@link #addDeclaration(String, String)} or {@link #enterScope()}.
*
* Typically called in a SAX handler after sending a endElement()
* event.
*
* @since 2.1.8
*/
public void leaveScope() {
Entry current = this.lastEntry;
// Purge declarations that were added but not included in a scope
while (current.closedScopes == 0) {
current = current.previous;
}
current.closedScopes--;
if (current.closedScopes == 0) {
this.lastDeclaredEntry = current;
} else {
// More than one scope closed here: no local declarations
this.lastDeclaredEntry = null;
}
while (current != null && current.closedScopes == 0) {
Entry overrides = current.overrides;
if (overrides != null) {
// No more overriden
overrides.overriden = false;
}
current = current.previous;
}
this.lastEntry = current;
}
/**
* Leave a scope. The namespace declarations that occured before the corresponding
* enterScope()
are no more visible using the resolution methods, but
* still available using {@link #getCurrentScopeDeclarations()} until the next call
* to {@link #addDeclaration(String, String)} or {@link #enterScope()}.
*
* Typically called in a SAX handler after sending a endElement()
* event.
*
* @param handler the handler that will receive endPrefixMapping events.
* @throws SAXException
* @since 2.1.8
*/
public void leaveScope(ContentHandler handler) throws SAXException {
Entry current = this.lastEntry;
// Purge declarations that were added but not included in a scope
while (current.closedScopes == 0) {
current = current.previous;
}
current.closedScopes--;
if (current.closedScopes == 0) {
this.lastDeclaredEntry = current;
} else {
// More than one scope closed here: no local declarations
this.lastDeclaredEntry = null;
}
while (current != null && current.closedScopes == 0) {
handler.endPrefixMapping(current.prefix);
Entry overrides = current.overrides;
if (overrides != null) {
// No more overriden
overrides.overriden = false;
}
current = current.previous;
}
this.lastEntry = current;
}
private static final Declaration[] NO_DECLS = new Declaration[0];
/**
* Get the declarations that were declared within the current scope.
*
* @return the declarations (possibly empty, but never null)
* @since 2.1.8
*/
public Declaration[] getCurrentScopeDeclarations() {
int count = 0;
Entry current = this.lastDeclaredEntry;
while (current != null && current.closedScopes == 0) {
count++;
current = current.previous;
}
if (count == 0)
return NO_DECLS;
Declaration[] decls = new Declaration[count];
count = 0;
current = this.lastDeclaredEntry;
while (current != null && current.closedScopes == 0) {
decls[count++] = current;
current = current.previous;
}
return decls;
}
/**
* Return the URI associated with the given prefix or null if the
* prefix was not mapped.
*/
public String getUri(String prefix) {
Entry current = this.lastEntry;
while (current != null) {
if (current.prefix.equals(prefix)) {
return current.uri;
}
current = current.previous;
}
// Not found
return null;
}
/**
* Return an array with all prefixes currently mapped to the specified URI.
*
* The array length might be zero if no prefixes are associated with
* the specified uri.
*
* @return A non-null String
array.
*/
public String[] getPrefixes(String uri) {
Entry current = this.lastEntry;
int count = 0;
while (current != null) {
if (!current.overriden && current.uri.equals(uri))
count++;
current = current.previous;
}
if (count == 0)
return (new String[0]);
String[] prefixes = new String[count];
count = 0;
current = this.lastEntry;
while (current != null) {
if (!current.overriden && current.uri.equals(uri))
prefixes[count++] = current.prefix;
current = current.previous;
}
return prefixes;
}
/**
* Return one of the prefixes currently mapped to the specified URI or
* null.
*/
public String getPrefix(String uri) {
Entry current = this.lastEntry;
while (current != null) {
if (!current.overriden && current.uri.equals(uri))
return current.prefix;
current = current.previous;
}
return null;
}
/**
* Resolve a namespace-aware name against the current namespaces
* declarations.
*
* @param uri The namespace URI or null if not known.
* @param raw The raw (complete) name or null if not known.
* @param prefix The namespace prefix or null if not known.
* @param local The local name or null if not known.
* @return A non-null Name
.
* @exception SAXException If the name cannot be resolved.
*/
public Name resolve(String uri, String raw, String prefix, String local) throws SAXException {
if (uri == null)
uri = "";
if (raw == null)
raw = "";
if (prefix == null)
prefix = "";
if (local == null)
local = "";
// Start examining the URI
if (raw.length() > 0) {
// The raw name was specified
int pos = raw.indexOf(':');
if (pos > 0) {
// We have a namespace prefix:local separator
String pre = raw.substring(0, pos);
String loc = raw.substring(pos + 1);
if (prefix.length() == 0)
prefix = pre;
else if (!prefix.equals(pre))
throw new SAXException("Raw/Prefix mismatch");
if (local.length() == 0)
local = loc;
else if (!local.equals(loc))
throw new SAXException("Raw/Local Name mismatch");
} else {
// We don't have a prefix:local separator
if (prefix.length() > 0)
throw new SAXException("Raw Name/Prefix mismatch");
if (local.length() == 0)
local = raw;
else if (!local.equals(raw))
throw new SAXException("Raw Name/Local Name mismatch");
}
} else {
// The raw name was not specified
if (local.length() == 0)
throw new SAXException("No Raw/Local Name");
if (prefix.length() == 0)
raw = local;
else
raw = prefix + ':' + local;
}
// We have resolved and checked data between the raw, local, and
// prefix... We have to doublecheck the namespaces.
if (uri.length() > 0) {
// We have a URI and a prefix, check them
if ((prefix.length() > 0) && (!uri.equals(this.getUri(prefix)))) {
throw new SAXException("URI/Prefix mismatch [" + prefix + "," + uri + "]");
}
String temp = this.getPrefix(uri);
if (temp == null)
throw new SAXException("URI not declared");
else if (temp.length() > 0) {
prefix = temp;
raw = prefix + ':' + local;
}
} else {
// We don't have a URI, check if we can find one from the prefix.
String temp = this.getUri(prefix);
if (temp == null) {
throw new SAXException("Prefix not declared");
}
uri = temp;
}
NameImpl name = new NameImpl();
if (uri.length() > 0)
name.uri = uri;
else
name.uri = null;
name.raw = raw;
name.prefix = prefix;
name.local = local;
return (name);
}
/**
* The internal entry structure for this table.
* @deprecated This API is deprecated, migrate code to the XML APIs provided by the JDK.
*/
@Deprecated(since = "2022-01-27")
private static class Entry implements Declaration {
/**
* The URI string.
*/
protected String uri = "";
/**
* The prefix string.
*/
protected String prefix = "";
/**
* The previous declaration.
*/
protected Entry previous;
protected Entry overrides;
protected int closedScopes = 0;
protected boolean overriden = false;
/**
* Create a new namespace declaration.
*/
protected static Entry create(String prefix, String uri) {
// Create a new entry
Entry e = new Entry();
// Set the prefix string.
if (prefix != null)
e.prefix = prefix;
// Set the uri string.
if (uri != null)
e.uri = uri;
// Return the entry
return e;
}
/**
* Return the namespace URI.
*/
public String getUri() {
return this.uri;
}
/**
* Return the namespace prefix.
*/
public String getPrefix() {
return this.prefix;
}
}
/**
* The default namespace-aware name declaration implementation
* @deprecated This API is deprecated, migrate code to the XML APIs provided by the JDK.
*/
@Deprecated(since = "2022-01-27")
private static class NameImpl implements Name {
/**
* The namespace URI.
*/
protected String uri;
/**
* The namespace prefix.
*/
protected String prefix;
/**
* The namespace local name.
*/
protected String local;
/**
* The namespace raw name.
*/
protected String raw;
/**
* Return the namespace URI.
*/
public String getUri() {
return this.uri;
}
/**
* Return the namespace prefix.
*/
public String getPrefix() {
return this.prefix;
}
/**
* Return the namespace local name.
*/
public String getLocalName() {
return this.local;
}
/**
* Return the namespace raw name.
*/
public String getQName() {
return this.raw;
}
}
/**
* A namespace-aware name. (This interface is used in conjunction
* with NamespacesTable
).
*
* @deprecated This API is deprecated, migrate code to the XML APIs provided by the JDK.
*/
@Deprecated(since = "2022-01-27")
public interface Name {
/**
* Return the namespace URI.
*/
String getUri();
/**
* Return the namespace prefix.
*/
String getPrefix();
/**
* Return the namespace local name.
*/
String getLocalName();
/**
* Return the namespace raw name.
*/
String getQName();
}
/**
* A namespace declaration. (This interface is used in conjunction
* with NamespacesTable
).
*
* @deprecated This API is deprecated, migrate code to the XML APIs provided by the JDK.
*/
@Deprecated(since = "2022-01-27")
public interface Declaration {
/**
* Return the namespace URI.
*/
String getUri();
/**
* Return the namespace prefix.
*/
String getPrefix();
}
}