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

de.odysseus.staxon.base.AbstractXMLStreamScope Maven / Gradle / Ivy

There is a newer version: 1.3
Show newest version
/*
 * Copyright 2011 Odysseus Software GmbH
 *
 * Licensed 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 de.odysseus.staxon.base;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;

/**
 * Represent document/element scope. Used to store namespace bindings and
 * attributes, implements {@link NamespaceContext}.
 */
public abstract class AbstractXMLStreamScope implements NamespaceContext {
	class Attr {
		private final String prefix;
		private final String localName;
		private final String namespaceURI;
		private final String value;

		Attr(String prefix, String localName, String namespaceURI, String value) {
			this.prefix = prefix;
			this.localName = localName;
			this.namespaceURI = namespaceURI;
			this.value = value;
		}

		String getPrefix() {
			if (prefix != null) {
				return prefix;
			} else if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) {
				return XMLConstants.DEFAULT_NS_PREFIX;
			} else {
				return AbstractXMLStreamScope.this.getNonEmptyPrefix(namespaceURI);
			}
		}
		
		String getLocalName() {
			return localName;
		}
		
		String getNamespaceURI() {
			if (namespaceURI != null) {
				return namespaceURI;
			} else if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
				return XMLConstants.NULL_NS_URI;
			} else {
				return AbstractXMLStreamScope.this.getNamespaceURI(prefix);
			}
		}

		String getValue() {
			return value;
		}
		
		void verify() throws XMLStreamException {
			if (prefix == null) {
				if (!XMLConstants.NULL_NS_URI.equals(namespaceURI)) {
					String prefix = AbstractXMLStreamScope.this.getNonEmptyPrefix(namespaceURI);
					if (prefix == null) {
						throw new XMLStreamException("No prefix found for attribute namespace: " + namespaceURI);
					}
				}
			} else if (namespaceURI == null) {
				if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
					String namespaceURI = AbstractXMLStreamScope.this.getNamespaceURI(prefix);
					if (namespaceURI == null || XMLConstants.NULL_NS_URI.equals(namespaceURI)) {
						throw new XMLStreamException("Unbound attribute prefix: " + prefix);
					}
				}
			} else {
				if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
					if (!XMLConstants.NULL_NS_URI.equals(namespaceURI)) {
						throw new XMLStreamException("Illegal namespace for unprefixed attribute: " + namespaceURI);								
					}
				} else if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) {
					if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
						throw new XMLStreamException("Illegal prefix for null namespace: " + prefix);
					}
				} else {
					if (!AbstractXMLStreamScope.this.getNamespaceURI(prefix).equals(namespaceURI)) {
						throw new XMLStreamException("Prefix '" + prefix +"' is not bound to: " + namespaceURI);
					}
				}
			}
		}
	}

	private final NamespaceContext parent;
	private final String prefix;
	private final String localName;
	private final String namespaceURI;

	private String defaultNamespace;
	private List attributes;
	private List> prefixes;
	private AbstractXMLStreamScope lastChild;
	private boolean startTagClosed;

	/**
	 * Create root scope.
	 * 
	 * @param defaultNamespace
	 */
	public AbstractXMLStreamScope(String defaultNamespace) {
		this.parent = null;
		this.prefix = null;
		this.localName = null;
		this.namespaceURI = XMLConstants.NULL_NS_URI;
		this.defaultNamespace = defaultNamespace;
		this.startTagClosed = true;
	}

	/**
	 * Create root scope.
	 * 
	 * @param parent
	 *            root namespace context
	 */
	public AbstractXMLStreamScope(NamespaceContext parent) {
		this.parent = parent;
		this.prefix = null;
		this.localName = null;
		this.namespaceURI = XMLConstants.NULL_NS_URI;
		this.defaultNamespace = parent.getNamespaceURI(XMLConstants.NULL_NS_URI);
		this.startTagClosed = true;
	}

	/**
	 * Create element scope.
	 * 
	 * @param parent
	 * @param prefix
	 * @param localName
	 */
	public AbstractXMLStreamScope(AbstractXMLStreamScope parent, String prefix, String localName, String namespaceURI) {
		this.parent = parent;
		this.prefix = prefix;
		this.localName = localName;
		this.namespaceURI = namespaceURI;
		this.startTagClosed = false;		
		this.defaultNamespace = parent.getNamespaceURI(XMLConstants.NULL_NS_URI);

		parent.lastChild = this;
		parent.startTagClosed = true;
	}

	void addAttribute(String prefix, String localName, String namespaceURI, String value) {
		if (attributes == null) {
			attributes = new LinkedList();
		}
		attributes.add(new Attr(prefix, localName, namespaceURI, value));
	}
	
	List getAttributes() {
		return attributes;
	}
	
	public String getPrefix() {
		return prefix == null ? getPrefix(namespaceURI) : prefix;
	}
	
	public String getLocalName() {
		return localName;
	}
	
	public String getNamespaceURI() {
		return namespaceURI == null ? getNamespaceURI(prefix) : namespaceURI;
	}

	public boolean isRoot() {
		return localName == null;
	}

	public AbstractXMLStreamScope getParent() {
		return isRoot() ? null : (AbstractXMLStreamScope)parent;
	}

	public AbstractXMLStreamScope getLastChild() {
		return lastChild;
	}

	public boolean isStartTagClosed() {
		return startTagClosed;
	}
	
	private void verify() throws XMLStreamException {
		if (prefix == null) {
			if (!XMLConstants.NULL_NS_URI.equals(namespaceURI) && getPrefix(namespaceURI) == null) {
				throw new XMLStreamException("No prefix for namespace URI: " + namespaceURI);
			}
		} else if (namespaceURI == null) {
			if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix) && XMLConstants.NULL_NS_URI.equals(getNamespaceURI(prefix))) {
				throw new XMLStreamException("Unbound prefix: " + prefix);
			}
		} else {
			if (!namespaceURI.equals(getNamespaceURI(prefix))) {
				if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
					throw new XMLStreamException("Prefix required for namespace URI: '" + namespaceURI);
				} else if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) {
					throw new XMLStreamException("Prefix '" + prefix +"' is bound to: " + getNamespaceURI(prefix));
				} else {
					throw new XMLStreamException("Prefix '" + prefix +"' is not bound to: " + namespaceURI);
				}
			}
		}
		if (attributes != null) {
			for (Attr attribute : attributes) {
				attribute.verify();
			}
		}
	}

	void setStartTagClosed(boolean startTagClosed) throws XMLStreamException {
		if (startTagClosed) {
			verify();
		}
		this.startTagClosed = startTagClosed;
	}

	private String findNonEmptyPrefix(String namespaceURI, AbstractXMLStreamScope descendent) {
		if (prefixes != null) {
			for (Pair pair : prefixes) {
				if (pair.getSecond().equals(namespaceURI)) {
					if (descendent == this || descendent.getNamespaceURI(pair.getFirst()).equals(namespaceURI)) {
						return pair.getFirst();
					}
				}
			}
		}
		if (isRoot()) {
			if (parent == null) {
				return null;
			} else {
				Iterator prefixes = parent.getPrefixes(namespaceURI);
				while (prefixes.hasNext()) {
					String prefix = prefixes.next().toString();
					if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
						if (descendent == this || descendent.getNamespaceURI(prefix).equals(namespaceURI)) {
							return prefix;
						}
					}
				}
				return null;
			}
		} else {
			return getParent().findNonEmptyPrefix(namespaceURI, descendent);
		}
	}
	
	String getNonEmptyPrefix(String namespaceURI) {
		if (namespaceURI == null) {
			throw new IllegalArgumentException("Namespace URI must not be null");
		} else if (XMLConstants.XML_NS_URI.equals(namespaceURI)) {
			return XMLConstants.XML_NS_PREFIX;
		} else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)) {
			return XMLConstants.XMLNS_ATTRIBUTE;
		} else {
			return findNonEmptyPrefix(namespaceURI, this);
		}
	}

	@Override
	public String getPrefix(String namespaceURI) {
		if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) {
			return null;
		} else if (defaultNamespace.equals(namespaceURI)) {
			return XMLConstants.DEFAULT_NS_PREFIX;
		} else {
			return getNonEmptyPrefix(namespaceURI);
		}
	}

	public void setPrefix(String prefix, String namespaceURI) {
		if (prefix == null || namespaceURI == null) {
			throw new IllegalArgumentException("Prefix and namespace URI must not be null");
		}
		if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
			defaultNamespace = namespaceURI;
		} else if (XMLConstants.XML_NS_PREFIX.equals(namespaceURI)) {
			throw new IllegalArgumentException("Cannot bind to prefix: " + prefix);
		} else if (XMLConstants.XMLNS_ATTRIBUTE.equals(namespaceURI)) {
			throw new IllegalArgumentException("Cannot bind to prefix: " + prefix);
		} else {
			if (prefixes == null) {
				prefixes = new LinkedList>();
			} else {
				Iterator> iterator = prefixes.iterator();
				while (iterator.hasNext()) {
					if (iterator.next().getFirst().equals(prefix)) {
						iterator.remove();
					}
				}
			}
			prefixes.add(new Pair(prefix, namespaceURI));
		}
	}

	@Override
	public Iterator getPrefixes(final String namespaceURI) {
		if (namespaceURI == null) {
			throw new IllegalArgumentException("Namespace URI must not be null");
		} else if (XMLConstants.XML_NS_URI.equals(namespaceURI)) {
			return Arrays.asList(XMLConstants.XML_NS_PREFIX).iterator();
		} else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)) {
			return Arrays.asList(XMLConstants.XMLNS_ATTRIBUTE).iterator();
		} else {
			return new Iterator() {
				int state = 0;
				String next = null;
				Iterator> pairs;
				Iterator above;

				private String next0() {
					if (state == 0) { // check default
						state = 1;
						if (namespaceURI.equals(defaultNamespace)) {
							return XMLConstants.DEFAULT_NS_PREFIX;
						}
					}
					if (state == 1) { // check pairs
						if (prefixes != null) {
							if (pairs == null) {
								pairs = prefixes.iterator();
							}
							while (pairs.hasNext()) {
								Pair pair = pairs.next();
								if (namespaceURI.equals(pair.getSecond())) {
									return pair.getFirst();
								}
							}
						}
						state = 2;
					}
					if (state == 2) { // check above
						if (parent != null) {
							if (above == null) {
								above = parent.getPrefixes(namespaceURI);
							}
							while (above.hasNext()) {
								String prefix = above.next().toString();
								if (getNamespaceURI(prefix).equals(namespaceURI)) {
									return prefix;
								}
							}
						}
						state = 3;
					}
					if (state == 3) { // check out...
						return null;
					}
					throw new IllegalStateException(); // should not happen
				}

				@Override
				public boolean hasNext() {
					if (next == null) {
						next = next0();
					}
					return next != null;
				}

				@Override
				public String next() {
					if (!hasNext()) {
						throw new NoSuchElementException();
					}
					String result = next;
					next = null;
					return result;
				}

				@Override
				public void remove() {
					throw new UnsupportedOperationException("Cannot remove prefix");
				}
			};
		}
	}

	@Override
	public String getNamespaceURI(String prefix) {
		if (prefix == null) {
			throw new IllegalArgumentException("Prefix must not be null");
		} else if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
			return defaultNamespace;
		} else if (XMLConstants.XML_NS_PREFIX.equals(prefix)) {
			return XMLConstants.XML_NS_URI;
		} else if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) {
			return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
		} else {
			if (prefixes != null) {
				for (Pair pair : prefixes) {
					if (pair.getFirst().equals(prefix)) {
						return pair.getSecond();
					}
				}
			}
			return parent == null ? XMLConstants.NULL_NS_URI : parent.getNamespaceURI(prefix);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy