Initial Contribution
diff --git a/awt/javax/imageio/metadata/IIOInvalidTreeException.java b/awt/javax/imageio/metadata/IIOInvalidTreeException.java
new file mode 100644
index 0000000..8690b2b
--- /dev/null
+++ b/awt/javax/imageio/metadata/IIOInvalidTreeException.java
@@ -0,0 +1,67 @@
+/*
+ *  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;
+
+/**
+ * The IIOInvalidTreeException provides notification about
+ * fails of IIOMetadataNodes tree parsing by IIOMetadata object. 
+ */
+public class IIOInvalidTreeException extends IIOException {
+    
+    /** The offending node. */
+    protected Node offendingNode = null;
+
+    /**
+     * Instantiates an IIOInvalidTreeException with the
+     * specified detailed message and specified offending
+     * Node.
+     * 
+     * @param message the detailed message.
+     * @param offendingNode the offending node.
+     */
+    public IIOInvalidTreeException(String message, Node offendingNode) {
+        super(message);
+        this.offendingNode = offendingNode;
+    }
+
+    /**
+     * Instantiates a new IIOInvalidTreeException with the
+     * specified detailed message and specified offending
+     * Node.
+     * 
+     * @param message the detailed message.
+     * @param cause the cause of this exception.
+     * @param offendingNode the offending node
+     */
+    public IIOInvalidTreeException(String message, Throwable cause, Node offendingNode) {
+        super(message, cause);
+        this.offendingNode = offendingNode;
+    }
+
+    /**
+     * Gets the offending node.
+     * 
+     * @return the offending node.
+     */
+    public Node getOffendingNode() {
+        return offendingNode;
+    }
+}
diff --git a/awt/javax/imageio/metadata/IIOMetadata.java b/awt/javax/imageio/metadata/IIOMetadata.java
new file mode 100644
index 0000000..f2387cc
--- /dev/null
+++ b/awt/javax/imageio/metadata/IIOMetadata.java
@@ -0,0 +1,371 @@
+/*
+ *  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 java.util.ArrayList;
+
+import org.apache.harmony.x.imageio.metadata.IIOMetadataUtils;
+import org.w3c.dom.Node;
+
+/**
+ * The Class IIOMetadata represents the metadata (bundled with an image)
+ * as a Dom-type tree.
+ */
+public abstract class IIOMetadata {
+
+    /** Whether the standard metadata format is supported. */
+    protected boolean standardFormatSupported;
+    
+    /** The native metadata format name. */
+    protected String nativeMetadataFormatName;
+    
+    /** The native metadata format class name. */
+    protected String nativeMetadataFormatClassName;
+    
+    /** The extra metadata format names. */
+    protected String[] extraMetadataFormatNames;
+    
+    /** The extra metadata format class names. */
+    protected String[] extraMetadataFormatClassNames;
+    
+    /** The default controller. */
+    protected IIOMetadataController defaultController;
+    
+    /** The controller. */
+    protected IIOMetadataController controller;
+
+    /**
+     * Instantiates a new IIOMetadata with no data set.
+     */
+    protected IIOMetadata() {}
+
+    /**
+     * Instantiates a new IIOMetadata with the specified data parameters.
+     * 
+     * @param standardMetadataFormatSupported whether the standard metadata format is supported
+     * @param nativeMetadataFormatName the native metadata format name
+     * @param nativeMetadataFormatClassName the native metadata format class name
+     * @param extraMetadataFormatNames the extra metadata format names
+     * @param extraMetadataFormatClassNames the extra metadata format class names
+     */
+    protected IIOMetadata(boolean standardMetadataFormatSupported,
+                          String nativeMetadataFormatName,
+                          String nativeMetadataFormatClassName,
+                          String[] extraMetadataFormatNames,
+                          String[] extraMetadataFormatClassNames) {
+        standardFormatSupported = standardMetadataFormatSupported;
+        this.nativeMetadataFormatName = nativeMetadataFormatName;
+        this.nativeMetadataFormatClassName = nativeMetadataFormatClassName;
+        if (extraMetadataFormatNames == null) {
+            if (extraMetadataFormatClassNames != null) {
+                throw new IllegalArgumentException(
+                        "extraMetadataFormatNames == null && extraMetadataFormatClassNames != null!"
+                );
+            }
+        } else {
+            if (extraMetadataFormatClassNames == null) {
+                throw new IllegalArgumentException(
+                        "extraMetadataFormatNames != null && extraMetadataFormatClassNames == null!"
+                );
+            }
+            if (extraMetadataFormatNames.length == 0) {
+                throw new IllegalArgumentException("extraMetadataFormatNames.length == 0!");
+            }
+            if (extraMetadataFormatClassNames.length != extraMetadataFormatNames.length) {
+                throw new IllegalArgumentException(
+                        "extraMetadataFormatClassNames.length != extraMetadataFormatNames.length!"
+                );
+            }
+            this.extraMetadataFormatNames = extraMetadataFormatNames.clone();
+            this.extraMetadataFormatClassNames = extraMetadataFormatClassNames.clone();
+        }
+    }
+
+    /**
+     * Gets the metadata as tree-type document.
+     * 
+     * @param formatName the format name
+     * 
+     * @return the node in tree format
+     */
+    public abstract Node getAsTree(String formatName);
+    
+    /**
+     * Checks if the metadata is read only.
+     * 
+     * @return true, if the metadata is read only.
+     */
+    public abstract boolean isReadOnly();
+    
+    /**
+     * Merges the specified tree with this metadata tree.
+     * 
+     * @param formatName the format of the specified tree
+     * @param root the root node of the metadata tree
+     * 
+     * @throws IIOInvalidTreeException if the specified tree
+     * is incompatible with the this metadata tree.
+     */
+    public abstract void mergeTree(String formatName, Node root) throws IIOInvalidTreeException;
+    
+    /**
+     * Resets the controller.
+     */
+    public abstract void reset();
+
+    /**
+     * Gets the controller associated with this metadata document.
+     * 
+     * @return the controller
+     */
+    public IIOMetadataController getController() {
+        return controller;
+    }
+
+    /**
+     * Checks whether this metadata has a controller.
+     * 
+     * @return true, if this metadata has a controller
+     */
+    public boolean hasController() {
+        return getController() != null;
+    }
+
+    /**
+     * Activate the controller.
+     * 
+     * @return true, if successful
+     */
+    public boolean activateController() {
+        if (!hasController()) {
+            throw new IllegalStateException("hasController() == false!");
+        }
+        return getController().activate(this);
+    }
+
+    /**
+     * Gets the default controller.
+     * 
+     * @return the default controller
+     */
+    public IIOMetadataController getDefaultController() {
+        return defaultController;
+    }
+
+    /**
+     * Gets the extra metadata format names.
+     * 
+     * @return the extra metadata format names
+     */
+    public String[] getExtraMetadataFormatNames() {
+        return extraMetadataFormatNames == null ? null : extraMetadataFormatNames.clone();
+    }
+
+    /**
+     * Gets the metadata format.
+     * 
+     * @param formatName the format name
+     * 
+     * @return the metadata format
+     */
+    public IIOMetadataFormat getMetadataFormat(String formatName) {
+        return IIOMetadataUtils.instantiateMetadataFormat(
+                formatName,
+                standardFormatSupported,
+                nativeMetadataFormatName, nativeMetadataFormatClassName,
+                extraMetadataFormatNames, extraMetadataFormatClassNames
+        );
+    }
+
+    /**
+     * Gets the native metadata format name.
+     * 
+     * @return the native metadata format name
+     */
+    public String getNativeMetadataFormatName() {
+        return nativeMetadataFormatName;
+    }
+
+    /**
+     * Checks if the standard metadata format is supported.
+     * 
+     * @return true, if the standard metadata format is supported
+     */
+    public boolean isStandardMetadataFormatSupported() {
+        return standardFormatSupported;
+    }
+
+    /**
+     * Gets the metadata format names.
+     * 
+     * @return the metadata format names
+     */
+    public String[] getMetadataFormatNames() {
+        ArrayList<String> res = new ArrayList<String>();
+
+        String nativeMetadataFormatName = getNativeMetadataFormatName();
+        boolean standardFormatSupported = isStandardMetadataFormatSupported();
+        String extraMetadataFormatNames[] = getExtraMetadataFormatNames();
+
+        if (standardFormatSupported) {
+            res.add(IIOMetadataFormatImpl.standardMetadataFormatName);
+        }
+        if (nativeMetadataFormatName != null) {
+            res.add(nativeMetadataFormatName);
+        }
+        if (extraMetadataFormatNames != null) {
+            for (String extraMetadataFormatName : extraMetadataFormatNames) {
+                res.add(extraMetadataFormatName);
+            }
+        }
+
+        return res.size() > 0 ? res.toArray(new String[0]) : null;
+    }
+
+    /**
+     * Gets the standard chroma node.
+     * 
+     * @return the standard chroma node
+     */
+    protected IIOMetadataNode getStandardChromaNode() {
+        return null;
+    }
+
+    /**
+     * Gets the standard compression node.
+     * 
+     * @return the standard compression node
+     */
+    protected IIOMetadataNode getStandardCompressionNode() {
+        return null;
+    }
+
+    /**
+     * Gets the standard data node.
+     * 
+     * @return the standard data node
+     */
+    protected IIOMetadataNode getStandardDataNode() {
+        return null;
+    }
+
+    /**
+     * Gets the standard dimension node.
+     * 
+     * @return the standard dimension node
+     */
+    protected IIOMetadataNode getStandardDimensionNode() {
+        return null;
+    }
+
+    /**
+     * Gets the standard document node.
+     * 
+     * @return the standard document node
+     */
+    protected IIOMetadataNode getStandardDocumentNode() {
+        return null;
+    }
+
+    /**
+     * Gets the standard text node.
+     * 
+     * @return the standard text node
+     */
+    protected IIOMetadataNode getStandardTextNode() {
+        return null;
+    }
+
+    /**
+     * Gets the standard tile node.
+     * 
+     * @return the standard tile node
+     */
+    protected IIOMetadataNode getStandardTileNode() {
+        return null;
+    }
+
+    /**
+     * Gets the standard transparency node.
+     * 
+     * @return the standard transparency node
+     */
+    protected IIOMetadataNode getStandardTransparencyNode() {
+        return null;
+    }
+
+    /**
+     * Gets the metadata as a tree in standard format.
+     * 
+     * @return the metadata as a tree in standard format
+     */
+    protected final IIOMetadataNode getStandardTree() {
+        // Create root node
+        IIOMetadataNode root = new IIOMetadataNode(IIOMetadataFormatImpl.standardMetadataFormatName);
+
+        Node node;
+        if ((node = getStandardChromaNode()) != null) {
+            root.appendChild(node);
+        }
+        if ((node = getStandardCompressionNode()) != null) {
+            root.appendChild(node);
+        }
+        if ((node = getStandardDataNode()) != null) {
+            root.appendChild(node);
+        }
+        if ((node = getStandardDimensionNode()) != null) {
+            root.appendChild(node);
+        }
+        if ((node = getStandardDocumentNode()) != null) {
+            root.appendChild(node);
+        }
+        if ((node = getStandardTextNode()) != null) {
+            root.appendChild(node);
+        }
+        if ((node = getStandardTileNode()) != null) {
+            root.appendChild(node);
+        }
+        if ((node = getStandardTransparencyNode()) != null) {
+            root.appendChild(node);
+        }
+        
+        return root;
+    }
+
+    /**
+     * Sets the controller.
+     * 
+     * @param controller the new controller
+     */
+    public void setController(IIOMetadataController controller) {
+        this.controller = controller;
+    }
+
+    /**
+     * Sets the from tree.
+     * 
+     * @param formatName the name of the metatdata format of the from tree
+     * @param root the root node of the from tree
+     * 
+     * @throws IIOInvalidTreeException if the tree or its format is not compatible with 
+     * this metadata.
+     */
+    public void setFromTree(String formatName, Node root) throws IIOInvalidTreeException {
+        reset();
+        mergeTree(formatName, root);
+    }
+}
diff --git a/awt/javax/imageio/metadata/IIOMetadataController.java b/awt/javax/imageio/metadata/IIOMetadataController.java
new file mode 100644
index 0000000..dfd4e5c
--- /dev/null
+++ b/awt/javax/imageio/metadata/IIOMetadataController.java
@@ -0,0 +1,45 @@
+/*
+ *  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.
+ */
+/**
+ * @author Sergey I. Salishev
+ * @version $Revision: 1.2 $
+ */
+package javax.imageio.metadata;
+
+/* 
+ * @author Sergey I. Salishev
+ * @version $Revision: 1.2 $
+ */
+
+/**
+ * The IIOMetadataController interface provides a method for 
+ * implementing objects to activate the controller without 
+ * defining how the controller obtains values.
+ */
+public interface IIOMetadataController {
+
+    /**
+     * Activates a controller.
+     * 
+     * @param metadata the metadata to be modified.
+     * 
+     * @return true if the IIOMetadata has been modified, 
+     * false otherwise.
+     */
+    public boolean activate(IIOMetadata metadata);
+}
+
diff --git a/awt/javax/imageio/metadata/IIOMetadataFormat.java b/awt/javax/imageio/metadata/IIOMetadataFormat.java
new file mode 100644
index 0000000..9e246b4
--- /dev/null
+++ b/awt/javax/imageio/metadata/IIOMetadataFormat.java
@@ -0,0 +1,347 @@
+/*
+ *  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;
+
+/**
+ * The Interface IIOMetadataFormat is implemented by classes that 
+ * describe the rules and allowed elements for a metadata document
+ * tree.
+ */
+public interface IIOMetadataFormat {
+
+    /** The CHILD_POLICY_EMPTY. */
+    int CHILD_POLICY_EMPTY = 0;
+    
+    /** The CHILD_POLICY_ALL. */
+    int CHILD_POLICY_ALL = 1;
+    
+    /** The CHILD_POLICY_SOME. */
+    int CHILD_POLICY_SOME = 2;
+    
+    /** The CHILD_POLICY_CHOICE. */
+    int CHILD_POLICY_CHOICE = 3;
+    
+    /** The CHILD_POLICY_SEQUENCE. */
+    int CHILD_POLICY_SEQUENCE = 4;
+    
+    /** The CHILD_POLICY_REPEAT. */
+    int CHILD_POLICY_REPEAT = 5;
+    
+    /** The maximum value for the child policy. */
+    int CHILD_POLICY_MAX = CHILD_POLICY_REPEAT;
+
+    /** The DATATYPE_STRING. */
+    int DATATYPE_STRING = 0;
+    
+    /** The DATATYPE_BOOLEAN. */
+    int DATATYPE_BOOLEAN = 1;
+    
+    /** The DATATYPE_INTEGER. */
+    int DATATYPE_INTEGER = 2;
+    
+    /** The DATATYPE_FLOAT. */
+    int DATATYPE_FLOAT = 3;
+    
+    /** The DATATYPE_DOUBLE. */
+    int DATATYPE_DOUBLE = 4;
+
+    /** The VALUE_NONE. */
+    int VALUE_NONE = 0;
+    
+    /** The VALUE_ARBITRARY. */
+    int VALUE_ARBITRARY = 1;
+    
+    /** The VALUE_RANGE. */
+    int VALUE_RANGE = 2;
+    
+    /** The VALUE_RANGE_MIN_INCLUSIVE_MASK. */
+    int VALUE_RANGE_MIN_INCLUSIVE_MASK = 4;
+    
+    /** The VALUE_RANGE_MAX_INCLUSIVE_MASK. */
+    int VALUE_RANGE_MAX_INCLUSIVE_MASK = 8;
+    
+    /** The VALUE_ENUMERATION. */
+    int VALUE_ENUMERATION = 16;
+    
+    /** The VALUE_LIST. */
+    int VALUE_LIST = 32;
+    
+    /** The VALUE_RANGE_MIN_INCLUSIVE. */
+    int VALUE_RANGE_MIN_INCLUSIVE = VALUE_RANGE | VALUE_RANGE_MIN_INCLUSIVE_MASK;
+    
+    /** The VALUE_RANGE_MAX_INCLUSIVE. */
+    int VALUE_RANGE_MAX_INCLUSIVE = VALUE_RANGE | VALUE_RANGE_MAX_INCLUSIVE_MASK;
+    
+    /** The VALUE_RANGE_MIN_MAX_INCLUSIVE. */
+    int VALUE_RANGE_MIN_MAX_INCLUSIVE =
+            VALUE_RANGE | VALUE_RANGE_MIN_INCLUSIVE_MASK | VALUE_RANGE_MAX_INCLUSIVE_MASK;
+
+    /**
+     * Tells whether the specified element is allowed for the specified 
+     * image type.
+     * 
+     * @param elementName the element name
+     * @param imageType the image type
+     * 
+     * @return true, if the specified element is allowed for the specified 
+     * image type
+     */
+    boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType);
+
+    /**
+     * Gets data type of the specified attribute of the specified element.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return the attribute's data type
+     */
+    int getAttributeDataType(String elementName, String attrName);
+    
+    /**
+     * Gets the default value of the specified attribute of the specified element.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return the attribute's default value
+     */
+    String getAttributeDefaultValue(String elementName, String attrName);
+    
+    /**
+     * Gets the user-friendly description of the attribute.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * @param locale the locale giving the desired language for the
+     * description
+     * 
+     * @return the attribute description
+     */
+    String getAttributeDescription(String elementName, String attrName, Locale locale);
+    
+    /**
+     * Gets the attribute enumerations.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return the attribute enumerations
+     */
+    String[] getAttributeEnumerations(String elementName, String attrName);
+    
+    /**
+     * Gets the maximum length of the attribute list.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return the maximum length of the attribute list
+     */
+    int getAttributeListMaxLength(String elementName, String attrName);
+    
+    /**
+     * Gets the minimum length of the attribute list.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return the minimum length of the attribute list
+     */
+    int getAttributeListMinLength(String elementName, String attrName);
+    
+    /**
+     * Gets the maximum value allowed for the attribute.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return the maximum value allowed for the attribute
+     */
+    String getAttributeMaxValue(String elementName, String attrName);
+    
+    /**
+     * Gets the minimum value allowed for the attribute.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return the minimum value allowed for the attribute
+     */
+    String getAttributeMinValue(String elementName, String attrName);
+    
+    /**
+     * Gets the attribute names allowed for the specified element.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the attribute names
+     */
+    String[] getAttributeNames(String elementName);
+    
+    /**
+     * Gets the attribute value type.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return the attribute value type
+     */
+    int getAttributeValueType(String elementName, String attrName);
+    
+    /**
+     * Checks whether the specified attribute is required 
+     * for the specified element.
+     * 
+     * @param elementName the element name
+     * @param attrName the attribute name
+     * 
+     * @return true, if the specified attribute is required for the 
+     * specified element
+     */
+    boolean isAttributeRequired(String elementName, String attrName);
+
+    /**
+     * Gets the names of the possible child elements for the given element.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the child names
+     */
+    String[] getChildNames(String elementName);
+    
+    /**
+     * Gets the constant describing the element's child policy.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the child policy
+     */
+    int getChildPolicy(String elementName);
+
+    /**
+     * Gets the user-friendly description of the element.
+     * 
+     * @param elementName the element name
+     * @param locale the locale giving the desired language for the
+     * description
+     * 
+     * @return the element description
+     */
+    String getElementDescription(String elementName, Locale locale);
+    
+    /**
+     * Gets the maximum number of children allowed for the element.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the maximum number of children allowed for the element
+     */
+    int getElementMaxChildren(String elementName);
+    
+    /**
+     * Gets the minimum number of children allowed for the element.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the minimum number of children allowed for the element
+     */
+    int getElementMinChildren(String elementName);
+
+    /**
+     * Gets the maximum object array length allowed for the element.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the maximum object array length allowed for the element
+     */
+    int getObjectArrayMaxLength(String elementName);
+    
+    /**
+     * Gets the minimum object array length allowed for the element.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the minimum object array length allowed for the element
+     */
+    int getObjectArrayMinLength(String elementName);
+    
+    /**
+     * Gets the object class corresponding to the specified element.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the object class corresponding to the specified element
+     */
+    Class<?> getObjectClass(String elementName);
+    
+    /**
+     * Gets the object default value for the element.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the object default value for the element
+     */
+    Object getObjectDefaultValue(String elementName);
+    
+    /**
+     * Gets the object enumerations.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the object enumerations
+     */
+    Object[] getObjectEnumerations(String elementName);
+    
+    /**
+     * Gets the maximum value allowed for the element's object.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the maximum value allowed for the element's object
+     */
+    Comparable<?> getObjectMaxValue(String elementName);
+    
+    /**
+     * Gets the minimum value allowed for the element's object.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the minimum value allowed for the element's object
+     */
+    Comparable<?> getObjectMinValue(String elementName);
+    
+    /**
+     * Gets the constant that indicates the type of the element's value.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the constant that indicates the type of the element's value
+     */
+    int getObjectValueType(String elementName);
+
+    /**
+     * Gets the name of the root element.
+     * 
+     * @return the name of the root element
+     */
+    String getRootName();
+}
diff --git a/awt/javax/imageio/metadata/IIOMetadataFormatImpl.java b/awt/javax/imageio/metadata/IIOMetadataFormatImpl.java
new file mode 100644
index 0000000..438ae90
--- /dev/null
+++ b/awt/javax/imageio/metadata/IIOMetadataFormatImpl.java
@@ -0,0 +1,939 @@
+/*
+ *  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.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * The IIOMetadataFormatImpl class provides an implementation of the 
+ * IIOMetadataFormat interface.
+ */
+public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat {
+    
+    /** The Constant standardMetadataFormatName. */
+    @SuppressWarnings({"ConstantDeclaredInAbstractClass"})
+    public static final String standardMetadataFormatName = "javax_imageio_1.0";
+
+    /** The standard format. */
+    @SuppressWarnings({"StaticNonFinalField"})
+    private static IIOMetadataFormatImpl standardFormat;
+
+    /** The root name. */
+    private String rootName;
+    
+    /** The element hash. */
+    private HashMap<String, Element> elementHash = new HashMap<String, Element>();
+
+    /** The resource base name. */
+    private String resourceBaseName = getClass().getName() + "Resources";
+
+    /**
+     * Instantiates an IIOMetadataFormatImpl with the specified root
+     * name and child policy (not CHILD_POLICY_REPEAT).
+     * 
+     * @param rootName the name of root element.
+     * @param childPolicy the child policy defined by one of the 
+     * CHILD_POLICY_* constants  (except CHILD_POLICY_REPEAT).
+     */
+    public IIOMetadataFormatImpl(String rootName, int childPolicy) {
+        if (rootName == null) {
+            throw new IllegalArgumentException("rootName is null");
+        }
+        if (
+                childPolicy < CHILD_POLICY_EMPTY ||
+                childPolicy > CHILD_POLICY_MAX ||
+                childPolicy == CHILD_POLICY_REPEAT
+        ) {
+            throw new IllegalArgumentException("childPolicy is not one of the predefined constants");
+        }
+
+        this.rootName = rootName;
+        Element root = new Element();
+        root.name = rootName;
+        root.childPolicy = childPolicy;
+        elementHash.put(rootName, root);
+    }
+
+    /**
+     * Instantiates an IIOMetadataFormatImpl with the specified root
+     * name and CHILD_POLICY_REPEAT child policy.
+     * 
+     * @param rootName the name of root element.
+     * @param minChildren the minimum number of children.
+     * @param maxChildren the maximum number of children
+     */
+    public IIOMetadataFormatImpl(String rootName, int minChildren, int maxChildren) {
+        if (rootName == null) {
+            throw new IllegalArgumentException("rootName is null");
+        }
+        if (minChildren < 0) {
+            throw new IllegalArgumentException("minChildren < 0!");
+        }
+        if (minChildren > maxChildren) {
+            throw new IllegalArgumentException("minChildren > maxChildren!");
+        }
+
+        this.rootName = rootName;
+        Element root = new Element();
+        root.name = rootName;
+        root.minChildren = minChildren;
+        root.maxChildren = maxChildren;
+        root.childPolicy = CHILD_POLICY_REPEAT;
+        elementHash.put(rootName, root);
+    }
+
+    @SuppressWarnings({"AbstractMethodOverridesAbstractMethod"})
+    public abstract boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType);
+
+    /**
+     * Adds a new attribute to an existing element.
+     * 
+     * @param elementName the name of the element to which the new attribute
+     * will be added.
+     * @param attrName the attribute name.
+     * @param dataType the data type of the new attribute.
+     * @param required the flag which indicates whether this attribute
+     * must be present.
+     * @param listMinLength the minimum legal number of list items.
+     * @param listMaxLength the the maximum legal number of list items.
+     */
+    protected void addAttribute(
+            String elementName, String attrName, int dataType,
+            boolean required, int listMinLength, int listMaxLength
+    ) {
+        if (attrName == null) {
+            throw new IllegalArgumentException("attrName == null!");
+        }
+        if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
+            throw new IllegalArgumentException("Invalid value for dataType!");
+        }
+        if (listMinLength < 0 || listMinLength > listMaxLength) {
+            throw new IllegalArgumentException("Invalid list bounds!");
+        }
+
+        Element element = findElement(elementName);
+        Attlist attr = new Attlist();
+        attr.name = attrName;
+        attr.dataType = dataType;
+        attr.required = required;
+        attr.listMinLength = listMinLength;
+        attr.listMaxLength = listMaxLength;
+        attr.valueType = VALUE_LIST;
+
+        element.attributes.put(attrName, attr);
+    }
+
+    /**
+     * Adds a new attribute to an existing element.
+     * 
+     * @param elementName the name of the element to which the new attribute
+     * will be added.
+     * @param attrName the attribute name.
+     * @param dataType the data type of the new attribute.
+     * @param required the flag which indicates whether this attribute
+     * must be present.
+     * @param defaultValue the default value of the attribute.
+     */
+    protected void addAttribute(
+            String elementName, String attrName, int dataType,
+            boolean required, String defaultValue
+    ) {
+        if (attrName == null) {
+            throw new IllegalArgumentException("attrName == null!");
+        }
+        if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
+            throw new IllegalArgumentException("Invalid value for dataType!");
+        }
+
+        Element element = findElement(elementName);
+        Attlist attr = new Attlist();
+        attr.name = attrName;
+        attr.dataType = dataType;
+        attr.required = required;
+        attr.defaultValue = defaultValue;
+        attr.valueType = VALUE_ARBITRARY;
+
+        element.attributes.put(attrName, attr);
+    }
+
+    /**
+     * Adds a new attribute to an existing element.
+     * 
+     * @param elementName the name of the element to which the new attribute
+     * will be added.
+     * @param attrName the attribute name.
+     * @param dataType the data type of the new attribute.
+     * @param required the flag which indicates whether this attribute
+     * must be present.
+     * @param defaultValue the default value of the attribute.
+     * @param enumeratedValues the legal values for the attribute as 
+     * a list of strings.
+     */
+    protected void addAttribute(
+            String elementName, String attrName, int dataType,
+            boolean required, String defaultValue, List<String> enumeratedValues
+    ) {
+        if (attrName == null) {
+            throw new IllegalArgumentException("attrName == null!");
+        }
+        if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
+            throw new IllegalArgumentException("Invalid value for dataType!");
+        }
+        if (enumeratedValues == null || enumeratedValues.isEmpty()) {
+            throw new IllegalArgumentException("enumeratedValues is empty or null");
+        }
+
+        try {
+            for (String enumeratedValue : enumeratedValues) {
+                if (enumeratedValue == null) {
+                    throw new IllegalArgumentException("enumeratedValues contains a null!");
+                }
+            }
+        } catch (ClassCastException e) {
+            throw new IllegalArgumentException("enumeratedValues contains a non-String value!");
+        }
+
+        Element element = findElement(elementName);
+        Attlist attr = new Attlist();
+        attr.name = attrName;
+        attr.dataType = dataType;
+        attr.required = required;
+        attr.defaultValue = defaultValue;
+        attr.enumeratedValues = enumeratedValues;
+        attr.valueType = VALUE_ENUMERATION;
+
+        element.attributes.put(attrName, attr);
+    }
+
+    /**
+     * Adds a new attribute to an existing element.
+     * 
+     * @param elementName the name of the element to which the new attribute
+     * will be added.
+     * @param attrName the attribute name.
+     * @param dataType the data type of the new attribute.
+     * @param required the flag which indicates whether this attribute
+     * must be present.
+     * @param defaultValue the default value of attribute.
+     * @param minValue the minimum legal value of an attribute.
+     * @param maxValue the maximum legal value of an attribute.
+     * @param minInclusive the flag which indicates  
+     * whether the minValue is inclusive.
+     * @param maxInclusive the flag which indicates  
+     * whether the maxValue is inclusive.
+     */
+    protected void addAttribute(
+            String elementName, String attrName, int dataType,
+            boolean required, String defaultValue,
+            String minValue, String maxValue,
+            boolean minInclusive, boolean maxInclusive
+    ) {
+        if (attrName == null) {
+            throw new IllegalArgumentException("attrName == null!");
+        }
+        if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
+            throw new IllegalArgumentException("Invalid value for dataType!");
+        }
+
+        Element element = findElement(elementName);
+        Attlist attr = new Attlist();
+        attr.name = attrName;
+        attr.dataType = dataType;
+        attr.required = required;
+        attr.defaultValue = defaultValue;
+        attr.minValue = minValue;
+        attr.maxValue = maxValue;
+        attr.minInclusive = minInclusive;
+        attr.maxInclusive = maxInclusive;
+
+        attr.valueType = VALUE_RANGE;
+        attr.valueType |= minInclusive ? VALUE_RANGE_MIN_INCLUSIVE_MASK : 0;
+        attr.valueType |= maxInclusive ? VALUE_RANGE_MAX_INCLUSIVE_MASK : 0;
+
+        element.attributes.put(attrName, attr);
+    }
+
+    /**
+     * Adds a new attribute with boolean data type to an existing 
+     * element.
+     * 
+     * @param elementName the name of the element to which the new attribute
+     * will be added.
+     * @param attrName the attribute name.
+     * @param hasDefaultValue the flag which indicates whether this attribute
+     * must have a default value.
+     * @param defaultValue the default value.
+     */
+    protected void addBooleanAttribute(
+            String elementName, String attrName,
+            boolean hasDefaultValue, boolean defaultValue
+    ) {
+        String defaultVal = hasDefaultValue ? (defaultValue ? "TRUE" : "FALSE") : null;
+        ArrayList<String> values = new ArrayList<String>(2);
+        values.add("TRUE");
+        values.add("FALSE");
+
+        addAttribute(elementName, attrName, DATATYPE_BOOLEAN, true, defaultVal, values);
+    }
+
+    /**
+     * Adds an existing element to the list of child elements 
+     * of the specified parent element.
+     * 
+     * @param elementName the name of the element to be added.
+     * @param parentName the parent element name.
+     */
+    protected void addChildElement(String elementName, String parentName) {
+        Element parent = findElement(parentName);
+        Element element = findElement(elementName);
+        parent.children.add(element.name);
+    }
+
+    /**
+     * Adds a new element type to this IIOMetadataFormat with 
+     * a child policy (if policy is not CHILD_POLICY_REPEAT).
+     * 
+     * @param elementName the name of the element to be added.
+     * @param parentName the parent element name.
+     * @param childPolicy one of the CHILD_POLICY_* constants defined
+     * by IIOMetadataFormat.
+     */
+    protected void addElement(String elementName, String parentName, int childPolicy) {
+        if (
+                childPolicy < CHILD_POLICY_EMPTY ||
+                childPolicy > CHILD_POLICY_MAX ||
+                childPolicy == CHILD_POLICY_REPEAT
+        ) {
+            throw new IllegalArgumentException("childPolicy is not one of the predefined constants");
+        }
+        
+        Element parent = findElement(parentName);
+        Element element = new Element();
+        element.name = elementName;
+        element.childPolicy = childPolicy;
+        elementHash.put(elementName, element);
+        parent.children.add(elementName);
+    }
+
+    /**
+     * Adds a new element type to this IIOMetadataFormat with 
+     * CHILD_POLICY_REPEAT and the specified minimum and maximum
+     * number of child elements.
+     * 
+     * @param elementName the element name to be added.
+     * @param parentName the parent element name.
+     * @param minChildren the minimum number of child elements.
+     * @param maxChildren the maximum number of child elements.
+     */
+    protected void addElement(
+            String elementName, String parentName,
+            int minChildren, int maxChildren
+    ) {
+        if (minChildren < 0) {
+            throw new IllegalArgumentException("minChildren < 0!");
+        }
+        if (minChildren > maxChildren) {
+            throw new IllegalArgumentException("minChildren > maxChildren!");
+        }
+
+        Element parent = findElement(parentName);
+        Element element = new Element();
+        element.name = elementName;
+        element.childPolicy = CHILD_POLICY_REPEAT;
+        element.minChildren = minChildren;
+        element.maxChildren = maxChildren;
+        elementHash.put(elementName, element);
+        parent.children.add(elementName);
+    }
+
+    /**
+     * Adds an Object reference with the specified class type to be 
+     * stored as element's value. 
+     * 
+     * @param elementName the element name.
+     * @param classType the class indicates the legal types for 
+     * the object's value.
+     * @param arrayMinLength the minimum legal length for the array.
+     * @param arrayMaxLength the maximum legal length for the array.
+     */
+    protected void addObjectValue(
+            String elementName, Class<?> classType,
+            int arrayMinLength, int arrayMaxLength
+    ) {
+        Element element = findElement(elementName);
+
+        ObjectValue objVal = new ObjectValue();
+        objVal.classType = classType;
+        objVal.arrayMaxLength = arrayMaxLength;
+        objVal.arrayMinLength = arrayMinLength;
+        objVal.valueType = VALUE_LIST;
+
+        element.objectValue = objVal;
+    }
+
+    /**
+     * Adds an Object reference with the specified class type to be 
+     * stored as an element's value. 
+     * 
+     * @param elementName the element name.
+     * @param classType the class indicates the legal types for 
+     * the object's value.
+     * @param required a flag indicated that this object value 
+     * must be present.
+     * @param defaultValue the default value, or null.
+     */
+    protected <T> void addObjectValue(
+            String elementName, Class<T> classType,
+            boolean required, T defaultValue
+    ) {
+        // note: reqired is an unused parameter
+        Element element = findElement(elementName);
+
+        ObjectValue<T> objVal = new ObjectValue<T>();
+        objVal.classType = classType;
+        objVal.defaultValue = defaultValue;
+        objVal.valueType = VALUE_ARBITRARY;
+
+        element.objectValue = objVal;
+    }
+
+    /**
+     * Adds an Object reference with the specified class type to be 
+     * stored as the element's value. 
+     * 
+     * @param elementName the element name.
+     * @param classType the class indicates the legal types for 
+     * the object value.
+     * @param required a flag indicated that this object value 
+     * must be present.
+     * @param defaultValue the default value, or null.
+     * @param enumeratedValues the list of legal values for the object.
+     */
+    protected <T> void addObjectValue(
+            String elementName, Class<T> classType,
+            boolean required, T defaultValue,
+            List<? extends T> enumeratedValues
+    ) {
+        // note: reqired is an unused parameter
+        if (enumeratedValues == null || enumeratedValues.isEmpty()) {
+            throw new IllegalArgumentException("enumeratedValues is empty or null");
+        }
+
+        try {
+            for (T enumeratedValue : enumeratedValues) {
+                if (enumeratedValue == null) {
+                    throw new IllegalArgumentException("enumeratedValues contains a null!");
+                }
+            }
+        } catch (ClassCastException e) {
+            throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!");
+        }
+
+        Element element = findElement(elementName);
+
+        ObjectValue<T> objVal = new ObjectValue<T>();
+        objVal.classType = classType;
+        objVal.defaultValue = defaultValue;
+        objVal.enumeratedValues = enumeratedValues;
+        objVal.valueType = VALUE_ENUMERATION;
+
+        element.objectValue = objVal;
+    }
+
+    /**
+     * Adds an Object reference with the specified class type to be 
+     * stored as the element's value. 
+     * 
+     * @param elementName the element name.
+     * @param classType the class indicates the legal types for 
+     * the object value.
+     * @param defaultValue the default value, or null.
+     * @param minValue the minimum legal value for the object value. 
+     * @param maxValue the maximum legal value for the object value. 
+     * @param minInclusive the flag which indicates 
+     * whether the minValue is inclusive.
+     * @param maxInclusive the flag which indicates 
+     * whether the maxValue is inclusive.
+     */
+    protected <T extends Object & Comparable<? super T>> void addObjectValue(
+            String elementName, Class<T> classType,
+            T defaultValue, Comparable<? super T> minValue, Comparable<? super T> maxValue,
+            boolean minInclusive, boolean maxInclusive
+    ) {
+        Element element = findElement(elementName);
+
+        ObjectValue<T> objVal = new ObjectValue<T>();
+        objVal.classType = classType;
+        objVal.defaultValue = defaultValue;
+        objVal.minValue = minValue;
+        objVal.maxValue = maxValue;
+        objVal.minInclusive = minInclusive;
+        objVal.maxInclusive = maxInclusive;
+
+        objVal.valueType = VALUE_RANGE;
+        objVal.valueType |= minInclusive ? VALUE_RANGE_MIN_INCLUSIVE_MASK : 0;
+        objVal.valueType |= maxInclusive ? VALUE_RANGE_MAX_INCLUSIVE_MASK : 0;
+
+        element.objectValue = objVal;
+    }
+
+    public int getAttributeDataType(String elementName, String attrName) {
+        Attlist attr = findAttribute(elementName, attrName);
+        return attr.dataType;
+    }
+
+    public String getAttributeDefaultValue(String elementName, String attrName) {
+        Attlist attr = findAttribute(elementName, attrName);
+        return attr.defaultValue;
+    }
+
+    public String getAttributeDescription(String elementName, String attrName, Locale locale) {
+        findAttribute(elementName, attrName);
+        return getResourceString(elementName + "/" + attrName, locale);
+    }
+
+    public String[] getAttributeEnumerations(String elementName, String attrName) {
+        Attlist attr = findAttribute(elementName, attrName);
+        if (attr.valueType != VALUE_ENUMERATION) {
+            throw new IllegalArgumentException("Attribute is not an enumeration!");
+        }
+
+        return attr.enumeratedValues.toArray(new String[attr.enumeratedValues.size()]);
+    }
+
+    public int getAttributeListMaxLength(String elementName, String attrName) {
+        Attlist attr = findAttribute(elementName, attrName);
+        if (attr.valueType != VALUE_LIST) {
+            throw new IllegalArgumentException("Attribute is not a list!");
+        }
+        return attr.listMaxLength;
+    }
+
+    public int getAttributeListMinLength(String elementName, String attrName) {
+        Attlist attr = findAttribute(elementName, attrName);
+        if (attr.valueType != VALUE_LIST) {
+            throw new IllegalArgumentException("Attribute is not a list!");
+        }
+        return attr.listMinLength;
+    }
+
+    public String getAttributeMaxValue(String elementName, String attrName) {
+        Attlist attr = findAttribute(elementName, attrName);
+        if ((attr.valueType & VALUE_RANGE) == 0) {
+            throw new IllegalArgumentException("Attribute is not a range!");
+        }
+        return attr.maxValue;        
+    }
+
+    public String getAttributeMinValue(String elementName, String attrName) {
+        Attlist attr = findAttribute(elementName, attrName);
+        if ((attr.valueType & VALUE_RANGE) == 0) {
+            throw new IllegalArgumentException("Attribute is not a range!");
+        }
+        return attr.minValue;
+    }
+
+    public String[] getAttributeNames(String elementName) {
+        Element element = findElement(elementName);
+        return element.attributes.keySet().toArray(new String[element.attributes.size()]);
+    }
+
+    public int getAttributeValueType(String elementName, String attrName) {
+        Attlist attr = findAttribute(elementName, attrName);
+        return attr.valueType;                
+    }
+
+    public String[] getChildNames(String elementName) {
+        Element element = findElement(elementName);
+        if (element.childPolicy == CHILD_POLICY_EMPTY) { // Element cannot have children
+            return null;
+        }
+        return element.children.toArray(new String[element.children.size()]);
+    }
+
+    public int getChildPolicy(String elementName) {
+        Element element = findElement(elementName);
+        return element.childPolicy;
+    }
+
+    public String getElementDescription(String elementName, Locale locale) {
+        findElement(elementName); // Check if there is such element
+        return getResourceString(elementName, locale);
+    }
+
+    public int getElementMaxChildren(String elementName) {
+        Element element = findElement(elementName);
+        if (element.childPolicy != CHILD_POLICY_REPEAT) {
+            throw new IllegalArgumentException("Child policy is not CHILD_POLICY_REPEAT!");
+        }
+        return element.maxChildren;
+    }
+
+    public int getElementMinChildren(String elementName) {
+        Element element = findElement(elementName);
+        if (element.childPolicy != CHILD_POLICY_REPEAT) {
+            throw new IllegalArgumentException("Child policy is not CHILD_POLICY_REPEAT!");
+        }
+        return element.minChildren;
+    }
+
+    public int getObjectArrayMaxLength(String elementName) {
+        Element element = findElement(elementName);
+        ObjectValue v = element.objectValue;
+        if (v == null || v.valueType != VALUE_LIST) {
+            throw new IllegalArgumentException("Not a list!");
+        }
+        return v.arrayMaxLength;
+    }
+
+    public int getObjectArrayMinLength(String elementName) {
+        Element element = findElement(elementName);
+        ObjectValue v = element.objectValue;
+        if (v == null || v.valueType != VALUE_LIST) {
+            throw new IllegalArgumentException("Not a list!");
+        }
+        return v.arrayMinLength;
+    }
+
+    public Class<?> getObjectClass(String elementName) {
+        ObjectValue v = findObjectValue(elementName);
+        return v.classType;
+    }
+
+    public Object getObjectDefaultValue(String elementName) {
+        ObjectValue v = findObjectValue(elementName);
+        return v.defaultValue;
+    }
+
+    public Object[] getObjectEnumerations(String elementName) {
+        Element element = findElement(elementName);
+        ObjectValue v = element.objectValue;
+        if (v == null || v.valueType != VALUE_ENUMERATION) {
+            throw new IllegalArgumentException("Not an enumeration!");
+        }
+        return v.enumeratedValues.toArray();
+    }
+
+    public Comparable<?> getObjectMaxValue(String elementName) {
+        Element element = findElement(elementName);
+        ObjectValue v = element.objectValue;
+        if (v == null || (v.valueType & VALUE_RANGE) == 0) {
+            throw new IllegalArgumentException("Not a range!");
+        }
+        return v.maxValue;
+    }
+
+    public Comparable<?> getObjectMinValue(String elementName) {
+        Element element = findElement(elementName);
+        ObjectValue v = element.objectValue;
+        if (v == null || (v.valueType & VALUE_RANGE) == 0) {
+            throw new IllegalArgumentException("Not a range!");
+        }
+        return v.minValue;
+    }
+
+    public int getObjectValueType(String elementName) {
+        Element element = findElement(elementName);
+        if (element.objectValue == null) {
+            return VALUE_NONE;
+        }
+        return element.objectValue.valueType;
+    }
+
+    /**
+     * Gets the resource base name for locating ResourceBundles.
+     * 
+     * @return the current resource base name.
+     */
+    protected String getResourceBaseName() {
+        return resourceBaseName;
+    }
+
+    public String getRootName() {
+        return rootName;
+    }
+
+    /**
+     * Gets the standard format instance.
+     * 
+     * @return the IIOMetadataFormat instance.
+     */
+    public static IIOMetadataFormat getStandardFormatInstance() {
+        if (standardFormat == null) {
+            standardFormat = new IIOStandardMetadataFormat();
+        }
+
+        return standardFormat;
+    }
+
+    public boolean isAttributeRequired(String elementName, String attrName) {
+        return findAttribute(elementName, attrName).required;
+    }
+
+    /**
+     * Removes the specified attribute from the specified element. 
+     *  
+     * @param elementName the specified element name.
+     * @param attrName the specified attribute name.
+     */
+    protected void removeAttribute(String elementName, String attrName) {
+        Element element = findElement(elementName);
+        element.attributes.remove(attrName);
+    }
+
+    /**
+     * Removes the specified element from this format.
+     * 
+     * @param elementName the specified element name.
+     */
+    protected void removeElement(String elementName) {
+        Element element;
+        if ((element = elementHash.get(elementName)) != null) {
+            elementHash.remove(elementName);
+            for (Element e : elementHash.values()) {
+                e.children.remove(element.name);
+            }
+        }
+    }
+
+    /**
+     * Removes the object value from the specified element.
+     * 
+     * @param elementName the element name.
+     */
+    protected void removeObjectValue(String elementName) {
+        Element element = findElement(elementName);
+        element.objectValue = null;
+    }
+    
+    /**
+     * Sets a new base name for ResourceBundles containing 
+     * descriptions of elements and attributes for this format.
+     * 
+     * @param resourceBaseName the new resource base name.
+     */
+    protected void setResourceBaseName(String resourceBaseName) {
+        if (resourceBaseName == null) {
+            throw new IllegalArgumentException("resourceBaseName == null!");
+        }
+        this.resourceBaseName = resourceBaseName;
+    }
+
+    /**
+     * The Class Element.
+     */
+    @SuppressWarnings({"ClassWithoutConstructor"})
+    private class Element {
+        
+        /** The name. */
+        String name;
+
+        /** The children. */
+        ArrayList<String> children = new ArrayList<String>();
+        
+        /** The attributes. */
+        HashMap<String, Attlist> attributes = new HashMap<String, Attlist>();
+
+        /** The min children. */
+        int minChildren;
+        
+        /** The max children. */
+        int maxChildren;
+        
+        /** The child policy. */
+        int childPolicy;
+
+        /** The object value. */
+        ObjectValue objectValue;
+    }
+
+    /**
+     * The Class Attlist.
+     */
+    @SuppressWarnings({"ClassWithoutConstructor"})
+    private class Attlist {
+        
+        /** The name. */
+        String name;
+
+        /** The data type. */
+        int dataType;
+        
+        /** The required. */
+        boolean required;
+        
+        /** The list min length. */
+        int listMinLength;
+        
+        /** The list max length. */
+        int listMaxLength;
+        
+        /** The default value. */
+        String defaultValue;
+        
+        /** The enumerated values. */
+        List<String> enumeratedValues;
+        
+        /** The min value. */
+        String minValue;
+        
+        /** The max value. */
+        String maxValue;
+        
+        /** The min inclusive. */
+        boolean minInclusive;
+        
+        /** The max inclusive. */
+        boolean maxInclusive;
+
+        /** The value type. */
+        int valueType;
+    }
+
+    /**
+     * The Class ObjectValue.
+     */
+    @SuppressWarnings({"ClassWithoutConstructor"})
+    private class ObjectValue<T> {
+        
+        /** The class type. */
+        Class<T> classType;
+        
+        /** The array min length. */
+        int arrayMinLength;
+        
+        /** The array max length. */
+        int arrayMaxLength;
+        
+        /** The default value. */
+        T defaultValue;
+        
+        /** The enumerated values. */
+        List<? extends T> enumeratedValues;
+        
+        /** The min value. */
+        Comparable<? super T> minValue;
+        
+        /** The max value. */
+        Comparable<? super T> maxValue;
+        
+        /** The min inclusive. */
+        boolean minInclusive;
+        
+        /** The max inclusive. */
+        boolean maxInclusive;
+
+        /** The value type. */
+        int valueType;
+    }
+
+    /**
+     * Find element.
+     * 
+     * @param name the name
+     * 
+     * @return the element
+     */
+    private Element findElement(String name) {
+        Element element;
+        if ((element = elementHash.get(name)) == null) {
+            throw new IllegalArgumentException("element name is null or no such element: " + name);
+        }
+
+        return element;
+    }
+
+    /**
+     * Find attribute.
+     * 
+     * @param elementName the element name
+     * @param attributeName the attribute name
+     * 
+     * @return the attlist
+     */
+    private Attlist findAttribute(String elementName, String attributeName) {
+        Element element = findElement(elementName);
+        Attlist attribute;
+        if ((attribute = element.attributes.get(attributeName)) == null) {
+            throw new IllegalArgumentException("attribute name is null or no such attribute: " + attributeName);
+        }
+
+        return attribute;
+    }
+
+    /**
+     * Find object value.
+     * 
+     * @param elementName the element name
+     * 
+     * @return the object value
+     */
+    private ObjectValue findObjectValue(String elementName) {
+        Element element = findElement(elementName);
+        ObjectValue v = element.objectValue;
+        if (v == null) {
+            throw new IllegalArgumentException("No object within element");
+        }
+        return v;
+    }
+
+    /**
+     * Gets the resource string.
+     * 
+     * @param key the key
+     * @param locale the locale
+     * 
+     * @return the resource string
+     */
+    private String getResourceString(String key, Locale locale) {
+        if (locale == null) {
+            locale = Locale.getDefault();
+        }
+
+        // Get the context class loader and try to locate the bundle with it first
+        ClassLoader contextClassloader = AccessController.doPrivileged(
+                new PrivilegedAction<ClassLoader>() {
+                    public ClassLoader run() {
+                        return Thread.currentThread().getContextClassLoader();
+                    }
+        });
+
+        // Now try to get the resource bundle
+        ResourceBundle rb;
+        try {
+            rb = ResourceBundle.getBundle(resourceBaseName, locale, contextClassloader);
+        } catch (MissingResourceException e) {
+            try {
+                rb = ResourceBundle.getBundle(resourceBaseName, locale);
+            } catch (MissingResourceException e1) {
+                return null;
+            }
+        }
+
+        try {
+            return rb.getString(key);
+        } catch (MissingResourceException e) {
+            return null;
+        } catch (ClassCastException e) {
+            return null; // Not a string resource
+        }
+    }
+}
diff --git a/awt/javax/imageio/metadata/IIOMetadataNode.java b/awt/javax/imageio/metadata/IIOMetadataNode.java
new file mode 100644
index 0000000..d5ab7a5
--- /dev/null
+++ b/awt/javax/imageio/metadata/IIOMetadataNode.java
@@ -0,0 +1,675 @@
+/*
+ *  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 java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+//???AWT
+//import org.w3c.dom.TypeInfo;
+//import org.w3c.dom.UserDataHandler;
+
+/**
+ * The Class IIOMetadataNode represents a node of the 
+ * (DOM-style) metadata tree.
+ */
+public class IIOMetadataNode implements Element, NodeList {
+
+    /** The node name. */
+    private String nodeName;
+    
+    /** The node value. */
+    private String nodeValue;
+    
+    /** The attributes. */
+    private IIOMetadataNodeList attrs = new IIOMetadataNodeList(new ArrayList<IIOMetadataNode>());
+
+    /** The parent node. */
+    private IIOMetadataNode parent;
+    
+    /** The first child node. */
+    private IIOMetadataNode firstChild;
+    
+    /** The last child node. */
+    private IIOMetadataNode lastChild;
+    
+    /** The previous sibling. */
+    private IIOMetadataNode previousSibling;
+    
+    /** The next sibling. */
+    private IIOMetadataNode nextSibling;
+
+    /** The number of children. */
+    private int nChildren;
+
+    /** The user object associated with this node. */
+    private Object userObject;
+
+    /** The text content of this node. */
+    private String textContent;
+
+    /**
+     * Instantiates a new empty node.
+     */
+    public IIOMetadataNode() {
+    }
+
+    /**
+     * Instantiates a new empty node with the specified name.
+     * 
+     * @param nodeName the node name
+     */
+    public IIOMetadataNode(String nodeName) {
+        this.nodeName = nodeName;
+    }
+
+    /**
+     * Instantiates a new IIOMetadataNode with the specified 
+     * name and value.
+     * 
+     * @param nodeName the node name
+     * @param nodeValue the node value
+     */
+    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<IIOMetadataNode> nodes = new ArrayList<IIOMetadataNode>();
+
+        // 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);
+    }
+
+    //???AWT
+    /*
+    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");
+    }
+
+    //???AWT
+    /*
+    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;
+    }
+
+    /**
+     * Gets the user object associated with this node.
+     * 
+     * @return the user object associated with this node
+     */
+    public Object getUserObject() {
+        return userObject;
+    }
+
+    /**
+     * Sets the user object associated with this node.
+     * 
+     * @param userObject the new user object associated with this node
+     */
+    public void setUserObject(Object userObject) {
+        this.userObject = userObject;
+    }
+
+    /**
+     * The Class IIOMetadataAttr.
+     */
+    private class IIOMetadataAttr extends IIOMetadataNode implements Attr {
+        
+        /** The owner element. */
+        private Element ownerElement;
+
+        /**
+         * Instantiates a new iIO metadata attr.
+         * 
+         * @param name the name
+         * @param value the value
+         * @param owner the owner
+         */
+        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;
+        }
+
+        /**
+         * Sets the owner element.
+         * 
+         * @param ownerElement the new owner element
+         */
+        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;
+        }
+    }
+
+    /**
+     * The Class IIOMetadataNodeList.
+     */
+    private class IIOMetadataNodeList implements NodeList, NamedNodeMap {
+        
+        /** The list. */
+        private List<IIOMetadataNode> list;
+
+        /**
+         * Instantiates a new iIO metadata node list.
+         * 
+         * @param list the list
+         */
+        IIOMetadataNodeList(List<IIOMetadataNode> 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!");
+        }
+    }
+}
diff --git a/awt/javax/imageio/metadata/IIOStandardMetadataFormat.java b/awt/javax/imageio/metadata/IIOStandardMetadataFormat.java
new file mode 100644
index 0000000..94d2125
--- /dev/null
+++ b/awt/javax/imageio/metadata/IIOStandardMetadataFormat.java
@@ -0,0 +1,316 @@
+/*
+ *  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.ArrayList;
+
+/**
+ * The Class IIOStandardMetadataFormat describes the rules of the 
+ * standard metadata format.
+ */
+class IIOStandardMetadataFormat  extends IIOMetadataFormatImpl {
+    
+    /**
+     * Instantiates a new IIOStandardMetadataFormat.
+     */
+    public IIOStandardMetadataFormat() {
+        super(standardMetadataFormatName, CHILD_POLICY_SOME);
+        buildDTD();
+    }
+
+    @Override
+    public boolean canNodeAppear(String elementName, ImageTypeSpecifier imageType) {
+        return true;
+    }
+
+    /**
+     * Builds the dtd that describes the standard metadata format.
+     */
+    private void buildDTD() {
+        // CHROMA
+        addElement("Chroma", standardMetadataFormatName, CHILD_POLICY_SOME);
+
+        addElement("ColorSpaceType", "Chroma", CHILD_POLICY_EMPTY);
+
+        ArrayList<String> values = new ArrayList<String>(27);
+        values.add("XYZ");
+        values.add("Lab");
+        values.add("Luv");
+        values.add("YCbCr");
+        values.add("Yxy");
+        values.add("YCCK");
+        values.add("PhotoYCC");
+        values.add("RGB");
+        values.add("GRAY");
+        values.add("HSV");
+        values.add("HLS");
+        values.add("CMYK");
+        values.add("CMY");
+        values.add("2CLR");
+        values.add("3CLR");
+        values.add("4CLR");
+        values.add("5CLR");
+        values.add("6CLR");
+        values.add("7CLR");
+        values.add("8CLR");
+        values.add("9CLR");
+        values.add("ACLR");
+        values.add("BCLR");
+        values.add("CCLR");
+        values.add("DCLR");
+        values.add("ECLR");
+        values.add("FCLR");
+        addAttribute("ColorSpaceType", "name", DATATYPE_STRING, true, null, values);
+
+        addElement("NumChannels", "Chroma", CHILD_POLICY_EMPTY);
+        addAttribute("NumChannels", "value", DATATYPE_INTEGER, true, 0, Integer.MAX_VALUE); // list - why?
+
+        addElement("Gamma", "Chroma", CHILD_POLICY_EMPTY);
+        addAttribute("Gamma", "value", DATATYPE_FLOAT, true, null);
+
+        addElement("BlackIsZero", "Chroma", CHILD_POLICY_EMPTY);
+        addBooleanAttribute("BlackIsZero", "value", true, true);
+
+        addElement("Palette", "Chroma", 0, Integer.MAX_VALUE); // CHILD_POLICY_REPEAT
+        addElement("PaletteEntry", "Palette", CHILD_POLICY_EMPTY);
+        addAttribute("PaletteEntry", "index", DATATYPE_INTEGER, true, null);
+        addAttribute("PaletteEntry", "red", DATATYPE_INTEGER, true, null);
+        addAttribute("PaletteEntry", "green", DATATYPE_INTEGER, true, null);
+        addAttribute("PaletteEntry", "blue", DATATYPE_INTEGER, true, null);
+        addAttribute("PaletteEntry", "alpha", DATATYPE_INTEGER, false, "255");
+
+        addElement("BackgroundIndex", "Chroma", CHILD_POLICY_EMPTY);
+        addAttribute("BackgroundIndex", "value", DATATYPE_INTEGER, true, null);
+
+        addElement("BackgroundColor", "Chroma", CHILD_POLICY_EMPTY);
+        addAttribute("BackgroundColor", "red", DATATYPE_INTEGER, true, null);
+        addAttribute("BackgroundColor", "green", DATATYPE_INTEGER, true, null);
+        addAttribute("BackgroundColor", "blue", DATATYPE_INTEGER, true, null);
+
+        // COMPRESSION
+        addElement("Compression", standardMetadataFormatName, CHILD_POLICY_SOME);
+
+        addElement("CompressionTypeName", "Compression", CHILD_POLICY_EMPTY);
+        addAttribute("CompressionTypeName", "value", DATATYPE_STRING, true, null);
+
+        addElement("Lossless", "Compression", CHILD_POLICY_EMPTY);
+        addBooleanAttribute("Lossless", "value", true, true);
+
+        addElement("NumProgressiveScans", "Compression", CHILD_POLICY_EMPTY);
+        addAttribute("NumProgressiveScans", "value", DATATYPE_INTEGER, true, null);
+
+        addElement("BitRate", "Compression", CHILD_POLICY_EMPTY);
+        addAttribute("BitRate", "value", DATATYPE_FLOAT, true, null);
+
+        // DATA
+        addElement("Data", standardMetadataFormatName, CHILD_POLICY_SOME);
+
+        addElement("PlanarConfiguration", "Data", CHILD_POLICY_EMPTY);
+        values = new ArrayList<String>(4);
+        values.add("PixelInterleaved");
+        values.add("PlaneInterleaved");
+        values.add("LineInterleaved");
+        values.add("TileInterleaved");
+        addAttribute("PlanarConfiguration", "value", DATATYPE_STRING, true, null, values);
+
+        addElement("SampleFormat", "Data", CHILD_POLICY_EMPTY);
+        values = new ArrayList<String>(4);
+        values.add("SignedIntegral");
+        values.add("UnsignedIntegral");
+        values.add("Real");
+        values.add("Index");
+        addAttribute("SampleFormat", "value", DATATYPE_STRING, true, null, values);
+
+        addElement("BitsPerSample", "Data", CHILD_POLICY_EMPTY);
+        addAttribute("BitsPerSample", "value", DATATYPE_INTEGER, true, 1, Integer.MAX_VALUE); // list
+
+        addElement("SignificantBitsPerSample", "Data", CHILD_POLICY_EMPTY);
+        addAttribute(
+                "SignificantBitsPerSample", "value",
+                DATATYPE_INTEGER, true, 1, Integer.MAX_VALUE
+        ); // list
+
+        addElement("SampleMSB", "Data", CHILD_POLICY_EMPTY);
+        addAttribute("SampleMSB", "value", DATATYPE_INTEGER, true, 1, Integer.MAX_VALUE); // list
+
+        // DIMENSION
+        addElement("Dimension", standardMetadataFormatName, CHILD_POLICY_SOME);
+
+        addElement("PixelAspectRatio", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("PixelAspectRatio", "value", DATATYPE_FLOAT, true, null);
+
+        addElement("ImageOrientation", "Dimension", CHILD_POLICY_EMPTY);
+        values = new ArrayList<String>(8);
+        values.add("Normal");
+        values.add("Rotate90");
+        values.add("Rotate180");
+        values.add("Rotate270");
+        values.add("FlipH");
+        values.add("FlipV");
+        values.add("FlipHRotate90");
+        values.add("FlipVRotate90");
+        addAttribute("ImageOrientation", "value", DATATYPE_STRING, true, null, values);
+
+        addElement("HorizontalPixelSize", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("HorizontalPixelSize", "value", DATATYPE_FLOAT, true, null);
+
+        addElement("VerticalPixelSize", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("VerticalPixelSize", "value", DATATYPE_FLOAT, true, null);
+
+        addElement("HorizontalPhysicalPixelSpacing", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("HorizontalPhysicalPixelSpacing", "value", DATATYPE_FLOAT, true, null);
+
+        addElement("VerticalPhysicalPixelSpacing", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("VerticalPhysicalPixelSpacing", "value", DATATYPE_FLOAT, true, null);
+
+        addElement("HorizontalPosition", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("HorizontalPosition", "value", DATATYPE_FLOAT, true, null);
+
+        addElement("VerticalPosition", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("VerticalPosition", "value", DATATYPE_FLOAT, true, null);
+
+        addElement("HorizontalPixelOffset", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("HorizontalPixelOffset", "value", DATATYPE_INTEGER, true, null);
+
+        addElement("VerticalPixelOffset", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("VerticalPixelOffset", "value", DATATYPE_INTEGER, true, null);
+
+        addElement("HorizontalScreenSize", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("HorizontalScreenSize", "value", DATATYPE_INTEGER, true, null);
+
+        addElement("VerticalScreenSize", "Dimension", CHILD_POLICY_EMPTY);
+        addAttribute("VerticalScreenSize", "value", DATATYPE_INTEGER, true, null);
+
+        // DOCUMENT
+        addElement("Document", standardMetadataFormatName, CHILD_POLICY_SOME);
+
+        addElement("FormatVersion", "Document", CHILD_POLICY_EMPTY);
+        addAttribute("FormatVersion", "value", DATATYPE_STRING, true, null);
+
+        addElement("SubimageInterpretation", "Document", CHILD_POLICY_EMPTY);
+        values = new ArrayList<String>(14);
+        values.add("Standalone");
+        values.add("SinglePage");
+        values.add("FullResolution");
+        values.add("ReducedResolution");
+        values.add("PyramidLayer");
+        values.add("Preview");
+        values.add("VolumeSlice");
+        values.add("ObjectView");
+        values.add("Panorama");
+        values.add("AnimationFrame");
+        values.add("TransparencyMask");
+        values.add("CompositingLayer");
+        values.add("SpectralSlice");
+        values.add("Unknown");
+        addAttribute("SubimageInterpretation", "value", DATATYPE_STRING, true, null, values);
+
+        addElement("ImageCreationTime", "Document", CHILD_POLICY_EMPTY);
+        addAttribute("ImageCreationTime", "year", DATATYPE_INTEGER, true, null);
+        addAttribute(
+                "ImageCreationTime", "month",
+                DATATYPE_INTEGER, true, null, "1", "12", true, true
+        );
+        addAttribute(
+                "ImageCreationTime", "day",
+                DATATYPE_INTEGER, true, null, "1", "31", true, true
+        );
+        addAttribute(
+                "ImageCreationTime", "hour",
+                DATATYPE_INTEGER, false, "0", "0", "23", true, true
+        );
+        addAttribute(
+                "ImageCreationTime", "minute",
+                DATATYPE_INTEGER, false, "0", "0", "59", true, true
+        );
+        addAttribute(
+                "ImageCreationTime", "second",
+                DATATYPE_INTEGER, false, "0", "0", "60", true, true
+        );
+
+        addElement("ImageModificationTime", "Document", CHILD_POLICY_EMPTY);
+        addAttribute("ImageModificationTime", "year", DATATYPE_INTEGER, true, null);
+        addAttribute(
+                "ImageModificationTime", "month",
+                DATATYPE_INTEGER, true, null, "1", "12", true, true
+        );
+        addAttribute(
+                "ImageModificationTime", "day",
+                DATATYPE_INTEGER, true, null, "1", "31", true, true
+        );
+        addAttribute(
+                "ImageModificationTime", "hour",
+                DATATYPE_INTEGER, false, "0", "0", "23", true, true
+        );
+        addAttribute(
+                "ImageModificationTime", "minute",
+                DATATYPE_INTEGER, false, "0", "0", "59", true, true
+        );
+        addAttribute(
+                "ImageModificationTime", "second",
+                DATATYPE_INTEGER, false, "0", "0", "60", true, true
+        );
+
+        // TEXT
+        addElement("Text", standardMetadataFormatName, 0, Integer.MAX_VALUE); // CHILD_POLICY_REPEAT
+
+        addElement("TextEntry", "Text", CHILD_POLICY_EMPTY);
+        addAttribute("TextEntry", "keyword", DATATYPE_STRING, false, null);
+        addAttribute("TextEntry", "value", DATATYPE_STRING, true, null);
+        addAttribute("TextEntry", "language", DATATYPE_STRING, false, null);
+        addAttribute("TextEntry", "encoding", DATATYPE_STRING, false, null);
+        values = new ArrayList<String>(5);
+        values.add("none");
+        values.add("lzw");
+        values.add("zip");
+        values.add("bzip");
+        values.add("other");
+        addAttribute("TextEntry", "compression", DATATYPE_STRING, false, "none", values);
+
+        // TRANSPARENCY
+        addElement("Transparency", standardMetadataFormatName, CHILD_POLICY_SOME);
+
+        addElement("Alpha", "Transparency", CHILD_POLICY_EMPTY);
+        values = new ArrayList<String>(3);
+        values.add("none");
+        values.add("premultiplied");
+        values.add("nonpremultiplied");
+        addAttribute("Alpha", "value", DATATYPE_STRING, false, "none", values);
+
+        addElement("TransparentIndex", "Transparency", CHILD_POLICY_EMPTY);
+        addAttribute("TransparentIndex", "value", DATATYPE_INTEGER, true, null);
+
+        addElement("TransparentColor", "Transparency", CHILD_POLICY_EMPTY);
+        addAttribute("TransparentColor", "value", DATATYPE_INTEGER, true, 0, Integer.MAX_VALUE);
+
+        addElement("TileTransparencies", "Transparency", 0, Integer.MAX_VALUE); // CHILD_POLICY_REPEAT
+
+        addElement("TransparentTile", "TileTransparencies", CHILD_POLICY_EMPTY);
+        addAttribute("TransparentTile", "x", DATATYPE_INTEGER, true, null);
+        addAttribute("TransparentTile", "y", DATATYPE_INTEGER, true, null);
+
+        addElement("TileOpacities", "Transparency", 0, Integer.MAX_VALUE); // CHILD_POLICY_REPEAT
+
+        addElement("OpaqueTile", "TileOpacities", CHILD_POLICY_EMPTY);
+        addAttribute("OpaqueTile", "x", DATATYPE_INTEGER, true, null);
+        addAttribute("OpaqueTile", "y", DATATYPE_INTEGER, true, null);
+    }
+}
+
diff --git a/awt/javax/imageio/metadata/IIOStandardMetadataFormatResources.properties b/awt/javax/imageio/metadata/IIOStandardMetadataFormatResources.properties
new file mode 100644
index 0000000..d185808
--- /dev/null
+++ b/awt/javax/imageio/metadata/IIOStandardMetadataFormatResources.properties
@@ -0,0 +1,133 @@
+# 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.
+
+# Descriptions of elements and attributes of the plugin neutral metadata format
+# (see IIOStandardMetadataFormat)
+
+# Messages for EN locale
+Chroma=Chroma (color) information
+ColorSpaceType=The raw color space of the image
+ColorSpaceType/name=The raw color space of the image
+NumChannels=The number of channels in the raw image, including alpha
+NumChannels/value=The number of channels in the raw image, including alpha
+Gamma=The image gamma
+Gamma/value=The image gamma
+BlackIsZero=True if smaller values represent darker shades
+BlackIsZero/value=True if smaller values represent darker shades
+Palette=Palette-color information
+PaletteEntry=A palette entry
+PaletteEntry/index=The index of the palette entry
+PaletteEntry/red=The red value for the palette entry
+PaletteEntry/green=The green value for the palette entry
+PaletteEntry/blue=The blue value for the palette entry
+PaletteEntry/alpha=The alpha value for the palette entry
+BackgroundIndex=A palette index to be used as a background
+BackgroundIndex/value=A palette index to be used as a background
+BackgroundColor=An RGB triple to be used as a background
+BackgroundColor/red=The red background value
+BackgroundColor/green=The green background value
+BackgroundColor/blue=The blue background value
+
+Compression=Compression information
+CompressionTypeName=The name of the compression scheme in use
+CompressionTypeName/value=The name of the compression scheme in use
+Lossless=True if the compression scheme is lossless
+Lossless/value=True if the compression scheme is lossless
+NumProgressiveScans=The number of progressive scans used in the image encoding
+NumProgressiveScans/value=The number of progressive scans used in the image encoding
+BitRate=The estimated bit rate of the compression scheme
+BitRate/value=The estimated bit rate of the compression scheme
+
+Data=Information on the image layout
+PlanarConfiguration=The organization of image samples in the stream
+PlanarConfiguration/value=The organization of image samples in the stream
+SampleFormat=The numeric format of image samples
+SampleFormat/value=The numeric format of image samples
+BitsPerSample=The number of bits per sample
+BitsPerSample/value=A list of integers, one per channel
+SignificantBitsPerSample=The number of significant bits per sample
+SignificantBitsPerSample/value=A list of integers, one per channel
+SampleMSB=The position of the most significant bit of each sample
+SampleMSB/value=A list of integers, one per channel
+
+Dimension=Dimension information
+PixelAspectRatio=The width of a pixel divided by its height
+PixelAspectRatio/value=The width of a pixel divided by its height
+ImageOrientation=The desired orientation of the image in terms of flips and counter-clockwise rotations
+ImageOrientation/value=The desired orientation of the image in terms of flips and counter-clockwise rotations
+HorizontalPixelSize=The width of a pixel, in millimeters, as it should be rendered on media
+HorizontalPixelSize/value=The width of a pixel, in millimeters, as it should be rendered on media
+VerticalPixelSize=The height of a pixel, in millimeters, as it should be rendered on media
+VerticalPixelSize/value=The height of a pixel, in millimeters, as it should be rendered on media
+HorizontalPhysicalPixelSpacing=The horizontal distance in the subject of the image, in millimeters, represented by one pixel at the center of the image
+HorizontalPhysicalPixelSpacing/value=The horizontal distance in the subject of the image, in millimeters, represented by one pixel at the center of the image
+VerticalPhysicalPixelSpacing=The vertical distance in the subject of the image, in millimeters, represented by one pixel at the center of the image
+VerticalPhysicalPixelSpacing/value=The vertical distance in the subject of the image, in millimeters, represented by one pixel at the center of the image
+HorizontalPosition=The horizontal position, in millimeters, where the image should be rendered on media
+HorizontalPosition/value=The horizontal position, in millimeters, where the image should be rendered on media
+VerticalPosition=The vertical position, in millimeters, where the image should be rendered on media
+VerticalPosition/value=The vertical position, in millimeters, where the image should be rendered on media
+HorizontalPixelOffset=The horizonal position, in pixels, where the image should be rendered onto a raster display
+HorizontalPixelOffset/value=The horizonal position, in pixels, where the image should be rendered onto a raster display
+VerticalPixelOffset=The vertical position, in pixels, where the image should be rendered onto a raster display
+VerticalPixelOffset/value=The vertical position, in pixels, where the image should be rendered onto a raster display
+HorizontalScreenSize=The width, in pixels, of the raster display into which the image should be rendered
+HorizontalScreenSize/value=The width, in pixels, of the raster display into which the image should be rendered
+VerticalScreenSize=The height, in pixels, of the raster display into which the image should be rendered
+VerticalScreenSize/value=The height, in pixels, of the raster display into which the image should be rendered
+
+Document=Document information
+FormatVersion=The version of the format used by the stream
+FormatVersion/value=The version of the format used by the stream
+SubimageInterpretation=The interpretation of this image in relation to the other images stored in the same stream
+SubimageInterpretation/value=The interpretation of this image in relation to the other images stored in the same stream
+ImageCreationTime=The time of image creation
+ImageCreationTime/year=The full year (e.g., 1967, not 67)
+ImageCreationTime/month=The month, with January = 1
+ImageCreationTime/day=The day of the month
+ImageCreationTime/hour=The hour from 0 to 23
+ImageCreationTime/minute=The minute from 0 to 59
+ImageCreationTime/second=The second from 0 to 60 (60 = leap second)
+ImageModificationTime=The time of the last image modification
+ImageModificationTime/year=The full year (e.g., 1967, not 67)
+ImageModificationTime/month=The month, with January = 1
+ImageModificationTime/day=The day of the month
+ImageModificationTime/hour=The hour from 0 to 23
+ImageModificationTime/minute=The minute from 0 to 59
+ImageModificationTime/second=The second from 0 to 60 (60 = leap second)
+
+Text=Text information
+TextEntry=A text entry
+TextEntry/keyword=A keyword associated with the text entry
+TextEntry/value=the text entry
+TextEntry/language=The language of the text
+TextEntry/encoding=The encoding of the text
+TextEntry/compression=The method used to compress the text
+
+Transparency=Transparency information
+Alpha=The type of alpha information contained in the image
+Alpha/value=The type of alpha information contained in the image
+TransparentIndex=A palette index to be treated as transparent
+TransparentIndex/value=A palette index to be treated as transparent
+TransparentColor=An RGB color to be treated as transparent
+TransparentColor/value=An RGB color to be treated as transparent
+TileTransparencies=A list of completely transparent tiles
+TransparentTile=The index of a completely transparent tile
+TransparentTile/x=The tile's X index
+TransparentTile/y=The tile's Y index
+TileOpacities=A list of completely opaque tiles
+OpaqueTile=The index of a completely opaque tile
+OpaqueTile/x=The tile's X index
+OpaqueTile/y=The tile's Y index