Initial Contribution
diff --git a/awt/javax/imageio/IIOException.java b/awt/javax/imageio/IIOException.java
new file mode 100644
index 0000000..fbfeb42
--- /dev/null
+++ b/awt/javax/imageio/IIOException.java
@@ -0,0 +1,52 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import java.io.IOException;
+
+/**
+ * The IIOException class indicates errors in reading/writing operations.
+ */
+public class IIOException extends IOException {
+
+    /** The Constant serialVersionUID. */
+    private static final long serialVersionUID = -3216210718638985251L;
+
+    /**
+     * Instantiates a new IIOException.
+     * 
+     * @param message the detailed message.
+     */
+    public IIOException(String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new IIOException.
+     * 
+     * @param message the detailed message.
+     * @param cause the cause of this exception.
+     */
+    public IIOException(String message, Throwable cause) {
+        super(message);
+        initCause(cause);
+    }
+}
diff --git a/awt/javax/imageio/IIOImage.java b/awt/javax/imageio/IIOImage.java
new file mode 100644
index 0000000..e17a9fc
--- /dev/null
+++ b/awt/javax/imageio/IIOImage.java
@@ -0,0 +1,203 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import javax.imageio.metadata.IIOMetadata;
+import java.awt.image.RenderedImage;
+import java.awt.image.Raster;
+import java.awt.image.BufferedImage;
+import java.util.List;
+
+/**
+ * The IIOImage class combines the image, image's thumbnail and image's metadata.
+ * The image can be presented as RenderedImage or Raster object.
+ */
+public class IIOImage {
+
+    /** The image of this IIOImage. */
+    protected RenderedImage image;
+    
+    /** The raster of this IIOImage. */
+    protected Raster raster;
+    
+    /** The list with thumbnails associated with the image. */
+    protected List<? extends BufferedImage> thumbnails;
+    
+    /** The metadata associated with the image. */
+    protected IIOMetadata metadata;
+
+    /**
+     * Instantiates a new IIOImage with the specified RenderedImage, 
+     * list of thumbnails and metadata.
+     * 
+     * @param image the image specified by RenderedImage.
+     * @param thumbnails the list of BufferedImage objects which 
+     * represent the thumbnails of the image.
+     * @param metadata the metadata of the image.
+     */
+    public IIOImage(RenderedImage image, List<? extends BufferedImage> thumbnails, IIOMetadata metadata) {
+        if (image == null) {
+            throw new IllegalArgumentException("image should not be NULL");
+        }
+        this.raster = null;
+        this.image = image;
+        this.thumbnails = thumbnails;
+        this.metadata = metadata;
+    }
+
+    /**
+     * Instantiates a new IIOImage with the specified Raster, list of
+     * thumbnails and metadata.
+     * 
+     * @param raster the Raster.
+     * @param thumbnails the list of BufferedImage objects which 
+     * represent the thumbnails of Raster data.
+     * @param metadata the metadata.
+     */
+    public IIOImage(Raster raster, List<? extends BufferedImage> thumbnails, IIOMetadata metadata) {
+        if (raster == null) {
+            throw new IllegalArgumentException("raster should not be NULL");
+        }
+        this.image = null;
+        this.raster = raster;
+        this.thumbnails = thumbnails;
+        this.metadata = metadata;
+    }
+
+    /**
+     * Gets the RenderedImage object or returns null if this IIOImage 
+     * object is associated with a Raster.
+     * 
+     * @return the RenderedImage object or null if this IIOImage 
+     * object is associated with a Raster.
+     */
+    public RenderedImage getRenderedImage() {
+        return image;
+    }
+
+    /**
+     * Sets the RenderedImage to this IIOImage object.
+     * 
+     * @param image the RenderedImage to be set to this IIOImage.
+     */
+    public void setRenderedImage(RenderedImage image) {
+        if (image == null) {
+            throw new IllegalArgumentException("image should not be NULL");
+        }
+        raster = null;
+        this.image = image;
+    }
+
+    /**
+     * Returns true if the IIOImage object associated with a Raster, or 
+     * false if it's associated with a RenderedImage.
+     * 
+     * @return true if the IIOImage object associated with a Raster, or 
+     * false if it's associated with a RenderedImage.
+     */
+    public boolean hasRaster() {
+        return raster != null;
+    }
+
+    /**
+     * Gets the Raster object or returns null if this IIOImage object is
+     * associated with a RenderedImage.
+     * 
+     * @return the Raster or null if this IIOImage object
+     * is associated with a RenderedImage.
+     */
+    public Raster getRaster() {
+        return raster;
+    }
+
+    /**
+     * Sets the Raster to the IIOImage.
+     * 
+     * @param raster the new Raster to the IIOImage.
+     */
+    public void setRaster(Raster raster) {
+        if (raster == null) {
+            throw new IllegalArgumentException("raster should not be NULL");
+        }
+        image = null;
+        this.raster = raster;
+    }
+
+    /**
+     * Gets the number of thumbnails for this IIOImage.
+     * 
+     * @return the number of thumbnails for this IIOImage.
+     */
+    public int getNumThumbnails() {
+        return thumbnails != null ? thumbnails.size() : 0;
+    }
+
+    /**
+     * Gets the thumbnail with the specified index in the list.
+     * 
+     * @param index the index of the thumbnail in the list.
+     * 
+     * @return the thumbnail with the specified index in the list.
+     */
+    public BufferedImage getThumbnail(int index) {
+        if (thumbnails != null) {
+            return thumbnails.get(index);
+        }
+        throw new IndexOutOfBoundsException("no thumbnails were set");
+    }
+
+    /**
+     * Gets the list of thumbnails.
+     * 
+     * @return the list of thumbnails.
+     */
+    public List<? extends BufferedImage> getThumbnails() {
+        return thumbnails;
+    }
+
+    /**
+     * Sets the list of thumbnails images to this IIOImage object.
+     * 
+     * @param thumbnails the list of BufferedImage which represent
+     * thumbnails.
+     */
+    public void setThumbnails(List<? extends BufferedImage> thumbnails) {
+        this.thumbnails = thumbnails;
+    }
+
+    /**
+     * Gets the metadata of this IIOImage.
+     * 
+     * @return the metadata of this IIOImage.
+     */
+    public IIOMetadata getMetadata() {
+        return metadata;
+    }
+
+    /**
+     * Sets the metadata to this IIOImage object.
+     * 
+     * @param metadata the IIOMetadata, or null.
+     */
+    public void setMetadata(IIOMetadata metadata) {
+        this.metadata = metadata;
+    }
+}
diff --git a/awt/javax/imageio/IIOParam.java b/awt/javax/imageio/IIOParam.java
new file mode 100644
index 0000000..d998b6e
--- /dev/null
+++ b/awt/javax/imageio/IIOParam.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.
+ */
+/**
+ * @author Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import java.awt.*;
+
+/**
+ * The IIOParam abstract class is superclass for     
+ * ImageReadParam and ImageWriteParam classes and provides 
+ * methods and variables which they share.
+ */
+public abstract class IIOParam {
+    
+    /** The source region. */
+    protected Rectangle sourceRegion;
+    
+    /** The source x subsampling. */
+    protected int sourceXSubsampling = 1;
+    
+    /** The source y subsampling. */
+    protected int sourceYSubsampling = 1;
+    
+    /** The subsampling x offset. */
+    protected int subsamplingXOffset;
+    
+    /** The subsampling y offset. */
+    protected int subsamplingYOffset;
+    
+    /** The source bands. */
+    protected int[] sourceBands;
+    
+    /** The destination type. */
+    protected ImageTypeSpecifier destinationType;
+    
+    /** The destination offset. */
+    protected Point destinationOffset = new Point(0, 0);
+    
+    /** The default controller. */
+    protected IIOParamController defaultController;
+    
+    /** The controller. */
+    protected IIOParamController controller;
+
+    /**
+     * Instantiates a new IIOParam.
+     */
+    protected IIOParam() {}
+
+    /**
+     * Sets the source region as a Rectangle object.
+     * 
+     * @param sourceRegion the Rectangle which specifies the source region.
+     */
+    public void setSourceRegion(Rectangle sourceRegion) {
+        if (sourceRegion != null) {
+            if (sourceRegion.x < 0) {
+                throw new IllegalArgumentException("x < 0");
+            }
+            if (sourceRegion.y < 0) {
+                throw new IllegalArgumentException("y < 0");
+            }
+            if (sourceRegion.width <= 0) {
+                throw new IllegalArgumentException("width <= 0");
+            }
+            if (sourceRegion.height <= 0) {
+                throw new IllegalArgumentException("height <= 0");
+            }
+
+            if (sourceRegion.width <= subsamplingXOffset) {
+                throw new IllegalArgumentException("width <= subsamplingXOffset");
+            }
+
+            if (sourceRegion.height <= subsamplingYOffset) {
+                throw new IllegalArgumentException("height <= subsamplingXOffset");
+            }
+            //-- clone it to avoid unexpected modifications
+            this.sourceRegion = (Rectangle) sourceRegion.clone();
+        } else {
+            this.sourceRegion = null;
+        }
+    }
+
+    /**
+     * Gets the source region.
+     * 
+     * @return the source region as Rectangle.
+     */
+    public Rectangle getSourceRegion() {
+        if (sourceRegion == null) {
+            return null;
+        }
+        //-- clone it to avoid unexpected modifications
+        return (Rectangle) sourceRegion.clone();
+    }
+
+    /**
+     * Sets the source subsampling. The sourceXSubsampling and 
+     * sourceYSubsampling parameters specify the number of rows 
+     * and columns to advance after every source pixel.
+     * 
+     * @param sourceXSubsampling the source X subsampling.
+     * @param sourceYSubsampling the source Y subsampling.
+     * @param subsamplingXOffset the subsampling X offset.
+     * @param subsamplingYOffset the subsampling Y offset.
+     */
+    public void setSourceSubsampling(int sourceXSubsampling,
+                                 int sourceYSubsampling,
+                                 int subsamplingXOffset,
+                                 int subsamplingYOffset) {
+
+        if (sourceXSubsampling <= 0) {
+            throw new IllegalArgumentException("sourceXSubsampling <= 0");
+        }
+        if (sourceYSubsampling <= 0) {
+            throw new IllegalArgumentException("sourceYSubsampling <= 0");
+        }
+
+        if (subsamplingXOffset <= 0 || subsamplingXOffset >= sourceXSubsampling) {
+            throw new IllegalArgumentException("subsamplingXOffset is wrong");
+        }
+
+        if (subsamplingYOffset <= 0 || subsamplingYOffset >= sourceYSubsampling) {
+            throw new IllegalArgumentException("subsamplingYOffset is wrong");
+        }
+
+        //-- does region contain pixels
+        if (sourceRegion != null) {
+            if (sourceRegion.width <= subsamplingXOffset ||
+                    sourceRegion.height <= subsamplingYOffset) {
+                throw new IllegalArgumentException("there are no pixels in region");
+            }
+        }
+
+        this.sourceXSubsampling = sourceXSubsampling;
+        this.sourceYSubsampling = sourceYSubsampling;
+        this.subsamplingXOffset = subsamplingXOffset;
+        this.subsamplingYOffset = subsamplingYOffset;
+    }
+
+    /**
+     * Gets the source X subsampling - the number of source 
+     * columns to advance for each pixel.
+     * 
+     * @return the source X subsampling.
+     */
+    public int getSourceXSubsampling() {
+        return sourceXSubsampling;
+    }
+
+    /**
+     * Gets the source Y subsampling - the number of source 
+     * rows to advance for each pixel.
+     * 
+     * @return the source Y subsampling.
+     */
+    public int getSourceYSubsampling() {
+        return sourceYSubsampling;
+    }
+
+    /**
+     * Gets the horizontal offset of the subsampling grid.
+     * 
+     * @return the horizontal offset of the subsampling grid.
+     */
+    public int getSubsamplingXOffset() {
+        return subsamplingXOffset;
+    }
+
+    /**
+     * Gets the vertical offset of the subsampling grid.
+     * 
+     * @return the vertical offset of the subsampling grid.
+     */
+    public int getSubsamplingYOffset() {
+        return subsamplingYOffset;
+    }
+
+    /**
+     * Sets the indices of the source bands.
+     * 
+     * @param sourceBands the indices of the source bands.
+     */
+    public void setSourceBands(int[] sourceBands) {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    /**
+     * Gets the array of source bands.
+     * 
+     * @return the array of source bands.
+     */
+    public int[] getSourceBands() {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    /**
+     * Sets the specified ImageTypeSpecifier for the destination image.
+     * 
+     * @param destinationType the ImageTypeSpecifier.
+     */
+    public void setDestinationType(ImageTypeSpecifier destinationType) {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    /**
+     * Gets the type of the destination image as an ImageTypeSpecifier. .
+     *  
+     * @return the ImageTypeSpecifier.
+     */
+    public ImageTypeSpecifier getDestinationType() {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    /**
+     * Sets the offset in the destination image where 
+     * the decoded pixels are placed as a result of reading, 
+     * or specified an area to be written while writing operation.
+     * 
+     * @param destinationOffset the destination offset.
+     */
+    public void setDestinationOffset(Point destinationOffset) {
+        if (destinationOffset == null) {
+            throw new IllegalArgumentException("destinationOffset == null!");
+        }
+        
+        this.destinationOffset = (Point) destinationOffset.clone();
+    }
+
+    /**
+     * Gets the offset in the destination image for placing pixels.
+     * 
+     * @return the offset in the destination image.
+     */
+    public Point getDestinationOffset() {
+        return (Point) destinationOffset.clone();        
+    }
+
+    /**
+     * Sets the IIOParamController to this IIOParam object for
+     * providing settings to this IIOParam.
+     * 
+     * @param controller the new IIOParamController.
+     */
+    public void setController(IIOParamController controller) {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    /**
+     * Gets the current IIOParamController controller 
+     * for this IIOParam.
+     * 
+     * @return the current IIOParamController controller 
+     * for this IIOParam.
+     */
+    public IIOParamController getController() {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    /**
+     * Gets the default IIOParamController controller 
+     * for this IIOParam.
+     * 
+     * @return the default IIOParamController controller 
+     * for this IIOParam, or null.
+     */
+    public IIOParamController getDefaultController() {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    /**
+     * Returns true if IIOParamController is installed for 
+     * this IIOParam. 
+     * 
+     * @return true if IIOParamController is installed for 
+     * this IIOParam, false otherwise.
+     */
+    public boolean hasController() {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+
+    /**
+     * Activates the controller.
+     * 
+     * @return true, if successful, false otherwise.
+     */
+    public boolean activateController() {
+        // TODO implement
+        throw new UnsupportedOperationException("not implemented yet");
+    }
+}
diff --git a/awt/javax/imageio/IIOParamController.java b/awt/javax/imageio/IIOParamController.java
new file mode 100644
index 0000000..31522c1
--- /dev/null
+++ b/awt/javax/imageio/IIOParamController.java
@@ -0,0 +1,43 @@
+/*
+ *  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;
+
+/* 
+ * @author Sergey I. Salishev
+ * @version $Revision: 1.2 $
+ */
+
+/**
+ * The IIOParamController specifies an activate method that invokes the 
+ * controller.
+ */
+public interface IIOParamController {
+
+    /**
+     * Activates the controller. 
+     * 
+     * @param param the IIOParam.
+     * 
+     * @return true if the IIOParam has been modified, false otherwise.
+     */
+    boolean activate(IIOParam param);
+}
+
diff --git a/awt/javax/imageio/ImageIO.java b/awt/javax/imageio/ImageIO.java
new file mode 100644
index 0000000..d4cd1dd
--- /dev/null
+++ b/awt/javax/imageio/ImageIO.java
@@ -0,0 +1,777 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
+import javax.imageio.spi.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.Arrays;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.net.URL;
+
+/**
+ * The ImageIO class provides static methods to perfom 
+ * reading and writing operations using registered
+ * ImageReader and ImageWriter objects.
+ */
+public final class ImageIO {
+
+    /** The Constant registry. */
+    private static final IIORegistry registry = IIORegistry.getDefaultInstance();
+
+    /**
+     * Instantiates a new image io.
+     */
+    private ImageIO() {}
+    
+
+    /**
+     * Scans for plug-ins in the class path, 
+     * loads spi classes, and registers them with the IIORegistry.
+     */
+    public static void scanForPlugins() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Sets flag which indicates whether a cache file is used when 
+     * creating ImageInputStreams and ImageOutputStreams or not.
+     * 
+     * @param useCache the use cache flag.
+     */
+    public static void setUseCache(boolean useCache) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets the flag which indicates whether a cache file is used when 
+     * creating ImageInputStreams and ImageOutputStreams or not.
+     * This method returns the current value which is set by setUseCache
+     * method.
+     * 
+     * @return the use cache flag.
+     */
+    public static boolean getUseCache() {
+        // TODO implement
+        return false;
+    }
+
+    /**
+     * Sets the cache directory.
+     * 
+     * @param cacheDirectory the File which specifies a cache directory.
+     */
+    public static void setCacheDirectory(File cacheDirectory) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets the directory where cache files are created, returned
+     * the file which is set by setCacheDirectory method, or null.
+     * 
+     * @return the File object which is set by setCacheDirectory method, 
+     * or null.
+     */
+    public static File getCacheDirectory() {
+        // TODO implement
+        //-- null indicates system-dep default temporary directory
+        return null;
+    }
+
+    /**
+     * Creates an ImageInputStream from the specified Object.
+     * The specified Object should obtain the input source
+     * such as File, or InputStream.   
+     * 
+     * @param input the input Object such as File, or InputStream.   
+     * 
+     * @return the ImageInputStream object, or null.
+     * 
+     * @throws IOException signals that an I/O exception has occurred.
+     */
+    public static ImageInputStream createImageInputStream(Object input)
+            throws IOException {
+
+        if (input == null) {
+            throw new IllegalArgumentException("input source cannot be NULL");
+        }
+
+        Iterator<ImageInputStreamSpi> it = registry.getServiceProviders(ImageInputStreamSpi.class, true);
+
+        while (it.hasNext()) {
+            ImageInputStreamSpi spi = it.next();
+            if (spi.getInputClass().isInstance(input)) {
+                return spi.createInputStreamInstance(input);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Creates an ImageOutputStream using the specified Object.
+     * The specified Object should obtain the output source
+     * such as File, or OutputStream.   
+     * 
+     * @param output the output Object such as File, or OutputStream.   
+     * 
+     * @return the ImageOutputStream object, or null.
+     * 
+     * @throws IOException signals that an I/O exception has occurred.
+     */
+    public static ImageOutputStream createImageOutputStream(Object output)
+            throws IOException {
+        if (output == null) {
+            throw new IllegalArgumentException("output destination cannot be NULL");
+        }
+
+        Iterator<ImageOutputStreamSpi> it = registry.getServiceProviders(ImageOutputStreamSpi.class, true);
+
+        while (it.hasNext()) {
+            ImageOutputStreamSpi spi = it.next();
+            if (spi.getOutputClass().isInstance(output)) {
+                // todo - use getUseCache and getCacheDir here
+                return spi.createOutputStreamInstance(output);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets the array of format names as String which can be 
+     * decoded by registered ImageReader objects.
+     * 
+     * @return the array of format names.
+     */
+    public static String[] getReaderFormatNames() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets the array of MIME types as String which can be 
+     * decoded by registered ImageReader objects.
+     * 
+     * @return the array of MIME types.
+     */
+    public static String[] getReaderMIMETypes() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets the Iterator of registered ImageReader which are able to 
+     * decode an imput data specified by input Object.
+     * 
+     * @param input the input Object with encoded data such as 
+     * ImageInputStream object.
+     * 
+     * @return the Iterator of registered ImageReader. 
+     */
+    public static Iterator<ImageReader> getImageReaders(Object input) {
+        if (input == null) {
+            throw new NullPointerException("input cannot be NULL");
+        }
+
+        Iterator<ImageReaderSpi> it = registry.getServiceProviders(ImageReaderSpi.class,
+                new CanReadFilter(input), true);
+
+        return new SpiIteratorToReadersIteratorWrapper(it);
+    }
+
+    /**
+     * Gets the Iterator of registered ImageReader which are able to 
+     * decode the specified format.
+     * 
+     * @param formatName the format name such as "jpeg", or "gif".
+     * 
+     * @return the Iterator of registered ImageReader.
+     */
+    public static Iterator<ImageReader> getImageReadersByFormatName(String formatName) {
+        if (formatName == null) {
+            throw new NullPointerException("format name cannot be NULL");
+        }
+
+        Iterator<ImageReaderSpi> it = registry.getServiceProviders(ImageReaderSpi.class,
+                new FormatFilter(formatName), true);
+
+        return new SpiIteratorToReadersIteratorWrapper(it);
+    }
+
+    /**
+     * Gets the Iterator which lists the registered ImageReader objects that
+     * are able to decode files with the specified suffix.
+     * 
+     * @param fileSuffix the file suffix such as "jpg".
+     * 
+     * @return the Iterator of registered ImageReaders.
+     */
+    public static Iterator<ImageReader> getImageReadersBySuffix(String fileSuffix) {
+        if (fileSuffix == null) {
+            throw new NullPointerException("suffix cannot be NULL");
+        }
+        Iterator<ImageReaderSpi> it = registry.getServiceProviders(ImageReaderSpi.class,
+                new SuffixFilter(fileSuffix), true);
+
+        return new SpiIteratorToReadersIteratorWrapper(it);
+    }
+
+    /**
+     * Gets the Iterator of registered ImageReader objects that
+     * are able to decode files with the specified MIME type.
+     * 
+     * @param MIMEType the MIME type such as "image/jpeg".
+     * 
+     * @return the Iterator of registered ImageReaders.
+     */
+    public static Iterator<ImageReader> getImageReadersByMIMEType(String MIMEType) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets an array of Strings giving the names of the formats supported 
+     * by registered ImageWriter objects.
+     * 
+     * @return the array of format names.
+     */
+    public static String[] getWriterFormatNames() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets an array of Strings giving the MIME types of the formats supported 
+     * by registered ImageWriter objects.
+     * 
+     * @return the array of MIME types.
+     */
+    public static String[] getWriterMIMETypes() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets the Iterator which lists the registered ImageReader objects that
+     * are able to encode the specified image format.
+     * 
+     * @param formatName the image format name such as "jpeg".
+     * 
+     * @return the Iterator of registered ImageWriter.
+     */
+    public static Iterator<ImageWriter> getImageWritersByFormatName(String formatName) {
+        if (formatName == null) {
+            throw new NullPointerException("format name cannot be NULL");
+        }
+
+        Iterator<ImageWriterSpi> it = registry.getServiceProviders(ImageWriterSpi.class,
+                new FormatFilter(formatName), true);
+
+        return new SpiIteratorToWritersIteratorWrapper(it);
+    }
+
+    /**
+     * Gets the Iterator which lists the registered ImageReader objects that
+     * are able to encode the specified suffix.
+     * 
+     * @param fileSuffix the file suffix such as "jpg".
+     * 
+     * @return the Iterator of registered ImageWriter.
+     */
+    public static Iterator<ImageWriter> getImageWritersBySuffix(String fileSuffix) {
+        if (fileSuffix == null) {
+            throw new NullPointerException("suffix cannot be NULL");
+        }
+        Iterator<ImageWriterSpi> it = registry.getServiceProviders(ImageWriterSpi.class,
+                new SuffixFilter(fileSuffix), true);
+        return new SpiIteratorToWritersIteratorWrapper(it);
+    }
+
+    /**
+     * Gets the Iterator which lists the registered ImageReader objects that
+     * are able to encode the specified MIME type.
+     * 
+     * @param MIMEType the MIME type such as "image/jpeg".
+     * 
+     * @return the Iterator of registered ImageWriter.
+     */
+    public static Iterator<ImageWriter> getImageWritersByMIMEType(String MIMEType) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets an ImageWriter object which corresponds to the 
+     * specified ImageReader, or returns null if the specified
+     * ImageReader is not registered. 
+     * 
+     * @param reader the specified ImageReader.
+     * 
+     * @return the ImageWriter, or null.
+     */
+    public static ImageWriter getImageWriter(ImageReader reader) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets an ImageReader object which corresponds to the 
+     * specified ImageWriter, or returns null if the specified
+     * ImageWriter is not registered. 
+     * 
+     * @param writer the registered ImageWriter object.
+     * 
+     * @return the ImageReader.
+     */
+    public static ImageReader getImageReader(ImageWriter writer) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets the Iterator of ImageWriter objects which are able to
+     * encode images with the specified ImageTypeSpecifier and
+     * format.
+     * 
+     * @param type the ImageTypeSpecifier, which defines layout.
+     * @param formatName the format name.
+     * 
+     * @return the Iterator of ImageWriter objects.
+     */
+    public static Iterator<ImageWriter> getImageWriters(ImageTypeSpecifier type,
+                                           String formatName) {
+        if (type == null) {
+            throw new NullPointerException("type cannot be NULL");
+        }
+
+        if (formatName == null) {
+            throw new NullPointerException("format name cannot be NULL");
+        }
+
+        Iterator<ImageWriterSpi> it = registry.getServiceProviders(ImageWriterSpi.class,
+                new FormatAndEncodeFilter(type, formatName), true);
+
+        return new SpiIteratorToWritersIteratorWrapper(it);
+    }
+
+    /**
+     * Gets the Iterator of registered ImageTranscoders which 
+     * are able to transcode the metadata of the specified
+     * ImageReader object to a suitable object for encoding 
+     * by the specified ImageWriter.
+     * 
+     * @param reader the specified ImageReader.
+     * @param writer the specified ImageWriter.
+     * 
+     * @return the Iterator of registered ImageTranscoders.
+     */
+    public static Iterator<ImageTranscoder> getImageTranscoders(ImageReader reader,
+                                               ImageWriter writer) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Reads image data from the specified File and decodes it using 
+     * the appropriate registered ImageReader object. 
+     * The File is wrapped in an ImageInputStream.
+     * 
+     * @param input the File to be read.
+     * 
+     * @return the BufferedImage decoded from the specified File, or null.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public static BufferedImage read(File input) throws IOException {
+        if (input == null) {
+            throw new IllegalArgumentException("input == null!");
+        }
+
+        ImageInputStream stream = createImageInputStream(input);
+        return read(stream);
+    }
+
+    /**
+     * Reads image data from the specified InputStream and decodes it 
+     * using an appropriate registered an ImageReader object.
+     * 
+     * @param input the InputStream.
+     * 
+     * @return the BufferedImage decoded from the specified InputStream,
+     * or null.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public static BufferedImage read(InputStream input) throws IOException {
+        if (input == null) {
+            throw new IllegalArgumentException("input == null!");
+        }
+
+        ImageInputStream stream = createImageInputStream(input);
+        return read(stream);
+    }
+
+    /**
+     * Reads image data from the specified URL and decodes it using 
+     * the appropriate registered ImageReader object. 
+     *  
+     * @param input the URL to be read.
+     * 
+     * @return the BufferedImage decoded from the specified URL, or null.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public static BufferedImage read(URL input) throws IOException {
+        if (input == null) {
+            throw new IllegalArgumentException("input == null!");
+        }
+
+        InputStream stream = input.openStream();
+        BufferedImage res = read(stream);
+        stream.close();
+        
+        return res;
+    }
+
+    /**
+     * Reads image data from the specified ImageInputStream and decodes it 
+     * using appropriate registered an ImageReader object.
+     * 
+     * @param stream the ImageInputStream.
+     * 
+     * @return the BufferedImage decoded from the specified ImageInputStream,
+     * or null.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public static BufferedImage read(ImageInputStream stream) throws IOException {
+        if (stream == null) {
+            throw new IllegalArgumentException("stream == null!");
+        }
+
+        Iterator<ImageReader> imageReaders = getImageReaders(stream);
+        if (!imageReaders.hasNext()) {
+            return null;
+        }
+
+        ImageReader reader = imageReaders.next();
+        reader.setInput(stream, false, true);
+        BufferedImage res = reader.read(0);
+        reader.dispose();
+
+        try {
+            stream.close();
+        } catch (IOException e) {
+            // Stream could be already closed, proceed silently in this case
+        }
+        
+        return res;
+    }
+
+    /**
+     * Writes the specified image in the specified format (using an 
+     * appropriate ImageWriter) to the specified ImageOutputStream.
+     * 
+     * @param im the RenderedImage.
+     * @param formatName the format name.
+     * @param output the ImageOutputStream where Image to be written.
+     * 
+     * @return true, if Image is written successfully, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public static boolean write(RenderedImage im,
+                                String formatName,
+                                ImageOutputStream output)
+            throws IOException {
+
+        if (im == null) {
+            throw new IllegalArgumentException("image cannot be NULL");
+        }
+        if (formatName == null) {
+            throw new IllegalArgumentException("format name cannot be NULL");
+        }
+        if (output == null) {
+            throw new IllegalArgumentException("output cannot be NULL");
+        }
+
+        Iterator<ImageWriter> it = getImageWriters(ImageTypeSpecifier.createFromRenderedImage(im), formatName);
+        if (it.hasNext()) {
+            ImageWriter writer = it.next();
+            writer.setOutput(output);
+            writer.write(im);
+            output.flush();
+            writer.dispose();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Writes the specified image in the specified format (using an 
+     * appropriate ImageWriter) to the specified File.
+     * 
+     * @param im the RenderedImage.
+     * @param formatName the format name.
+     * @param output the output File where Image to be written.
+     * 
+     * @return true, if Image is written successfully, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public static boolean write(RenderedImage im,
+                                String formatName,
+                                File output)
+            throws IOException {
+
+        if (output == null) {
+            throw new IllegalArgumentException("output cannot be NULL");
+        }
+
+        if (output.exists()) {
+            output.delete();
+        }
+
+        ImageOutputStream ios = createImageOutputStream(output);
+        boolean rt = write(im, formatName, ios);
+        ios.close();
+        return rt;
+    }
+
+    /**
+     * Writes the specified image in the specified format (using an 
+     * appropriate ImageWriter) to the specified OutputStream.
+     * 
+     * @param im the RenderedImage.
+     * @param formatName the format name.
+     * @param output the OutputStream where Image is to be written.
+     * 
+     * @return true, if Image is written successfully, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public static boolean write(RenderedImage im,
+                                String formatName,
+                                OutputStream output)
+            throws IOException {
+
+        if (output == null) {
+            throw new IllegalArgumentException("output cannot be NULL");
+        }
+
+        ImageOutputStream ios = createImageOutputStream(output);
+        boolean rt = write(im, formatName, ios);
+        ios.close();
+        return rt;
+    }
+
+
+    /**
+     * Filter to match spi by format name.
+     */
+    static class FormatFilter implements ServiceRegistry.Filter {
+        
+        /** The name. */
+        private String name;
+
+        /**
+         * Instantiates a new format filter.
+         * 
+         * @param name the name
+         */
+        public FormatFilter(String name) {
+            this.name = name;
+        }
+
+        public boolean filter(Object provider) {
+            ImageReaderWriterSpi spi = (ImageReaderWriterSpi) provider;
+            return Arrays.asList(spi.getFormatNames()).contains(name);
+        }
+    }
+
+    /**
+     * Filter to match spi by format name and encoding possibility.
+     */
+    static class FormatAndEncodeFilter extends FormatFilter {
+
+        /** The type. */
+        private ImageTypeSpecifier type;
+
+        /**
+         * Instantiates a new format and encode filter.
+         * 
+         * @param type the type
+         * @param name the name
+         */
+        public FormatAndEncodeFilter(ImageTypeSpecifier type, String name) {
+            super(name);
+            this.type = type;
+        }
+
+        @Override
+        public boolean filter(Object provider) {
+            ImageWriterSpi spi = (ImageWriterSpi) provider;
+            return super.filter(provider) && spi.canEncodeImage(type);
+        }
+    }
+
+    /**
+     * Filter to match spi by suffix.
+     */
+    static class SuffixFilter implements ServiceRegistry.Filter {
+        
+        /** The suf. */
+        private String suf;
+
+        /**
+         * Instantiates a new suffix filter.
+         * 
+         * @param suf the suf
+         */
+        public SuffixFilter(String suf) {
+            this.suf = suf;
+        }
+
+        public boolean filter(Object provider) {
+            ImageReaderWriterSpi spi = (ImageReaderWriterSpi) provider;
+            return Arrays.asList(spi.getFileSuffixes()).contains(suf);
+        }
+    }
+
+    /**
+     * Filter to match spi by decoding possibility.
+     */
+    static class CanReadFilter implements ServiceRegistry.Filter {
+        
+        /** The input. */
+        private Object input;
+
+        /**
+         * Instantiates a new can read filter.
+         * 
+         * @param input the input
+         */
+        public CanReadFilter(Object input) {
+            this.input = input;
+        }
+
+        public boolean filter(Object provider) {
+            ImageReaderSpi spi = (ImageReaderSpi) provider;
+            try {
+                return spi.canDecodeInput(input);
+            } catch (IOException e) {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Wraps Spi's iterator to ImageWriter iterator.
+     */
+    static class SpiIteratorToWritersIteratorWrapper implements Iterator<ImageWriter> {
+
+        /** The backend. */
+        private Iterator<ImageWriterSpi> backend;
+
+        /**
+         * Instantiates a new spi iterator to writers iterator wrapper.
+         * 
+         * @param backend the backend
+         */
+        public SpiIteratorToWritersIteratorWrapper(Iterator<ImageWriterSpi> backend) {
+            this.backend = backend;
+        }
+
+        /**
+         * Next.
+         * 
+         * @return the image writer
+         */
+        public ImageWriter next() {
+            try {
+                return backend.next().createWriterInstance();
+            } catch (IOException e) {
+                e.printStackTrace();
+                return null;
+            }
+        }
+
+        /**
+         * Checks for next.
+         * 
+         * @return true, if successful
+         */
+        public boolean hasNext() {
+            return backend.hasNext();
+        }
+
+        /**
+         * Removes the.
+         */
+        public void remove() {
+            throw new UnsupportedOperationException("Use deregisterServiceprovider instead of Iterator.remove()");
+        }
+    }
+
+    /**
+     * Wraps spi's iterator to ImageReader iterator.
+     */
+    static class SpiIteratorToReadersIteratorWrapper implements Iterator<ImageReader> {
+        
+        /** The backend. */
+        private Iterator<ImageReaderSpi> backend;
+
+        /**
+         * Instantiates a new spi iterator to readers iterator wrapper.
+         * 
+         * @param backend the backend
+         */
+        public SpiIteratorToReadersIteratorWrapper(Iterator<ImageReaderSpi> backend) {
+            this.backend = backend;
+        }
+
+        /**
+         * Next.
+         * 
+         * @return the image reader
+         */
+        public ImageReader next() {
+            try {
+                return backend.next().createReaderInstance();
+            } catch (IOException e) {
+                e.printStackTrace();
+                return null;
+            }
+        }
+
+        /**
+         * Checks for next.
+         * 
+         * @return true, if successful
+         */
+        public boolean hasNext() {
+            return backend.hasNext();
+        }
+
+        /**
+         * Removes the.
+         */
+        public void remove() {
+            throw new UnsupportedOperationException("Use deregisterServiceprovider instead of Iterator.remove()");
+        }
+    }
+}
diff --git a/awt/javax/imageio/ImageReadParam.java b/awt/javax/imageio/ImageReadParam.java
new file mode 100644
index 0000000..e67ed7d
--- /dev/null
+++ b/awt/javax/imageio/ImageReadParam.java
@@ -0,0 +1,193 @@
+/*
+ *  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;
+
+import java.awt.Dimension;
+import java.awt.image.BufferedImage;
+
+/*
+ * @author Sergey I. Salishev
+ * @version $Revision: 1.2 $
+ */
+
+/**
+ * The ImageReadParam class provides information to the ImageReader about
+ * how an image is to be decoded.
+ */
+
+public class ImageReadParam extends IIOParam {
+
+    /** 
+     * This flag indicates if this ImageReadParam supports setting the source 
+     * rendering size.  
+     */
+    protected boolean canSetSourceRenderSize;
+    
+    /** 
+     * The destination BufferedImage. 
+     */
+    protected BufferedImage destination;
+    
+    /** The destination bands. */
+    protected int[] destinationBands;
+    
+    /** 
+     * The minimum progressive pass.  
+     */
+    protected int minProgressivePass;
+    
+    /** 
+     * The number of progressive passes.  
+     */
+    protected int numProgressivePasses;
+    
+    /** The source render size. */
+    protected Dimension sourceRenderSize;
+
+    /**
+     * Returns true if this ImageReaderParam supports rendering a
+     * source image at an arbitrary size.
+     * 
+     * @return true if this ImageReaderParam supports rendering a
+     * source image at an arbitrary size, false otherwise.
+     */
+    public boolean canSetSourceRenderSize() {
+        return canSetSourceRenderSize;
+    }
+
+    /**
+     * Gets the current destination image as BufferedImage.
+     * 
+     * @return the BufferedImage which represents the destination.
+     */
+    public BufferedImage getDestination() {
+        return destination;
+    }
+
+    /**
+     * Gets the indices of destination bands.
+     * 
+     * @return the array of destination bands.
+     */
+    public int[] getDestinationBands() {
+        return destinationBands;
+    }
+
+    /**
+     * Gets the index of the maximum pass to be decoded.
+     * This method returns Integer.MAX_VALUE, if 
+     * getSourceNumProgressivePasses() method returns value
+     * that is equal to Integer.MAX_VALUE. Otherwise
+     * this method returns 
+     * getSourceMinProgressivePass() + getSourceNumProgressivePasses() - 1.
+     * 
+     * @return the index of the maximum pass to be decoded.
+     */
+    public int getSourceMaxProgressivePass() {
+        if (getSourceNumProgressivePasses() == Integer.MAX_VALUE) {
+            return Integer.MAX_VALUE;
+        }
+        return getSourceMinProgressivePass() + getSourceNumProgressivePasses() - 1;
+    }
+
+    /**
+     * Gets the index of the minimum progressive pass that is decoded,
+     * default is 0. 
+     * 
+     * @return the index of the minimum progressive pass that is decoded,
+     * default is 0.
+     */
+    public int getSourceMinProgressivePass() {
+        return minProgressivePass;
+    }
+
+    /**
+     * Gets the number of progressive passes.
+     * The default value is Integer.MAX_VALUE. 
+     * 
+     * @return the number of progressive passes.
+     */
+    public int getSourceNumProgressivePasses() {
+        return numProgressivePasses;
+    }
+
+    /**
+     * Gets the dimension of source image which will be rendered
+     * during decoding process.
+     * 
+     * @return the source render size.
+     */
+    public Dimension getSourceRenderSize() {
+        return sourceRenderSize;
+    }
+
+    /**
+     * Sets the specified destination image.
+     * This image will be used by read, readAll, and readRaster methods,
+     * and a reference to it will be returned by those methods.
+     * 
+     * @param destination the destination image.
+     */
+    public void setDestination(BufferedImage destination) {
+        this.destination = destination;
+    }
+
+    /**
+     * Sets the indices of the destination bands.
+     * 
+     * @param destinationBands the indices of the destination bands.
+     */
+    public void setDestinationBands(int[] destinationBands) {
+        this.destinationBands = destinationBands;
+    }
+
+    @Override
+    public void setDestinationType(ImageTypeSpecifier destinationType) {
+        this.destinationType = destinationType;
+    }
+
+    /**
+     * Sets the source progressive passes.
+     * 
+     * @param minPass the index of the minimum pass to be decoded.
+     * @param numPasses the number of passes to be decoded.
+     */
+    public void setSourceProgressivePasses(int minPass, int numPasses) {
+        minProgressivePass = minPass;
+        numProgressivePasses = numPasses;
+    }
+
+    /**
+     * Sets the dimension size of source image if an
+     * image can be rendered at an arbitrary size.
+     * 
+     * @param size the size of rendered image.
+     * 
+     * @throws UnsupportedOperationException the unsupported operation exception
+     */
+    public void setSourceRenderSize(Dimension size) throws UnsupportedOperationException {
+        if (!canSetSourceRenderSize) {
+            throw new UnsupportedOperationException("can't set source renderer size");
+        }
+        sourceRenderSize = size;        
+    }
+}
+
diff --git a/awt/javax/imageio/ImageReader.java b/awt/javax/imageio/ImageReader.java
new file mode 100644
index 0000000..780de26
--- /dev/null
+++ b/awt/javax/imageio/ImageReader.java
@@ -0,0 +1,1100 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.event.IIOReadWarningListener;
+import javax.imageio.event.IIOReadProgressListener;
+import javax.imageio.event.IIOReadUpdateListener;
+import java.util.Locale;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Set;
+import java.io.IOException;
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.*;
+
+/**
+ * The ImageReader class is an abstract class for decoding images.
+ * ImageReader objects are instantiated by the service provider 
+ * interface, ImageReaderSpi class, for the specific format. 
+ * ImageReaderSpi class should be registered with the IIORegistry, 
+ * which uses them for format recognition and presentation of available 
+ * format readers and writers.
+ */
+public abstract class ImageReader {
+
+    /** The originating provider. */
+    protected ImageReaderSpi originatingProvider;
+
+    /** The input object such as ImageInputStream. */
+    protected Object input;
+
+    /** The seek forward only. */
+    protected boolean seekForwardOnly;
+
+    /** 
+     * The ignore metadata flag indicates whether current input source 
+     * has been marked as metadata is allowed to be ignored by setInput. 
+     */
+    protected boolean ignoreMetadata;
+
+    /** The minimum index. */
+    protected int minIndex;
+
+    /** The available locales. */
+    protected Locale[] availableLocales;
+
+    /** The locale. */
+    protected Locale locale;
+
+    /** The list of warning listeners. */
+    protected List<IIOReadWarningListener> warningListeners;
+
+    /** The list of warning locales. */
+    protected List<Locale> warningLocales;
+
+    /** The list of progress listeners. */
+    protected List<IIOReadProgressListener> progressListeners;
+
+    /** The list of update listeners. */
+    protected List<IIOReadUpdateListener> updateListeners;
+
+    /**
+     * Instantiates a new ImageReader.
+     * 
+     * @param originatingProvider the ImageReaderSpi which 
+     * instanties this ImageReader.
+     */
+    protected ImageReader(ImageReaderSpi originatingProvider) {
+        this.originatingProvider = originatingProvider;
+    }
+
+    /**
+     * Gets the format name of this input source.
+     * 
+     * @return the format name of this input source.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public String getFormatName() throws IOException {
+        return originatingProvider.getFormatNames()[0];
+    }
+
+    /**
+     * Gets the ImageReaderSpi which instantiated this ImageReader. 
+     * 
+     * @return the ImageReaderSpi.
+     */
+    public ImageReaderSpi getOriginatingProvider() {
+        return originatingProvider;
+    }
+
+    /**
+     * Sets the specified Object as the input source of this ImageReader. 
+     * 
+     * @param input the input source, it can 
+     * be an ImageInputStream or other supported objects.
+     * @param seekForwardOnly indicates whether the stream must
+     * be read sequentially from its current starting point.
+     * @param ignoreMetadata parameter which indicates
+     * if metadata may be ignored during reads or not.
+     */
+    public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) {
+        if (input != null) {
+            if (!isSupported(input) && !(input instanceof ImageInputStream)) {
+                throw new IllegalArgumentException("input " + input + " is not supported");
+            }
+        }
+        this.minIndex = 0;
+        this.seekForwardOnly = seekForwardOnly;
+        this.ignoreMetadata = ignoreMetadata;
+        this.input = input;
+    }
+
+    /**
+     * Checks if is supported.
+     * 
+     * @param input the input
+     * 
+     * @return true, if is supported
+     */
+    private boolean isSupported(Object input) {
+        ImageReaderSpi spi = getOriginatingProvider();
+        if (null != spi) {
+            Class[] outTypes = spi.getInputTypes();
+            for (Class<?> element : outTypes) {
+                if (element.isInstance(input)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets the specified Object as the input source of this ImageReader.
+     * Metadata is not ignored.
+     * 
+     * @param input the input source, it can 
+     * be an ImageInputStream or other supported objects.
+     * @param seekForwardOnly indicates whether the stream must
+     * be read sequentially from its current starting point.
+     */
+    public void setInput(Object input, boolean seekForwardOnly) {
+        setInput(input, seekForwardOnly, false);
+    }
+
+    /**
+     * Sets the specified Object as the input source of this ImageReader.
+     * Metadata is not ignored and forward seeking is not required.
+     * 
+     * @param input the input source, it can 
+     * be ImageInputStream or other objects.
+     */
+    public void setInput(Object input) {
+        setInput(input, false, false);
+    }
+
+    /**
+     * Gets the input source object of this ImageReader, or returns null.
+     * 
+     * @return the the input source object such as ImageInputStream,
+     * or null.
+     */
+    public Object getInput() {
+        return input;
+    }
+
+    /**
+     * Checks if the input source supports only forward reading, or not.
+     * 
+     * @return true, if the input source supports only forward reading, 
+     * false otherwise.
+     */
+    public boolean isSeekForwardOnly() {
+        return seekForwardOnly;
+    }
+
+    /**
+     * Returns true if the current input source allows 
+     * to metadata to be ignored by passing true as 
+     * the ignoreMetadata argument to the setInput method.
+     * 
+     * @return true, if true if the current input source allows 
+     * to metadata to be ignored by passing true as 
+     * the ignoreMetadata argument to the setInput method.
+     */
+    public boolean isIgnoringMetadata() {
+        return ignoreMetadata;
+    }
+
+    /**
+     * Gets the minimum valid index for reading an image, thumbnail, 
+     * or image metadata. 
+     * 
+     * @return the minimum valid index for reading an image, thumbnail, 
+     * or image metadata.
+     */
+    public int getMinIndex() {
+        return minIndex;
+    }
+
+    /**
+     * Gets the available locales.
+     * 
+     * @return an array of the available locales.
+     */
+    public Locale[] getAvailableLocales() {
+        return availableLocales;
+    }
+
+    /**
+     * Sets the locale to this ImageReader.
+     * 
+     * @param locale the Locale.
+     */
+    public void setLocale(Locale locale) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Gets the locale of this ImageReader.
+     * 
+     * @return the locale of this ImageReader.
+     */
+    public Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Gets the number of images available in the current input source.
+     * 
+     * @param allowSearch the parameter which indicates what
+     * a search is required; if false, the reader may return -1  
+     * without searching.
+     * 
+     * @return the number of images.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract int getNumImages(boolean allowSearch) throws IOException;
+
+    /**
+     * Gets the width of the specified image in input source.
+     * 
+     * @param imageIndex the image index.
+     * 
+     * @return the width in pixels.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract int getWidth(int imageIndex) throws IOException;
+
+    /**
+     * Gets the height of the specified image in input source.
+     * 
+     * @param imageIndex the image index.
+     * 
+     * @return the height in pixels.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract int getHeight(int imageIndex) throws IOException;
+
+    /**
+     * Checks if the storage format of the specified image places 
+     * an impediment on random pixels access or not. 
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return true, if the storage format of the specified image places 
+     * an impediment on random pixels access, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean isRandomAccessEasy(int imageIndex) throws IOException {
+        return false; //def
+    }
+
+    /**
+     * Gets the aspect ratio (width devided by height) of the image.
+     * 
+     * @param imageIndex the image index.
+     * 
+     * @return the aspect ratio of the image.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public float getAspectRatio(int imageIndex) throws IOException {
+        return (float) getWidth(imageIndex) / getHeight(imageIndex);
+    }
+
+    /**
+     * Gets an ImageTypeSpecifier which indicates the type of the 
+     * specified image.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return the ImageTypeSpecifier.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Gets an Iterator of ImageTypeSpecifier objects which are associated
+     * with image types that may be used when decoding specified image.
+     * 
+     * @param imageIndex the image index.
+     * 
+     * @return an Iterator of ImageTypeSpecifier objects.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException;
+
+    /**
+     * Gets the default ImageReadParam object.
+     * 
+     * @return the ImageReadParam object.
+     */
+    public ImageReadParam getDefaultReadParam() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Gets an IIOMetadata object for this input source.
+     * 
+     * @return the IIOMetadata.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract IIOMetadata getStreamMetadata() throws IOException;
+
+    /**
+     * Gets an IIOMetadata object for this input source.
+     * 
+     * @param formatName the desired metadata format to be used in the 
+     * returned IIOMetadata object.
+     * @param nodeNames the node names of the document.
+     * 
+     * @return the IIOMetadata.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public IIOMetadata getStreamMetadata(String formatName, Set<String> nodeNames)
+            throws IOException {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Gets the image metadata of the specified image in input source.
+     * 
+     * @param imageIndex the image index.
+     * 
+     * @return the IIOMetadata.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract IIOMetadata getImageMetadata(int imageIndex) throws IOException;
+
+    /**
+     * Gets the image metadata of the specified image input source.
+     * 
+     * @param imageIndex the image index.
+     * @param formatName the desired metadata format to be used in the 
+     * returned IIOMetadata object.
+     * @param nodeNames the node names which can be contained in
+     * the document.
+     * 
+     * @return the IIOMetadata.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public IIOMetadata getImageMetadata(int imageIndex, String formatName,
+                                        Set<String> nodeNames) throws IOException {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Reads the specified image and returns it as a BufferedImage
+     * using the default ImageReadParam.
+     *  
+     * @param imageIndex the image index.
+     * 
+     * @return the BufferedImage.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public BufferedImage read(int imageIndex) throws IOException {
+        return read(imageIndex, null);
+    }
+
+    /**
+     * Reads the specified image and returns it as a BufferedImage
+     * using the specified ImageReadParam.
+     * 
+     * @param imageIndex the image index.
+     * @param param the ImageReadParam.
+     * 
+     * @return the BufferedImage.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract BufferedImage read(int imageIndex, ImageReadParam param) throws IOException;
+
+    /**
+     * Reads the specified image and returns an IIOImage with this image,
+     * thumbnails, and metadata for this image, using 
+     * the specified ImageReadParam.
+     * 
+     * @param imageIndex the image index.
+     * @param param the ImageReadParam.
+     * 
+     * @return the IIOImage.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public IIOImage readAll(int imageIndex, ImageReadParam param) throws IOException {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Returns an Iterator of IIOImages from the input source. 
+     * 
+     * @param params the Iterator of ImageReadParam objects.
+     * 
+     * @return the iterator of IIOImages.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public Iterator<IIOImage> readAll(Iterator<? extends ImageReadParam> params) throws IOException {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Checks whether or not this plug-in supports reading a Raster.
+     * 
+     * @return true, if this plug-in supports reading a Raster,
+     * false otherwise.
+     */
+    public boolean canReadRaster() {
+        return false; //def
+    }
+
+    /**
+     * Reads a new Raster object which contains the raw pixel data from 
+     * the image. 
+     * 
+     * @param imageIndex the image index.
+     * @param param the ImageReadParam.
+     * 
+     * @return the Raster.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public Raster readRaster(int imageIndex, ImageReadParam param) throws IOException {
+        throw new UnsupportedOperationException("Unsupported");
+    }
+
+    /**
+     * Checks if the specified image has tiles or not.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return true, if the specified image has tiles,
+     * false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean isImageTiled(int imageIndex) throws IOException {
+        return false; //def
+    }
+
+    /**
+     * Gets the tile width in the specified image.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return the tile width.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public int getTileWidth(int imageIndex) throws IOException {
+        return getWidth(imageIndex); //def
+    }
+
+    /**
+     * Gets the tile height in the specified image.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return the tile height.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public int getTileHeight(int imageIndex) throws IOException {
+        return getHeight(imageIndex); //def
+    }
+
+    /**
+     * Gets the X coordinate of the upper left corner of the tile grid in the 
+     * specified image.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return the X coordinate of the upper left corner of the tile grid.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public int getTileGridXOffset(int imageIndex) throws IOException {
+        return 0; //def
+    }
+
+    /**
+     * Gets the Y coordinate of the upper left corner of the tile grid in the 
+     * specified image.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return the Y coordinate of the upper left corner of the tile grid.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public int getTileGridYOffset(int imageIndex) throws IOException {
+        return 0; //def
+    }
+
+    /**
+     * Reads the tile specified by the tileX and tileY parameters
+     * of the specified image and returns it as a BufferedImage. 
+     * 
+     * @param imageIndex the image index.
+     * @param tileX the X index of tile.
+     * @param tileY the Y index of tile.
+     * 
+     * @return the BufferedImage.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public BufferedImage readTile(int imageIndex, int tileX, int tileY) throws IOException {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Reads the tile specified by the tileX and tileY parameters
+     * of the specified image and returns it as a Raster. 
+     * 
+     * @param imageIndex the image index.
+     * @param tileX the X index of tile.
+     * @param tileY the Y index of tile.
+     * 
+     * @return the Raster.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public Raster readTileRaster(int imageIndex, int tileX, int tileY) throws IOException {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Reads the specified image using the specified 
+     * ImageReadParam and returns it as a RenderedImage.
+     * 
+     * @param imageIndex the image index.
+     * @param param the ImageReadParam.
+     * 
+     * @return the RenderedImage.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public RenderedImage readAsRenderedImage(int imageIndex, ImageReadParam param) throws IOException {
+        return read(imageIndex, param);
+    }
+
+    /**
+     * Returns true if the image format supported by this reader 
+     * supports thumbnail preview images.
+     * 
+     * @return true if the image format supported by this reader 
+     * supports thumbnail preview images, false otherwise.
+     */
+    public boolean readerSupportsThumbnails() {
+        return false; //def
+    }
+
+    /**
+     * Checks if the specified image has thumbnails or not.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return true, if the specified image has thumbnails,
+     * false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean hasThumbnails(int imageIndex) throws IOException {
+        return getNumThumbnails(imageIndex) > 0; //def
+    }
+
+    /**
+     * Gets the number of thumbnails for the specified image.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return the number of thumbnails.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public int getNumThumbnails(int imageIndex) throws IOException {
+        return 0; //def
+    }
+
+    /**
+     * Gets the width of the specified thumbnail for the specified image.
+     * 
+     * @param imageIndex the image's index.
+     * @param thumbnailIndex the thumbnail's index.
+     * 
+     * @return the thumbnail width.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException {
+        return readThumbnail(imageIndex, thumbnailIndex).getWidth();  //def
+    }
+
+    /**
+     * Gets the height of the specified thumbnail for the specified image.
+     * 
+     * @param imageIndex the image's index.
+     * @param thumbnailIndex the thumbnail's index.
+     * 
+     * @return the thumbnail height.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException {
+        return readThumbnail(imageIndex, thumbnailIndex).getHeight();  //def
+    }
+
+    /**
+     * Reads the thumbnail image for the specified image
+     * as a BufferedImage.
+     *  
+     * @param imageIndex the image index.
+     * @param thumbnailIndex the thumbnail index.
+     * 
+     * @return the BufferedImage.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
+        throw new UnsupportedOperationException("Unsupported"); //def
+    }
+
+    /**
+     * Requests an abort operation for current reading operation. 
+     */
+    public void abort() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Checks whether or not a request to abort the current read operation 
+     * has been made successfully.
+     * 
+     * @return true, if the request to abort the current read operation 
+     * has been made successfully, false otherwise.
+     */
+    protected boolean abortRequested() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Clears all previous abort request, and abortRequested returns false
+     * after calling this method.
+     */
+    protected void clearAbortRequest() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Adds the IIOReadWarningListener.
+     * 
+     * @param listener the IIOReadWarningListener.
+     */
+    public void addIIOReadWarningListener(IIOReadWarningListener listener) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Removes the specified IIOReadWarningListener.
+     * 
+     * @param listener the IIOReadWarningListener to be removed.
+     */
+    public void removeIIOReadWarningListener(IIOReadWarningListener listener) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Removes all registered IIOReadWarningListeners.
+     */
+    public void removeAllIIOReadWarningListeners() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Adds the IIOReadProgressListener.
+     * 
+     * @param listener the IIOReadProgressListener.
+     */
+    public void addIIOReadProgressListener(IIOReadProgressListener listener) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Removes the specified IIOReadProgressListener.
+     * 
+     * @param listener the IIOReadProgressListener to be removed.
+     */
+    public void removeIIOReadProgressListener(IIOReadProgressListener listener) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Removes registered IIOReadProgressListeners.
+     */
+    public void removeAllIIOReadProgressListeners() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Adds the IIOReadUpdateListener.
+     * 
+     * @param listener the IIOReadUpdateListener.
+     */
+    public void addIIOReadUpdateListener(IIOReadUpdateListener listener) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Removes the specified IIOReadUpdateListener.
+     * 
+     * @param listener the IIOReadUpdateListener to be removed.
+     */
+    public void removeIIOReadUpdateListener(IIOReadUpdateListener listener) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Removes registered IIOReadUpdateListeners.
+     */
+    public void removeAllIIOReadUpdateListeners() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the start of an sequence of image reads
+     * by calling the sequenceStarted method on all registered 
+     * IIOReadProgressListeners. 
+     * 
+     * @param minIndex the minimum index.
+     */
+    protected void processSequenceStarted(int minIndex) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the completion of an sequence of image reads
+     * by calling sequenceComplete method on all registered 
+     * IIOReadProgressListeners. 
+     */
+    protected void processSequenceComplete() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the start of an image read by calling the imageStarted
+     * method on all registered IIOReadProgressListeners.
+     * 
+     * @param imageIndex the image index.
+     */
+    protected void processImageStarted(int imageIndex) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the current percentage of image completion by calling 
+     * the imageProgress method on all registered IIOReadProgressListeners.
+     * 
+     * @param percentageDone the percentage done.
+     */
+    protected void processImageProgress(float percentageDone) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes image completion by calling the imageComplete method
+     * on all registered IIOReadProgressListeners. 
+     */
+    protected void processImageComplete() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the start of a thumbnail read by calling the
+     * thumbnailStarted method on all registered IIOReadProgressListeners. 
+     * 
+     * @param imageIndex the image index.
+     * @param thumbnailIndex the thumbnail index.
+     */
+    protected void processThumbnailStarted(int imageIndex, int thumbnailIndex) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the current percentage of thumbnail completion 
+     * by calling the thumbnailProgress method on all registered 
+     * IIOReadProgressListeners.
+     * 
+     * @param percentageDone the percentage done.
+     */
+    protected void processThumbnailProgress(float percentageDone) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the completion of a thumbnail read 
+     * by calling the thumbnailComplete method 
+     * on all registered IIOReadProgressListeners. 
+     */
+    protected void processThumbnailComplete() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes a read aborted event by calling the readAborted 
+     * method on all registered IIOReadProgressListeners.
+     */
+    protected void processReadAborted() {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the beginning of a progressive pass by calling 
+     * the passStarted method on all registered IIOReadUpdateListeners.
+     * 
+     * @param theImage the image to be updated.
+     * @param pass the current pass index.
+     * @param minPass the minimum pass index.
+     * @param maxPass the maximum pass index.
+     * @param minX the X coordinate of of the upper left pixel. 
+     * @param minY the Y coordinate of of the upper left pixel.
+     * @param periodX the horizontal separation between pixels.
+     * @param periodY the vertical separation between pixels.
+     * @param bands the number of affected bands.
+     */
+    protected void processPassStarted(BufferedImage theImage,
+                                  int pass,
+                                  int minPass,
+                                  int maxPass,
+                                  int minX,
+                                  int minY,
+                                  int periodX,
+                                  int periodY,
+                                  int[] bands) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the update of a set of samples by calling 
+     * the imageUpdate method on all registered IIOReadUpdateListeners.  
+     * 
+     * @param theImage the image to be updated.
+     * @param minX the X coordinate of the upper left pixel.
+     * @param minY the Y coordinate of the upper left pixel.
+     * @param width the width of updated area.
+     * @param height the height of updated area.
+     * @param periodX the horizontal separation between pixels.
+     * @param periodY the vertical separation between pixels.
+     * @param bands the number of affected bands.
+     */
+    protected void processImageUpdate(BufferedImage theImage,
+                                  int minX,
+                                  int minY,
+                                  int width,
+                                  int height,
+                                  int periodX,
+                                  int periodY,
+                                  int[] bands) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the end of a progressive pass by calling passComplete
+     * method of registered IIOReadUpdateListeners.
+     * 
+     * @param theImage the image to be updated.
+     */
+    protected void processPassComplete(BufferedImage theImage) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the beginning of a thumbnail progressive pass
+     * by calling the thumbnailPassStarted method on all 
+     * registered IIOReadUpdateListeners.
+     * 
+     * @param theThumbnail the the thumbnail to be updated.
+     * @param pass the current pass index.
+     * @param minPass the minimum pass index.
+     * @param maxPass the maximum pass index.
+     * @param minX the X coordinate of the upper left pixel. 
+     * @param minY the Y coordinate of the upper left pixel.
+     * @param periodX the horizontal separation between pixels.
+     * @param periodY the vertical separation between pixels.
+     * @param bands the number of affected bands.
+     */
+    protected void processThumbnailPassStarted(BufferedImage theThumbnail,
+                                           int pass,
+                                           int minPass,
+                                           int maxPass,
+                                           int minX,
+                                           int minY,
+                                           int periodX,
+                                           int periodY,
+                                           int[] bands) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the update of a set of samples in a thumbnail 
+     * image by calling the thumbnailUpdate method on all
+     * registered IIOReadUpdateListeners. 
+     * 
+     * @param theThumbnail the the thumbnail to be updated.
+     * @param minX the X coordinate of the upper left pixel. 
+     * @param minY the Y coordinate of the upper left pixel.
+     * @param periodX the horizontal separation between pixels.
+     * @param periodY the vertical separation between pixels.
+     * @param bands the number of affected bands.
+     */
+    protected void processThumbnailUpdate(BufferedImage theThumbnail,
+                                      int minX,
+                                      int minY,
+                                      int width,
+                                      int height,
+                                      int periodX,
+                                      int periodY,
+                                       int[] bands) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes the end of a thumbnail progressive pass 
+     * by calling the thumbnailPassComplete method
+     * on all registered IIOReadUpdateListeners. 
+     * 
+     * @param theThumbnail the thumbnail to be updated.
+     */
+    protected void processThumbnailPassComplete(BufferedImage theThumbnail) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes a warning message by calling warningOccurred method
+     * of registered IIOReadWarningListeners.
+     * 
+     * @param warning the warning.
+     */
+    protected void processWarningOccurred(String warning) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Processes a warning by calling the warningOccurred method 
+     * of on all registered IIOReadWarningListeners.
+     * 
+     * @param baseName the base name of ResourceBundles.
+     * @param keyword the keyword to index the warning among ResourceBundles.
+     */
+    protected void processWarningOccurred(String baseName, String keyword) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Resets this ImageReader.
+     */
+    public void reset() {
+        // def
+        setInput(null, false);
+        setLocale(null);
+        removeAllIIOReadUpdateListeners();
+        removeAllIIOReadWarningListeners();
+        removeAllIIOReadProgressListeners();
+        clearAbortRequest();
+    }
+
+    /**
+     * Disposes of any resources.
+     */
+    public void dispose() {
+        // do nothing by def
+    }
+
+    /**
+     * Gets the region of source image that should be read with the 
+     * specified width, height and ImageReadParam. 
+     * 
+     * @param param the ImageReadParam object, or null.
+     * @param srcWidth the source image's width.
+     * @param srcHeight the source image's  height.
+     * 
+     * @return the Rectangle of source region.
+     */
+    protected static Rectangle getSourceRegion(ImageReadParam param, int srcWidth, int srcHeight) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Computes the specified source region and the specified destination 
+     * region with the specified the width and height of the source image,
+     * an optional destination image, and an ImageReadParam. 
+     * 
+     * @param param the an ImageReadParam object, or null.
+     * @param srcWidth the source image's width.
+     * @param srcHeight the source image's height.
+     * @param image the destination image.
+     * @param srcRegion the source region.
+     * @param destRegion the destination region.
+     */
+    protected static void computeRegions(ImageReadParam param,
+                                     int srcWidth,
+                                     int srcHeight,
+                                     BufferedImage image,
+                                     Rectangle srcRegion,
+                                     Rectangle destRegion) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Checks the validity of the source and destination band and is called
+     * when the reader knows the number of bands of the source image and 
+     * the number of bands of the destination image. 
+     * 
+     * @param param the ImageReadParam for reading the Image.
+     * @param numSrcBands the number of bands in the source.
+     * @param numDstBands the number of bands in the destination.
+     */
+    protected static void checkReadParamBandSettings(ImageReadParam param,
+                                                 int numSrcBands,
+                                                 int numDstBands) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Gets the destination image where the decoded data is written. 
+     * 
+     * @param param the ImageReadParam.
+     * @param imageTypes the iterator of ImageTypeSpecifier objects.
+     * @param width the width of the image being decoded.
+     * @param height the height of the image being decoded.
+     * 
+     * @return the BufferedImage where decoded pixels should be written.
+     * 
+     * @throws IIOException the IIOException is thrown if 
+     * there is no suitable ImageTypeSpecifier.
+     */
+    protected static BufferedImage getDestination(ImageReadParam param, Iterator<ImageTypeSpecifier> imageTypes,
+                                              int width,
+                                              int height)
+                                       throws IIOException {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+}
diff --git a/awt/javax/imageio/ImageTranscoder.java b/awt/javax/imageio/ImageTranscoder.java
new file mode 100644
index 0000000..1a0de76
--- /dev/null
+++ b/awt/javax/imageio/ImageTranscoder.java
@@ -0,0 +1,60 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.ImageTypeSpecifier;
+
+/**
+ * The ImageTranscoder interface is to be implemented by classes that
+ * perform image transcoding operations, that is, take images written
+ * in one format and write them in another format using
+ * read/write operations. Some image data can be lost in such processes.
+ * The ImageTranscoder interface converts metadata objects (IIOMetadata)
+ * of ImageReader to apropriate metadata object for ImageWriter.
+ */
+public interface ImageTranscoder {
+    
+    /**
+     * Converts the specified IIOMetadata object using the specified
+     * ImageWriteParam for obtaining writer's metadata structure.
+     * 
+     * @param inData the IIOMetadata.
+     * @param param the ImageWriteParam.
+     * 
+     * @return the IIOMetadata, or null.
+     */
+    IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param);
+
+    /**
+     * Converts the specified IIOMetadata object using the specified
+     * ImageWriteParam for obtaining writer's metadata structure 
+     * and ImageTypeSpecifier object for obtaining the layout and 
+     * color information of the image for this metadata.
+     * 
+     * @param inData the IIOMetadata.
+     * @param imageType the ImageTypeSpecifier.
+     * @param param the ImageWriteParam.
+     * 
+     * @return the IIOMetadata, or null.
+     */
+    IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param);
+}
diff --git a/awt/javax/imageio/ImageTypeSpecifier.java b/awt/javax/imageio/ImageTypeSpecifier.java
new file mode 100644
index 0000000..c93f269
--- /dev/null
+++ b/awt/javax/imageio/ImageTypeSpecifier.java
@@ -0,0 +1,339 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import java.awt.image.ColorModel;
+import java.awt.image.SampleModel;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.awt.color.ColorSpace;
+
+/**
+ * The ImageTypeSpecifier class performs conversion operations
+ * on the SampleModel and the ColorModel of an image.
+ */
+public class ImageTypeSpecifier {
+    
+    /** 
+     * The ColorModel of this ImageTypeSpecifier.
+     */
+    protected ColorModel colorModel;
+    
+    /** 
+     * The SampleModel of this ImageTypeSpecifier. 
+     */
+    protected SampleModel sampleModel;
+
+    /**
+     * Instantiates a new ImageTypeSpecifier with the specified
+     * ColorModel and SampleModel objects.
+     * 
+     * @param colorModel the ColorModel.
+     * @param sampleModel the SampleModel.
+     */
+    public ImageTypeSpecifier(ColorModel colorModel, SampleModel sampleModel) {
+        if (colorModel == null) {
+            throw new IllegalArgumentException("color model should not be NULL");
+        }
+        if (sampleModel == null) {
+            throw new IllegalArgumentException("sample model should not be NULL");
+        }
+        if (!colorModel.isCompatibleSampleModel(sampleModel)) {
+            throw new IllegalArgumentException("color and sample models are not compatible");
+        }
+
+        this.colorModel = colorModel;
+        this.sampleModel = sampleModel;
+    }
+
+    /**
+     * Instantiates a new ImageTypeSpecifier using the specified 
+     * RenderedImage.
+     * 
+     * @param renderedImage the RenderedImage.
+     */
+    public ImageTypeSpecifier(RenderedImage renderedImage) {
+        if (renderedImage == null) {
+            throw new IllegalArgumentException("image should not be NULL");
+        }
+        this.colorModel = renderedImage.getColorModel();
+        this.sampleModel = renderedImage.getSampleModel();
+    }
+
+    /**
+     * Creates an ImageTypeSpecifier with the specified
+     * DirectColorModel and a packed SampleModel.
+     * 
+     * @param colorSpace the ColorSpace.
+     * @param redMask the red mask.
+     * @param greenMask the green mask.
+     * @param blueMask the blue mask.
+     * @param alphaMask the alpha mask.
+     * @param transferType the transfer type.
+     * @param isAlphaPremultiplied the parameter indicates
+     * if the color channel is premultiplied by alpha.
+     * 
+     * @return the ImageTypeSpecifier.
+     */
+    public static ImageTypeSpecifier createPacked(ColorSpace colorSpace,
+                                                  int redMask,
+                                                  int greenMask,
+                                                  int blueMask,
+                                                  int alphaMask,
+                                                  int transferType,
+                                                  boolean isAlphaPremultiplied) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Creates an ImageTypeSpecifier with specified
+     * ComponentColorModel and a PixelInterleavedSampleModel.
+     * 
+     * @param colorSpace the ColorSpace.
+     * @param bandOffsets the band offsets.
+     * @param dataType the data type.
+     * @param hasAlpha the parameter indicates if alpha channel
+     * is needed.
+     * @param isAlphaPremultiplied the parameter indicates
+     * if the color channel is premultiplied by alpha.
+     * 
+     * @return the ImageTypeSpecifier.
+     */
+    public static ImageTypeSpecifier createInterleaved(ColorSpace colorSpace,
+                                                       int[] bandOffsets,
+                                                       int dataType,
+                                                       boolean hasAlpha,
+                                                       boolean isAlphaPremultiplied) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+
+    /**
+     * Creates a ImageTypeSpecifier for a image with a 
+     * BandedSampleModel and a ComponentColorModel.
+     * 
+     * @param colorSpace the ColorSpace.
+     * @param bankIndices the bank indices.
+     * @param bandOffsets the band offsets.
+     * @param dataType the data type.
+     * @param hasAlpha the parameter indicates a presence of alpha channel.
+     * @param isAlphaPremultiplied the parameter indicates whether
+     * or not color channel is alpha premultiplied.
+     * 
+     * @return the image type specifier
+     */
+    public static ImageTypeSpecifier createBanded(ColorSpace colorSpace,
+                                                  int[] bankIndices,
+                                                  int[] bandOffsets,
+                                                  int dataType,
+                                                  boolean hasAlpha,
+                                                  boolean isAlphaPremultiplied) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Creates a ImageTypeSpecifier for a grayscale image.
+     * 
+     * @param bits the number of bits per gray value.
+     * @param dataType the data type.
+     * @param isSigned a signed flag.
+     * 
+     * @return the ImageTypeSpecifier.
+     */
+    public static ImageTypeSpecifier createGrayscale(int bits,
+                                                     int dataType,
+                                                     boolean isSigned) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Creates a ImageTypeSpecifier for a grayscale image.
+     * 
+     * @param bits the number of bits per gray value.
+     * @param dataType the data type.
+     * @param isSigned a signed flag.
+     * @param isAlphaPremultiplied the parameter indicates
+     * if color channel is premultiplied by alpha, or not.
+     * 
+     * @return the ImageTypeSpecifier.
+     */
+    public static ImageTypeSpecifier createGrayscale(int bits,
+                                                     int dataType,
+                                                     boolean isSigned,
+                                                     boolean isAlphaPremultiplied) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Creates a ImageTypeSpecifier with the indexed image format.
+     * 
+     * @param redLUT the red values of indecies.
+     * @param greenLUT the green values of indecies.
+     * @param blueLUT the blue values of indecies.
+     * @param alphaLUT the alpha values of indecies.
+     * @param bits the bits number for each index.
+     * @param dataType the data type.
+     * 
+     * @return the ImageTypeSpecifier.
+     */
+    public static ImageTypeSpecifier createIndexed(byte[] redLUT,
+                                                   byte[] greenLUT,
+                                                   byte[] blueLUT,
+                                                   byte[] alphaLUT,
+                                                   int bits,
+                                                   int dataType) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Creates the ImageTypeSpecifier from 
+     * the specified buffered image type.
+     * 
+     * @param bufferedImageType the buffered image type.
+     * 
+     * @return the ImageTypeSpecifier.
+     */
+    public static ImageTypeSpecifier createFromBufferedImageType(int bufferedImageType) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Creates the ImageTypeSpecifier from 
+     * the specified RenderedImage.
+     * 
+     * @param image the RenderedImage.
+     * 
+     * @return the ImageTypeSpecifier.
+     */
+    public static ImageTypeSpecifier createFromRenderedImage(RenderedImage image) {
+        if (null == image) {
+            throw new IllegalArgumentException("image should not be NULL");
+        }
+        return new ImageTypeSpecifier(image);
+    }
+
+    /**
+     * Gets the BufferedImage type.
+     * 
+     * @return the BufferedImage type.
+     */
+    public int getBufferedImageType() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets the number of components.
+     * 
+     * @return the number of components
+     */
+    public int getNumComponents() {
+        return colorModel.getNumComponents();
+    }
+
+    /**
+     * Gets the number of bands.
+     * 
+     * @return the number of bands
+     */
+    public int getNumBands() {
+        return sampleModel.getNumBands();
+    }
+
+    /**
+     * Gets the number of bits per the specified band.
+     * 
+     * @param band the index of band.
+     * 
+     * @return the number of bits per the specified band.
+     */
+    public int getBitsPerBand(int band) {
+        if (band < 0 || band >= getNumBands()) {
+            throw new IllegalArgumentException();
+        }
+        return sampleModel.getSampleSize(band);
+    }
+
+    /**
+     * Gets the SampleModel associated with this ImageTypeSpecifier.
+     * 
+     * @return the SampleModel associated with this ImageTypeSpecifier.
+     */
+    public SampleModel getSampleModel() {
+        return sampleModel;
+    }
+
+    /**
+     * Gets a compatible SampleModel with the specified width and height.
+     * 
+     * @param width the width.
+     * @param height the height.
+     * 
+     * @return the SampleModel.
+     */
+    public SampleModel getSampleModel(int width, int height) {
+        if ((long)width*height > Integer.MAX_VALUE) {
+            throw new IllegalArgumentException("width * height > Integer.MAX_VALUE");
+        }
+        return sampleModel.createCompatibleSampleModel(width, height);
+    }
+
+    /**
+     * Gets the ColorModel associated with this ImageTypeSpecifier.
+     * 
+     * @return the ColorModel associated with this ImageTypeSpecifier. 
+     */
+    public ColorModel getColorModel() {
+        return colorModel;
+    }
+
+    /**
+     * Creates the BufferedImage with the specified width and height 
+     * and the ColorMadel and SampleModel which are specified by this 
+     * ImageTypeSpecifier.
+     * 
+     * @param width the width of the BufferedImage.
+     * @param height the height of the BufferedImage.
+     * 
+     * @return the BufferedImage.
+     */
+    public BufferedImage createBufferedImage(int width, int height) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Compares this ImageTypeSpecifier object with the specified 
+     * object.
+     * 
+     * @param o the Object to be compared.
+     * 
+     * @return true, if the object is an ImageTypeSpecifier with the same
+     * data as this ImageTypeSpecifier, false otherwise.
+     */
+    @Override
+    public boolean equals(Object o) {
+        boolean rt = false;
+        if (o instanceof ImageTypeSpecifier) {
+            ImageTypeSpecifier ts = (ImageTypeSpecifier) o;
+            rt = colorModel.equals(ts.colorModel) && sampleModel.equals(ts.sampleModel);
+        }
+        return rt;
+    }
+}
\ No newline at end of file
diff --git a/awt/javax/imageio/ImageWriteParam.java b/awt/javax/imageio/ImageWriteParam.java
new file mode 100644
index 0000000..d32fa59
--- /dev/null
+++ b/awt/javax/imageio/ImageWriteParam.java
@@ -0,0 +1,633 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import java.util.Locale;
+import java.awt.*;
+
+/**
+ * The ImageWriteParam class provides information to an ImageWriter 
+ * about how an image is to be encoded.
+ */
+public class ImageWriteParam extends IIOParam {
+
+    /** 
+     * The Constant MODE_DISABLED indicates that 
+     * stream is not tiled, progressive, or compressed.
+     */
+    public static final int MODE_DISABLED = 0;
+    
+    /** 
+     * The Constant MODE_DEFAULT indicates that the stream will be tiled, 
+     * progressive, or compressed according to the plug-in's default. 
+     */
+    public static final int MODE_DEFAULT = 1;
+    
+    /** 
+     * The Constant MODE_EXPLICIT indicates that the stream will be tiled,
+     * progressive, or compressed according to current settings
+     * which are defined by set methods. 
+     */
+    public static final int MODE_EXPLICIT = 2;
+    
+    /** 
+     * The Constant MODE_COPY_FROM_METADATA indicates that the stream
+     * will be tiled, progressive, or compressed according to
+     * stream or image metadata. 
+     */
+    public static final int MODE_COPY_FROM_METADATA = 3;
+    
+    /** Whether the ImageWriter can write tiles. */
+    protected boolean canWriteTiles = false;
+    
+    /** The tiling mode. */
+    protected int tilingMode = MODE_COPY_FROM_METADATA;
+    
+    /** The preferred tile sizes. */
+    protected Dimension[] preferredTileSizes = null;
+    
+    /** The tiling set. */
+    protected boolean tilingSet = false;
+    
+    /** The tile width. */
+    protected int tileWidth = 0;
+    
+    /** The tile height. */
+    protected int tileHeight = 0;
+    
+    /** Whether the ImageWriter can offset tiles. */
+    protected boolean canOffsetTiles = false;
+    
+    /** The tile grid x offset. */
+    protected int tileGridXOffset = 0;
+    
+    /** The tile grid y offset. */
+    protected int tileGridYOffset = 0;
+    
+    /** Whether the ImageWriter can write in progressive mode. */
+    protected boolean canWriteProgressive = false;
+    
+    /** The progressive mode. */
+    protected int progressiveMode = MODE_COPY_FROM_METADATA;
+    
+    /** Whether the ImageWriter can write in compressed mode. */
+    protected boolean canWriteCompressed = false;
+    
+    /** The compression mode. */
+    protected int compressionMode = MODE_COPY_FROM_METADATA;
+    
+    /** The compression types. */
+    protected String[] compressionTypes = null;
+    
+    /** The compression type. */
+    protected String compressionType = null;
+    
+    /** The compression quality. */
+    protected float compressionQuality = 1.0f;
+    
+    /** The locale. */
+    protected Locale locale = null;
+
+    /**
+     * Instantiates a new ImageWriteParam.
+     */
+    protected ImageWriteParam() {}
+
+    /**
+     * Instantiates a new ImageWriteParam with the specified Locale.
+     * 
+     * @param locale the Locale.
+     */
+    public ImageWriteParam(Locale locale) {
+        this.locale = locale;
+
+    }
+
+    /**
+     * Gets the mode for writing the stream in a progressive sequence. 
+     * 
+     * @return the current progressive mode.
+     */
+    public int getProgressiveMode() {
+        if (canWriteProgressive()) {
+            return progressiveMode;
+        }
+        throw new UnsupportedOperationException("progressive mode is not supported");
+    }
+
+    /**
+     * Returns true if images can be written using 
+     * increasing quality passes by progressive.  
+     * 
+     * @return true if images can be written using 
+     * increasing quality passes by progressive, false otherwise.
+     */
+    public boolean canWriteProgressive() {
+        return canWriteProgressive;
+    }
+
+    /**
+     * Sets the progressive mode which defines whether the stream 
+     * contains a progressive sequence of increasing quality
+     * during writing. The progressive mode should be one of
+     * the following values: MODE_DISABLED, MODE_DEFAULT, or
+     * MODE_COPY_FROM_METADATA.
+     * 
+     * @param mode the new progressive mode.
+     */
+    public void setProgressiveMode(int mode) {
+        if (canWriteProgressive()) {
+            if (mode < MODE_DISABLED || mode > MODE_COPY_FROM_METADATA || mode == MODE_EXPLICIT) {
+                throw new IllegalArgumentException("mode is not supported");
+            }
+            this.progressiveMode = mode;
+        }
+        throw new UnsupportedOperationException("progressive mode is not supported");
+    }
+
+    /**
+     * Returns true if the writer can use tiles with non zero 
+     * grid offsets while writing. 
+     * 
+     * @return true if the writer can use tiles with non zero 
+     * grid offsets while writing, false otherwise.
+     */
+    public boolean canOffsetTiles() {
+        return canOffsetTiles;
+    }
+
+    /**
+     * Returns true if this writer can write images with 
+     * compression.  
+     * 
+     * @return true, true if this writer can write images with 
+     * compression, false otherwise.
+     */
+    public boolean canWriteCompressed() {
+        return canWriteCompressed;
+    }
+
+    /**
+     * Returns true if the writer can write tiles.
+     * 
+     * @return true if the writer can write tiles, false otherwise.
+     */
+    public boolean canWriteTiles() {
+        return canWriteTiles;
+    }
+
+    /**
+     * Check write compressed.
+     */
+    private final void checkWriteCompressed() {
+        if (!canWriteCompressed()) {
+            throw new UnsupportedOperationException("Compression not supported.");
+        }
+    }
+
+    /**
+     * Check compression mode.
+     */
+    private final void checkCompressionMode() {
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Compression mode not MODE_EXPLICIT!");
+        }
+    }
+
+    /**
+     * Check compression type.
+     */
+    private final void checkCompressionType() {
+        if (getCompressionTypes() != null && getCompressionType() == null) {
+            throw new IllegalStateException("No compression type set!");
+        }
+    }
+
+    /**
+     * Gets the compression mode.
+     * 
+     * @return the compression mode if it's supported.
+     */
+    public int getCompressionMode() {
+        checkWriteCompressed();
+        return compressionMode;
+    }
+
+    /**
+     * Gets the an array of supported compression types.
+     * 
+     * @return the an array of supported compression types.
+     */
+    public String[] getCompressionTypes() {
+        checkWriteCompressed();
+        if (compressionTypes != null) {
+            return compressionTypes.clone();
+        }
+        return null;
+    }
+
+    /**
+     * Gets the current compression type, or returns null.
+     * 
+     * @return the current compression type, or returns null 
+     * if it is not set.
+     */
+    public String getCompressionType() {
+        checkWriteCompressed();
+        checkCompressionMode();
+        return compressionType;
+    }
+
+    /**
+     * Gets a bit rate which represents an estimate of the number of bits 
+     * of output data for each bit of input image data with the specified 
+     * quality.
+     * 
+     * @param quality the quality.
+     * 
+     * @return an estimate of the bit rate, or -1.0F if there is no 
+     * estimate. 
+     */
+    public float getBitRate(float quality) {
+        checkWriteCompressed();
+        checkCompressionMode();
+        checkCompressionType();
+        if (quality < 0 || quality > 1) {
+            throw new IllegalArgumentException("Quality out-of-bounds!");
+        }
+        return -1.0f;
+    }
+
+    /**
+     * Gets the compression quality.
+     * 
+     * @return the compression quality.
+     */
+    public float getCompressionQuality() {
+        checkWriteCompressed();
+        checkCompressionMode();
+        checkCompressionType();
+        return compressionQuality;
+    }
+
+    /**
+     * Gets the array of compression quality descriptions.
+     * 
+     * @return the string array of compression quality descriptions.
+     */
+    public String[] getCompressionQualityDescriptions() {
+        checkWriteCompressed();
+        checkCompressionMode();
+        checkCompressionType();
+        return null;
+    }
+
+    /**
+     * Gets an array of floats which decribe 
+     * compression quality levels. 
+     * 
+     * @return the array of compression quality values.
+     */
+    public float[] getCompressionQualityValues() {
+        checkWriteCompressed();
+        checkCompressionMode();
+        checkCompressionType();
+        return null;
+    }
+
+    /**
+     * Gets the locale of this ImageWriteParam.
+     * 
+     * @return the locale of this ImageWriteParam.
+     */
+    public Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Gets the current compression type using the current Locale. 
+     * 
+     * @return the current compression type using the current Locale.
+     */
+    public String getLocalizedCompressionTypeName() {
+        checkWriteCompressed();
+        checkCompressionMode();
+
+        String compressionType = getCompressionType();
+        if (compressionType == null) {
+            throw new IllegalStateException("No compression type set!");
+        }
+        return compressionType;
+
+    }
+
+    /**
+     * Check tiling.
+     */
+    private final void checkTiling() {
+        if (!canWriteTiles()) {
+            throw new UnsupportedOperationException("Tiling not supported!");
+        }
+    }
+
+    /**
+     * Check tiling mode.
+     */
+    private final void checkTilingMode() {
+        if (getTilingMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Tiling mode not MODE_EXPLICIT!");
+        }
+    }
+
+    /**
+     * Check tiling params.
+     */
+    private final void checkTilingParams() {
+        if (!tilingSet) {
+            throw new IllegalStateException("Tiling parameters not set!");
+        }
+    }
+
+    /**
+     * Gets the tiling mode if tiling is supported.
+     * 
+     * @return the tiling mode if tiling is supported.
+     */
+    public int getTilingMode() {
+        checkTiling();
+        return tilingMode;
+    }
+
+    /**
+     * Gets an array of Dimensions giving the sizes of the tiles as 
+     * they are encoded in the output file or stream. 
+     * 
+     * @return the preferred tile sizes.
+     */
+    public Dimension[] getPreferredTileSizes() {
+        checkTiling();
+        if (preferredTileSizes == null) {
+            return null;
+        }
+
+        Dimension[] retval = new Dimension[preferredTileSizes.length];
+        for (int i = 0; i < preferredTileSizes.length; i++) {
+            retval[i] = new Dimension(retval[i]);
+        }
+        return retval;
+    }
+
+    /**
+     * Gets the tile grid X offset for encoding.
+     * 
+     * @return the tile grid X offset for encoding.
+     */
+    public int getTileGridXOffset() {
+        checkTiling();
+        checkTilingMode();
+        checkTilingParams();
+        return tileGridXOffset;
+    }
+
+    /**
+     * Gets the tile grid Y offset for encoding.
+     * 
+     * @return the tile grid Y offset for encoding.
+     */
+    public int getTileGridYOffset() {
+        checkTiling();
+        checkTilingMode();
+        checkTilingParams();
+        return tileGridYOffset;
+    }
+
+    /**
+     * Gets the tile height in an image as it is written to the 
+     * output stream.
+     * 
+     * @return the tile height in an image as it is written to the 
+     * output stream.
+     */
+    public int getTileHeight() {
+        checkTiling();
+        checkTilingMode();
+        checkTilingParams();
+        return tileHeight;
+    }
+
+    /**
+     * Gets the tile width in an image as it is written to the 
+     * output stream.
+     * 
+     * @return the tile width in an image as it is written to the 
+     * output stream.
+     */
+    public int getTileWidth() {
+        checkTiling();
+        checkTilingMode();
+        checkTilingParams();
+        return tileWidth;
+    }
+
+    /**
+     * Checks if the current compression type has lossless 
+     * compression or not. 
+     * 
+     * @return true, if the current compression type has lossless 
+     * compression, false otherwise.
+     */
+    public boolean isCompressionLossless() {
+        checkWriteCompressed();
+        checkCompressionMode();
+        checkCompressionType();
+        return true;
+    }
+
+    /**
+     * Removes current compression type.
+     */
+    public void unsetCompression() {
+        checkWriteCompressed();
+        checkCompressionMode();
+        compressionType = null;
+        compressionQuality = 1;
+    }
+
+    /**
+     * Sets the compression mode to the specified value.
+     * The specified mode can be one of the predefined
+     * constants: MODE_DEFAULT, MODE_DISABLED, MODE_EXPLICIT, 
+     * or MODE_COPY_FROM_METADATA.
+     *  
+     * @param mode the new compression mode to be set.
+     */
+    public void setCompressionMode(int mode) {
+        checkWriteCompressed();
+        switch (mode) {
+            case MODE_EXPLICIT: {
+                compressionMode = mode;
+                unsetCompression();
+                break;
+            }
+            case MODE_COPY_FROM_METADATA:
+            case MODE_DISABLED:
+            case MODE_DEFAULT: {
+                compressionMode = mode;
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException("Illegal value for mode!");
+            }
+        }
+    }
+
+    /**
+     * Sets the compression quality. The value should be between 0 and 1.
+     * 
+     * @param quality the new compression quality, 
+     * float value between 0 and 1. 
+     */
+    public void setCompressionQuality(float quality) {
+        checkWriteCompressed();
+        checkCompressionMode();
+        checkCompressionType();
+        if (quality < 0 || quality > 1) {
+            throw new IllegalArgumentException("Quality out-of-bounds!");
+        }
+        compressionQuality = quality;
+    }
+
+    /**
+     * Sets the compression type. The specified string
+     * should be one of the values returned 
+     * by getCompressionTypes method.
+     * 
+     * @param compressionType the new compression type.
+     */
+    public void setCompressionType(String compressionType) {
+        checkWriteCompressed();
+        checkCompressionMode();
+
+        if (compressionType == null) { // Don't check anything
+            this.compressionType = null;
+        } else {
+            String[] compressionTypes = getCompressionTypes();
+            if (compressionTypes == null) {
+                throw new UnsupportedOperationException("No settable compression types");
+            }
+
+            for (int i = 0; i < compressionTypes.length; i++) {
+                if (compressionTypes[i].equals(compressionType)) {
+                    this.compressionType = compressionType;
+                    return;
+                }
+            }
+
+            // Compression type is not in the list.
+            throw new IllegalArgumentException("Unknown compression type!");
+        }
+    }
+
+    /**
+     * Sets the instruction that tiling should be performed for 
+     * the image in the output stream with the specified parameters. 
+     * 
+     * @param tileWidth the tile's width.
+     * @param tileHeight the tile's height.
+     * @param tileGridXOffset the tile grid's x offset.
+     * @param tileGridYOffset the tile grid's y offset.
+     */
+    public void setTiling(int tileWidth, int tileHeight, int tileGridXOffset, int tileGridYOffset) {
+        checkTiling();
+        checkTilingMode();
+
+        if (!canOffsetTiles() && (tileGridXOffset != 0 || tileGridYOffset != 0)) {
+            throw new UnsupportedOperationException("Can't offset tiles!");
+        }
+
+        if (tileWidth <=0 || tileHeight <= 0) {
+            throw new IllegalArgumentException("tile dimensions are non-positive!");
+        }
+
+        Dimension preferredTileSizes[] = getPreferredTileSizes();
+        if (preferredTileSizes != null) {
+            for (int i = 0; i < preferredTileSizes.length; i+=2) {
+                Dimension minSize = preferredTileSizes[i];
+                Dimension maxSize = preferredTileSizes[i+1];
+                if (
+                        tileWidth < minSize.width || tileWidth > maxSize.width ||
+                        tileHeight < minSize.height || tileHeight > maxSize.height
+                ) {
+                    throw new IllegalArgumentException("Illegal tile size!");
+                }
+            }
+        }
+
+        tilingSet = true;
+        this.tileWidth = tileWidth;
+        this.tileHeight = tileHeight;
+        this.tileGridXOffset = tileGridXOffset;
+        this.tileGridYOffset = tileGridYOffset;
+    }
+
+    /**
+     * Clears all tiling settings.
+     */
+    public void unsetTiling() {
+        checkTiling();
+        checkTilingMode();
+
+        tilingSet = false;
+        tileWidth = 0;
+        tileHeight = 0;
+        tileGridXOffset = 0;
+        tileGridYOffset = 0;
+    }
+
+    /**
+     * Sets the tiling mode. The specified mode should be one of the 
+     * following values: MODE_DISABLED, MODE_DEFAULT, MODE_EXPLICIT,
+     * or MODE_COPY_FROM_METADATA.
+     * 
+     * @param mode the new tiling mode.
+     */
+    public void setTilingMode(int mode) {
+        checkTiling();
+
+        switch (mode) {
+            case MODE_EXPLICIT: {
+                tilingMode = mode;
+                unsetTiling();
+                break;
+            }
+            case MODE_COPY_FROM_METADATA:
+            case MODE_DISABLED:
+            case MODE_DEFAULT: {
+                tilingMode = mode;
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException("Illegal value for mode!");
+            }
+        }
+    }
+}
+
diff --git a/awt/javax/imageio/ImageWriter.java b/awt/javax/imageio/ImageWriter.java
new file mode 100644
index 0000000..d6119b0
--- /dev/null
+++ b/awt/javax/imageio/ImageWriter.java
@@ -0,0 +1,951 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.imageio.event.IIOWriteProgressListener;
+import javax.imageio.event.IIOWriteWarningListener;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.spi.ImageWriterSpi;
+
+/**
+ * The ImageWriter class is an abstract class for encoding images.
+ * ImageWriter objects are instantiated by the service provider 
+ * interface, ImageWriterSpi class, for the specific format. 
+ * ImageWriterSpi class should be registered with the IIORegistry, 
+ * which uses them for format recognition and presentation of available 
+ * format readers and writers.
+ */
+public abstract class ImageWriter implements ImageTranscoder {
+
+    /** The available locales. */
+    protected Locale[] availableLocales;
+    
+    /** The locale. */
+    protected Locale locale;
+    
+    /** The originating provider. */
+    protected ImageWriterSpi originatingProvider;
+    
+    /** The output. */
+    protected Object output;
+    
+    /** The progress listeners. */
+    protected List<IIOWriteProgressListener> progressListeners;
+    
+    /** The warning listeners. */
+    protected List<IIOWriteWarningListener> warningListeners;
+    
+    /** The warning locales. */
+    protected List<Locale> warningLocales;
+
+    // Indicates that abort operation is requested
+    // Abort mechanism should be thread-safe
+    /** The aborted. */
+    private boolean aborted;
+
+    /**
+     * Instantiates a new ImageWriter.
+     * 
+     * @param originatingProvider the ImageWriterSpi which 
+     * instanties this ImageWriter.
+     */
+    protected ImageWriter(ImageWriterSpi originatingProvider) {
+        this.originatingProvider = originatingProvider;
+    }
+
+    public abstract IIOMetadata convertStreamMetadata(IIOMetadata iioMetadata,
+                                             ImageWriteParam imageWriteParam);
+
+    public abstract IIOMetadata convertImageMetadata(IIOMetadata iioMetadata,
+                                            ImageTypeSpecifier imageTypeSpecifier,
+                                            ImageWriteParam imageWriteParam);
+
+    /**
+     * Gets the ImageWriterSpi which instantiated this ImageWriter. 
+     * 
+     * @return the ImageWriterSpi.
+     */
+    public ImageWriterSpi getOriginatingProvider() {
+        return originatingProvider;
+    }
+
+    /**
+     * Processes the start of an image read by calling their imageStarted
+     * method of registered IIOWriteProgressListeners.
+     * 
+     * @param imageIndex the image index.
+     */
+    protected void processImageStarted(int imageIndex) {
+        if (null != progressListeners) {
+            for (IIOWriteProgressListener listener : progressListeners) {
+                listener.imageStarted(this, imageIndex);
+            }
+        }
+    }
+
+    /**
+     * Processes the current percentage of image completion by calling 
+     * imageProgress method of registered IIOWriteProgressListener.
+     * 
+     * @param percentageDone the percentage done.
+     */
+    protected void processImageProgress(float percentageDone) {
+        if (null != progressListeners) {
+            for (IIOWriteProgressListener listener : progressListeners) {
+                listener.imageProgress(this, percentageDone);
+            }
+        }
+    }
+
+    /**
+     * Processes image completion by calling imageComplete method
+     * of registered IIOWriteProgressListeners. 
+     */
+    protected void processImageComplete() {
+        if (null != progressListeners) {
+            for (IIOWriteProgressListener listener : progressListeners) {
+                listener.imageComplete(this);
+            }
+        }
+    }
+
+    /**
+     * Processes a warning message by calling warningOccurred method
+     * of registered IIOWriteWarningListeners.
+     * 
+     * @param imageIndex the image index.
+     * @param warning the warning.
+     */
+    protected void processWarningOccurred(int imageIndex, String warning) {
+        if (null == warning) {
+            throw new NullPointerException("warning message should not be NULL");
+        }
+        if (null != warningListeners) {
+            for (IIOWriteWarningListener listener : warningListeners) {
+                listener.warningOccurred(this, imageIndex, warning);
+            }
+        }
+    }
+
+    /**
+     * Processes a warning message by calling warningOccurred method
+     * of registered IIOWriteWarningListeners with string from 
+     * ResourceBundle.
+     * 
+     * @param imageIndex the image index.
+     * @param bundle the name of ResourceBundle.
+     * @param key the keyword.
+     */
+    protected void processWarningOccurred(int imageIndex, String bundle, String key) {
+        if (warningListeners != null) { // Don't check the parameters
+            return;
+        }
+
+        if (bundle == null) {
+            throw new IllegalArgumentException("baseName == null!");
+        }
+        if (key == null) {
+            throw new IllegalArgumentException("keyword == null!");
+        }
+
+        // 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();
+                    }
+        });
+
+        // Iterate through both listeners and locales
+        int n = warningListeners.size();
+        for (int i=0; i < n; i++) {
+            IIOWriteWarningListener listener = warningListeners.get(i);
+            Locale locale = warningLocales.get(i);
+
+            // Now try to get the resource bundle
+            ResourceBundle rb;
+            try {
+                rb = ResourceBundle.getBundle(bundle, locale, contextClassloader);
+            } catch (MissingResourceException e) {
+                try {
+                    rb = ResourceBundle.getBundle(bundle, locale);
+                } catch (MissingResourceException e1) {
+                    throw new IllegalArgumentException("Bundle not found!");
+                }
+            }
+
+            try {
+                String warning = rb.getString(key);
+                listener.warningOccurred(this, imageIndex, warning);
+            } catch (MissingResourceException e) {
+                throw new IllegalArgumentException("Resource is missing!");
+            } catch (ClassCastException e) {
+                throw new IllegalArgumentException("Resource is not a String!");
+            }
+        }
+    }
+
+    /**
+     * Sets the specified Object to the output of this ImageWriter. 
+     * 
+     * @param output the Object which represents destination, it can 
+     * be ImageOutputStream or other objects.
+     */
+    public void setOutput(Object output) {
+        if (output != null) {
+            ImageWriterSpi spi = getOriginatingProvider();
+            if (null != spi) {
+                Class[] outTypes = spi.getOutputTypes();
+                boolean supported = false;
+                for (Class<?> element : outTypes) {
+                    if (element.isInstance(output)) {
+                        supported = true;
+                        break;
+                    }
+                }
+                if (!supported) {
+                    throw new IllegalArgumentException("output " + output + " is not supported");
+                }
+            }
+        }
+        this.output = output;
+    }
+
+    /**
+     * Writes a completed image stream that contains the specified image, 
+     * default metadata, and thumbnails to the output.
+     * 
+     * @param image the specified image to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred
+     * during writting.
+     */
+    public void write(IIOImage image) throws IOException {
+        write(null, image, null);
+    }
+
+    /**
+     * Writes a completed image stream that contains the specified 
+     * rendered image, default metadata, and thumbnails to the output.
+     * 
+     * @param image the specified RenderedImage to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred
+     * during writting.
+     */
+    public void write(RenderedImage image) throws IOException {
+        write(null, new IIOImage(image, null, null), null);
+    }
+
+    /**
+     * Writes a completed image stream that contains the specified image,
+     * metadata and thumbnails to the output.
+     * 
+     * @param streamMetadata the stream metadata, or null.
+     * @param image the specified image to be written, if
+     * canWriteRaster() method returns false, then Image must contain 
+     * only RenderedImage.
+     * @param param the ImageWriteParam, or null.
+     * 
+     * @throws IOException - if an error occurs during writing.
+     */
+    public abstract void write(IIOMetadata streamMetadata,
+                               IIOImage image, ImageWriteParam param) throws IOException;
+
+    /**
+     * Disposes of any resources.
+     */
+    public void dispose() {
+        // def impl. does nothing according to the spec.
+    }
+
+    /**
+     * Requests an abort operation for current writing operation. 
+     */
+    public synchronized void abort() {
+        aborted = true;
+    }
+
+    /**
+     * Checks whether or not a request to abort the current write operation 
+     * has been made successfully.
+     * 
+     * @return true, if the request to abort the current write operation 
+     * has been made successfully, false otherwise.
+     */
+    protected synchronized boolean abortRequested() {
+        return aborted;
+    }
+
+    /**
+     * Clears all previous abort request, and abortRequested returns false
+     * after calling this method.
+     */
+    protected synchronized void clearAbortRequest() {
+        aborted = false;
+    }
+
+    /**
+     * Adds the IIOWriteProgressListener listener.
+     * 
+     * @param listener the IIOWriteProgressListener listener.
+     */
+    public void addIIOWriteProgressListener(IIOWriteProgressListener listener) {
+        if (listener == null) {
+            return;
+        }
+
+        if (progressListeners == null) {
+            progressListeners = new ArrayList<IIOWriteProgressListener>();
+        }
+
+        progressListeners.add(listener);
+    }
+
+    /**
+     * Adds the IIOWriteWarningListener.
+     * 
+     * @param listener the IIOWriteWarningListener listener.
+     */
+    public void addIIOWriteWarningListener(IIOWriteWarningListener listener) {
+        if (listener == null) {
+            return;
+        }
+
+        if (warningListeners == null) {
+            warningListeners = new ArrayList<IIOWriteWarningListener>();
+            warningLocales = new ArrayList<Locale>();
+        }
+
+        warningListeners.add(listener);
+        warningLocales.add(getLocale());
+    }
+
+    /**
+     * Gets the output object that was set by setOutput method.
+     * 
+     * @return the output object such as ImageOutputStream, or null if
+     * it is not set.
+     */
+    public Object getOutput() {
+        return output;
+    }
+
+    /**
+     * Check output return false.
+     * 
+     * @return true, if successful
+     */
+    private final boolean checkOutputReturnFalse() {
+        if (getOutput() == null) {
+            throw new IllegalStateException("getOutput() == null!");
+        }
+        return false;
+    }
+
+    /**
+     * Unsupported operation.
+     */
+    private final void unsupportedOperation() {
+        if (getOutput() == null) {
+            throw new IllegalStateException("getOutput() == null!");
+        }
+        throw new UnsupportedOperationException("Unsupported write variant!");
+    }
+
+
+    /**
+     * Returns true if a new empty image can be inserted at 
+     * the specified index. 
+     * 
+     * @param imageIndex the specified index of image.
+     * 
+     * @return true if a new empty image can be inserted at 
+     * the specified index, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean canInsertEmpty(int imageIndex) throws IOException {
+        return checkOutputReturnFalse();
+    }
+
+    /**
+     * Returns true if a new image can be inserted at the specified index. 
+     * 
+     * @param imageIndex the specified index of image.
+     * 
+     * @return true if a new image can be inserted at the specified index, 
+     * false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean canInsertImage(int imageIndex) throws IOException {
+        return checkOutputReturnFalse();
+    }
+
+    /**
+     * Returnes true if the image with the specified index can be removed.
+     * 
+     * @param imageIndex the specified index of image.
+     * 
+     * @return true if the image with the specified index can be removed,
+     * false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean canRemoveImage(int imageIndex) throws IOException {
+        return checkOutputReturnFalse();
+    }
+
+    /**
+     * Returns true if metadata of the image with the specified index
+     * can be replaced.
+     * 
+     * @param imageIndex the specified image index.
+     * 
+     * @return true if metadata of the image with the specified index
+     * can be replaced, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean canReplaceImageMetadata(int imageIndex) throws IOException {
+        return checkOutputReturnFalse();
+    }
+
+    /**
+     * Returns true if pixels of the image with the specified index 
+     * can be replaced by the replacePixels  methods.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @return true if pixels of the image with the specified index 
+     * can be replaced by the replacePixels  methods, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean canReplacePixels(int imageIndex) throws IOException {
+        return checkOutputReturnFalse();
+    }
+
+    /**
+     * Returns true if the stream metadata presented in the output
+     * can be removed.
+     * 
+     * @return true if the stream metadata presented in the output
+     * can be removed, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean canReplaceStreamMetadata() throws IOException {
+        return checkOutputReturnFalse();
+    }
+
+    /**
+     * Returns true if the writing of a complete image stream which
+     * contains a single image is supported with undefined pixel 
+     * values and associated metadata and thumbnails to the output. 
+     * 
+     * @return true if the writing of a complete image stream which
+     * contains a single image is supported, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public boolean canWriteEmpty() throws IOException {
+        return checkOutputReturnFalse();
+    }
+
+    /**
+     * Returns true if the methods which taken an IIOImageParameter 
+     * can deal with a Raster source image.
+     * 
+     * @return true if the methods which taken an IIOImageParameter 
+     * can deal with a Raster source image, false otherwise.
+     */
+    public boolean canWriteRasters() {
+        return false;
+    }
+
+    /**
+     * Returns true if the writer can add an image to stream that
+     * already contains header information.
+     * 
+     * @return if the writer can add an image to stream that
+     * already contains header information, false otherwise.
+     */
+    public boolean canWriteSequence() {
+        return false;
+    }
+
+    /**
+     * Ends the insertion of a new image.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void endInsertEmpty() throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Ends the repalce pixels operation.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void endReplacePixels() throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Ends an empty write operation.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void endWriteEmpty() throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Ends the sequence of write operations.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void endWriteSequence() throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Gets an array of available locales.
+     * 
+     * @return an of array available locales.
+     */
+    public Locale[] getAvailableLocales() {
+        if (availableLocales == null) {
+            return null;
+        }
+
+        return availableLocales.clone();
+    }
+
+    /**
+     * Gets an IIOMetadata object that contains default values 
+     * for encoding an image with the specified type. 
+     * 
+     * @param imageType the ImageTypeSpecifier.
+     * @param param the ImageWriteParam.
+     * 
+     * @return the IIOMetadata object.
+     */
+    public abstract IIOMetadata getDefaultImageMetadata(
+            ImageTypeSpecifier imageType,
+            ImageWriteParam param
+    );
+
+    /**
+     * Gets an IIOMetadata object that contains default values 
+     * for encoding a stream of images.
+     * 
+     * @param param the ImageWriteParam.
+     * 
+     * @return the IIOMetadata object.
+     */
+    public abstract IIOMetadata getDefaultStreamMetadata(ImageWriteParam param);
+
+    /**
+     * Gets the current locale of this ImageWriter.
+     * 
+     * @return the current locale of this ImageWriter.
+     */
+    public Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Gets the default write param.
+     * Gets a new ImageWriteParam object for this ImageWriter with the
+     * current Locale.
+     * 
+     * @return a new ImageWriteParam object for this ImageWriter.
+     */
+    public ImageWriteParam getDefaultWriteParam() {
+        return new ImageWriteParam(getLocale());
+    }
+
+    /**
+     * Gets the number of thumbnails suported by the format 
+     * being written with supported image type, image write 
+     * parameters, stream, and image metadata objects.
+     * 
+     * @param imageType the ImageTypeSpecifier.
+     * @param param the image's parameters.
+     * @param streamMetadata the stream metadata.
+     * @param imageMetadata the image metadata.
+     * 
+     * @return the number of thumbnails supported
+     */
+    public int getNumThumbnailsSupported(
+            ImageTypeSpecifier imageType,
+            ImageWriteParam param,
+            IIOMetadata streamMetadata,
+            IIOMetadata imageMetadata
+    ) {
+        return 0;
+    }
+
+    /**
+     * Gets the preferred thumbnail sizes.
+     * Gets an array of Dimensions with the sizes for thumbnail images 
+     * as they are encoded in the output file or stream. 
+     * 
+     * @param imageType the ImageTypeSpecifier.
+     * @param param the ImageWriteParam.
+     * @param streamMetadata the stream metadata.
+     * @param imageMetadata the image metadata.
+     * 
+     * @return the preferred thumbnail sizes
+     */
+    public Dimension[] getPreferredThumbnailSizes(
+            ImageTypeSpecifier imageType,
+            ImageWriteParam param,
+            IIOMetadata streamMetadata,
+            IIOMetadata imageMetadata
+    ) {
+        return null;
+    }
+
+    /**
+     * Prepares insertion of an empty image by requesting the insertion of 
+     * a new image into an existing image stream.
+     * 
+     * @param imageIndex the image index.
+     * @param imageType the image type.
+     * @param width the width of the image.
+     * @param height the height of the image.
+     * @param imageMetadata the image metadata, or null.
+     * @param thumbnails the array thumbnails for this image, or null.
+     * @param param the ImageWriteParam, or null.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void prepareInsertEmpty(
+            int imageIndex, ImageTypeSpecifier imageType,
+            int width, int height,
+            IIOMetadata imageMetadata, List<? extends BufferedImage> thumbnails,
+            ImageWriteParam param
+    ) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Prepares the writer to call the replacePixels method for the
+     * specified region. 
+     * 
+     * @param imageIndex the image's index.
+     * @param region the specified region.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void prepareReplacePixels(int imageIndex, Rectangle region) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Prepares the writer for writing an empty image by beginning the 
+     * process of writing a complete image stream that contains a single image 
+     * with undefined pixel values, metadata and thumbnails, 
+     * to the output. 
+     * 
+     * @param streamMetadata the stream metadata.
+     * @param imageType the image type.
+     * @param width the width of the image.
+     * @param height the height of the image.
+     * @param imageMetadata the image's metadata, or null.
+     * @param thumbnails the image's thumbnails, or null.
+     * @param param the image's parameters, or null.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void prepareWriteEmpty(
+            IIOMetadata streamMetadata, ImageTypeSpecifier imageType,
+            int width, int height,
+            IIOMetadata imageMetadata, List<? extends BufferedImage> thumbnails,
+            ImageWriteParam param
+    ) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Prepares a stream to accept calls of writeToSequence method 
+     * using the metadata object. 
+     * 
+     * @param streamMetadata the stream metadata.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void prepareWriteSequence(IIOMetadata streamMetadata) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Processes the completion of a thumbnail read 
+     * by calling their thumbnailComplete method 
+     * of registered IIOWriteProgressListeners. 
+     */
+    protected void processThumbnailComplete() {
+        if (progressListeners != null) {
+            for (IIOWriteProgressListener listener : progressListeners) {
+                listener.thumbnailComplete(this);
+            }
+        }
+    }
+
+    /**
+     * Processes the current percentage of thumbnail completion 
+     * by calling their thumbnailProgress method of registered 
+     * IIOWriteProgressListeners.
+     * 
+     * @param percentageDone the percentage done.
+     */
+    protected void processThumbnailProgress(float percentageDone) {
+        if (progressListeners != null) {
+            for (IIOWriteProgressListener listener : progressListeners) {
+                listener.thumbnailProgress(this, percentageDone);
+            }
+        }
+    }
+
+    /**
+     * Processes the start of a thumbnail read by calling 
+     * thumbnailStarted method of registered IIOWriteProgressListeners. 
+     * 
+     * @param imageIndex the image index.
+     * @param thumbnailIndex the thumbnail index.
+     */
+    protected void processThumbnailStarted(int imageIndex, int thumbnailIndex) {
+        if (progressListeners != null) {
+            for (IIOWriteProgressListener listener : progressListeners) {
+                listener.thumbnailStarted(this, imageIndex, thumbnailIndex);
+            }
+        }
+    }
+
+    /**
+     * Processes that the writing has been aborted by calling writeAborted 
+     * method of registered IIOWriteProgressListeners.
+     */
+    protected void processWriteAborted() {
+        if (progressListeners != null) {
+            for (IIOWriteProgressListener listener : progressListeners) {
+                listener.writeAborted(this);
+            }
+        }
+    }
+
+    /**
+     * Removes the all IIOWriteProgressListener listeners.
+     */
+    public void removeAllIIOWriteProgressListeners() {
+        progressListeners = null;
+    }
+
+    /**
+     * Removes the all IIOWriteWarningListener listeners.
+     */
+    public void removeAllIIOWriteWarningListeners() {
+        warningListeners = null;
+        warningLocales = null;
+    }
+
+    /**
+     * Removes the specified IIOWriteProgressListener listener.
+     * 
+     * @param listener the registered IIOWriteProgressListener 
+     * to be removed.
+     */
+    public void removeIIOWriteProgressListener(IIOWriteProgressListener listener) {
+        if (progressListeners != null && listener != null) {
+            if (progressListeners.remove(listener) && progressListeners.isEmpty()) {
+                progressListeners = null;
+            }
+        }
+    }
+
+    /**
+     * Removes the specified IIOWriteWarningListener listener.
+     * 
+     * @param listener the registered IIOWriteWarningListener listener
+     * to be removed.
+     */
+    public void removeIIOWriteWarningListener(IIOWriteWarningListener listener) {
+        if (warningListeners == null || listener == null) {
+            return;
+        }
+
+        int idx = warningListeners.indexOf(listener);
+        if (idx > -1) {
+            warningListeners.remove(idx);
+            warningLocales.remove(idx);
+
+            if (warningListeners.isEmpty()) {
+                warningListeners = null;
+                warningLocales = null;
+            }
+        }
+    }
+
+    /**
+     * Removes the image with the specified index from the stream.
+     * 
+     * @param imageIndex the image's index.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void removeImage(int imageIndex) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Replaces image metadata of the image with specified index.
+     * 
+     * @param imageIndex the image's index.
+     * @param imageMetadata the image metadata.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void replaceImageMetadata(int imageIndex, IIOMetadata imageMetadata) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Replaces a part of an image presented in the output
+     * with the specified RenderedImage.
+     * 
+     * @param image the RenderedImage.
+     * @param param the ImageWriteParam.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void replacePixels(RenderedImage image, ImageWriteParam param) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Replaces a part of an image presented in the output
+     * with the specified Raster.
+     * 
+     * @param raster the Raster.
+     * @param param the ImageWriteParam.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void replacePixels(Raster raster, ImageWriteParam param) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Replaces the stream metadata of the output with new IIOMetadata.
+     * 
+     * @param streamMetadata the new stream metadata.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void replaceStreamMetadata(IIOMetadata streamMetadata) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Sets the locale of this ImageWriter.
+     * 
+     * @param locale the new locale.
+     */
+    public void setLocale(Locale locale) {
+        if (locale == null) {
+            this.locale = null;
+            return;
+        }
+
+        Locale[] locales = getAvailableLocales();
+        boolean validLocale = false;
+        if (locales != null) {
+            for (int i = 0; i < locales.length; i++) {
+                if (locale.equals(locales[i])) {
+                    validLocale = true;
+                    break;
+                }
+            }
+        }
+
+        if (validLocale) {
+            this.locale = locale;
+        } else {
+            throw new IllegalArgumentException("Invalid locale!");
+        }
+    }
+
+    /**
+     * Resets this ImageWriter.
+     */
+    public void reset() {
+        setOutput(null);
+        setLocale(null);
+        removeAllIIOWriteWarningListeners();
+        removeAllIIOWriteProgressListeners();
+        clearAbortRequest();
+    }
+
+    /**
+     * Inserts image into existing output stream. 
+     * 
+     * @param imageIndex the image index where an image will be written.
+     * @param image the specified image to be written.
+     * @param param the ImageWriteParam, or null.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void writeInsert(int imageIndex, IIOImage image, ImageWriteParam param) throws IOException {
+        unsupportedOperation();
+    }
+
+    /**
+     * Writes the specified image to the sequence.
+     * 
+     * @param image the image to be written.
+     * @param param the ImageWriteParam, or null.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred 
+     * during writting.
+     */
+    public void writeToSequence(IIOImage image, ImageWriteParam param) throws IOException {
+        unsupportedOperation();
+    }
+}
diff --git a/awt/javax/imageio/event/IIOReadProgressListener.java b/awt/javax/imageio/event/IIOReadProgressListener.java
new file mode 100644
index 0000000..3d65807
--- /dev/null
+++ b/awt/javax/imageio/event/IIOReadProgressListener.java
@@ -0,0 +1,103 @@
+/*
+ *  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.event;
+
+import java.util.EventListener;
+import javax.imageio.ImageReader;
+
+/**
+ * The IIOReadProgressListener interface notifies callers 
+ * about the progress of the image and thumbnail reading methods.
+ */
+public interface IIOReadProgressListener extends EventListener {
+
+    /**
+     * Notifies this listener that the image reading has been completed. 
+     * 
+     * @param source the ImageReader object which calls this method.
+     */
+    void imageComplete(ImageReader source);
+    
+    /**
+     * Notifies this listener about the degree of completion of the read call.
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param percentageDone the percentage of decoding done.
+     */
+    void imageProgress(ImageReader source, float percentageDone);
+    
+    /**
+     * Notifies this listener that an image read operation has been started. 
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param imageIndex the index of the image in an input file or 
+     * stream to be read.
+     */
+    void imageStarted(ImageReader source, int imageIndex);
+    
+    /**
+     * Notifies this listener that a read operation has been aborted.
+     * 
+     * @param source the ImageReader object which calls this method.
+     */
+    void readAborted(ImageReader source);
+    
+    /**
+     * Notifies this listener that a sequence of read operations has been completed. 
+     * 
+     * @param source the ImageReader object which calls this method.
+     */
+    void sequenceComplete(ImageReader source);
+    
+    /**
+     * Notifies this listener that a sequence of read operation has been started. 
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param minIndex the index of the first image to be read.
+     */
+    void sequenceStarted(ImageReader source, int minIndex);
+    
+    /**
+     * Notifies that a thumbnail read operation has been completed.
+     * 
+     * @param source the ImageReader object which calls this method.
+     */
+    void thumbnailComplete(ImageReader source);
+    
+    /**
+     * Notifies this listener about the degree of completion of the read call.
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param percentageDone the percentage of decoding done.
+     */
+    void thumbnailProgress(ImageReader source, float percentageDone);
+    
+    /**
+     * Notifies this listener that a thumbnail reading operation has been started. 
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param imageIndex the index of the image in an input file or 
+     * stream to be read.
+     * @param thumbnailIndex the index of the thumbnail to be read.
+     */
+    void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex);
+}
+
diff --git a/awt/javax/imageio/event/IIOReadUpdateListener.java b/awt/javax/imageio/event/IIOReadUpdateListener.java
new file mode 100644
index 0000000..ce5e2f1
--- /dev/null
+++ b/awt/javax/imageio/event/IIOReadUpdateListener.java
@@ -0,0 +1,138 @@
+/*
+ *  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.event;
+
+import java.awt.image.BufferedImage;
+import java.util.EventListener;
+import javax.imageio.ImageReader;
+
+/*
+ * @author Sergey I. Salishev
+ * @version $Revision: 1.2 $
+ */
+
+/**
+ * The IIOReadUpdateListener interface provides functionality 
+ * to receive notification of pixel updates during image and thumbnail 
+ * reading operations.
+ */
+public interface IIOReadUpdateListener extends EventListener {
+
+    /**
+     * Notifies this listener that the specified area of the image has been updated. 
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param theImage the image to be updated.
+     * @param minX the minimum X coordinate of the pixels in the updated area.
+     * @param minY the minimum Y coordinate of the pixels in the updated area.
+     * @param width the width of updated area.
+     * @param height the height of updated area.
+     * @param periodX the horizontal spacing period between updated 
+     * pixels, if it equals 1, there is no space between pixels. 
+     * @param periodY the vertical spacing period between updated 
+     * pixels, if it equals 1, there is no space between pixels.
+     * @param bands the array of int values indicating the bands being updated.
+     */
+    void imageUpdate(ImageReader source, BufferedImage theImage, int minX,
+            int minY, int width, int height, int periodX, int periodY,
+            int[] bands);
+    
+    /**
+     * Notifies this listener that the current read operation has completed a 
+     * progressive pass.
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param theImage the image to be updated.
+     */
+    void passComplete(ImageReader source, BufferedImage theImage);
+    
+    /**
+     * Notifies this listener that the current read operation has begun 
+     * a progressive pass.
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param theImage the image to be updated.
+     * @param pass the numer of the pass.
+     * @param minPass the index of the first pass that will be decoded.
+     * @param maxPass the index of the last pass that will be decoded.
+     * @param minX the minimum X coordinate of the pixels in the updated area.
+     * @param minY the minimum Y coordinate of the pixels in the updated area.
+     * @param periodX the horizontal spacing period between updated 
+     * pixels, if it equals 1, there is no space between pixels. 
+      * @param periodY the vertical spacing period between updated 
+     * pixels, if it equals 1, there is no space between pixels.
+     * @param bands the array of int values indicating the bands being updated.
+     */
+    void passStarted(ImageReader source, BufferedImage theImage, int pass,
+            int minPass, int maxPass, int minX, int minY, int periodX,
+            int periodY, int[] bands);
+
+    /**
+     * Notifies this listener that the current thumbnail read operation has  
+     * completed a progressive pass.
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param theImage the thumbnail to be updated.
+     */
+    void thumbnailPassComplete(ImageReader source, BufferedImage theImage);
+    
+    /**
+     * Notifies this listener that the current thumbnail read operation has  
+     * begun a progressive pass.
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param theThumbnail the thumbnail to be updated.
+     * @param pass the numer of the pass.
+     * @param minPass the index of the first pass that will be decoded.
+     * @param maxPass the index of the last pass that will be decoded.
+     * @param minX the minimum X coordinate of the pixels in the updated area.
+     * @param minY the minimum Y coordinate of the pixels in the updated area.
+     * @param periodX the horizontal spacing period between updated 
+     * pixels, if it equals 1, there is no space between pixels. 
+     * @param periodY the vertical spacing period between updated 
+     * pixels, if it equals 1, there is no space between pixels.
+     * @param bands the array of int values indicating the bands being updated.
+     */
+    void thumbnailPassStarted(ImageReader source, BufferedImage theThumbnail,
+            int pass, int minPass, int maxPass, int minX, int minY,
+            int periodX, int periodY, int[] bands);
+    
+    /**
+     * Notifies this listener that a specified area of a thumbnail image has been 
+     * updated.
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param theThumbnail the thumbnail to be updated.
+     * @param minX the minimum X coordinate of the pixels in the updated area.
+     * @param minY the minimum Y coordinate of the pixels in the updated area.
+     * @param width the width of updated area.
+     * @param height the height of updated area.
+     * @param periodX the horizontal spacing period between updated 
+     * pixels, if it equals 1, there is no space between pixels. 
+     * @param periodY the vertical spacing period between updated 
+     * pixels, if it equals 1, there is no space between pixels.
+     * @param bands the array of int values indicating the bands being updated.
+     */
+    void thumbnailUpdate(ImageReader source, BufferedImage theThumbnail,
+            int minX, int minY, int width, int height, int periodX,
+            int periodY, int[] bands);
+}
+
diff --git a/awt/javax/imageio/event/IIOReadWarningListener.java b/awt/javax/imageio/event/IIOReadWarningListener.java
new file mode 100644
index 0000000..92fa275
--- /dev/null
+++ b/awt/javax/imageio/event/IIOReadWarningListener.java
@@ -0,0 +1,46 @@
+/*
+ *  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.event;
+
+import java.util.EventListener;
+import javax.imageio.ImageReader;
+
+/* 
+ * @author Sergey I. Salishev
+ * @version $Revision: 1.2 $
+ */
+
+/**
+ * The IIOReadWarningListener provides methods to receive notification
+ * of warning messages generated by image 
+ * and thumbnail reading methods.
+ */
+public interface IIOReadWarningListener extends EventListener {
+
+    /**
+     * Notifies this listener about a warning (non-fatal error) during decoding. 
+     * 
+     * @param source the ImageReader object which calls this method.
+     * @param warning the string describing the warning.
+     */
+    public void warningOccurred(ImageReader source, String warning);
+}
+
diff --git a/awt/javax/imageio/event/IIOWriteProgressListener.java b/awt/javax/imageio/event/IIOWriteProgressListener.java
new file mode 100644
index 0000000..19ae495
--- /dev/null
+++ b/awt/javax/imageio/event/IIOWriteProgressListener.java
@@ -0,0 +1,86 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.event;
+
+import javax.imageio.ImageWriter;
+import java.util.EventListener;
+
+/**
+ * The IIOWriteProgressListener interface provides methods to 
+ * receive notification about the progress of the image and 
+ * thumbnail writing methods.
+ */
+public interface IIOWriteProgressListener extends EventListener {
+    
+    /**
+     * Notifies this listener that an image write operation has been started. 
+     * 
+     * @param source the ImageWriter object which calls this method.
+     * @param imageIndex the index of the image being written.
+     */
+    void imageStarted(ImageWriter source, int imageIndex);
+    
+    /**
+     * Notifies this listener about the degree of completion of the write call.
+     * 
+     * @param source the ImageWriter object which calls this method.
+     * @param percentageDone the percentage of encoding done.
+     */
+    void imageProgress(ImageWriter source, float percentageDone);
+    
+    /**
+     * Notifies this listener that the image writing has been completed. 
+     * 
+     * @param source the ImageWriter object which calls this method.
+     */
+    void imageComplete(ImageWriter source);
+    
+    /**
+     * Notifies this listener that a thumbnail write operation has been started. 
+     * 
+     * @param source the ImageWriter object which calls this method.
+     * @param imageIndex the index of the image being written.
+     * @param thumbnailIndex the index of the thumbnail being written.
+     */
+    void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex);
+    
+    /**
+     * Notifies this listener about the degree of completion of the write call.
+     * 
+     * @param source the ImageWriter object which calls this method.
+     * @param percentageDone the percentage of encoding done.
+     */
+    void thumbnailProgress(ImageWriter source, float percentageDone);
+    
+    /**
+     * Notifies this listener that a thumbnail write operation has been completed.
+     * 
+     * @param source the ImageWriter object which calls this method.
+     */
+    void thumbnailComplete(ImageWriter source);
+    
+    /**
+     * Notifies this listener that writing operation has been aborted.
+     * 
+     * @param source the ImageWriter object which calls this method.
+     */
+    void writeAborted(ImageWriter source);
+}
diff --git a/awt/javax/imageio/event/IIOWriteWarningListener.java b/awt/javax/imageio/event/IIOWriteWarningListener.java
new file mode 100644
index 0000000..f530d25
--- /dev/null
+++ b/awt/javax/imageio/event/IIOWriteWarningListener.java
@@ -0,0 +1,40 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.event;
+
+import javax.imageio.ImageWriter;
+import java.util.EventListener;
+
+/**
+ * The IIOWriteWarningListener provides methods to receive notification
+ * of warnings generated by image and thumbnail writing methods.
+ */
+public interface IIOWriteWarningListener extends EventListener {
+    
+    /**
+     * Notifies this listener about a warning (non-fatal error) during encoding. 
+     * 
+     * @param source the ImageWriter object which calls this method.
+     * @param imageIndex the index of the image generating the warning.
+     * @param warning the string describing the warning.
+     */
+    void warningOccurred(ImageWriter source, int imageIndex, String warning);
+}
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
diff --git a/awt/javax/imageio/plugins/bmp/BMPImageWriteParam.java b/awt/javax/imageio/plugins/bmp/BMPImageWriteParam.java
new file mode 100644
index 0000000..0cd44db
--- /dev/null
+++ b/awt/javax/imageio/plugins/bmp/BMPImageWriteParam.java
@@ -0,0 +1,75 @@
+/*
+ *  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.plugins.bmp;
+
+import javax.imageio.ImageWriteParam;
+import java.util.Locale;
+
+/**
+ * The BMPImageWriteParam class allows encoding an image in
+ * BMP format.
+ */
+public class BMPImageWriteParam extends ImageWriteParam {
+    
+    /** The top down. */
+    private boolean topDown; // Default is bottom-up
+
+    /**
+     * Instantiates a new BMPImageWriteParam with default values of all
+     * parameters.
+     */
+    public BMPImageWriteParam() {
+        this(null);
+    }
+
+    /**
+     * Instantiates a new BMPImageWriteParam with the specified Locale.
+     * 
+     * @param locale the specified Locale.
+     */
+    public BMPImageWriteParam(Locale locale) {
+        super(locale);
+
+        // Set the compression
+        canWriteCompressed = true;
+        compressionTypes = new String[] {"BI_RGB", "BI_RLE8", "BI_RLE4", "BI_BITFIELDS"};
+        compressionType = compressionTypes[0]; 
+    }
+
+    /**
+     * Sets true if the data will be written in a top-down order, 
+     * false otherwise.
+     * 
+     * @param topDown the new top-down value. 
+     */
+    public void setTopDown(boolean topDown) {
+        this.topDown = topDown;
+    }
+
+    /**
+     * Returns true if the data is written in top-down order, false
+     * otherwise.
+     * 
+     * @return true if the data is written in top-down order, false
+     * otherwise.
+     */
+    public boolean isTopDown() {
+        return topDown;
+    }
+}
diff --git a/awt/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java b/awt/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java
new file mode 100644
index 0000000..398c960
--- /dev/null
+++ b/awt/javax/imageio/plugins/jpeg/JPEGHuffmanTable.java
@@ -0,0 +1,213 @@
+/*
+ *  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.plugins.jpeg;
+
+/**
+ * The JPEGHuffmanTable class represents a single JPEG Huffman table. 
+ * It contains the standard tables from the JPEG specification.
+ */
+public class JPEGHuffmanTable {
+    
+    /** The standard DC luminance Huffman table . */
+    public static final JPEGHuffmanTable StdDCLuminance = new JPEGHuffmanTable(
+            new short[] {0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0},
+            new short[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0A, 0x0B},
+            false
+    );
+
+    /** The standard DC chrominance Huffman table. */
+    public static final JPEGHuffmanTable StdDCChrominance = new JPEGHuffmanTable(
+            new short[] {0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
+            new short[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x0A, 0x0B},
+            false
+    );
+
+    /** The standard AC luminance Huffman table. */
+    public static final JPEGHuffmanTable StdACLuminance = new JPEGHuffmanTable(
+            new short[] {0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7D},
+            new short[] {
+                    0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+                    0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08,
+                    0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72,
+                    0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
+                    0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45,
+                    0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+                    0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75,
+                    0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+                    0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3,
+                    0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
+                    0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
+                    0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
+                    0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4,
+                    0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
+            },
+            false
+    );
+
+    /** 
+     * The standard AC chrominance Huffman table. */
+    public static final JPEGHuffmanTable StdACChrominance = new JPEGHuffmanTable(
+            new short[] {0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77},
+            new short[] {
+                    0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
+                    0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+                    0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1,
+                    0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
+                    0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+                    0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+                    0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74,
+                    0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+                    0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A,
+                    0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4,
+                    0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+                    0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
+                    0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4,
+                    0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
+            },
+            false
+    );
+
+    /** The lengths. */
+    private short lengths[];
+    
+    /** The values. */
+    private short values[];
+
+    /**
+     * Instantiates a new jPEG huffman table.
+     * 
+     * @param lengths the lengths
+     * @param values the values
+     * @param copy the copy
+     */
+    JPEGHuffmanTable(short[] lengths, short[] values, boolean copy) {
+        // Construction of standard tables without checks
+        // The third param is dummy
+        // Could be also used for copying of the existing tables
+        this.lengths = lengths;
+        this.values = values;
+    }
+
+    /**
+     * Instantiates a new JPEGHuffmanTable.
+     * 
+     * @param lengths the array of shorts lengths.
+     * @param values the array of shorts containing 
+     * the values in order of increasing code length.
+     */
+    public JPEGHuffmanTable(short[] lengths, short[] values) {
+        if (lengths == null) {
+            throw new IllegalArgumentException("lengths array is null!");
+        }
+        if (values == null) {
+            throw new IllegalArgumentException("values array is null!");
+        }
+        if (lengths.length > 16) { // According to the spec
+            throw new IllegalArgumentException("lengths array is too long!");
+        }
+        if (values.length > 256) { // According to the spec
+            throw new IllegalArgumentException("values array is too long");
+        }
+        for (short length : lengths) {
+            if (length < 0) {
+                throw new IllegalArgumentException("Values in lengths array must be non-negative.");
+            }
+        }
+        for (short value : values) {
+            if (value < 0) {
+                throw new IllegalArgumentException("Values in values array must be non-negative.");
+            }
+        }
+
+        checkHuffmanTable(lengths, values);
+
+        this.lengths = new short[lengths.length];
+        this.values = new short[values.length];
+        System.arraycopy(lengths, 0, this.lengths, 0, lengths.length);
+        System.arraycopy(values, 0, this.values, 0, values.length);
+    }
+
+    /**
+     * Gets an array of lengths in the Huffman table.
+     * 
+     * @return the array of short values representing the
+     * length values in the Huffman table.
+     */
+    public short[] getLengths() {
+        short newLengths[] = new short[lengths.length];
+        System.arraycopy(lengths, 0, newLengths, 0, lengths.length);
+        return newLengths;
+    }
+
+    /**
+     * Gets an array of values represented by increasing length of 
+     * their codes.
+     * 
+     * @return the array of values.
+     */
+    public short[] getValues() {
+        short newValues[] = new short[values.length];
+        System.arraycopy(values, 0, newValues, 0, values.length);
+        return newValues;
+    }
+
+    /**
+     * Check huffman table.
+     * 
+     * @param lengths the lengths
+     * @param values the values
+     */
+    private static void checkHuffmanTable(short[] lengths, short[] values) {
+        int numLeaves = 0;
+        int possibleLeaves = 2;
+        for (short length : lengths) {
+            numLeaves += length;
+            possibleLeaves -= length;
+            if (possibleLeaves < 0) {
+                throw new IllegalArgumentException("Invalid Huffman table provided, lengths are incorrect.");
+            }
+            possibleLeaves <<= 1;
+        }
+
+        if (values.length != numLeaves) {
+            throw new IllegalArgumentException("Invalid Huffman table provided, sum of lengths != values.");
+        }
+    }
+
+    /**
+     * Returns the string representation of this JPEGHuffmanTable object.
+     * 
+     * @return the string representation of this JPEGHuffmanTable object.
+     */
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("JPEGHuffmanTable:\nlengths:");
+        for (short length : lengths) {
+            sb.append(' ').append(length);
+        }
+
+        sb.append("\nvalues:");
+        for (short value : values) {
+            sb.append(' ').append(value);
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/awt/javax/imageio/plugins/jpeg/JPEGImageReadParam.java b/awt/javax/imageio/plugins/jpeg/JPEGImageReadParam.java
new file mode 100644
index 0000000..dd08d51
--- /dev/null
+++ b/awt/javax/imageio/plugins/jpeg/JPEGImageReadParam.java
@@ -0,0 +1,116 @@
+/*
+ *  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.plugins.jpeg;
+
+import javax.imageio.ImageReadParam;
+
+/**
+ * The JPEGImageReadParam class provides functionality to set Huffman tables 
+ * and quantization tables when using the JPEG reader plug-in.
+ */
+public class JPEGImageReadParam extends ImageReadParam {
+    
+    /** The q tables. */
+    private JPEGQTable qTables[];
+    
+    /** The dc huffman tables. */
+    private JPEGHuffmanTable dcHuffmanTables[];
+    
+    /** The ac huffman tables. */
+    private JPEGHuffmanTable acHuffmanTables[];
+
+    /**
+     * Instantiates a new JPEGImageReadParam.
+     */
+    public JPEGImageReadParam() {
+    }
+
+    /**
+     * Returns true if tables are set, false otherwise.
+     * 
+     * @return true if tables are set, false otherwise.
+     */
+    public boolean areTablesSet() {
+        return qTables != null;
+    }
+
+    /**
+     * Sets the quantization and Huffman tables for using in 
+     * decoding streams.
+     * 
+     * @param qTables the quantization tables.
+     * @param DCHuffmanTables the standart DC Huffman tables.
+     * @param ACHuffmanTables the standart AC huffman tables.
+     */
+    public void setDecodeTables(
+            JPEGQTable[] qTables,
+            JPEGHuffmanTable[] DCHuffmanTables,
+            JPEGHuffmanTable[] ACHuffmanTables
+    ) {
+        if (qTables == null || DCHuffmanTables == null || ACHuffmanTables == null) {
+            throw new IllegalArgumentException("Invalid JPEG table arrays");
+        }
+        if(DCHuffmanTables.length != ACHuffmanTables.length) {
+            throw new IllegalArgumentException("Invalid JPEG table arrays");
+        }
+        if (qTables.length > 4 || DCHuffmanTables.length > 4) {
+            throw new IllegalArgumentException("Invalid JPEG table arrays");
+        }
+
+        // Do the shallow copy, it should be enough
+        this.qTables = qTables.clone();
+        dcHuffmanTables = DCHuffmanTables.clone();
+        acHuffmanTables = ACHuffmanTables.clone();
+    }
+
+    /**
+     * Unset all decoded tables.
+     */
+    public void unsetDecodeTables() {
+        qTables = null;
+        dcHuffmanTables = null;
+        acHuffmanTables = null;
+    }
+
+    /**
+     * Gets the quantization tables.
+     * 
+     * @return the quantization tables, or null.
+     */
+    public JPEGQTable[] getQTables() {
+        return qTables == null ? null : qTables.clone();
+    }
+
+    /**
+     * Gets the DC Huffman tables.
+     * 
+     * @return the DC Huffman tables which are set, or null.
+     */
+    public JPEGHuffmanTable[] getDCHuffmanTables() {
+        return dcHuffmanTables == null ? null : dcHuffmanTables.clone();
+    }
+
+    /**
+     * Gets the AC Huffman tables.
+     * 
+     * @return the AC Huffman tables which are set, or null.
+     */
+    public JPEGHuffmanTable[] getACHuffmanTables() {
+        return acHuffmanTables == null ? null : acHuffmanTables.clone();
+    }    
+}
diff --git a/awt/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java b/awt/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java
new file mode 100644
index 0000000..34a3cd9
--- /dev/null
+++ b/awt/javax/imageio/plugins/jpeg/JPEGImageWriteParam.java
@@ -0,0 +1,193 @@
+/*
+ *  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.plugins.jpeg;
+
+import org.apache.harmony.x.imageio.plugins.jpeg.JPEGConsts;
+
+import javax.imageio.ImageWriteParam;
+import java.util.Locale;
+
+/**
+ * The JPEGImageWriteParam class allows to set JPEG Huffman tables 
+ * and quantization when using the JPEG writer plug-in.
+ */
+public class JPEGImageWriteParam extends ImageWriteParam {
+    
+    /** The Constant COMP_QUALITY_VALUES. */
+    private static final float[] COMP_QUALITY_VALUES = {0.05f, 0.75f, 0.95f};
+    
+    /** The Constant COMP_QUALITY_DESCRIPTIONS. */
+    private static final String[] COMP_QUALITY_DESCRIPTIONS = {
+            "Minimum useful",
+            "Visually lossless",
+            "Maximum useful"
+    };
+
+    /** The q tables. */
+    private JPEGQTable[] qTables;
+    
+    /** The dc huffman tables. */
+    private JPEGHuffmanTable[] dcHuffmanTables;
+    
+    /** The ac huffman tables. */
+    private JPEGHuffmanTable[] acHuffmanTables;
+
+    /** The optimize huffman tables. */
+    private boolean optimizeHuffmanTables;
+
+    /**
+     * Instantiates a new JPEGImageWriteParam object with 
+     * the specified Locale.
+     * 
+     * @param locale the Locale.
+     */
+    public JPEGImageWriteParam(Locale locale) {
+        super(locale);
+
+        canWriteProgressive = true;
+        progressiveMode = ImageWriteParam.MODE_DISABLED;
+
+        canWriteCompressed = true;
+        compressionTypes = new String[]{"JPEG"};
+        compressionType = compressionTypes[0]; 
+        compressionQuality = JPEGConsts.DEFAULT_JPEG_COMPRESSION_QUALITY;
+    }
+
+    /**
+     * Returns true if tables are set, false otherwise.
+     * 
+     * @return true if tables are set, false otherwise.
+     */
+    public boolean areTablesSet() {
+        return qTables != null;
+    }
+
+    /**
+     * Sets the quantization and Huffman tables for using in 
+     * encoding streams.
+     * 
+     * @param qTables the quantization tables.
+     * @param DCHuffmanTables the standart DC Huffman tables.
+     * @param ACHuffmanTables the standart AC huffman tables.
+     */
+    public void setEncodeTables(
+            JPEGQTable[] qTables,
+            JPEGHuffmanTable[] DCHuffmanTables,
+            JPEGHuffmanTable[] ACHuffmanTables
+    ) {
+        if (qTables == null || DCHuffmanTables == null || ACHuffmanTables == null) {
+            throw new IllegalArgumentException("Invalid JPEG table arrays");
+        }
+        if(DCHuffmanTables.length != ACHuffmanTables.length) {
+            throw new IllegalArgumentException("Invalid JPEG table arrays");
+        }
+        if (qTables.length > 4 || DCHuffmanTables.length > 4) {
+            throw new IllegalArgumentException("Invalid JPEG table arrays");
+        }
+
+        // Do the shallow copy, it should be enough
+        this.qTables = qTables.clone();
+        dcHuffmanTables = DCHuffmanTables.clone();
+        acHuffmanTables = ACHuffmanTables.clone();
+    }
+
+    /**
+     * Unset all encoded tables.
+     */
+    public void unsetEncodeTables() {
+        qTables = null;
+        dcHuffmanTables = null;
+        acHuffmanTables = null;
+    }
+
+    /**
+     * Gets the DC Huffman tables.
+     * 
+     * @return the DC Huffman tables which are set, or null.
+     */
+    public JPEGHuffmanTable[] getDCHuffmanTables() {
+        return dcHuffmanTables == null ? null : dcHuffmanTables.clone();
+    }
+
+    /**
+     * Gets the AC Huffman tables.
+     * 
+     * @return the AC Huffman tables which are set, or null.
+     */
+    public JPEGHuffmanTable[] getACHuffmanTables() {
+        return acHuffmanTables == null ? null : acHuffmanTables.clone();
+    }
+
+    /**
+     * Gets the quantization tables.
+     * 
+     * @return the quantization tables, or null.
+     */
+    public JPEGQTable[] getQTables() {
+        return qTables == null ? null : qTables.clone();
+    }
+
+    @Override
+    public String[] getCompressionQualityDescriptions() {
+        super.getCompressionQualityDescriptions();
+        return COMP_QUALITY_DESCRIPTIONS.clone();
+    }
+
+    @Override
+    public float[] getCompressionQualityValues() {
+        super.getCompressionQualityValues();
+        return COMP_QUALITY_VALUES.clone();
+    }
+
+    /**
+     * Sets the flag indicated that the writer will generate optimized 
+     * Huffman tables for the image as part of the writing process.
+     * 
+     * @param optimize the flag of optimizing huffman tables.
+     */
+    public void setOptimizeHuffmanTables(boolean optimize) {
+        optimizeHuffmanTables = optimize;
+    }
+
+    /**
+     * Returns true if the writer generates optimized Huffman tables,
+     * false otherwise.
+     * 
+     * @return the true if the writer generates optimized Huffman tables,
+     * false otherwise.
+     */
+    public boolean getOptimizeHuffmanTables() {
+        return optimizeHuffmanTables;
+    }
+
+    @Override
+    public boolean isCompressionLossless() {
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Compression mode not MODE_EXPLICIT!");
+        }
+        return false;
+    }
+
+    @Override
+    public void unsetCompression() {
+        if (getCompressionMode() != MODE_EXPLICIT) {
+            throw new IllegalStateException("Compression mode not MODE_EXPLICIT!");
+        }
+        compressionQuality = JPEGConsts.DEFAULT_JPEG_COMPRESSION_QUALITY;
+    }
+}
diff --git a/awt/javax/imageio/plugins/jpeg/JPEGQTable.java b/awt/javax/imageio/plugins/jpeg/JPEGQTable.java
new file mode 100644
index 0000000..0c5b37e
--- /dev/null
+++ b/awt/javax/imageio/plugins/jpeg/JPEGQTable.java
@@ -0,0 +1,162 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.plugins.jpeg;
+
+/**
+ * The JPEGQTable class represents a single JPEG quantization table 
+ * and provides for the standard tables taken from the JPEG specification.
+ */
+public class JPEGQTable {
+
+    /** The Constant SIZE. */
+    private final static int SIZE = 64;
+    
+    /** The Constant BASELINE_MAX. */
+    private final static int BASELINE_MAX = 255;
+    
+    /** The Constant MAX. */
+    private final static int MAX = 32767;
+
+
+    /** The table. */
+    private int[] theTable;
+
+    /*
+     * K1 & K2 tables can be found in the JPEG format specification 
+     * at http://www.w3.org/Graphics/JPEG/itu-t81.pdf
+     */
+
+    /** The Constant K1LumTable. */
+    private static final int[] K1LumTable = new int[] {
+        16,  11,  10,  16,  24,  40,  51,  61,
+        12,  12,  14,  19,  26,  58,  60,  55,
+        14,  13,  16,  24,  40,  57,  69,  56,
+        14,  17,  22,  29,  51,  87,  80,  62,
+        18,  22,  37,  56,  68,  109, 103, 77,
+        24,  35,  55,  64,  81,  104, 113, 92,
+        49,  64,  78,  87,  103, 121, 120, 101,
+        72,  92,  95,  98,  112, 100, 103, 99
+    };
+
+    /** The Constant K2ChrTable. */
+    private static final int[] K2ChrTable = new int[] {
+        17,  18,  24,  47,  99,  99,  99,  99,
+        18,  21,  26,  66,  99,  99,  99,  99,
+        24,  26,  56,  99,  99,  99,  99,  99,
+        47,  66,  99,  99,  99,  99,  99,  99,
+        99,  99,  99,  99,  99,  99,  99,  99,
+        99,  99,  99,  99,  99,  99,  99,  99,
+        99,  99,  99,  99,  99,  99,  99,  99,
+        99,  99,  99,  99,  99,  99,  99,  99
+    };
+
+    /** 
+     * The K1Luminance indicates standart table K.1 from JPEG
+     * specification and produces "good" quality output. 
+     */
+    public static final JPEGQTable K1Luminance = new JPEGQTable(K1LumTable);
+    
+    /** 
+     * The K1Div2Luminance indicates K.1 table from JPEG
+     * specification with all elements divided by 2 and produces
+     * "very good" quality output. 
+     */
+    public static final JPEGQTable K1Div2Luminance = K1Luminance.getScaledInstance(0.5f, true);
+    
+    /** 
+     * The K2Chrominance indicates K.2 table from JPEG
+     * specification and produces "good" quality output. 
+     */
+    public static final JPEGQTable K2Chrominance = new JPEGQTable(K2ChrTable);
+    
+    /** 
+     * The Constant K2Div2Chrominance indicates K.2 table from JPEG
+     * specification with all elements divided by 2 and produces
+     * "very good" quality output. 
+     */
+    public static final JPEGQTable K2Div2Chrominance = K2Chrominance.getScaledInstance(0.5f, true);;
+
+
+    /**
+     * Instantiates a new JPEGQTable from the array, which 
+     * should contain 64 elements in natural order.
+     * 
+     * @param table the quantization table.
+     */
+    public JPEGQTable(int[] table) {
+        if (table == null) {
+            throw new IllegalArgumentException("table should not be NULL");
+        }
+        if (table.length != SIZE) {
+            throw new IllegalArgumentException("illegal table size: " + table.length);
+        }
+        theTable = table.clone();
+    }
+
+    /**
+     * Gets the current quantization table as an array of int values.
+     * 
+     * @return the current quantization table as an array of int values.
+     */
+    public int[] getTable() {
+        return theTable.clone();
+    }
+
+    /**
+     * Gets the scaled instance as quantization table where 
+     * the values are multiplied by the scaleFactor and then clamped 
+     * if forceBaseline is true.
+     * 
+     * @param scaleFactor the scale factor of table.
+     * @param forceBaseline the force baseline flag, the values 
+     * should be clamped if true.
+     * 
+     * @return the new quantization table.
+     */
+    public JPEGQTable getScaledInstance(float scaleFactor, boolean forceBaseline) {
+        int table[] = new int[SIZE];
+
+        int maxValue = forceBaseline ? BASELINE_MAX : MAX;
+
+        for (int i = 0; i < theTable.length; i++) {
+            int rounded = Math.round(theTable[i] * scaleFactor);
+            if (rounded < 1) {
+                rounded = 1;
+            }
+            if (rounded > maxValue) {
+                rounded = maxValue;
+            }
+            table[i] = rounded;
+        }
+        return new JPEGQTable(table);
+    }
+
+    /**
+     * Returns the string representation of this JPEGQTable object.
+     * 
+     * @return the string representation of this JPEGQTable object.
+     */
+    @Override
+    public String toString() {
+        //-- TODO more informative info
+        return "JPEGQTable";
+    }
+}
diff --git a/awt/javax/imageio/spi/IIORegistry.java b/awt/javax/imageio/spi/IIORegistry.java
new file mode 100644
index 0000000..3c1c989
--- /dev/null
+++ b/awt/javax/imageio/spi/IIORegistry.java
@@ -0,0 +1,110 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import java.util.Arrays;
+
+import org.apache.harmony.x.imageio.plugins.jpeg.JPEGImageReaderSpi;
+import org.apache.harmony.x.imageio.plugins.jpeg.JPEGImageWriterSpi;
+import org.apache.harmony.x.imageio.plugins.png.PNGImageReaderSpi;
+import org.apache.harmony.x.imageio.plugins.png.PNGImageWriterSpi;
+import org.apache.harmony.x.imageio.spi.FileIISSpi;
+import org.apache.harmony.x.imageio.spi.FileIOSSpi;
+import org.apache.harmony.x.imageio.spi.InputStreamIISSpi;
+import org.apache.harmony.x.imageio.spi.OutputStreamIOSSpi;
+import org.apache.harmony.x.imageio.spi.RAFIISSpi;
+import org.apache.harmony.x.imageio.spi.RAFIOSSpi;
+
+/*
+ * @author Rustem V. Rafikov, Viskov Nikolay
+ * @version $Revision: 1.3 $
+ */
+
+/**
+ * The IIORegistry class registers service provider instances 
+ * (SPI). Service provider instances are recognized by specific 
+ * meta-information in the JAR files containing them. The JAR
+ * files with SPI classes are loaded from the application class 
+ * path. 
+ */
+public final class IIORegistry extends ServiceRegistry {
+
+    /** The instance. */
+    private static IIORegistry instance;
+
+    /** The Constant CATEGORIES. */
+    private static final Class[] CATEGORIES = new Class[] {
+        javax.imageio.spi.ImageWriterSpi.class,
+        javax.imageio.spi.ImageReaderSpi.class,
+        javax.imageio.spi.ImageInputStreamSpi.class,
+        //javax.imageio.spi.ImageTranscoderSpi.class,
+        javax.imageio.spi.ImageOutputStreamSpi.class
+    };
+
+    /**
+     * Instantiates a new iIO registry.
+     */
+    private IIORegistry() {
+        super(Arrays.<Class<?>>asList(CATEGORIES).iterator());
+        registerBuiltinSpis();
+        registerApplicationClasspathSpis();
+    }
+
+    /**
+     * Register builtin spis.
+     */
+    private void registerBuiltinSpis() {
+        registerServiceProvider(new JPEGImageWriterSpi());
+        registerServiceProvider(new JPEGImageReaderSpi());
+        registerServiceProvider(new PNGImageReaderSpi());
+        registerServiceProvider(new PNGImageWriterSpi());
+        registerServiceProvider(new FileIOSSpi());
+        registerServiceProvider(new FileIISSpi());
+        registerServiceProvider(new RAFIOSSpi());
+        registerServiceProvider(new RAFIISSpi());
+        registerServiceProvider(new OutputStreamIOSSpi());        
+        registerServiceProvider(new InputStreamIISSpi());
+        //-- TODO implement
+    }
+
+    /**
+     * Gets the default IIORegistry instance.
+     * 
+     * @return the default IIORegistry instance.
+     */
+    public static IIORegistry getDefaultInstance() {
+        // TODO implement own instance for each ThreadGroup (see also ThreadLocal)
+        synchronized (IIORegistry.class) {
+            if (instance == null) {
+                instance = new IIORegistry();
+            }
+            return instance;
+        }
+    }
+
+    /**
+     * Registers all service providers from the application class 
+     * path.
+     */
+    public void registerApplicationClasspathSpis() {
+        //-- TODO implement for non-builtin plugins
+    }
+}
diff --git a/awt/javax/imageio/spi/IIOServiceProvider.java b/awt/javax/imageio/spi/IIOServiceProvider.java
new file mode 100644
index 0000000..f5873bf
--- /dev/null
+++ b/awt/javax/imageio/spi/IIOServiceProvider.java
@@ -0,0 +1,97 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import java.util.Locale;
+
+/**
+ * The IIOServiceProvider abstract class provides base functionality 
+ * for imageio service provider interfaces (SPIs). 
+ */
+public abstract class IIOServiceProvider implements RegisterableService {
+
+    /** The vendor name of this service provider. */
+    protected String vendorName;
+    
+    /** The version of this service provider. */
+    protected String version;
+
+    /**
+     * Instantiates a new IIOServiceProvider.
+     * 
+     * @param vendorName the vendor name of service provider.
+     * @param version the version of service provider.
+     */
+    public IIOServiceProvider(String vendorName, String version) {
+        if (vendorName == null) {
+            throw new NullPointerException("vendor name cannot be NULL");
+        }
+        if (version == null) {
+            throw new NullPointerException("version name cannot be NULL");
+        }
+        this.vendorName = vendorName;
+        this.version = version;
+    }
+
+    /**
+     * Instantiates a new IIOServiceProvider.
+     */
+    public IIOServiceProvider() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    public void onRegistration(ServiceRegistry registry, Class<?> category) {
+        // the default impl. does nothing
+    }
+
+    public void onDeregistration(ServiceRegistry registry, Class<?> category) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets the vendor name of this service provider.
+     * 
+     * @return the vendor name of this service provider.
+     */
+    public String getVendorName() {
+        return vendorName;
+    }
+
+    /**
+     * Gets the version of this service provider.
+     * 
+     * @return the version of this service provider.
+     */
+    public String getVersion() {
+        return version;
+    }
+
+    /**
+     * Gets a description of this service provider.   
+     * The result string should be localized for the specified 
+     * Locale.
+     * 
+     * @param locale the specified Locale.
+     * 
+     * @return the description of this service provider.
+     */
+    public abstract String getDescription(Locale locale);
+}
diff --git a/awt/javax/imageio/spi/ImageInputStreamSpi.java b/awt/javax/imageio/spi/ImageInputStreamSpi.java
new file mode 100644
index 0000000..47d210a
--- /dev/null
+++ b/awt/javax/imageio/spi/ImageInputStreamSpi.java
@@ -0,0 +1,126 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import java.io.File;
+import java.io.IOException;
+import javax.imageio.stream.ImageInputStream;
+
+/**
+ * The ImageInputStreamSpi abstract class is a service provider 
+ * interface (SPI) for ImageInputStreams.  
+ */
+public abstract class ImageInputStreamSpi extends IIOServiceProvider implements
+        RegisterableService {
+    
+    /** The input class. */
+    protected Class<?> inputClass;
+
+    /**
+     * Instantiates a new ImageInputStreamSpi.
+     */
+    protected ImageInputStreamSpi() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Instantiates a new ImageInputStreamSpi.
+     * 
+     * @param vendorName the vendor name.
+     * @param version the version.
+     * @param inputClass the input class.
+     */
+    public ImageInputStreamSpi(String vendorName, String version, Class<?> inputClass) {
+        super(vendorName, version);
+        this.inputClass = inputClass;
+    }
+
+    /**
+     * Gets an input Class object that represents class or 
+     * interface that must be implemented by an input source.
+     * 
+     * @return the input class.
+     */
+    public Class<?> getInputClass() {
+        return inputClass;
+    }
+
+    /**
+     * Returns true if the ImageInputStream can use a cache 
+     * file. If this method returns false, the value of the 
+     * useCache parameter of createInputStreamInstance will 
+     * be ignored. The default implementation returns false.
+     * 
+     * @return true if the ImageInputStream can use a cache 
+     * file, false otherwise.
+     */
+    public boolean canUseCacheFile() {
+        return false; //-- def
+    }
+
+    /**
+     * Returns true if the ImageInputStream implementation 
+     * requires the use of a cache file. The default implementation 
+     * returns false.
+     * 
+     * @return true if the ImageInputStream implementation 
+     * requires the use of a cache file, false otherwise.
+     */
+    public boolean needsCacheFile() {
+        return false; // def
+    }
+
+    /**
+     * Creates the ImageInputStream associated with this 
+     * service provider. The input object should
+     * be an instance of the class returned by th getInputClass 
+     * method. This method uses the specified directory
+     * for the cache file if the useCache parameter is true. 
+     * 
+     * @param input the input Object.
+     * @param useCache the flag indicating if a cache file 
+     * is needed or not.
+     * @param cacheDir the cache directory.
+     * 
+     * @return the ImageInputStream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract ImageInputStream createInputStreamInstance(Object input, boolean useCache,
+            File cacheDir) throws IOException;
+
+    /**
+     * Creates the ImageInputStream associated with this 
+     * service provider. The input object should
+     * be an instance of the class returned by getInputClass 
+     * method. This method uses the default system directory
+     * for the cache file, if it is needed. 
+     * 
+     * @param input the input Object.
+     * 
+     * @return the ImageInputStream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public ImageInputStream createInputStreamInstance(Object input) throws IOException {
+        return createInputStreamInstance(input, true, null);
+    }
+}
diff --git a/awt/javax/imageio/spi/ImageOutputStreamSpi.java b/awt/javax/imageio/spi/ImageOutputStreamSpi.java
new file mode 100644
index 0000000..d45e24c
--- /dev/null
+++ b/awt/javax/imageio/spi/ImageOutputStreamSpi.java
@@ -0,0 +1,126 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import javax.imageio.stream.ImageOutputStream;
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * The ImageOutputStreamSpi abstract class is a service provider 
+ * interface (SPI) for ImageOutputStreams. 
+ */
+public abstract class ImageOutputStreamSpi extends IIOServiceProvider implements
+        RegisterableService {
+    
+    /** The output class. */
+    protected Class<?> outputClass;
+
+    /**
+     * Instantiates a new ImageOutputStreamSpi.
+     */
+    protected ImageOutputStreamSpi() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Instantiates a new ImageOutputStreamSpi.
+     * 
+     * @param vendorName the vendor name.
+     * @param version the version.
+     * @param outputClass the output class.
+     */
+    public ImageOutputStreamSpi(String vendorName, String version, Class<?> outputClass) {
+        super(vendorName, version);
+        this.outputClass = outputClass;
+    }
+
+    /**
+     * Gets an output Class object that represents the class or 
+     * interface that must be implemented by an output source.
+     * 
+     * @return the output class.
+     */
+    public Class<?> getOutputClass() {
+        return outputClass;
+    }
+
+    /**
+     * Returns true if the ImageOutputStream can use a cache 
+     * file. If this method returns false, the value of the 
+     * useCache parameter of createOutputStreamInstance will 
+     * be ignored. The default implementation returns false.
+     * 
+     * @return true if the ImageOutputStream can use a cache 
+     * file, false otherwise.
+     */
+    public boolean canUseCacheFile() {
+        return false; // def
+    }
+
+    /**
+     * Returns true if the ImageOutputStream  implementation 
+     * requires the use of a cache file. The default implementation 
+     * returns false.
+     * 
+     * @return true if the ImageOutputStream  implementation 
+     * requires the use of a cache file, false otherwise.
+     */
+    public boolean needsCacheFile() {
+        return false; // def
+    }
+
+    /**
+     * Creates the ImageOutputStream associated with this 
+     * service provider. The output object should
+     * be an instance of the class returned by getOutputClass 
+     * method. This method uses the default system directory
+     * for the cache file, if it is needed. 
+     * 
+     * @param output the output Object.
+     * 
+     * @return the ImageOutputStream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public ImageOutputStream createOutputStreamInstance(Object output) throws IOException {
+        return createOutputStreamInstance(output, true, null);
+    }
+
+    /**
+     * Creates the ImageOutputStream associated with this 
+     * service provider. The output object should
+     * be an instance of the class returned by getInputClass 
+     * method. This method uses the specified directory
+     * for the cache file, if the useCache parameter is true. 
+     * 
+     * @param output the output Object.
+     * @param useCache the flag indicating if cache file 
+     * is needed or not.
+     * @param cacheDir the cache directory.
+     * 
+     * @return the ImageOutputStream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract ImageOutputStream createOutputStreamInstance(Object output,
+            boolean useCache, File cacheDir) throws IOException;
+}
diff --git a/awt/javax/imageio/spi/ImageReaderSpi.java b/awt/javax/imageio/spi/ImageReaderSpi.java
new file mode 100644
index 0000000..2e2484c
--- /dev/null
+++ b/awt/javax/imageio/spi/ImageReaderSpi.java
@@ -0,0 +1,186 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.ImageReader;
+import java.io.IOException;
+
+/**
+ * The ImageReaderSpi abstract class is a service provider 
+ * interface (SPI) for ImageReaders.
+ */
+public abstract class ImageReaderSpi extends ImageReaderWriterSpi {
+
+    /** 
+     * The STANDARD_INPUT_TYPE contains ImageInputStream.class. 
+     */
+    public static final Class[] STANDARD_INPUT_TYPE = new Class[] {ImageInputStream.class};
+
+    /** The input types. */
+    protected Class[] inputTypes;
+    
+    /** The writer SPI names. */
+    protected String[] writerSpiNames;
+
+    /**
+     * Instantiates a new ImageReaderSpi.
+     */
+    protected ImageReaderSpi() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Instantiates a new ImageReaderSpi.
+     * 
+     * @param vendorName the vendor name.
+     * @param version the version.
+     * @param names the format names.
+     * @param suffixes the array of strings representing the file suffixes. 
+     * @param MIMETypes the an array of strings representing MIME types.
+     * @param pluginClassName the plugin class name.
+     * @param inputTypes the input types.
+     * @param writerSpiNames the array of strings with class names of all 
+     * associated ImageWriters.
+     * @param supportsStandardStreamMetadataFormat the value indicating
+     * if stream metadata can be described by standart metadata format.
+     * @param nativeStreamMetadataFormatName the native stream metadata 
+     * format name, returned by getNativeStreamMetadataFormatName.
+     * @param nativeStreamMetadataFormatClassName the native stream 
+     * metadata format class name, returned by getNativeStreamMetadataFormat.
+     * @param extraStreamMetadataFormatNames the extra stream metadata 
+     * format names, returned by getExtraStreamMetadataFormatNames.
+     * @param extraStreamMetadataFormatClassNames the extra stream metadata 
+     * format class names, returned by getStreamMetadataFormat.
+     * @param supportsStandardImageMetadataFormat the value indicating
+     * if image metadata can be described by standart metadata format.
+     * @param nativeImageMetadataFormatName the native image metadata 
+     * format name, returned by getNativeImageMetadataFormatName.
+     * @param nativeImageMetadataFormatClassName the native image
+     * metadata format class name, returned by getNativeImageMetadataFormat.
+     * @param extraImageMetadataFormatNames the extra image metadata 
+     * format names, returned by getExtraImageMetadataFormatNames.
+     * @param extraImageMetadataFormatClassNames the extra image metadata 
+     * format class names, returned by getImageMetadataFormat.
+     */
+    public ImageReaderSpi(String vendorName, String version, String[] names, String[] suffixes,
+                             String[] MIMETypes, String pluginClassName,
+                             Class[] inputTypes, String[] writerSpiNames,
+                             boolean supportsStandardStreamMetadataFormat,
+                             String nativeStreamMetadataFormatName,
+                             String nativeStreamMetadataFormatClassName,
+                             String[] extraStreamMetadataFormatNames,
+                             String[] extraStreamMetadataFormatClassNames,
+                             boolean supportsStandardImageMetadataFormat,
+                             String nativeImageMetadataFormatName,
+                             String nativeImageMetadataFormatClassName,
+                             String[] extraImageMetadataFormatNames,
+                             String[] extraImageMetadataFormatClassNames) {
+        super(vendorName, version, names, suffixes, MIMETypes, pluginClassName,
+                supportsStandardStreamMetadataFormat, nativeStreamMetadataFormatName,
+                nativeStreamMetadataFormatClassName, extraStreamMetadataFormatNames,
+                extraStreamMetadataFormatClassNames, supportsStandardImageMetadataFormat,
+                nativeImageMetadataFormatName, nativeImageMetadataFormatClassName,
+                extraImageMetadataFormatNames, extraImageMetadataFormatClassNames);
+
+        if (inputTypes == null || inputTypes.length == 0) {
+            throw new NullPointerException("input types array cannot be NULL or empty");
+        }
+        this.inputTypes = inputTypes;
+        this.writerSpiNames = writerSpiNames;
+    }
+
+    /**
+     * Gets an array of Class objects whose types can be used 
+     * as input for this reader.
+     * 
+     * @return the input types.
+     */
+    public Class[] getInputTypes() {
+        return inputTypes;
+    }
+
+    /**
+     * Returns true if the format of source object is
+     * supported by this reader.
+     * 
+     * @param source the source object to be decoded 
+     * (for example an ImageInputStream).
+     * 
+     * @return true if the format of source object is
+     * supported by this reader, false otherwise.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract boolean canDecodeInput(Object source) throws IOException;
+
+    /**
+     * Returns an instance of the ImageReader implementation for
+     * this service provider.
+     * 
+     * @return the ImageReader.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public ImageReader createReaderInstance() throws IOException {
+        return createReaderInstance(null);
+    }
+
+    /**
+     * Returns an instance of the ImageReader implementation for
+     * this service provider.
+     * 
+     * @param extension the a plugin specific extension object, or null.
+     * 
+     * @return the ImageReader.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract ImageReader createReaderInstance(Object extension) throws IOException;
+
+    /**
+     * Checks whether or not the specified ImageReader object 
+     * is an instance of the ImageReader associated with this 
+     * service provider or not.
+     * 
+     * @param reader the ImageReader.
+     * 
+     * @return true, if the specified ImageReader object 
+     * is an instance of the ImageReader associated with this 
+     * service provider, false otherwise.
+     */
+    public boolean isOwnReader(ImageReader reader) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets an array of strings with names of the ImageWriterSpi 
+     * classes that support the internal metadata representation 
+     * used by the ImageReader of this service provider, or null if 
+     * there are no such ImageWriters.
+     * 
+     * @return an array of strings with names of the ImageWriterSpi 
+     * classes.
+     */
+    public String[] getImageWriterSpiNames() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+}
diff --git a/awt/javax/imageio/spi/ImageReaderWriterSpi.java b/awt/javax/imageio/spi/ImageReaderWriterSpi.java
new file mode 100644
index 0000000..b3c0f92
--- /dev/null
+++ b/awt/javax/imageio/spi/ImageReaderWriterSpi.java
@@ -0,0 +1,315 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import org.apache.harmony.x.imageio.metadata.IIOMetadataUtils;
+
+import javax.imageio.metadata.IIOMetadataFormat;
+
+/**
+ * The ImageReaderWriterSpi class is a superclass for the 
+ * ImageReaderSpi and ImageWriterSpi SPIs.
+ */
+public abstract class ImageReaderWriterSpi extends IIOServiceProvider
+        implements RegisterableService {
+
+    /** The names. */
+    protected String[] names;
+    
+    /** The suffixes. */
+    protected String[] suffixes;
+    
+    /** The MIME types. */
+    protected String[] MIMETypes;
+    
+    /** The plugin class name. */
+    protected String pluginClassName;
+    
+    /** Whether the reader/writer supports standard stream metadata format. */
+    protected boolean supportsStandardStreamMetadataFormat;
+    
+    /** The native stream metadata format name. */
+    protected String nativeStreamMetadataFormatName;
+    
+    /** The native stream metadata format class name. */
+    protected String nativeStreamMetadataFormatClassName;
+    
+    /** The extra stream metadata format names. */
+    protected String[] extraStreamMetadataFormatNames;
+    
+    /** The extra stream metadata format class names. */
+    protected String[] extraStreamMetadataFormatClassNames;
+    
+    /** Whether the reader/writer supports standard image metadata format. */
+    protected boolean supportsStandardImageMetadataFormat;
+    
+    /** The native image metadata format name. */
+    protected String nativeImageMetadataFormatName;
+    
+    /** The native image metadata format class name. */
+    protected String nativeImageMetadataFormatClassName;
+    
+    /** The extra image metadata format names. */
+    protected String[] extraImageMetadataFormatNames;
+    
+    /** The extra image metadata format class names. */
+    protected String[] extraImageMetadataFormatClassNames;
+
+    /**
+     * Instantiates a new ImageReaderWriterSpi.
+     * 
+     * @param vendorName the vendor name.
+     * @param version the version.
+     * @param names the format names.
+     * @param suffixes the array of strings representing the file suffixes. 
+     * @param MIMETypes the an array of strings representing MIME types.
+     * @param pluginClassName the plugin class name.
+     * @param supportsStandardStreamMetadataFormat the value indicating
+     * if stream metadata can be described by standart metadata format.
+     * @param nativeStreamMetadataFormatName the native stream metadata 
+     * format name, returned by getNativeStreamMetadataFormatName.
+     * @param nativeStreamMetadataFormatClassName the native stream 
+     * metadata format class name, returned by getNativeStreamMetadataFormat.
+     * @param extraStreamMetadataFormatNames the extra stream metadata 
+     * format names, returned by getExtraStreamMetadataFormatNames.
+     * @param extraStreamMetadataFormatClassNames the extra stream metadata 
+     * format class names, returned by getStreamMetadataFormat.
+     * @param supportsStandardImageMetadataFormat the value indicating
+     * if image metadata can be described by standard metadata format.
+     * @param nativeImageMetadataFormatName the native image metadata 
+     * format name, returned by getNativeImageMetadataFormatName.
+     * @param nativeImageMetadataFormatClassName the native image
+     * metadata format class name, returned by getNativeImageMetadataFormat.
+     * @param extraImageMetadataFormatNames the extra image metadata 
+     * format names, returned by getExtraImageMetadataFormatNames.
+     * @param extraImageMetadataFormatClassNames the extra image metadata 
+     * format class names, returned by getImageMetadataFormat.
+     */
+    public ImageReaderWriterSpi(String vendorName, String version, String[] names,
+                                String[] suffixes, String[] MIMETypes,
+                                String pluginClassName,
+                                boolean supportsStandardStreamMetadataFormat,
+                                String nativeStreamMetadataFormatName,
+                                String nativeStreamMetadataFormatClassName,
+                                String[] extraStreamMetadataFormatNames,
+                                String[] extraStreamMetadataFormatClassNames,
+                                boolean supportsStandardImageMetadataFormat,
+                                String nativeImageMetadataFormatName,
+                                String nativeImageMetadataFormatClassName,
+                                String[] extraImageMetadataFormatNames,
+                                String[] extraImageMetadataFormatClassNames) {
+        super(vendorName, version);
+
+        if (names == null || names.length == 0) {
+            throw new NullPointerException("format names array cannot be NULL or empty");
+        }
+
+        if (pluginClassName == null) {
+            throw new NullPointerException("Plugin class name cannot be NULL");
+        }
+
+        // We clone all the arrays to be consistent with the fact that
+        // some methods of this class must return clones of the arrays
+        // as it is stated in the spec.
+        this.names = names.clone();
+        this.suffixes = suffixes == null ? null : suffixes.clone();
+        this.MIMETypes = MIMETypes == null ? null : MIMETypes.clone();
+        this.pluginClassName = pluginClassName;
+        this.supportsStandardStreamMetadataFormat = supportsStandardStreamMetadataFormat;
+        this.nativeStreamMetadataFormatName = nativeStreamMetadataFormatName;
+        this.nativeStreamMetadataFormatClassName = nativeStreamMetadataFormatClassName;
+
+        this.extraStreamMetadataFormatNames =
+                extraStreamMetadataFormatNames == null ?
+                null : extraStreamMetadataFormatNames.clone();
+
+        this.extraStreamMetadataFormatClassNames =
+                extraStreamMetadataFormatClassNames == null ?
+                null : extraStreamMetadataFormatClassNames.clone();
+
+        this.supportsStandardImageMetadataFormat = supportsStandardImageMetadataFormat;
+        this.nativeImageMetadataFormatName = nativeImageMetadataFormatName;
+        this.nativeImageMetadataFormatClassName = nativeImageMetadataFormatClassName;
+
+        this.extraImageMetadataFormatNames =
+                extraImageMetadataFormatNames == null ?
+                null : extraImageMetadataFormatNames.clone();
+
+        this.extraImageMetadataFormatClassNames =
+                extraImageMetadataFormatClassNames == null ?
+                null : extraImageMetadataFormatClassNames.clone();
+    }
+
+    /**
+     * Instantiates a new ImageReaderWriterSpi.
+     */
+    public ImageReaderWriterSpi() {}
+
+    /**
+     * Gets an array of strings representing names of the formats 
+     * that can be used by the ImageReader 
+     * or ImageWriter implementation associated with this service 
+     * provider. 
+     * 
+     * @return an array of supported format names.
+     */
+    public String[] getFormatNames() {
+        return names.clone();
+    }
+
+    /**
+     * Gets an array of strings representing file suffixes 
+     * associated with the formats that can be used by the 
+     * ImageReader or ImageWriter implementation of this
+     * service provider.
+     * 
+     * @return an array of file suffixes.
+     */
+    public String[] getFileSuffixes() {
+        return suffixes == null ? null : suffixes.clone();
+    }
+
+    /**
+     * Gets an array of strings with the names of 
+     * additional formats of the image metadata objects 
+     * produced or consumed by this plug-in.
+     * 
+     * @return the array of extra image metadata format names.
+     */
+    public String[] getExtraImageMetadataFormatNames() {
+        return extraImageMetadataFormatNames == null ? null : extraImageMetadataFormatNames.clone();
+    }
+
+    /**
+     * Gets an array of strings with the names of 
+     * additional formats of the stream metadata objects 
+     * produced or consumed by this plug-in.
+     * 
+     * @return the array of extra stream metadata format names.
+     */
+    public String[] getExtraStreamMetadataFormatNames() {
+        return extraStreamMetadataFormatNames == null ? null : extraStreamMetadataFormatNames.clone();
+    }
+
+    /**
+     * Gets an IIOMetadataFormat object for the specified image 
+     * metadata format name. 
+     * 
+     * @param formatName the format name.
+     * 
+     * @return the IIOMetadataFormat, or null.
+     */
+    public IIOMetadataFormat getImageMetadataFormat(String formatName) {
+        return IIOMetadataUtils.instantiateMetadataFormat(
+                formatName, supportsStandardImageMetadataFormat,
+                nativeImageMetadataFormatName, nativeImageMetadataFormatClassName,
+                extraImageMetadataFormatNames, extraImageMetadataFormatClassNames
+        );
+    }
+
+    /**
+     * Gets an IIOMetadataFormat object for the specified stream 
+     * metadata format name. 
+     * 
+     * @param formatName the format name.
+     * 
+     * @return the IIOMetadataFormat, or null.
+     */
+    public IIOMetadataFormat getStreamMetadataFormat(String formatName) {
+        return IIOMetadataUtils.instantiateMetadataFormat(
+                formatName, supportsStandardStreamMetadataFormat,
+                nativeStreamMetadataFormatName, nativeStreamMetadataFormatClassName,
+                extraStreamMetadataFormatNames, extraStreamMetadataFormatClassNames
+        );
+    }
+
+    /**
+     * Gets an array of strings representing the MIME types 
+     * of the formats that are supported by the 
+     * ImageReader or ImageWriter implementation of this 
+     * service provider.
+     * 
+     * @return the array MIME types.
+     */
+    public String[] getMIMETypes() {
+        return MIMETypes == null ? null : MIMETypes.clone();
+    }
+
+    /**
+     * Gets the name of the native image metadata format for 
+     * this reader/writer, which allows for lossless encoding
+     * or decoding of the image metadata with the format.
+     * 
+     * @return the string with native image metadata format name, 
+     * or null.
+     */
+    public String getNativeImageMetadataFormatName() {
+        return nativeImageMetadataFormatName;
+    }
+
+    /**
+     * Gets the name of the native stream metadata format for 
+     * this reader/writer, which allows for lossless encoding
+     * or decoding of the stream metadata with the format.
+     * 
+     * @return the string with native stream metadata format name, 
+     * or null.
+     */
+    public String getNativeStreamMetadataFormatName() {
+        return nativeStreamMetadataFormatName;
+    }
+
+    /**
+     * Gets the class name of the ImageReader 
+     * or ImageWriter associated with this service provider.
+     * 
+     * @return the class name.
+     */
+    public String getPluginClassName() {
+        return pluginClassName;
+    }
+
+    /**
+     * Checks if the standard metadata format is supported 
+     * by the getAsTree and setFromTree methods for the 
+     * image metadata objects produced or consumed by this 
+     * reader or writer.
+     * 
+     * @return true, if standard image metadata format is 
+     * supported, false otherwise.
+     */
+    public boolean isStandardImageMetadataFormatSupported() {
+        return supportsStandardImageMetadataFormat;
+    }
+
+    /**
+     * Checks if the standard metadata format is supported 
+     * by the getAsTree and setFromTree methods for the 
+     * stream metadata objects produced or consumed by this 
+     * reader or writer.
+     * 
+     * @return true, if standard stream metadata format is 
+     * supported, false otherwise.
+     */
+    public boolean isStandardStreamMetadataFormatSupported() {
+        return supportsStandardStreamMetadataFormat;
+    }
+}
diff --git a/awt/javax/imageio/spi/ImageTranscoderSpi.java b/awt/javax/imageio/spi/ImageTranscoderSpi.java
new file mode 100644
index 0000000..68c4024
--- /dev/null
+++ b/awt/javax/imageio/spi/ImageTranscoderSpi.java
@@ -0,0 +1,74 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import javax.imageio.ImageTranscoder;
+
+/**
+ * The ImageTranscoderSpi class is a service provider interface (SPI) 
+ * for ImageTranscoders.
+ */
+public abstract class ImageTranscoderSpi extends IIOServiceProvider
+        implements RegisterableService {
+
+    /**
+     * Instantiates a new ImageTranscoderSpi.
+     */
+    protected ImageTranscoderSpi() {
+    }
+
+    /**
+     * Instantiates a new ImageTranscoderSpi with the specified
+     * vendor name and version.
+     * 
+     * @param vendorName the vendor name.
+     * @param version the version.
+     */
+    public ImageTranscoderSpi(String vendorName, String version) {
+        super(vendorName, version);
+    }
+
+    /**
+     * Gets the class name of an ImageReaderSpi that 
+     * produces IIOMetadata objects that can be used as 
+     * input to this transcoder.
+     * 
+     * @return the class name of an ImageReaderSpi.
+     */
+    public abstract String getReaderServiceProviderName();
+
+    /**
+     * Gets the class name of an ImageWriterSpi that 
+     * produces IIOMetadata objects that can be used as 
+     * input to this transcoder.
+     * 
+     * @return the class name of an ImageWriterSpi.
+     */
+    public abstract String getWriterServiceProviderName();
+
+    /**
+     * Creates an instance of the ImageTranscoder associated 
+     * with this service provider.
+     * 
+     * @return the ImageTranscoder instance.
+     */
+    public abstract ImageTranscoder createTranscoderInstance();
+}
diff --git a/awt/javax/imageio/spi/ImageWriterSpi.java b/awt/javax/imageio/spi/ImageWriterSpi.java
new file mode 100644
index 0000000..979ef77
--- /dev/null
+++ b/awt/javax/imageio/spi/ImageWriterSpi.java
@@ -0,0 +1,209 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriter;
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+
+/**
+ * The ImageWriterSpi abstract class is a service provider 
+ * interface (SPI) for ImageWriters.
+ */
+public abstract class ImageWriterSpi extends ImageReaderWriterSpi {
+
+    /** The STANDARD_OUTPUT_TYPE contains ImageInputStream.class. */
+    public static final Class[] STANDARD_OUTPUT_TYPE = new Class[] {ImageInputStream.class};
+
+    /** The output types. */
+    protected Class[] outputTypes;
+    
+    /** The reader spi names. */
+    protected String[] readerSpiNames;
+
+    /**
+     * Instantiates a new ImageWriterSpi.
+     */
+    protected ImageWriterSpi() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Instantiates a new ImageWriterSpi with the specified parameters.
+     * 
+     * @param vendorName the vendor name.
+     * @param version the version.
+     * @param names the format names.
+     * @param suffixes the array of strings representing the file suffixes. 
+     * @param MIMETypes the an array of strings representing MIME types.
+     * @param pluginClassName the plugin class name.
+     * @param outputTypes the output types.
+     * @param readerSpiNames the array of strings with class names of all 
+     * associated ImageReaders.
+     * @param supportsStandardStreamMetadataFormat the value indicating
+     * if stream metadata can be described by standard metadata format.
+     * @param nativeStreamMetadataFormatName the native stream metadata 
+     * format name, returned by getNativeStreamMetadataFormatName.
+     * @param nativeStreamMetadataFormatClassName the native stream 
+     * metadata format class name, returned by getNativeStreamMetadataFormat.
+     * @param extraStreamMetadataFormatNames the extra stream metadata 
+     * format names, returned by getExtraStreamMetadataFormatNames.
+     * @param extraStreamMetadataFormatClassNames the extra stream metadata 
+     * format class names, returned by getStreamMetadataFormat.
+     * @param supportsStandardImageMetadataFormat the value indicating
+     * if image metadata can be described by standard metadata format.
+     * @param nativeImageMetadataFormatName the native image metadata 
+     * format name, returned by getNativeImageMetadataFormatName.
+     * @param nativeImageMetadataFormatClassName the native image
+     * metadata format class name, returned by getNativeImageMetadataFormat.
+     * @param extraImageMetadataFormatNames the extra image metadata 
+     * format names, returned by getExtraImageMetadataFormatNames.
+     * @param extraImageMetadataFormatClassNames the extra image metadata 
+     * format class names, returned by getImageMetadataFormat.
+     */
+    public ImageWriterSpi(String vendorName, String version, String[] names,
+                             String[] suffixes, String[] MIMETypes,
+                             String pluginClassName,
+                             Class[] outputTypes, String[] readerSpiNames,
+                             boolean supportsStandardStreamMetadataFormat,
+                             String nativeStreamMetadataFormatName,
+                             String nativeStreamMetadataFormatClassName,
+                             String[] extraStreamMetadataFormatNames,
+                             String[] extraStreamMetadataFormatClassNames,
+                             boolean supportsStandardImageMetadataFormat,
+                             String nativeImageMetadataFormatName,
+                             String nativeImageMetadataFormatClassName,
+                             String[] extraImageMetadataFormatNames,
+                             String[] extraImageMetadataFormatClassNames) {
+        super(vendorName, version, names, suffixes, MIMETypes, pluginClassName,
+                supportsStandardStreamMetadataFormat, nativeStreamMetadataFormatName,
+                nativeStreamMetadataFormatClassName, extraStreamMetadataFormatNames,
+                extraStreamMetadataFormatClassNames, supportsStandardImageMetadataFormat,
+                nativeImageMetadataFormatName, nativeImageMetadataFormatClassName,
+                extraImageMetadataFormatNames, extraImageMetadataFormatClassNames);
+
+        if (outputTypes == null || outputTypes.length == 0) {
+            throw new NullPointerException("output types array cannot be NULL or empty");
+        }
+
+        this.outputTypes = outputTypes;
+        this.readerSpiNames = readerSpiNames;
+    }
+
+    /**
+     * Returns true if the format of the writer's output is lossless. 
+     * The default implementation returns true.
+     * 
+     * @return true, if a format is lossless, false otherwise.
+     */
+    public boolean isFormatLossless() {
+        return true;
+    }
+
+    /**
+     * Gets an array of Class objects whose types 
+     * can be used as output for this writer.
+     * 
+     * @return the output types.
+     */
+    public Class[] getOutputTypes() {
+        return outputTypes;
+    }
+
+    /**
+     * Checks whether or not the ImageWriter implementation associated 
+     * with this service provider can encode an image with 
+     * the specified type.
+     * 
+     * @param type the ImageTypeSpecifier.
+     * 
+     * @return true, if an image with the specified type can be
+     * encoded, false otherwise. 
+     */
+    public abstract boolean canEncodeImage(ImageTypeSpecifier type);
+
+    /**
+     * Checks whether or not the ImageWriter implementation associated 
+     * with this service provider can encode the specified RenderedImage.
+     * 
+     * @param im the RenderedImage.
+     * 
+     * @return true, if RenderedImage can be encoded, 
+     * false otherwise. 
+     */
+    public boolean canEncodeImage(RenderedImage im) {
+        return canEncodeImage(ImageTypeSpecifier.createFromRenderedImage(im));
+    }
+
+    /**
+     * Returns an instance of the ImageWriter implementation for
+     * this service provider.
+     * 
+     * @return the ImageWriter.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public ImageWriter createWriterInstance() throws IOException {
+        return createWriterInstance(null);
+    }
+
+    /**
+     * Returns an instance of the ImageWriter implementation for
+     * this service provider.
+     * 
+     * @param extension the a plugin specific extension object, or null.
+     * 
+     * @return the ImageWriter.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public abstract ImageWriter createWriterInstance(Object extension) throws IOException;
+
+    /**
+     * Checks whether or not the specified ImageWriter object 
+     * is an instance of the ImageWriter associated with this 
+     * service provider or not.
+     * 
+     * @param writer the ImageWriter.
+     * 
+     * @return true, if the specified ImageWriter object 
+     * is an instance of the ImageWriter associated with this 
+     * service provider, false otherwise.
+     */
+    public boolean isOwnWriter(ImageWriter writer) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets an array of strings with names of the ImageReaderSpi 
+     * classes that support the internal metadata representation 
+     * used by the ImageWriter of this service provider, or null if 
+     * there are no such ImageReaders.
+     * 
+     * @return an array of strings with names of the ImageWriterSpi 
+     * classes.
+     */
+    public String[] getImageReaderSpiNames() {
+        return readerSpiNames;
+    }
+}
diff --git a/awt/javax/imageio/spi/RegisterableService.java b/awt/javax/imageio/spi/RegisterableService.java
new file mode 100644
index 0000000..b50754e
--- /dev/null
+++ b/awt/javax/imageio/spi/RegisterableService.java
@@ -0,0 +1,50 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+/**
+ * The RegisterableService interface provides service provider 
+ * objects that can be registered by a ServiceRegistry, and 
+ * notifications that registration and deregistration have been
+ * performed.
+ */
+public interface RegisterableService {
+    
+    /**
+     * This method is called when the object which implements this
+     * interface is registered to the specified category of the 
+     * specified registry.
+     * 
+     * @param registry the ServiceRegistry to be registered.
+     * @param category the class representing a category.
+     */
+    void onRegistration(ServiceRegistry registry, Class<?> category);
+    
+    /**
+     * This method is called when the object which implements this
+     * interface is deregistered to the specified category of the 
+     * specified registry.
+     * 
+     * @param registry the ServiceRegistry to be registered.
+     * @param category the class representing a category.
+     */
+    void onDeregistration(ServiceRegistry registry, Class<?> category);
+}
diff --git a/awt/javax/imageio/spi/ServiceRegistry.java b/awt/javax/imageio/spi/ServiceRegistry.java
new file mode 100644
index 0000000..1a18b02
--- /dev/null
+++ b/awt/javax/imageio/spi/ServiceRegistry.java
@@ -0,0 +1,516 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.spi;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+/**
+ * The ServiceRegistry class provides ability to register, 
+ * deregister, look up and obtain service provider instances (SPIs).
+ * A service means a set of interfaces and classes, and a service 
+ * provider is an implementation of a service. Service providers can 
+ * be associated with one or more categories. Each category is defined 
+ * by a class or interface. Only a single instance of a each class is 
+ * allowed to be registered as a category. 
+ */
+public class ServiceRegistry {
+
+    /** The categories. */
+    CategoriesMap categories = new CategoriesMap(this);
+
+    /**
+     * Instantiates a new ServiceRegistry with the specified categories.
+     * 
+     * @param categoriesIterator an Iterator of Class objects 
+     * for defining of categories.
+     */
+    public ServiceRegistry(Iterator<Class<?>> categoriesIterator) {
+        if (null == categoriesIterator) {
+            throw new IllegalArgumentException("categories iterator should not be NULL");
+        }
+        while(categoriesIterator.hasNext()) {
+            Class<?> c =  categoriesIterator.next();
+            categories.addCategory(c);
+        }
+    }
+
+    /**
+     * Looks up and instantiates the available providers of this service using 
+     * the specified class loader.
+     * 
+     * @param providerClass the Class object of the provider to be looked up.
+     * @param loader the class loader to be used.
+     * 
+     * @return the iterator of providers objects for this service.
+     */
+    public static <T> Iterator<T> lookupProviders(Class<T> providerClass, ClassLoader loader) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Looks up and instantiates the available providers of this service using 
+     * the context class loader.
+     * 
+     * @param providerClass the Class object of the provider to be looked up.
+     * 
+     * @return the iterator of providers objects for this service.
+     */
+    public static <T> Iterator<T> lookupProviders(Class<T> providerClass) {
+        return lookupProviders(providerClass, Thread.currentThread().getContextClassLoader());
+    }
+
+    /**
+     * Registers the specified service provider object in the
+     * specified categories.
+     * 
+     * @param provider the specified provider to be registered.
+     * @param category the category.
+     * 
+     * @return true if no provider of the same class is registered 
+     * in this category, false otherwise.
+     */
+    public <T> boolean registerServiceProvider(T provider, Class<T> category) {
+        return categories.addProvider(provider, category);
+    }
+
+    /**
+     * Registers a list of service providers.
+     * 
+     * @param providers the list of service providers.
+     */
+    public void registerServiceProviders(Iterator<?> providers) {
+        for (Iterator<?> iterator = providers; iterator.hasNext();) {
+            categories.addProvider(iterator.next(), null);
+        }
+    }
+
+    /**
+     * Registers the specified service provider object in all
+     * categories.
+     * 
+     * @param provider the service provider.
+     */
+    public void registerServiceProvider(Object provider) {
+        categories.addProvider(provider, null);
+    }
+
+    /**
+     * Deregisters the specifies service provider from the
+     * specified category.
+     * 
+     * @param provider the service provider to be deregistered.
+     * @param category the specified category.
+     * 
+     * @return true if the provider was already registered 
+     * in the specified category, false otherwise.
+     */
+    public <T> boolean deregisterServiceProvider(T provider, Class<T> category) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Deregisters the specified service provider from all
+     * categories.
+     * 
+     * @param provider the specified service provider.
+     */
+    public void deregisterServiceProvider(Object provider) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets an Iterator of registered service providers
+     * in the specified category which satisfy the specified Filter. 
+     * The useOrdering parameter indicates whether the iterator will 
+     * return all of the server provider objects in a set order. 
+     * 
+     * @param category the specified category.
+     * @param filter the specified filter.
+     * @param useOrdering the flag indicating that providers are ordered
+     * in the returned Iterator.
+     * 
+     * @return the iterator of registered service providers.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> Iterator<T> getServiceProviders(Class<T> category, Filter filter, boolean useOrdering) {
+        return new FilteredIterator<T>(filter, (Iterator<T>)categories.getProviders(category, useOrdering));
+    }
+
+    /**
+     * Gets an Iterator of all registered service providers
+     * in the specified category. The useOrdering parameter
+     * indicates whether the iterator will return all of the server 
+     * provider objects in a set order. 
+     * 
+     * @param category the specified category.
+     * @param useOrdering the flag indicating that providers are ordered
+     * in the returned Iterator.
+     * 
+     * @return the Iterator of service providers.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> Iterator<T> getServiceProviders(Class<T> category, boolean useOrdering) {
+        return (Iterator<T>)categories.getProviders(category, useOrdering);
+    }
+
+    /**
+     * Gets the registered service provider object that has the 
+     * specified class type.
+     * 
+     * @param providerClass the specified provider class.
+     * 
+     * @return the service provider object.
+     */
+    public <T> T getServiceProviderByClass(Class<T> providerClass) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Sets an ordering between two service provider objects 
+     * within the specified category. 
+     * 
+     * @param category the specified category.
+     * @param firstProvider the first provider.
+     * @param secondProvider the second provider.
+     * 
+     * @return true if a previously unset order was set.
+     */
+    public <T> boolean setOrdering(Class<T> category, T firstProvider, T secondProvider) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Unsets an ordering between two service provider objects 
+     * within the specified category.
+     * 
+     * @param category the specified category.
+     * @param firstProvider the first provider.
+     * @param secondProvider the second provider.
+     * 
+     * @return true if a previously unset order was removed.
+     */
+    public <T> boolean unsetOrdering(Class<T> category, T firstProvider, T secondProvider) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Deregisters all providers from the specified category.
+     * 
+     * @param category the specified category.
+     */
+    public void deregisterAll(Class<?> category) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Deregister all providers from all categories.
+     */
+    public void deregisterAll() {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Finalizes this object. 
+     * 
+     * @throws Throwable throws if an error occurs during 
+     * finalization.
+     */
+    @Override
+    public void finalize() throws Throwable {
+        //TODO uncomment when deregisterAll is implemented
+        //deregisterAll();
+    }
+
+    /**
+     * Checks whether the specified provider has been already registered.
+     * 
+     * @param provider the provider to be checked.
+     * 
+     * @return true, if the specified provider has been already registered,
+     * false otherwise.
+     */
+    public boolean contains(Object provider) {
+        throw new UnsupportedOperationException("Not supported yet");
+    }
+
+    /**
+     * Gets an iterator of Class objects representing the current 
+     * categories.
+     * 
+     * @return the Iterator of Class objects.
+     */
+    public Iterator<Class<?>> getCategories() {
+        return categories.list();
+    }
+
+    /**
+     * The ServiceRegistry.Filter interface is used by 
+     * ServiceRegistry.getServiceProviders to filter providers according
+     * to the specified criterion. 
+     */
+    public static interface Filter {
+        
+        /**
+         * Returns true if the specified provider satisfies the 
+         * criterion of this Filter.
+         * 
+         * @param provider the provider.
+         * 
+         * @return true if the specified provider satisfies the 
+         * criterion of this Filter, false otherwise.
+         */
+        boolean filter(Object provider);
+    }
+
+    /**
+     * The Class CategoriesMap.
+     */
+    private static class CategoriesMap {
+        
+        /** The categories. */
+        Map<Class<?>, ProvidersMap> categories = new HashMap<Class<?>, ProvidersMap>();
+
+        /** The registry. */
+        ServiceRegistry registry;
+
+        /**
+         * Instantiates a new categories map.
+         * 
+         * @param registry the registry
+         */
+        public CategoriesMap(ServiceRegistry registry) {
+            this.registry = registry;
+        }
+
+        //-- TODO: useOrdering
+        /**
+         * Gets the providers.
+         * 
+         * @param category the category
+         * @param useOrdering the use ordering
+         * 
+         * @return the providers
+         */
+        Iterator<?> getProviders(Class<?> category, boolean useOrdering) {
+            ProvidersMap providers = categories.get(category);
+            if (null == providers) {
+                throw new IllegalArgumentException("Unknown category: " + category);
+            }
+            return providers.getProviders(useOrdering);
+        }
+
+        /**
+         * List.
+         * 
+         * @return the iterator< class<?>>
+         */
+        Iterator<Class<?>> list() {
+            return categories.keySet().iterator();
+        }
+
+        /**
+         * Adds the category.
+         * 
+         * @param category the category
+         */
+        void addCategory(Class<?> category) {
+            categories.put(category, new ProvidersMap());
+        }
+
+        /**
+         * Adds a provider to the category. If <code>category</code> is
+         * <code>null</code> then the provider will be added to all categories
+         * which the provider is assignable from.
+         * 
+         * @param provider provider to add
+         * @param category category to add provider to
+         * 
+         * @return if there were such provider in some category
+         */
+        boolean addProvider(Object provider, Class<?> category) {
+            if (provider == null) {
+                throw new IllegalArgumentException("provider should be != NULL");
+            }
+
+            boolean rt;
+            if (category == null) {
+                rt = findAndAdd(provider);
+            } else {
+                rt  = addToNamed(provider, category);
+            }
+
+            if (provider instanceof RegisterableService) {
+                ((RegisterableService) provider).onRegistration(registry, category);
+            }
+
+            return rt;
+        }
+
+        /**
+         * Adds the to named.
+         * 
+         * @param provider the provider
+         * @param category the category
+         * 
+         * @return true, if successful
+         */
+        private boolean addToNamed(Object provider, Class<?> category) {
+            Object obj = categories.get(category);
+
+            if (null == obj) {
+                throw new IllegalArgumentException("Unknown category: " + category);
+            }
+
+            return ((ProvidersMap) obj).addProvider(provider);
+        }
+
+        /**
+         * Find and add.
+         * 
+         * @param provider the provider
+         * 
+         * @return true, if successful
+         */
+        private boolean findAndAdd(Object provider) {
+            boolean rt = false;
+            for (Entry<Class<?>, ProvidersMap> e : categories.entrySet()) {
+                if (e.getKey().isAssignableFrom(provider.getClass())) {
+                    rt |= e.getValue().addProvider(provider);
+                }
+            }
+            return rt;
+        }
+    }
+
+    /**
+     * The Class ProvidersMap.
+     */
+    private static class ProvidersMap {
+        //-- TODO: providers ordering support
+
+        /** The providers. */
+        Map<Class<?>, Object> providers = new HashMap<Class<?>, Object>();
+
+        /**
+         * Adds the provider.
+         * 
+         * @param provider the provider
+         * 
+         * @return true, if successful
+         */
+        boolean addProvider(Object provider) {
+            return providers.put(provider.getClass(), provider) != null;
+        }
+
+        /**
+         * Gets the provider classes.
+         * 
+         * @return the provider classes
+         */
+        Iterator<Class<?>> getProviderClasses() {
+            return providers.keySet().iterator();
+        }
+
+        //-- TODO ordering
+        /**
+         * Gets the providers.
+         * 
+         * @param userOrdering the user ordering
+         * 
+         * @return the providers
+         */
+        Iterator<?> getProviders(boolean userOrdering) {
+            return providers.values().iterator();
+        }
+    }
+
+    /**
+     * The Class FilteredIterator.
+     */
+    private static class FilteredIterator<E> implements Iterator<E> {
+
+        /** The filter. */
+        private Filter filter;
+        
+        /** The backend. */
+        private Iterator<E> backend;
+        
+        /** The next obj. */
+        private E nextObj;
+
+        /**
+         * Instantiates a new filtered iterator.
+         * 
+         * @param filter the filter
+         * @param backend the backend
+         */
+        public FilteredIterator(Filter filter, Iterator<E> backend) {
+            this.filter = filter;
+            this.backend = backend;
+            findNext();
+        }
+
+        /**
+         * Next.
+         * 
+         * @return the e
+         */
+        public E next() {
+            if (nextObj == null) {
+                throw new NoSuchElementException();
+            }
+            E tmp = nextObj;
+            findNext();
+            return tmp;
+        }
+
+        /**
+         * Checks for next.
+         * 
+         * @return true, if successful
+         */
+        public boolean hasNext() {
+            return nextObj != null;
+        }
+
+        /**
+         * Removes the.
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        /**
+         * Sets nextObj to a next provider matching the criterion given by the filter.
+         */
+        private void findNext() {
+            nextObj = null;
+            while (backend.hasNext()) {
+                E o = backend.next();
+                if (filter.filter(o)) {
+                    nextObj = o;
+                    return;
+                }
+            }
+        }
+    }
+}
diff --git a/awt/javax/imageio/stream/FileCacheImageInputStream.java b/awt/javax/imageio/stream/FileCacheImageInputStream.java
new file mode 100644
index 0000000..47bc189
--- /dev/null
+++ b/awt/javax/imageio/stream/FileCacheImageInputStream.java
@@ -0,0 +1,131 @@
+/*
+ *  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.stream;
+
+import java.io.*;
+
+/**
+ * The FileCacheImageInputStream class is an implementation of
+ * ImageInputStream which reads from its InputStream
+ * and uses a temporary file as a cache. 
+ */
+public class FileCacheImageInputStream extends ImageInputStreamImpl {
+    
+    /** The is. */
+    private InputStream is;
+    
+    /** The file. */
+    private File file;
+    
+    /** The raf. */
+    private RandomAccessFile raf;
+
+
+    /**
+     * Instantiates a new FileCacheImageInputStream from
+     * the specified InputStream and using the specified 
+     * File as its cache directory.
+     * 
+     * @param stream the InputStream for reading.
+     * @param cacheDir the cache directory where the chache file
+     * will be created.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public FileCacheImageInputStream(InputStream stream, File cacheDir) throws IOException {
+        if (stream == null) {
+            throw new IllegalArgumentException("stream == null!");
+        }
+        is = stream;
+
+        if (cacheDir == null || cacheDir.isDirectory()) {
+            file = File.createTempFile(FileCacheImageOutputStream.IIO_TEMP_FILE_PREFIX, null, cacheDir);
+            file.deleteOnExit();
+        } else {
+            throw new IllegalArgumentException("Not a directory!");
+        }
+
+        raf = new RandomAccessFile(file, "rw");
+    }
+
+    @Override
+    public int read() throws IOException {
+        bitOffset = 0;
+
+        if (streamPos >= raf.length()) {
+            int b = is.read();
+
+            if (b < 0) {
+                return -1;
+            }
+
+            raf.seek(streamPos++);
+            raf.write(b);
+            return b;
+        }
+
+        raf.seek(streamPos++);
+        return raf.read();
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        bitOffset = 0;
+
+        if (streamPos >= raf.length()) {
+            int nBytes = is.read(b, off, len);
+
+            if (nBytes < 0) {
+                return -1;
+            }
+
+            raf.seek(streamPos);
+            raf.write(b, off, nBytes);
+            streamPos += nBytes;
+            return nBytes;
+        }
+
+        raf.seek(streamPos);
+        int nBytes = raf.read(b, off, len);
+        streamPos += nBytes;
+        return nBytes;
+    }
+
+    @Override
+    public boolean isCached() {
+        return true;
+    }
+
+    @Override
+    public boolean isCachedFile() {
+        return true;
+    }
+
+    @Override
+    public boolean isCachedMemory() {
+        return false;
+    }
+
+    @Override
+    public void close() throws IOException {
+        super.close();
+        raf.close();
+        file.delete();
+    }
+}
diff --git a/awt/javax/imageio/stream/FileCacheImageOutputStream.java b/awt/javax/imageio/stream/FileCacheImageOutputStream.java
new file mode 100644
index 0000000..ae48585
--- /dev/null
+++ b/awt/javax/imageio/stream/FileCacheImageOutputStream.java
@@ -0,0 +1,184 @@
+/*
+ *  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.stream;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+/**
+ * The FileCacheImageOutputStream class is an implementation of 
+ * ImageOutputStream that writes to its OutputStream
+ * using a temporary file as a cache. 
+ */
+public class FileCacheImageOutputStream extends ImageOutputStreamImpl {
+    
+    /** The Constant IIO_TEMP_FILE_PREFIX. */
+    static final String IIO_TEMP_FILE_PREFIX = "iioCache";
+    
+    /** The Constant MAX_BUFFER_LEN. */
+    static final int MAX_BUFFER_LEN = 1048575; // 1 MB - is it not too much?
+
+    /** The os. */
+    private OutputStream os;
+    
+    /** The file. */
+    private File file;
+    
+    /** The raf. */
+    private RandomAccessFile raf;
+
+    /**
+     * Instantiates a FileCacheImageOutputStream.
+     * 
+     * @param stream the OutputStream for writing.
+     * @param cacheDir the cache directory where the chache file
+     * will be created.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public FileCacheImageOutputStream(OutputStream stream, File cacheDir) throws IOException {
+        if (stream == null) {
+            throw new IllegalArgumentException("stream == null!");
+        }
+        os = stream;
+
+        if (cacheDir == null || cacheDir.isDirectory()) {
+            file = File.createTempFile(IIO_TEMP_FILE_PREFIX, null, cacheDir);
+            file.deleteOnExit();
+        } else {
+            throw new IllegalArgumentException("Not a directory!");
+        }
+
+        raf = new RandomAccessFile(file, "rw");
+    }
+
+    @Override
+    public void close() throws IOException {
+        flushBefore(raf.length());
+        super.close();
+        raf.close();
+        file.delete();
+    }
+
+    @Override
+    public boolean isCached() {
+        return true;
+    }
+
+    @Override
+    public boolean isCachedFile() {
+        return true;
+    }
+
+    @Override
+    public boolean isCachedMemory() {
+        return false;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        flushBits(); // See the flushBits method description
+        
+        raf.write(b);
+        streamPos++;
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        flushBits(); // See the flushBits method description
+
+        raf.write(b, off, len);
+        streamPos += len;
+    }
+
+    @Override
+    public int read() throws IOException {
+        bitOffset = 0; // Should reset
+
+        int res = raf.read();
+        if (res >= 0) {
+            streamPos++;
+        }
+
+        return res;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        bitOffset = 0;
+
+        int numRead = raf.read(b, off, len);
+        if (numRead > 0) {
+            streamPos += numRead;
+        }
+
+        return numRead;
+    }
+
+    @Override
+    public void flushBefore(long pos) throws IOException {
+        long readFromPos = flushedPos;
+        super.flushBefore(pos);
+
+        long bytesToRead = pos - readFromPos;
+        raf.seek(readFromPos);
+
+        if (bytesToRead < MAX_BUFFER_LEN) {
+            byte buffer[] = new byte[(int)bytesToRead];
+            raf.readFully(buffer);
+            os.write(buffer);
+        } else {
+            byte buffer[] = new byte[MAX_BUFFER_LEN];
+            while (bytesToRead > 0) {
+                int count = (int) Math.min(MAX_BUFFER_LEN, bytesToRead);
+                raf.readFully(buffer, 0, count);
+                os.write(buffer, 0, count);
+                bytesToRead -= count;
+            }
+        }
+
+        os.flush();
+
+        if (pos != streamPos) {
+            raf.seek(streamPos); // Reset the position
+        }
+    }
+
+    @Override
+    public void seek(long pos) throws IOException {
+        if (pos < flushedPos) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        raf.seek(pos);
+        streamPos = raf.getFilePointer();        
+        bitOffset = 0;
+    }
+
+    @Override
+    public long length() {
+        try {
+            return raf.length();
+        } catch(IOException e) {
+            return -1L;
+        }
+    }
+}
diff --git a/awt/javax/imageio/stream/FileImageInputStream.java b/awt/javax/imageio/stream/FileImageInputStream.java
new file mode 100644
index 0000000..6680ae0
--- /dev/null
+++ b/awt/javax/imageio/stream/FileImageInputStream.java
@@ -0,0 +1,115 @@
+/*
+ *  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.stream;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * The FileImageInputStream class implements ImageInputStream 
+ * and obtains its input data from a File or RandomAccessFile. 
+ */
+public class FileImageInputStream extends ImageInputStreamImpl {
+    
+    /** The raf. */
+    RandomAccessFile raf;
+
+    /**
+     * Instantiates a new FileImageInputStream from the specified File.
+     * 
+     * @param f the File of input data.
+     * 
+     * @throws FileNotFoundException if the specified file 
+     * doesn't exist.
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    @SuppressWarnings({"DuplicateThrows"})
+    public FileImageInputStream(File f) throws FileNotFoundException, IOException {
+        if (f == null) {
+            throw new IllegalArgumentException("f == null!");
+        }
+
+        raf = new RandomAccessFile(f, "r");
+    }
+
+    /**
+     * Instantiates a new FileImageInputStream from the specified 
+     * RandomAccessFile.
+     * 
+     * @param raf the RandomAccessFile of input data.
+     */
+    public FileImageInputStream(RandomAccessFile raf) {
+        if (raf == null) {
+            throw new IllegalArgumentException("raf == null!");
+        }
+
+        this.raf = raf;
+    }
+
+    @Override
+    public int read() throws IOException {
+        bitOffset = 0;
+
+        int res = raf.read();
+        if (res != -1) {
+            streamPos++;
+        }
+        return res;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        bitOffset = 0;
+
+        int numRead = raf.read(b, off, len);
+        if (numRead >= 0) {
+            streamPos += numRead;
+        }
+
+        return numRead;
+    }
+
+    @Override
+    public long length() {
+        try {
+            return raf.length();
+        } catch(IOException e) {
+            return -1L;
+        }
+    }
+
+    @Override
+    public void seek(long pos) throws IOException {
+        if (pos < getFlushedPosition()) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        raf.seek(pos);
+        streamPos = raf.getFilePointer();
+        bitOffset = 0;
+    }
+
+    @Override
+    public void close() throws IOException {
+        super.close();
+        raf.close();
+    }
+}
diff --git a/awt/javax/imageio/stream/FileImageOutputStream.java b/awt/javax/imageio/stream/FileImageOutputStream.java
new file mode 100644
index 0000000..eaafe14
--- /dev/null
+++ b/awt/javax/imageio/stream/FileImageOutputStream.java
@@ -0,0 +1,123 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.stream;
+
+import java.io.*;
+
+/**
+ * The FileImageOutputStream class implements ImageOutputStream 
+ * and writes the output data to a File or RandomAccessFile. 
+ */
+public class FileImageOutputStream extends ImageOutputStreamImpl {
+
+    /** The file. */
+    RandomAccessFile file;
+
+    /**
+     * Instantiates a new FileImageOutputStream with the specified
+     * File.
+     * 
+     * @param f the output File.
+     * 
+     * @throws FileNotFoundException if the file not found.
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public FileImageOutputStream(File f) throws FileNotFoundException, IOException {
+        this(f != null
+                ? new RandomAccessFile(f, "rw")
+                : null);
+    }
+
+    /**
+     * Instantiates a new FileImageOutputStream with the specified
+     * RandomAccessFile.
+     * 
+     * @param raf the output RandomAccessFile.
+     */
+    public FileImageOutputStream(RandomAccessFile raf) {
+        if (raf == null) {
+            throw new IllegalArgumentException("file should not be NULL");
+        }
+        file = raf;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        checkClosed();
+        // according to the spec for ImageOutputStreamImpl#flushBits()
+        flushBits();
+        file.write(b);
+        streamPos++;
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        checkClosed();
+        // according to the spec for ImageOutputStreamImpl#flushBits()
+        flushBits();
+        file.write(b, off, len);
+        streamPos += len;
+    }
+
+    @Override
+    public int read() throws IOException {
+        checkClosed();
+        int rt = file.read();
+        if (rt != -1) {
+            streamPos++;
+        }
+        return rt;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        checkClosed();
+        int rt = file.read(b, off, len);
+        if (rt != -1) {
+            streamPos += rt;
+        }
+        return rt;
+    }
+
+    @Override
+    public long length() {
+        try {
+            checkClosed();
+            return file.length();
+        } catch(IOException e) {
+            return super.length(); // -1L
+        }
+    }
+
+    @Override
+    public void seek(long pos) throws IOException {
+        //-- checkClosed() is performed in super.seek()
+        super.seek(pos);
+        file.seek(pos);
+        streamPos = file.getFilePointer();
+    }
+
+    @Override
+    public void close() throws IOException {
+        super.close();
+        file.close();
+    }
+}
diff --git a/awt/javax/imageio/stream/IIOByteBuffer.java b/awt/javax/imageio/stream/IIOByteBuffer.java
new file mode 100644
index 0000000..961a7b3
--- /dev/null
+++ b/awt/javax/imageio/stream/IIOByteBuffer.java
@@ -0,0 +1,111 @@
+/*
+ *  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.stream;
+
+/** 
+* @author Sergey I. Salishev
+* @version $Revision: 1.2 $
+*/
+
+/**
+ * The IIOByteBuffer class represents a byte array with offset and 
+ * length that is used by ImageInputStream for obtaining a sequence 
+ * of bytes.
+ */
+public class IIOByteBuffer {
+    
+    /** The data. */
+    private byte[] data;
+    
+    /** The offset. */
+    private int offset;
+    
+    /** The length. */
+    private int length;
+
+    /**
+     * Instantiates a new IIOByteBuffer.
+     * 
+     * @param data the byte array.
+     * @param offset the offset in the array.
+     * @param length the length of array.
+     */
+    public IIOByteBuffer(byte[] data, int offset, int length) {
+        this.data = data;
+        this.offset = offset;
+        this.length = length;
+    }
+
+    /**
+     * Gets the byte array of this IIOByteBuffer.
+     * 
+     * @return the byte array.
+     */
+    public byte[] getData() {
+        return data;
+    }
+
+    /**
+     * Gets the length in the array which will be used.
+     * 
+     * @return the length of the data.
+     */
+    public int getLength() {
+        return length;
+    }
+
+    /**
+     * Gets the offset of this IIOByteBuffer.
+     * 
+     * @return the offset of this IIOByteBuffer.
+     */
+    public int getOffset() {
+        return offset;
+    }
+
+    /**
+     * Sets the new data array to this IIOByteBuffer object.
+     * 
+     * @param data the new data array.
+     */
+    public void setData(byte[] data) {
+        this.data = data;
+    }
+
+    /**
+     * Sets the length of data which will be used.
+     * 
+     * @param length the new length.
+     */
+    public void setLength(int length) {
+        this.length = length;
+    }
+
+    /**
+     * Sets the offset in the data array of this IIOByteBuffer.
+     * 
+     * @param offset the new offset.
+     */
+    public void setOffset(int offset) {
+        this.offset = offset;
+    }
+}
+
diff --git a/awt/javax/imageio/stream/ImageInputStream.java b/awt/javax/imageio/stream/ImageInputStream.java
new file mode 100644
index 0000000..771e9ff
--- /dev/null
+++ b/awt/javax/imageio/stream/ImageInputStream.java
@@ -0,0 +1,485 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.2 $
+ */
+package javax.imageio.stream;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.nio.ByteOrder;
+
+/**
+ * The ImageInputStream represents input stream interface that is 
+ * used by ImageReaders.
+ */
+public interface ImageInputStream extends DataInput {
+
+    /**
+     * Sets the specified byte order for reading of data values 
+     * from this stream. 
+     * 
+     * @param byteOrder the byte order.
+     */
+    void setByteOrder(ByteOrder byteOrder);
+
+    /**
+     * Gets the byte order. 
+     * 
+     * @return the byte order.
+     */
+    ByteOrder getByteOrder();
+
+    /**
+     * Reads a byte from the stream.
+     * 
+     * @return the byte of the stream, or -1 for EOF indicating. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int read() throws IOException;
+
+    /**
+     * Reads number of bytes which is equal to the specified array's length
+     * and stores a result to this array.
+     * 
+     * @param b the byte array.
+     * 
+     * @return the number of read bytes, or -1 indicated EOF.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int read(byte[] b) throws IOException;
+
+    /**
+     * Reads the number of bytes specified by len parameter from 
+     * the stream and stores a result to the specified array
+     * with the specified offset.
+     * 
+     * @param b the byte array.
+     * @param off the offset.
+     * @param len the number of bytes to be read.
+     * 
+     * @return the number of read bytes, or -1 indicated EOF.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int read(byte[] b, int off, int len) throws IOException;
+
+    /**
+     * Reads the number of bytes specified by len parameter 
+     * from the stream, and modifies the specified IIOByteBuffer 
+     * with the byte array, offset, and length.
+     * 
+     * @param buf the IIOByteBuffer.
+     * @param len the number of bytes to be read.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readBytes(IIOByteBuffer buf, int len) throws IOException;
+
+    /**
+     * Reads a byte from the stream and returns a boolean true value 
+     * if it is non zero, false if it is zero.
+     * 
+     * @return a boolean value for read byte. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    boolean readBoolean() throws IOException;
+
+    /**
+     * Reads a byte from the stream and returns its value
+     * as signed byte.
+     * 
+     * @return a signed byte value for read byte. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    byte readByte() throws IOException;
+
+    /**
+     * Reads a byte from the stream and returns its value
+     * as int.
+     * 
+     * @return a unsigned byte value for read byte as int. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int readUnsignedByte() throws IOException;
+
+    /**
+     * Reads 2 bytes from the stream, and returns the result 
+     * as a short.
+     * 
+     * @return the signed short value from the stream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    short readShort() throws IOException;
+
+    /**
+     * Reads 2 bytes from the stream and returns its value
+     * as an unsigned short.
+     * 
+     * @return a unsigned short value coded in an int. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int readUnsignedShort() throws IOException;
+
+    /**
+     * Reads 2 bytes from the stream and returns their 
+     * unsigned char value.
+     * 
+     * @return the unsigned char value.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    char readChar() throws IOException;
+
+    /**
+     * Reads 4 bytes from the stream, and returns the result 
+     * as an int.
+     * 
+     * @return the signed int value from the stream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int readInt() throws IOException;
+
+    /**
+     * Reads 4 bytes from the stream and returns its value
+     * as long.
+     * 
+     * @return a unsigned int value as long. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    long readUnsignedInt() throws IOException;
+
+    /**
+     * Reads 8 bytes from the stream, and returns the result 
+     * as a long.
+     * 
+     * @return the long value from the stream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    long readLong() throws IOException;
+
+    /**
+     * Reads 4 bytes from the stream, and returns the result 
+     * as a float.
+     * 
+     * @return the float value from the stream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    float readFloat() throws IOException;
+
+    /**
+     * Reads 8 bytes from the stream, and returns the result 
+     * as a double.
+     * 
+     * @return the double value from the stream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    double readDouble() throws IOException;
+
+    /**
+     * Reads a line from the stream.
+     * 
+     * @return the string contained the line from the stream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    String readLine() throws IOException;
+
+    /**
+     * Reads bytes from the stream in a string that has been encoded 
+     * in a modified UTF-8 format.
+     * 
+     * @return the string read from stream and modified UTF-8 format.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    String readUTF() throws IOException;
+
+    /**
+     * Reads the specified number of bytes from the stream, 
+     * and stores the result into the specified array starting at 
+     * the specified index offset. 
+     * 
+     * @param b the byte array.
+     * @param off the offset.
+     * @param len the number of bytes to be read.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readFully(byte[] b, int off, int len) throws IOException;
+
+    /**
+     * Reads number of bytes from the stream which is equal to 
+     * the specified array's length, and stores them into 
+     * this array.
+     * 
+     * @param b the byte array.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readFully(byte[] b) throws IOException;
+
+    /**
+     * Reads the specified number of shorts from the stream, 
+     * and stores the result into the specified array starting at 
+     * the specified index offset. 
+     * 
+     * @param s the short array.
+     * @param off the offset.
+     * @param len the number of shorts to be read.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readFully(short[] s, int off, int len) throws IOException;
+
+    /**
+     * Reads the specified number of chars from the stream, 
+     * and stores the result into the specified array starting at 
+     * the specified index offset. 
+     * 
+     * @param c the char array.
+     * @param off the offset.
+     * @param len the number of chars to be read.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readFully(char[] c, int off, int len) throws IOException;
+
+    /**
+     * Reads the specified number of ints from the stream, 
+     * and stores the result into the specified array starting at 
+     * the specified index offset. 
+     * 
+     * @param i the int array.
+     * @param off the offset.
+     * @param len the number of ints to be read.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readFully(int[] i, int off, int len) throws IOException;
+
+    /**
+     * Reads the specified number of longs from the stream, 
+     * and stores the result into the specified array starting at 
+     * the specified index offset. 
+     * 
+     * @param l the long array.
+     * @param off the offset.
+     * @param len the number of longs to be read.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readFully(long[] l, int off, int len) throws IOException;
+
+    /**
+     * Reads the specified number of floats from the stream, 
+     * and stores the result into the specified array starting at 
+     * the specified index offset. 
+     * 
+     * @param f the float array.
+     * @param off the offset.
+     * @param len the number of floats to be read.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readFully(float[] f, int off, int len) throws IOException;
+
+    /**
+     * Reads the specified number of doubles from the stream, 
+     * and stores the result into the specified array starting at 
+     * the specified index offset. 
+     * 
+     * @param d the double array.
+     * @param off the offset.
+     * @param len the number of doubles to be read.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void readFully(double[] d, int off, int len) throws IOException;
+
+    /**
+     * Gets the stream position.
+     * 
+     * @return the stream position.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    long getStreamPosition() throws IOException;
+
+    /**
+     * Gets the bit offset.
+     * 
+     * @return the bit offset.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int getBitOffset() throws IOException;
+
+    /**
+     * Sets the bit offset to an integer between 0 and 7. 
+     * 
+     * @param bitOffset the bit offset.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void setBitOffset(int bitOffset) throws IOException;
+
+    /**
+     * Reads a bit from the stream and returns the value 0 or 1.
+     * 
+     * @return the value of single bit: 0 or 1.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int readBit() throws IOException;
+
+    /**
+     * Read the specified number of bits and returns their values as long.
+     * 
+     * @param numBits the number of bits to be read.
+     * 
+     * @return the bit string as a long.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    long readBits(int numBits) throws IOException;
+
+    /**
+     * Returns the length of the stream. 
+     *  
+     * @return the length of the stream, or -1 if unknown. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    long length() throws IOException;
+
+    /**
+     * Skipes the specified number of bytes by moving stream position. 
+     * 
+     * @param n the number of bytes.
+     * 
+     * @return the actual skipped number of bytes.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    int skipBytes(int n) throws IOException;
+
+    /**
+     * Skipes the specified number of bytes by moving stream position. 
+     * 
+     * @param n the number of bytes.
+     * 
+     * @return the actual skipped number of bytes.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    long skipBytes(long n) throws IOException;
+
+    /**
+     * Sets the current stream position to the specified location. 
+     * 
+     * @param pos a file pointer position.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void seek(long pos) throws IOException;
+
+    /**
+     * Marks a position in the stream to be returned to by a subsequent 
+     * call to reset. 
+     */
+    void mark();
+
+    /**
+     * Returns the file pointer to its previous position.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void reset() throws IOException;
+
+    /**
+     * Flushes the initial position in this stream prior to the
+     * specified stream position.
+     * 
+     * @param pos the position.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void flushBefore(long pos) throws IOException;
+
+    /**
+     * Flushes the initial position in this stream prior to the
+     * current stream position.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void flush() throws IOException;
+
+    /**
+     * Gets the flushed position.
+     * 
+     * @return the flushed position.
+     */
+    long getFlushedPosition();
+
+    /**
+     * Returns true if this ImageInputStream caches data in order 
+     * to allow seeking backwards.
+     * 
+     * @return true if this ImageInputStream caches data in order 
+     * to allow seeking backwards, false otherwise.
+     */
+    boolean isCached();
+
+    /**
+     * Returns true if this ImageInputStream caches data in order 
+     * to allow seeking backwards, and keeps it in memory.
+     * 
+     * @return true if this ImageInputStream caches data in order 
+     * to allow seeking backwards, and keeps it in memory.
+     */
+    boolean isCachedMemory();
+
+    /**
+     * Returns true if this ImageInputStream caches data in order 
+     * to allow seeking backwards, and keeps it in a temporary file.
+     * 
+     * @return true if this ImageInputStream caches data in order 
+     * to allow seeking backwards, and keeps it in a temporary file.
+     */
+    boolean isCachedFile();
+
+    /**
+     * Closes this stream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void close() throws IOException;
+}
diff --git a/awt/javax/imageio/stream/ImageInputStreamImpl.java b/awt/javax/imageio/stream/ImageInputStreamImpl.java
new file mode 100644
index 0000000..83ac13a
--- /dev/null
+++ b/awt/javax/imageio/stream/ImageInputStreamImpl.java
@@ -0,0 +1,394 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.stream;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteOrder;
+
+/**
+ * The ImageInputStreamImpl abstract class implements
+ * the ImageInputStream interface.
+ */
+public abstract class ImageInputStreamImpl implements ImageInputStream {
+
+    /** The byte order. */
+    protected ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
+
+    /** The stream position. */
+    protected long streamPos = 0;
+    
+    /** The flushed position. */
+    protected long flushedPos = 0;
+    
+    /** The bit offset. */
+    protected int bitOffset = 0;
+
+    /** The closed. */
+    private boolean closed = false;
+
+    /** The position stack. */
+    private final PositionStack posStack = new PositionStack();
+
+    /**
+     * Instantiates a new ImageInputStreamImpl.
+     */
+    public ImageInputStreamImpl() {}
+
+    /**
+     * Check if the stream is closed and if true, throws an IOException.
+     * 
+     * @throws IOException Signals that the stream is closed.
+     */
+    protected final void checkClosed() throws IOException {
+        if (closed) {
+            throw new IOException("stream is closed");
+        }
+    }
+
+    public void setByteOrder(ByteOrder byteOrder) {
+        this.byteOrder = byteOrder;
+    }
+
+    public ByteOrder getByteOrder() {
+        return byteOrder;
+    }
+
+    public abstract int read() throws IOException;
+
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    public abstract int read(byte[] b, int off, int len) throws IOException;
+
+    public void readBytes(IIOByteBuffer buf, int len) throws IOException {
+        if (buf == null) {
+            throw new NullPointerException("buffer is NULL");
+        }
+
+        byte[] b = new byte[len];
+        len = read(b, 0, b.length);
+
+        buf.setData(b);
+        buf.setOffset(0);
+        buf.setLength(len);
+    }
+
+    public boolean readBoolean() throws IOException {
+        int b = read();
+        if (b < 0) {
+            throw new EOFException("EOF reached");
+        }
+        return b != 0;
+    }
+
+    public byte readByte() throws IOException {
+        int b = read();
+        if (b < 0) {
+            throw new EOFException("EOF reached");
+        }
+        return (byte) b;
+    }
+
+    public int readUnsignedByte() throws IOException {
+        int b = read();
+        if (b < 0) {
+            throw new EOFException("EOF reached");
+        }
+        return b;
+    }
+
+    public short readShort() throws IOException {
+        int b1 = read();
+        int b2 = read();
+
+        if (b1 < 0 || b2 < 0) {
+            throw new EOFException("EOF reached");
+        }
+
+        return byteOrder == ByteOrder.BIG_ENDIAN ?
+                (short) ((b1 << 8) | (b2 & 0xff)) :
+                (short) ((b2 << 8) | (b1 & 0xff));
+    }
+
+    public int readUnsignedShort() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public char readChar() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public int readInt() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public long readUnsignedInt() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public long readLong() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public float readFloat() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public double readDouble() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public String readLine() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public String readUTF() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void readFully(byte[] b, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void readFully(byte[] b) throws IOException {
+        readFully(b, 0, b.length);
+    }
+
+    public void readFully(short[] s, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void readFully(char[] c, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void readFully(int[] i, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void readFully(long[] l, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void readFully(float[] f, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void readFully(double[] d, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public long getStreamPosition() throws IOException {
+        checkClosed();
+        return streamPos;
+    }
+
+    public int getBitOffset() throws IOException {
+        checkClosed();
+        return bitOffset;
+    }
+
+    public void setBitOffset(int bitOffset) throws IOException {
+        checkClosed();
+        this.bitOffset = bitOffset;
+    }
+
+    public int readBit() throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public long readBits(int numBits) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public long length() {
+        return -1L;
+    }
+
+    public int skipBytes(int n) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public long skipBytes(long n) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void seek(long pos) throws IOException {
+        checkClosed();
+        if (pos < getFlushedPosition()) {
+            throw new IllegalArgumentException("trying to seek before flushed pos");
+        }
+        bitOffset = 0;
+        streamPos = pos;
+    }
+
+    public void mark() {
+        try {
+            posStack.push(getStreamPosition());
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException("Stream marking error");
+        }
+    }
+
+    public void reset() throws IOException {
+        //-- TODO bit pos
+        if (!posStack.isEmpty()) {
+            long p = posStack.pop();
+            if (p < flushedPos) {
+                throw new IOException("marked position lies in the flushed portion of the stream");
+            }
+            seek(p);
+        }
+    }
+
+    public void flushBefore(long pos) throws IOException {
+        if (pos > getStreamPosition()) {
+            throw new IndexOutOfBoundsException("Trying to flush outside of current position");
+        }
+        if (pos < flushedPos) {
+            throw new IndexOutOfBoundsException("Trying to flush within already flushed portion");
+        }
+        flushedPos = pos;
+        //-- TODO implement
+    }
+
+    public void flush() throws IOException {
+        flushBefore(getStreamPosition());
+    }
+
+    public long getFlushedPosition() {
+        return flushedPos;
+    }
+
+    public boolean isCached() {
+        return false; //def
+    }
+
+    public boolean isCachedMemory() {
+        return false; //def
+    }
+
+    public boolean isCachedFile() {
+        return false; //def
+    }
+
+    public void close() throws IOException {
+        checkClosed();
+        closed = true;
+
+    }
+
+    /**
+     * Finalizes this object.
+     * 
+     * @throws Throwable if an error occurs.
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        if (!closed) {
+            try {
+                close();
+            } finally {
+                super.finalize();
+            }
+        }
+    }
+
+    /**
+     * The Class PositionStack.
+     */
+    private static class PositionStack {
+        
+        /** The Constant SIZE. */
+        private static final int SIZE = 10;
+
+        /** The values. */
+        private long[] values = new long[SIZE];
+        
+        /** The pos. */
+        private int pos = 0;
+
+
+        /**
+         * Push.
+         * 
+         * @param v the v
+         */
+        void push(long v) {
+            if (pos >= values.length) {
+                ensure(pos+1);
+            }
+            values[pos++] = v;
+        }
+
+        /**
+         * Pop.
+         * 
+         * @return the long
+         */
+        long pop() {
+            return values[--pos];
+        }
+
+        /**
+         * Checks if is empty.
+         * 
+         * @return true, if is empty
+         */
+        boolean isEmpty() {
+            return pos == 0;
+        }
+
+        /**
+         * Ensure.
+         * 
+         * @param size the size
+         */
+        private void ensure(int size) {
+            long[] arr = new long[Math.max(2 * values.length, size)];
+            System.arraycopy(values, 0, arr, 0, values.length);
+            values = arr;
+        }
+    }
+}
diff --git a/awt/javax/imageio/stream/ImageOutputStream.java b/awt/javax/imageio/stream/ImageOutputStream.java
new file mode 100644
index 0000000..e59b69d
--- /dev/null
+++ b/awt/javax/imageio/stream/ImageOutputStream.java
@@ -0,0 +1,270 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.2 $
+ */
+package javax.imageio.stream;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * The ImageOutputStream represents output stream interface that is 
+ * used by ImageWriters.
+ */
+public interface ImageOutputStream extends DataOutput, ImageInputStream {
+
+    /**
+     * Writes a single byte to the stream at the current position. 
+     * 
+     * @param b the int value, of which the 8 lowest bits 
+     * will be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void write(int b) throws IOException;
+
+    /**
+     * Writes the bytes array to the stream.
+     * 
+     * @param b the byte array to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void write(byte[] b) throws IOException;
+
+    /**
+     * Writes a number of bytes from the specified byte array
+     * beggining from the specified offset.
+     * 
+     * @param b the byte array.
+     * @param off the offset.
+     * @param len the number of bytes to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void write(byte[] b, int off, int len) throws IOException;
+
+    /**
+     * Writes the specified boolean value to the stream, 1 if it is true,
+     * 0 if it is false.
+     * 
+     * @param b the boolean value to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeBoolean(boolean b) throws IOException;
+
+    /**
+     * Writes the 8 lowest bits of the specified int value to the stream. 
+     * 
+     * @param b the specified int value.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeByte(int b) throws IOException;
+
+    /**
+     * Writes a short value to the output stream. 
+     * 
+     * @param v the short value to be written. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeShort(int v) throws IOException;
+
+    /**
+     * Writes the 16 lowest bits of the specified int value to the stream.
+     * 
+     * @param v the specified int value.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeChar(int v) throws IOException;
+
+    /**
+     * Writes an integer value to the output stream. 
+     * 
+     * @param v the integer value to be written. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeInt(int v) throws IOException;
+
+    /**
+     * Write long.
+     * 
+     * @param v the long value
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeLong(long v) throws IOException;
+
+    /**
+     * Writes a float value to the output stream. 
+     * 
+     * @param v the float which contains value to be written. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeFloat(float v) throws IOException;
+
+    /**
+     * Writes a double value to the output stream. 
+     * 
+     * @param v the double which contains value to be written. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeDouble(double v) throws IOException;
+
+    /**
+     * Writes the specified string to the stream.
+     * 
+     * @param s the string to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeBytes(String s) throws IOException;
+
+    /**
+     * Writes the specified String to the output stream.
+     * 
+     * @param s the String to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeChars(String s) throws IOException;
+
+    /**
+     * Writes 2 bytes to the output stream in 
+     * the modified UTF-8  representation of every character of
+     * the specified string.      
+     * 
+     * @param s the specified string to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeUTF(String s) throws IOException;
+
+    /**
+     * Flushes the initial position in this stream prior to the
+     * specified stream position.
+     * 
+     * @param pos the position.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void flushBefore(long pos) throws IOException;
+
+
+    /**
+     * Writes a len number of short values from the specified array
+     * to the stream.
+     * 
+     * @param s the shorts array to be written.
+     * @param off the offset in the char array.
+     * @param len the length of chars to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeShorts(short[] s, int off, int len) throws IOException;
+
+    /**
+     * Writes a len number of chars to the stream.
+     * 
+     * @param c the char array to be written.
+     * @param off the offset in the char array.
+     * @param len the length of chars to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeChars(char[] c, int off, int len) throws IOException;
+
+    /**
+     * Writes a len number of int values from the specified array
+     * to the stream.
+     * 
+     * @param i the int array to be written.
+     * @param off the offset in the char array.
+     * @param len the length of chars to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeInts(int[] i, int off, int len) throws IOException;
+
+    /**
+     * Writes a len number of long values from the specified array
+     * to the stream.
+     * 
+     * @param l the long array to be written.
+     * @param off the offset in the char array.
+     * @param len the length of chars to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeLongs(long[] l, int off, int len) throws IOException;
+
+    /**
+     * Writes a len number of float values from the specified array
+     * to the stream.
+     * 
+     * @param f the float array to be written.
+     * @param off the offset in the char array.
+     * @param len the length of chars to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeFloats(float[] f, int off, int len) throws IOException;
+
+    /**
+     * Writes a len number of double values from the specified array
+     * to the stream.
+     * 
+     * @param d the double array to be written.
+     * @param off the offset in the char array.
+     * @param len the length of chars to be written.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeDoubles(double[] d, int off, int len) throws IOException;
+
+    /**
+     * Writes a single bit at the current position.
+     * 
+     * @param bit the an int whose least significant bit is to be 
+     * written to the stream.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeBit(int bit) throws IOException;
+
+    /**
+     * Writes a sequence of bits beggining from the current position.
+     * 
+     * @param bits a long value containing the bits to be written,
+     * starting with the bit in position numBits - 1 down to the 
+     * least significant bit.
+     * @param numBits the number of significant bit , 
+     * it can be between 0 and 64. 
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    void writeBits(long bits, int numBits) throws IOException;
+
+}
diff --git a/awt/javax/imageio/stream/ImageOutputStreamImpl.java b/awt/javax/imageio/stream/ImageOutputStreamImpl.java
new file mode 100644
index 0000000..c3d80fa
--- /dev/null
+++ b/awt/javax/imageio/stream/ImageOutputStreamImpl.java
@@ -0,0 +1,169 @@
+/*
+ *  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 Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+package javax.imageio.stream;
+
+import java.io.IOException;
+import java.nio.ByteOrder;
+
+/* 
+ * @author Rustem V. Rafikov
+ * @version $Revision: 1.3 $
+ */
+
+/**
+ * The ImageOutputStreamImpl abstract class implements
+ * the ImageOutputStream interface.
+ */
+public abstract class ImageOutputStreamImpl extends ImageInputStreamImpl
+        implements ImageOutputStream {
+
+    /**
+     * Instantiates a new ImageOutputStreamImpl.
+     */
+    public ImageOutputStreamImpl() {}
+
+    public abstract void write(int b) throws IOException;
+
+    public void write(byte[] b) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    public abstract void write(byte[] b, int off, int len) throws IOException;
+
+    public void writeBoolean(boolean v) throws IOException {
+        write(v ? 1 : 0);
+    }
+
+    public void writeByte(int v) throws IOException {
+        write(v);
+    }
+
+    public void writeShort(int v) throws IOException {
+        if (byteOrder == ByteOrder.BIG_ENDIAN) {
+
+        } else {
+
+        }
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeChar(int v) throws IOException {
+        writeShort(v);
+    }
+
+    public void writeInt(int v) throws IOException {
+        if (byteOrder == ByteOrder.BIG_ENDIAN) {
+
+        } else {
+
+        }
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeLong(long v) throws IOException {
+        if (byteOrder == ByteOrder.BIG_ENDIAN) {
+
+        } else {
+
+        }
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeFloat(float v) throws IOException {
+        writeInt(Float.floatToIntBits(v));
+    }
+
+    public void writeDouble(double v) throws IOException {
+        writeLong(Double.doubleToLongBits(v));
+    }
+
+    public void writeBytes(String s) throws IOException {
+        write(s.getBytes());
+    }
+
+    public void writeChars(String s) throws IOException {
+        char[] chs = s.toCharArray();
+        writeChars(chs, 0, chs.length);
+    }
+
+    public void writeUTF(String s) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeShorts(short[] s, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeChars(char[] c, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeInts(int[] i, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeLongs(long[] l, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeFloats(float[] f, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeDoubles(double[] d, int off, int len) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeBit(int bit) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    public void writeBits(long bits, int numBits) throws IOException {
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Flushes the bits. This method should be called in the write
+     * methods by subclasses.
+     * 
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    protected final void flushBits() throws IOException {
+        if (bitOffset == 0) {
+            return;
+        }
+        
+        //-- TODO implement
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+}
diff --git a/awt/javax/imageio/stream/MemoryCacheImageInputStream.java b/awt/javax/imageio/stream/MemoryCacheImageInputStream.java
new file mode 100644
index 0000000..a3d470b
--- /dev/null
+++ b/awt/javax/imageio/stream/MemoryCacheImageInputStream.java
@@ -0,0 +1,113 @@
+/*
+ *  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.stream;
+
+import org.apache.harmony.x.imageio.stream.RandomAccessMemoryCache;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * The MemoryCacheImageInputStream class implements ImageInputStream
+ * using a memory buffer for caching the data.
+ */
+public class MemoryCacheImageInputStream  extends ImageInputStreamImpl {
+    
+    /** The is. */
+    private InputStream is;
+    
+    /** The ramc. */
+    private RandomAccessMemoryCache ramc = new RandomAccessMemoryCache();
+
+    /**
+     * Instantiates a new MemoryCacheImageInputStream
+     * which reads from the specified InputStream.
+     * 
+     * @param stream the InputStream to be read.
+     */
+    public MemoryCacheImageInputStream(InputStream stream) {
+        if (stream == null) {
+            throw new IllegalArgumentException("stream == null!");
+        }
+        is = stream;
+    }
+
+    @Override
+    public int read() throws IOException {
+        bitOffset = 0;
+
+        if (streamPos >= ramc.length()) {
+            int count = (int)(streamPos - ramc.length() + 1);
+            int bytesAppended = ramc.appendData(is, count);
+
+            if (bytesAppended < count) {
+                return -1;
+            }
+        }
+
+        int res = ramc.getData(streamPos);
+        if (res >= 0) {
+            streamPos++;
+        }
+        return res;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        bitOffset = 0;
+
+        if (streamPos >= ramc.length()) {
+            int count = (int)(streamPos - ramc.length() + len);
+            ramc.appendData(is, count);
+        }
+
+        int res = ramc.getData(b, off, len, streamPos);
+        if (res > 0) {
+            streamPos += res;
+        }
+        return res;
+    }
+
+    @Override
+    public boolean isCached() {
+        return true;
+    }
+
+    @Override
+    public boolean isCachedFile() {
+        return false;
+    }
+
+    @Override
+    public boolean isCachedMemory() {
+        return true;
+    }
+
+    @Override
+    public void close() throws IOException {
+        super.close();
+        ramc.close();
+    }
+
+    @Override
+    public void flushBefore(long pos) throws IOException {
+        super.flushBefore(pos);
+        ramc.freeBefore(getFlushedPosition());
+    }
+}
diff --git a/awt/javax/imageio/stream/MemoryCacheImageOutputStream.java b/awt/javax/imageio/stream/MemoryCacheImageOutputStream.java
new file mode 100644
index 0000000..96ded43
--- /dev/null
+++ b/awt/javax/imageio/stream/MemoryCacheImageOutputStream.java
@@ -0,0 +1,130 @@
+/*
+ *  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.stream;
+
+import org.apache.harmony.x.imageio.stream.RandomAccessMemoryCache;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+
+/**
+ * The MemoryCacheImageOutputStream class implements ImageOutputStream
+ * using a memory buffer for caching the data.
+ */
+public class MemoryCacheImageOutputStream extends ImageOutputStreamImpl {
+    
+    /** The os. */
+    OutputStream os;
+    
+    /** The ramc. */
+    RandomAccessMemoryCache ramc = new RandomAccessMemoryCache();
+
+    /**
+     * Instantiates a new MemoryCacheImageOutputStream
+     * which writes to the specified OutputStream.
+     * 
+     * @param stream the OutputStream.
+     */
+    public MemoryCacheImageOutputStream(OutputStream stream) {
+        if (stream == null) {
+            throw new IllegalArgumentException("stream == null!");
+        }
+        os = stream;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        flushBits(); // See the flushBits method description
+
+        ramc.putData(b, streamPos);
+        streamPos++;
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        flushBits(); // See the flushBits method description
+
+        ramc.putData(b, off, len, streamPos);
+        streamPos += len;
+    }
+
+    @Override
+    public int read() throws IOException {
+        bitOffset = 0;
+
+        int res = ramc.getData(streamPos);
+        if (res >= 0) {
+            streamPos++;
+        }
+        return res;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        bitOffset = 0;
+
+        int res = ramc.getData(b, off, len, streamPos);
+        if (res > 0) {
+            streamPos += res;
+        }
+        return res;
+    }
+
+    @Override
+    public long length() {
+        return ramc.length();
+    }
+
+    @Override
+    public boolean isCached() {
+        return true;
+    }
+
+    @Override
+    public boolean isCachedMemory() {
+        return true;
+    }
+
+    @Override
+    public boolean isCachedFile() {
+        return false;
+    }
+
+    @Override
+    public void close() throws IOException {
+        flushBefore(length());
+        super.close();
+        ramc.close();
+    }
+
+    @Override
+    public void flushBefore(long pos) throws IOException {
+        long flushedPosition = getFlushedPosition();
+        super.flushBefore(pos);
+
+        long newFlushedPosition = getFlushedPosition();
+        int nBytes = (int)(newFlushedPosition - flushedPosition);
+
+        ramc.getData(os, nBytes, flushedPosition);
+        ramc.freeBefore(newFlushedPosition);
+
+        os.flush();        
+    }
+}