Index: src/main/java/javax/imageio/metadata/IIOInvalidTreeException.java =================================================================== --- src/main/java/javax/imageio/metadata/IIOInvalidTreeException.java (revision 0) +++ src/main/java/javax/imageio/metadata/IIOInvalidTreeException.java (revision 0) @@ -0,0 +1,39 @@ +/* + * 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 javax.imageio.metadata; + +import org.w3c.dom.Node; +import javax.imageio.IIOException; + +public class IIOInvalidTreeException extends IIOException { + protected Node offendingNode = null; + + public IIOInvalidTreeException(String message, Node offendingNode) { + super(message); + this.offendingNode = offendingNode; + } + + public IIOInvalidTreeException(String message, Throwable cause, Node offendingNode) { + super(message, cause); + this.offendingNode = offendingNode; + } + + public Node getOffendingNode() { + return offendingNode; + } +} Index: src/main/java/javax/imageio/metadata/IIOMetadataFormat.java =================================================================== --- src/main/java/javax/imageio/metadata/IIOMetadataFormat.java (revision 0) +++ src/main/java/javax/imageio/metadata/IIOMetadataFormat.java (revision 0) @@ -0,0 +1,82 @@ +/* + * 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 javax.imageio.metadata; + +import javax.imageio.ImageTypeSpecifier; +import java.util.Locale; + +public interface IIOMetadataFormat { + + int CHILD_POLICY_EMPTY = 0; + int CHILD_POLICY_ALL = 1; + int CHILD_POLICY_SOME = 2; + int CHILD_POLICY_CHOICE = 3; + int CHILD_POLICY_SEQUENCE = 4; + int CHILD_POLICY_REPEAT = 5; + int CHILD_POLICY_MAX = CHILD_POLICY_REPEAT; + + int DATATYPE_STRING = 0; + int DATATYPE_BOOLEAN = 1; + int DATATYPE_INTEGER = 2; + int DATATYPE_FLOAT = 3; + int DATATYPE_DOUBLE = 4; + + int VALUE_NONE = 0; + int VALUE_ARBITRARY = 1; + int VALUE_RANGE = 2; + int VALUE_RANGE_MIN_INCLUSIVE_MASK = 4; + int VALUE_RANGE_MAX_INCLUSIVE_MASK = 8; + int VALUE_ENUMERATION = 16; + int VALUE_LIST = 32; + int VALUE_RANGE_MIN_INCLUSIVE = VALUE_RANGE | VALUE_RANGE_MIN_INCLUSIVE_MASK; + int VALUE_RANGE_MAX_INCLUSIVE = VALUE_RANGE | VALUE_RANGE_MAX_INCLUSIVE_MASK; + int VALUE_RANGE_MIN_MAX_INCLUSIVE = + VALUE_RANGE | VALUE_RANGE_MIN_INCLUSIVE_MASK | VALUE_RANGE_MAX_INCLUSIVE_MASK; + + boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType); + + int getAttributeDataType(String elementName, String attrName); + String getAttributeDefaultValue(String elementName, String attrName); + String getAttributeDescription(String elementName, String attrName, Locale locale); + String[] getAttributeEnumerations(String elementName, String attrName); + int getAttributeListMaxLength(String elementName, String attrName); + int getAttributeListMinLength(String elementName, String attrName); + String getAttributeMaxValue(String elementName, String attrName); + String getAttributeMinValue(String elementName, String attrName); + String[] getAttributeNames(String elementName); + int getAttributeValueType(String elementName, String attrName); + boolean isAttributeRequired(String elementName, String attrName); + + String[] getChildNames(String elementName); + int getChildPolicy(String elementName); + + String getElementDescription(String elementName, Locale locale); + int getElementMaxChildren(String elementName); + int getElementMinChildren(String elementName); + + int getObjectArrayMaxLength(String elementName); + int getObjectArrayMinLength(String elementName); + Class getObjectClass(String elementName); + Object getObjectDefaultValue(String elementName); + Object[] getObjectEnumerations(String elementName); + Comparable getObjectMaxValue(String elementName); + Comparable getObjectMinValue(String elementName); + int getObjectValueType(String elementName); + + String getRootName(); +} Index: src/main/java/javax/imageio/metadata/IIOMetadataNode.java =================================================================== --- src/main/java/javax/imageio/metadata/IIOMetadataNode.java (revision 0) +++ src/main/java/javax/imageio/metadata/IIOMetadataNode.java (revision 0) @@ -0,0 +1,588 @@ +/* + * 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 javax.imageio.metadata; + +import org.w3c.dom.*; +import java.util.List; +import java.util.ArrayList; + +public class IIOMetadataNode implements Element, NodeList { + + private String nodeName; + private String nodeValue; + private IIOMetadataNodeList attrs = new IIOMetadataNodeList(new ArrayList()); + + private IIOMetadataNode parent; + private IIOMetadataNode firstChild; + private IIOMetadataNode lastChild; + private IIOMetadataNode previousSibling; + private IIOMetadataNode nextSibling; + + private int nChildren; + + private Object userObject; + + private String textContent; + + public IIOMetadataNode() { + } + + public IIOMetadataNode(String nodeName) { + this.nodeName = nodeName; + } + + private IIOMetadataNode(String nodeName, String nodeValue) { + this.nodeName = nodeName; + this.nodeValue = nodeValue; + } + + public String getTagName() { + return nodeName; + } + + public String getAttribute(String name) { + Attr attrNode = (Attr) attrs.getNamedItem(name); + return (attrNode == null) ? "" : attrNode.getValue(); + } + + public void setAttribute(String name, String value) throws DOMException { + Attr attr = (Attr) attrs.getNamedItem(name); + if (attr != null) { + attr.setValue(value); + } else { + attrs.list.add(new IIOMetadataAttr(name, value, this)); + } + } + + public void removeAttribute(String name) throws DOMException { + IIOMetadataAttr attr = (IIOMetadataAttr) attrs.getNamedItem(name); + if (attr != null) { + attr.setOwnerElement(null); + attrs.list.remove(attr); + } + } + + public Attr getAttributeNode(String name) { + return (Attr) attrs.getNamedItem(name); + } + + public Attr setAttributeNode(Attr newAttr) throws DOMException { + // Check if this attribute is already in use. + Element owner = newAttr.getOwnerElement(); + if (owner != null) { + if (owner == this) { // Replacing an attribute node by itself has no effect + return null; + } else { + throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, "Attribute is already in use"); + } + } + + String name = newAttr.getName(); + Attr oldAttr = getAttributeNode(name); + if (oldAttr != null) { + removeAttributeNode(oldAttr); + } + + IIOMetadataAttr iioAttr; + if (newAttr instanceof IIOMetadataAttr) { + iioAttr = (IIOMetadataAttr) newAttr; + iioAttr.setOwnerElement(this); + } else { + iioAttr = new IIOMetadataAttr(name, newAttr.getValue(), this); + } + + attrs.list.add(iioAttr); + + return oldAttr; + } + + public Attr removeAttributeNode(Attr oldAttr) throws DOMException { + if (!attrs.list.remove(oldAttr)) { // Not found + throw new DOMException(DOMException.NOT_FOUND_ERR, "No such attribute!"); + } + + ((IIOMetadataAttr)oldAttr).setOwnerElement(null); + + return oldAttr; + } + + public NodeList getElementsByTagName(String name) { + ArrayList nodes = new ArrayList(); + + // Non-recursive tree walk + Node pos = this; + + while (pos != null) { + if (pos.getNodeName().equals(name)) { + nodes.add((IIOMetadataNode)pos); + } + + Node nextNode = pos.getFirstChild(); + + while (nextNode == null) { + if (pos == this) { + break; + } + + nextNode = pos.getNextSibling(); + + if (nextNode == null) { + pos = pos.getParentNode(); + + if (pos == null || pos == this) { + nextNode = null; + break; + } + } + } + pos = nextNode; + } + + return new IIOMetadataNodeList(nodes); + } + + public String getAttributeNS(String namespaceURI, String localName) throws DOMException { + return getAttribute(localName); + } + + public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException { + setAttribute(qualifiedName, value); + } + + public void removeAttributeNS(String namespaceURI, String localName) throws DOMException { + removeAttribute(localName); + } + + public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException { + return getAttributeNode(localName); + } + + public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { + return setAttributeNode(newAttr); + } + + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException { + return getElementsByTagName(localName); + } + + public boolean hasAttribute(String name) { + return attrs.getNamedItem(name) != null; + } + + public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException { + return hasAttribute(localName); + } + + public TypeInfo getSchemaTypeInfo() { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public void setIdAttribute(String name, boolean isId) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public String getNodeName() { + return nodeName; + } + + public String getNodeValue() throws DOMException { + return nodeValue; + } + + public void setNodeValue(String nodeValue) throws DOMException { + this.nodeValue = nodeValue; + } + + public short getNodeType() { + return ELEMENT_NODE; + } + + public Node getParentNode() { + return parent; + } + + public NodeList getChildNodes() { + return this; + } + + public Node getFirstChild() { + return firstChild; + } + + public Node getLastChild() { + return lastChild; + } + + public Node getPreviousSibling() { + return previousSibling; + } + + public Node getNextSibling() { + return nextSibling; + } + + public NamedNodeMap getAttributes() { + return attrs; + } + + public Document getOwnerDocument() { + return null; + } + + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + if (newChild == null) { + throw new IllegalArgumentException("newChild == null!"); + } + + IIOMetadataNode newIIOChild = (IIOMetadataNode) newChild; + IIOMetadataNode refIIOChild = (IIOMetadataNode) refChild; + + newIIOChild.parent = this; + + if (refIIOChild == null) { + newIIOChild.nextSibling = null; + newIIOChild.previousSibling = lastChild; + + // Fix this node + lastChild = newIIOChild; + if (firstChild == null) { + firstChild = newIIOChild; + } + } else { + newIIOChild.nextSibling = refIIOChild; + newIIOChild.previousSibling = refIIOChild.previousSibling; + + // Fix this node + if (firstChild == refIIOChild) { + firstChild = newIIOChild; + } + + // Fix next node + if (refIIOChild != null) { + refIIOChild.previousSibling = newIIOChild; + } + } + + // Fix prev node + if (newIIOChild.previousSibling != null) { + newIIOChild.previousSibling.nextSibling = newIIOChild; + } + + nChildren++; + + return newIIOChild; + } + + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + if (newChild == null) { + throw new IllegalArgumentException("newChild == null!"); + } + + IIOMetadataNode newIIOChild = (IIOMetadataNode) newChild; + IIOMetadataNode oldIIOChild = (IIOMetadataNode) oldChild; + + IIOMetadataNode next = oldIIOChild.nextSibling; + IIOMetadataNode previous = oldIIOChild.previousSibling; + + // Fix new node + newIIOChild.parent = this; + newIIOChild.nextSibling = next; + newIIOChild.previousSibling = previous; + + // Fix this node + if (lastChild == oldIIOChild) { + lastChild = newIIOChild; + } + if (firstChild == oldIIOChild) { + firstChild = newIIOChild; + } + + // Fix siblings + if (next != null) { + next.previousSibling = newIIOChild; + } + if (previous != null) { + previous.nextSibling = newIIOChild; + } + + // Fix old child + oldIIOChild.parent = null; + oldIIOChild.nextSibling = next; + oldIIOChild.previousSibling = previous; + + return oldIIOChild; + } + + public Node removeChild(Node oldChild) throws DOMException { + if (oldChild == null) { + throw new IllegalArgumentException("oldChild == null!"); + } + + IIOMetadataNode oldIIOChild = (IIOMetadataNode) oldChild; + + // Fix next and previous + IIOMetadataNode previous = oldIIOChild.previousSibling; + IIOMetadataNode next = oldIIOChild.nextSibling; + + if (previous != null) { + previous.nextSibling = next; + } + if (next != null) { + next.previousSibling = previous; + } + + // Fix this node + if (lastChild == oldIIOChild) { + lastChild = previous; + } + if (firstChild == oldIIOChild) { + firstChild = next; + } + nChildren--; + + // Fix old child + oldIIOChild.parent = null; + oldIIOChild.previousSibling = null; + oldIIOChild.nextSibling = null; + + return oldIIOChild; + } + + public Node appendChild(Node newChild) throws DOMException { + return insertBefore(newChild, null); + } + + public boolean hasChildNodes() { + return nChildren != 0; + } + + public Node cloneNode(boolean deep) { + IIOMetadataNode cloned = new IIOMetadataNode(nodeName); + cloned.setUserObject(getUserObject()); + + if (deep) { // Clone recursively + IIOMetadataNode c = firstChild; + while (c != null) { + cloned.insertBefore(c.cloneNode(true), null); + c = c.nextSibling; + } + } + + return cloned; //To change body of implemented methods use File | Settings | File Templates. + } + + public void normalize() { + // Do nothing + } + + public boolean isSupported(String feature, String version) { + return false; + } + + public String getNamespaceURI() { + return null; + } + + public String getPrefix() { + return null; + } + + public void setPrefix(String prefix) throws DOMException { + // Do nothing + } + + public String getLocalName() { + return nodeName; + } + + public boolean hasAttributes() { + return attrs.list.size() > 0; + } + + public String getBaseURI() { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public short compareDocumentPosition(Node other) throws DOMException { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public String getTextContent() throws DOMException { + return textContent; + } + + public void setTextContent(String textContent) throws DOMException { + this.textContent = textContent; + } + + public boolean isSameNode(Node other) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public String lookupPrefix(String namespaceURI) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public boolean isDefaultNamespace(String namespaceURI) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public String lookupNamespaceURI(String prefix) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public boolean isEqualNode(Node arg) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public Object getFeature(String feature, String version) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public Object setUserData(String key, Object data, UserDataHandler handler) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public Object getUserData(String key) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + public Node item(int index) { + if (index < 0 || index >= nChildren) { + return null; + } + + Node n; + for (n = getFirstChild(); index > 0; index--) { + n = n.getNextSibling(); + } + + return n; + } + + public int getLength() { + return nChildren; + } + + public Object getUserObject() { + return userObject; + } + + public void setUserObject(Object userObject) { + this.userObject = userObject; + } + + private class IIOMetadataAttr extends IIOMetadataNode implements Attr { + private Element ownerElement; + + public IIOMetadataAttr(String name, String value, Element owner) { + super(name, value); + this.ownerElement = owner; + } + + public String getName() { + return getNodeName(); + } + + public boolean getSpecified() { + return true; + } + + public String getValue() { + return nodeValue; + } + + public void setValue(String value) throws DOMException { + nodeValue = value; + } + + public Element getOwnerElement() { + return ownerElement; + } + + public void setOwnerElement(Element ownerElement) { + this.ownerElement = ownerElement; + } + + public boolean isId() { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Method not supported"); + } + + @Override + public short getNodeType() { + return ATTRIBUTE_NODE; + } + } + + private class IIOMetadataNodeList implements NodeList, NamedNodeMap { + private List list; + + IIOMetadataNodeList(List list) { + this.list = list; + } + + public Node item(int index) { + try { + return list.get(index); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + public int getLength() { + return list.size(); + } + + public Node getNamedItem(String name) { + for(IIOMetadataNode node:list) { + if (name.equals(node.getNodeName())) { + return node; + } + } + return null; + } + + public Node setNamedItem(Node arg) throws DOMException { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "This NamedNodeMap is read-only!"); + } + + public Node removeNamedItem(String name) throws DOMException { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "This NamedNodeMap is read-only!"); + } + + public Node getNamedItemNS(String namespaceURI, String localName) throws DOMException { + return getNamedItem(localName); + } + + public Node setNamedItemNS(Node arg) throws DOMException { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "This NamedNodeMap is read-only!"); + } + + public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, "This NamedNodeMap is read-only!"); + } + } +}