Spec-Zone .ru
спецификации, руководства, описания, API
Содержание документации
СОДЕРЖАНИЕ | ПРЕДЫДУЩИЙ | NEXT

4.3 Запись Плагинов Читателя

Минимальный плагин читателя состоит из ImageReaderSpi подкласс, и ImageReader подкласс. Дополнительно, это может содержать реализации IIOMetadata интерфейс, представляющий поток и метаданные изображения, и IIOMetadataFormat объект, описывающий структуру метаданных.

В следующих разделах мы изобразим схематически реализацию простого плагина читателя для гипотетического формата по имени "MyFormat". Это будет состоять из классов MyFormatImageReaderSpi, MyFormatImageReader, и MyFormatMetadata.

Сам формат определяется, чтобы начаться с символов `myformat\n', сопровождается двумя четырехбайтовыми целыми числами, представляющими ширину, высоту, и единственный байт, указывающий на цветной тип изображения, которое может быть или серым или RGB. Затем, после символа новой строки, значения метаданных могут сохраненный как переменные строки, содержащие ключевое слово и значение, завершенное специальным ключевым словом `КОНЕЦ'. Строковые значения сохранены, используя кодирование UTF8, сопровождаемое новой строкой. Наконец, выборки изображения сохранены в слева направо, порядок от начала до конца или как полутоновые значения байта, или как три байта, представляющие красный, зеленый, и синий.

MyFormatImageReaderSpi MyFormatImageReaderSpi класс предоставляет информацию о плагине, включая имя поставщика, сменную строку версии и описание, имя формата, суффиксы файла, связанные с форматом, типы MIME, связанные с форматом, входные исходные классы, которые плагин может обработать, и ImageWriterSpis плагинов, которые в состоянии взаимодействовать особенно с читателем. Это также должно обеспечить реализацию canDecodeInput метод, который используется, чтобы определить местоположение плагинов, основанных на содержании файла исходного изображения.

ImageReaderSpi класс обеспечивает реализации большинства своих методов. Эти методы, главным образом, возвращают значение различных защищенных переменных экземпляра, который MyFormatImageReaderSpi может установить непосредственно или через конструктора суперкласса, как в примере ниже:

package com.mycompany.imageio;

import java.io.IOException;
import java.util.Locale;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;

public class MyFormatImageReaderSpi extends ImageReaderSpi {

        static final String vendorName = "My Company";
        static final String version = "1.0_beta33_build9467";
        static final String readerClassName =
                "com.mycompany.imageio.MyFormatImageReader";
        static final String[] names = { "myformat" };
        static final String[] suffixes = { "myf" };
        static final String[] MIMETypes = {
                "image/x-myformat" };
        static final String[] writerSpiNames = {
                "com.mycompany.imageio.MyFormatImageWriterSpi" };

        // Metadata formats, more information below
        static final boolean supportsStandardStreamMetadataFormat = false;
        static final String nativeStreamMetadataFormatName = null
        static final String nativeStreamMetadataFormatClassName = null;
        static final String[] extraStreamMetadataFormatNames = null;
        static final String[] extraStreamMetadataFormatClassNames = null;
        static final boolean supportsStandardImageMetadataFormat = false;
        static final String nativeImageMetadataFormatName =
                "com.mycompany.imageio.MyFormatMetadata_1.0";
        static final String nativeImageMetadataFormatClassName =
                "com.mycompany.imageio.MyFormatMetadata";
        static final String[] extraImageMetadataFormatNames = null;
        static final String[] extraImageMetadataFormatClassNames = null;

        public MyFormatImageReaderSpi() {
                super(vendorName, version,
                      names, suffixes, MIMETypes,
                      readerClassName,
                      STANDARD_INPUT_TYPE, // Accept ImageInputStreams
                      writerSpiNames,
                      supportsStandardStreamMetadataFormat,
                      nativeStreamMetadataFormatName,
                      nativeStreamMetadataFormatClassName,
                      extraStreamMetadataFormatNames,
                      extraStreamMetadataFormatClassNames,
                      supportsStandardImageMetadataFormat,
                      nativeImageMetadataFormatName,
                      extraImageMetadataFormatNames,
                      extraImageMetadataFormatClassNames);
                }

                public String getDescription(Locale locale) {
                        // Localize as appropriate
                        return "Description goes here";
                }

                public boolean canDecodeInput(Object input)
                        throws IOException {
                        // see below
                }

            public ImageReader createReaderInstance(Object extension) {
                return new MyFormatImageReader(this);
            }
        }

Большинство плагинов нуждается только для чтения от ImageInputStream источники, так как возможно "обернуть" большинство других типов ввода с соответствующим ImageInputStream. Однако, для плагина возможно работать непосредственно с другим Objects, например Object это обеспечивает интерфейс для цифрового фотоаппарата или сканера. Эта интерфейсная потребность не обеспечивает "потоковое" представление устройства вообще. Скорее плагин, который знает об интерфейсе, может использовать его, чтобы управлять устройством непосредственно.

Плагин дает объявление, которые вводят классы, которые он может обработать через getInputTypes метод, который возвращает массив Class объекты. Реализация getInputTypes обеспечивается в суперклассе, который возвращает значение inputTypes переменная экземпляра, которая поочередно устанавливается седьмым параметром конструктору суперкласса. Значение, используемое в примере выше, STANDARD_INPUT_TYPE, сокращение для массива, содержащего единственный элемент javax.imageio.stream.ImageInputStream.class, указание, что плагин принимает только ImageInputStreams.

canDecodeInput метод ответственен за определение двух вещей: во-первых, ли входной параметр является экземпляром класса, который плагин может понять, и во-вторых, кажется ли содержание файла, находится в формате, обработанном плагином. Это должно оставить свой ввод в том же самом состоянии, как это было, когда в этом передали. Для ImageInputStream входной источник, метка и методы сброса могут использоваться. Например, так как файлы в "MyFormat" форматируют, все начинают с символов `myformat', canDecodeInput может быть реализован как:

public boolean canDecodeInput(Object input) {
        if (!(input instanceof ImageInputStream)) {
                return false;
        }

        ImageInputStream stream = (ImageInputStream)input;
        byte[] b = new byte[8];
        try {
                stream.mark();
                stream.readFully(b);
                stream.reset();
        } catch (IOException e) {
                return false;
        }

        // Cast unsigned character constants prior to comparison
        return (b[0] == (byte)'m' && b[1] == (byte)'y' &&
                b[2] == (byte)'f' && b[3] == (byte)'o' &&
                b[4] == (byte)'r' && b[5] == (byte)'m' &&               
                b[6] == (byte)'a' && b[7] == (byte)'t');
}
MyFormatImageReader Основа плагина читателя является своим расширением ImageReader класс. Этот класс ответственен за отвечание на запросы об изображениях, фактически сохраненных во входном файле или потоке, так же как фактическом чтении изображений, миниатюр, и метаданных. Для простоты мы проигнорируем изображения миниатюры в этом примере.

Эскиз некоторых из методов гипотетического класса MyFormatImageReader показывают ниже:

package com.mycompany.imageio;

public class MyFormatImageReader extends ImageReader {

        ImageInputStream stream = null;
        int width, height;
        int colorType;
        
        // Constants enumerating the values of colorType
        static final int COLOR_TYPE_GRAY = 0;
        static final int COLOR_TYPE_RGB = 1;

        boolean gotHeader = false;

        public MyFormatImageReader(ImageReaderSpi originatingProvider) {
                super(originatingProvider);
        }

        public void setInput(Object input, boolean isStreamable) {
                super.setInput(input, isStreamable);
                if (input == null) {
                        this.stream = null;
                        return;
                }
                if (input instanceof ImageInputStream) {
                        this.stream = (ImageInputStream)input;
                } else {
                        throw new IllegalArgumentException("bad input");
                }
        }

        public int getNumImages(boolean allowSearch)
                throws IIOException {
                return 1; // format can only encode a single image
        }

        private void checkIndex(int imageIndex) {
                if (imageIndex != 0) {
                        throw new IndexOutOfBoundsException("bad index");
                }
        }

        public int getWidth(int imageIndex)
                throws IIOException {
                checkIndex(imageIndex); // must throw an exception if != 0
                readHeader();
                return width;
        }

        public int getHeight(int imageIndex)
                throws IIOException {
                checkIndex(imageIndex);
                readHeader();
                return height;
        }
getImageTypes Метод читатель ответственен за указание на то, что сортирует изображений, может использоваться, чтобы содержать декодируемый вывод. ImageTypeSpecifier класс используется, чтобы содержать a SampleModel и ColorModel указание на юридическое расположение изображения. getImageTypes метод возвращается Iterator из ImageTypeSpecifiers:
        public Iterator getImageTypes(int imageIndex)
                throws IIOException {
                checkIndex(imageIndex);
                readHeader();

                ImageTypeSpecifier imageType = null;
                int datatype = DataBuffer.TYPE_BYTE;
                java.util.List l = new ArrayList();
                switch (colorType) {
                case COLOR_TYPE_GRAY:
                        imageType = ImageTypeSpecifier.createGrayscale(8,
                                                                       datatype,
                                                                       false);
                        break;

                case COLOR_TYPE_RGB:
                        ColorSpace rgb =
                                ColorSpace.getInstance(ColorSpace.CS_sRGB);
                        int[] bandOffsets = new int[3];
                        bandOffsets[0] = 0;
                        bandOffsets[1] = 1;
                        bandOffsets[2] = 2;
                        imageType =
                                ImageTypeSpecifier.createInterleaved(rgb,
                                                                     bandOffsets,
                                                                     datatype,
                                                                     false,
                                                                     false);
                        break;                          
                }
                l.add(imageType);
                return l.iterator();
        }

Анализируя Заголовок Изображения Несколько из методов выше зависят от a readHeader метод, который ответственен за чтение достаточного количества входного потока, чтобы определить ширину, высоту, и расположение изображения. readHeader определяется так, безопасно быть вызванным многократно (примечание, что мы не обеспокоены многопоточным доступом):
        public void readHeader() {
                if (gotHeader) {
                        return;
                }
                gotHeader = true;

                if (stream == null) {
                        throw new IllegalStateException("No input stream");
                }

                // Read `myformat\n' from the stream
                byte[] signature = new byte[9];
                try {
                        stream.readFully(signature);
                } catch (IOException e) {
                        throw new IIOException("Error reading signature", e);
                }
                if (signature[0] != (byte)'m' || ...) { // etc.
                        throw new IIOException("Bad file signature!");
                }
                // Read width, height, color type, newline
                try {
                        this.width = stream.readInt();
                        this.height = stream.readInt();
                        this.colorType = stream.readUnsignedByte();
                        stream.readUnsignedByte(); // skip newline character
                } catch (IOException e) {
                        throw new IIOException("Error reading header", e)
                }
        }
Фактическое чтение изображения обрабатывается read метод:
        public BufferedImage read(int imageIndex, ImageReadParam param)
                throws IIOException {
                readMetadata(); // Stream is positioned at start of image data

Обработка ImageReadParam, который первый раздел метода обеспокоен использованием предоставленного объекта ImageReadParam определить, какая область исходного изображения должна быть считана, какая подвыборка должна быть применена, выбор и перестановка полос, и смещение в месте назначения:
          
        // Compute initial source region, clip against destination later
        Rectangle sourceRegion = getSourceRegion(param, width, height);
                
        // Set everything to default values
        int sourceXSubsampling = 1;
        int sourceYSubsampling = 1;
        int[] sourceBands = null;
        int[] destinationBands = null;
        Point destinationOffset = new Point(0, 0);

        // Get values from the ImageReadParam, if any
        if (param != null) {
                sourceXSubsampling = param.getSourceXSubsampling();
                sourceYSubsampling = param.getSourceYSubsampling();
                sourceBands = param.getSourceBands();
                destinationBands = param.getDestinationBands();
                destinationOffset = param.getDestinationOffset();
        }

В этой точке область интереса, подвыборки, выбора полосы, и места назначения смещала, были инициализированы. Следующий шаг должен создать подходящее целевое изображение. ImageReader.getDestination метод возвратит любое изображение, которое было определено, используя ImageReadParam.setDestination, или иначе создаст подходящее целевое изображение, используя предоставленный ImageTypeSpecifier, в этом случае определенный, вызывая getImageTypes(0):
        // Get the specified detination image or create a new one
        BufferedImage dst = getDestination(param,
                                           getImageTypes(0),
                                           width, height);
        // Enure band settings from param are compatible with images
        int inputBands = (colorType == COLOR_TYPE_RGB) ? 3 : 1;
        checkReadParamBandSettings(param, inputBands,
                                   dst.getSampleModel().getNumBands());

Чтобы уменьшить объем кода, мы должны записать, мы создаем a Raster содержать ценность строки данных, и скопировать пиксели с этого Raster в фактическое изображение. Таким образом выбор полосы и детали пиксельного форматирования заботятся о, за счет дополнительной копии.
        int[] bandOffsets = new int[inputBands];
        for (int i = 0; i < inputBands; i++) {
                bandOffsets[i] = i;
        }
        int bytesPerRow = width*inputBands;
        DataBufferByte rowDB = new DataBufferByte(bytesPerRow);
        WritableRaster rowRas =
                Raster.createInterleavedRaster(rowDB,
                                               width, 1, bytesPerRow,
                                               inputBands, bandOffsets,
                                               new Point(0, 0));
        byte[] rowBuf = rowDB.getData();

        // Create an int[] that can a single pixel
        int[] pixel = rowRas.getPixel(0, 0, (int[])null);

Теперь у нас есть байтовый массив, rowBuf, который может быть заполнен в от входных данных, и который является также источником пиксельных данных для Raster rowRaster. Мы извлекаем (единственную) мозаику целевого изображения, и определяем его степень. Затем мы создаем дочерние растры и источника и места назначения, которые выбирают и упорядочивают их полосы согласно настройкам, ранее извлеченным из ImageReadParam:
          
        WritableRaster imRas = dst.getWritableTile(0, 0);
        int dstMinX = imRas.getMinX();
        int dstMaxX = dstMinX + imRas.getWidth() - 1;
        int dstMinY = imRas.getMinY();
        int dstMaxY = dstMinY + imRas.getHeight() - 1;

        // Create a child raster exposing only the desired source bands
        if (sourceBands != null) {
                rowRas = rowRas.createWritableChild(0, 0,
                                                    width, 1,
                                                    0, 0,
                                                    sourceBands);
        }

        // Create a child raster exposing only the desired dest bands
        if (destinationBands != null) {
                imRas = imRas.createWritableChild(0, 0,
                                                  imRas.getWidth(),
                                                  imRas.getHeight(),
                                                  0, 0,
                                                  destinationBands);
        }
Читая Пиксельные данные Теперь мы готовы начать пиксельные данные чтения с изображения. Мы считаем целые строки, и выполним подвыборку и место назначения, отсекающее, поскольку мы продолжаем. Горизонтальное отсечение усложняется потребностью принять подвыборку во внимание. Здесь мы выполняем отсечение на пиксель; более сложный читатель мог выполнить горизонталь, отсекающую однажды:
        for (int srcY = 0; srcY < height; srcY++) {
                // Read the row
                try {
                        stream.readFully(rowBuf);
                } catch (IOException e) {
                        throw new IIOException("Error reading line " + srcY, e);
                }
                        
                // Reject rows that lie outside the source region,
                // or which aren't part of the subsampling
                if ((srcY < sourceRegion.y) ||
                    (srcY >= sourceRegion.y + sourceRegion.height) ||
                    (((srcY - sourceRegion.y) %
                      sourceYSubsampling) != 0)) {
                        continue;
                }

                // Determine where the row will go in the destination
                int dstY = destinationOffset.y +
                        (srcY - sourceRegion.y)/sourceYSubsampling;
                if (dstY < dstMinY) {
                        continue; // The row is above imRas
                }
                if (dstY > dstMaxY) {
                        break; // We're done with the image
                }

                // Copy each (subsampled) source pixel into imRas
                for (int srcX = sourceRegion.x;
                     srcX < sourceRegion.x + sourceRegion.width;
                     srcX++) {
                        if (((srcX - sourceRegion.x) % sourceXSubsampling) != 0) {
                                continue;
                        }
                        int dstX = destinationOffset.x +
                                (srcX - sourceRegion.x)/sourceXSubsampling;
                        if (dstX < dstMinX) {
                                continue;  // The pixel is to the left of imRas
                        }
                        if (dstX > dstMaxX) {
                                break; // We're done with the row
                        }

                        // Copy the pixel, sub-banding is done automatically
                        rowRas.getPixel(srcX, 0, pixel);
                        imRas.setPixel(dstX, dstY, pixel);
                }
        }
        return dst;
Для производительности, случай, где sourceXSubsampling равно 1, может вспыхнуться отдельно, так как возможно скопировать многократные пиксели сразу:
        // Create an int[] that can hold a row's worth of pixels
        int[] pixels = rowRas.getPixels(0, 0, width, 1, (int[])null);

        // Clip against the left and right edges of the destination image
        int srcMinX =
                Math.max(sourceRegion.x,
                     dstMinX - destinationOffset.x + sourceRegion.x);
      int srcMaxX =
                Math.min(sourceRegion.x + sourceRegion.width - 1,
                         dstMaxX - destinationOffset.x + sourceRegion.x);
        int dstX = destinationOffset.x + (srcMinX - sourceRegion.x);
        int w = srcMaxX - srcMinX + 1;
        rowRas.getPixels(srcMinX, 0, w, 1, pixels);
        imRas.setPixels(dstX, dstY, w, 1, pixels);
                    
Есть несколько дополнительных функций, которые читатели должны реализовать, а именно, сообщая слушателям продвижения чтения, и позволяя процесс считывания быть прерванными от другого потока. Слушатели Там являются тремя типами слушателей, которые могут быть привязаны к читателю: IIOReadProgressListener, IIOReadUpdateListener, и IIOReadWarningListener. Любое число каждого типа может быть присоединено к читателю посредством различного, добавляют и удаляют методы, которые реализуются в суперклассе ImageReader. ImageReader также содержит различные методы процесса, которые широковещательно передают информацию всем присоединенным слушателям данного типа. Например, когда чтение изображения начинается, метод processImageStarted (imageIndex) нужно вызвать, чтобы сообщить, что все присоединили IIOReadProgressListeners события.

Плагин читателя обычно ответственен за вызов processImageStarted и processImageComplete вначале и конца его метода чтения, соответственно. processImageProgress нужно вызвать, по крайней мере, каждыми несколькими растровыми строками с оценкой завершения процента чтения. Важно, чтобы этот процент никогда не уменьшился во время чтения единственного изображения. Если читатель поддерживает миниатюры, corresponsing методы продвижения миниатюры нужно вызвать также. processSequenceStarted и processSequenceComplete методы IIOReadProgressListener только нужно вызвать, если плагин переопределяет реализацию суперкласса readAll.

Более усовершенствованные читатели, которые обрабатывают входящие данные в многократных передачах, могут хотеть поддерживать IIOReadUpdateListeners, которые получают больше detauled информации, о которой пиксели до сих пор читались. Приложения могут использовать эту информацию, чтобы выполнить выборочные обновления экранного изображения, например, или повторно закодировать данные изображения способом потоковой передачи.

Прерывая Процесс считывания, В то время как один поток выполняет чтение изображения, другой поток может вызвать метод аварийного прекращения работы читателя асинхронно. Поток чтения должен, периодически опрашивать состояние читателя используя abortRequested метод, и пытаться прервать декодирование. Частично декодируемое изображение должно все еще быть возвращено, хотя читатель не должен сделать гарантии о его содержании. Например, это могло содержать сжатые или зашифрованные данные в своем DataBuffer, который не имеет смысла визуально. Пример IIOReadProgressListener типичный набор вызовов IIOReadProgressListener мог бы быть похожим на это:

public BufferedImage read(int imageIndex, ImageReadParam param)
        throws IOException {
        // Clear any previous abort request
        boolean aborted = false;
        clearAbortRequested();

        // Inform IIOReadProgressListeners of the start of the image
        processImageStarted(imageIndex);

        // Compute xMin, yMin, xSkip, ySkip from the ImageReadParam
        // ...

        // Create a suitable image
        BufferedImage theImage = new BufferedImage(...);

        // Compute factors for use in reporting percentages
        int pixelsPerRow = (width - xMin + xSkip - 1)/xSkip;
        int rows = (height - yMin + ySkip - 1)/ySkip;
        long pixelsDecoded = 0L;
        long totalPixels = rows*pixelsPerRow;

        for (int y = yMin; y < height; y += yskip) {
                // Decode a (subsampled) scanline of the image
                // ...

                // Update the percentage estimate
                // This may be done only every few rows if desired
                pixelsDecoded += pixelsPerRow;
                processImageProgress(100.0F*pixelsDecoded/totalPixels);

                // Check for an asynchronous abort request
                if (abortRequested()) {
                        aborted = true;
                        break;
                }
        }

        // Handle the end of decoding
        if (aborted) {
                processImageAborted();
        } else {
                processImageComplete(imageIndex);
        }

        // If the read was aborted, we still return a partially decoded image
        return theImage;
}
Метаданные следующий набор методов в MyFormatImageReader соглашение с метаданными. Поскольку наш гипотетический формат только кодирует единственное изображение, мы можем проигнорировать понятие "потоковых" метаданных, и использовать метаданные "изображения" только:
        MyFormatMetadata metadata = null; // class defined below

        public IIOMetadata getStreamMetadata()
                throws IIOException {
                return null;
        }

        public IIOMetadata getImageMetadata(int imageIndex)
                throws IIOException {
                if (imageIndex != 0) {
                        throw new IndexOutOfBoundsException("imageIndex != 0!");
                }
                readMetadata();
                return metadata;
        }

Фактическая работа делается специфичным для формата методом readMetadata, который для этого формата заполняет пар ключевого слова/значения объекта метаданных,
        public void readMetadata() throws IIOException {
                if (metadata != null) {
                        return;
                }
                readHeader();
                this.metadata = new MyFormatMetadata();
                try {
                        while (true) {
                                String keyword = stream.readUTF();
                                stream.readUnsignedByte();
                                if (keyword.equals("END")) {
                                        break;
                                }
                                String value = stream.readUTF();
                                stream.readUnsignedByte();

                                metadata.keywords.add(keyword);
                                metadata.values.add(value);
                        } catch (IIOException e) {
                                throw new IIOException("Exception reading metadata",
                                                       e);
                        }
                }
        }

MyFormatMetadata Наконец, различные интерфейсы для извлечения и редактирования метаданных должны быть определены. Мы определяем вызванный класс MyFormatMetadata это расширяется IIOMetadata класс, и дополнительно может сохранить пар ключевого слова/значения, которые позволяются в формате файла:
package com.mycompany.imageio;

import org.w3c.dom.*;
import javax.xml.parsers.*; // Package name may change in J2SDK 1.4

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormat;
import javax.imageio.metadata.IIOMetadataNode;

public class MyFormatMetadata extends IIOMetadata {

        static final boolean standardMetadataFormatSupported = false;
        static final String nativeMetadataFormatName =
                "com.mycompany.imageio.MyFormatMetadata_1.0";
        static final String nativeMetadataFormatClassName =
                "com.mycompany.imageio.MyFormatMetadata";
        static final String[] extraMetadataFormatNames = null;
        static final String[] extraMetadataFormatClassNames = null;
    
        // Keyword/value pairs
        List keywords = new ArrayList();
        List values = new ArrayList();
Первый набор методов характерен для большинства реализаций IIOMetadata:
        public MyFormatMetadata() {
                super(standardMetadataFormatSupported,
                      nativeMetadataFormatName,
                      nativeMetadataFormatClassName,
                      extraMetadataFormatNames,
                      extraMetadataFormatClassNames);
        }

        public IIOMetadataFormat getMetadataFormat(String formatName) {
                if (!formatName.equals(nativeMetadataFormatName)) {
                        throw new IllegalArgumentException("Bad format name!");
                }
                return MyFormatMetadataFormat.getDefaultInstance();
        }

Самый важный метод для плагинов читателя getAsTree:
        public Node getAsTree(String formatName) {
                if (!formatName.equals(nativeMetadataFormatName)) {
                        throw new IllegalArgumentException("Bad format name!");
                }

                // Create a root node
                IIOMetadataNode root =
                        new IIOMetadataNode(nativeMetadataFormatName);

                // Add a child to the root node for each keyword/value pair
                Iterator keywordIter = keywords.iterator();
                Iterator valueIter = values.iterator();
                while (keywordIter.hasNext()) {
                        IIOMetadataNode node =
                                new IIOMetadataNode("KeywordValuePair");
                        node.setAttribute("keyword", (String)keywordIter.next());
                        node.setAttribute("value", (String)valueIter.next());
                        root.appendChild(node);
                }

                return root;
        }
Для плагинов писателя возможность отредактировать значения метаданных получается, реализовывая isReadOnly, reset, и mergeTree методы:
        public boolean isReadOnly() {
            return false;
        }

        public void reset() {
            this.keywords = new ArrayList();
            this.values = new ArrayList();
        }

        public void mergeTree(String formatName, Node root)
                throws IIOInvalidTreeException {
                if (!formatName.equals(nativeMetadataFormatName)) {
                        throw new IllegalArgumentException("Bad format name!");
                }

                Node node = root;
                if (!node.getNodeName().equals(nativeMetadataFormatName)) {
                        fatal(node, "Root must be " + nativeMetadataFormatName);
                }
                node = node.getFirstChild();
                while (node != null) {
                        if (!node.getNodeName().equals("KeywordValuePair")) {
                                fatal(node, "Node name not KeywordValuePair!");
                        }
                        NamedNodeMap attributes = node.getAttributes();
                        Node keywordNode = attributes.getNamedItem("keyword");
                        Node valueNode = attributes.getNamedItem("value");
                        if (keywordNode == null || valueNode == null) {
                                fatal(node, "Keyword or value missing!");
                        }

                        // Store keyword and value
                        keywords.add((String)keywordNode.getNodeValue());
                        values.add((String)valueNode.getNodeValue());

                        // Move to the next sibling
                        node = node.getNextSibling();
                }
        }

        private void fatal(Node node, String reason)
                throws IIOInvalidTreeException {
                throw new IIOInvalidTreeException(reason, node);
        }
}
MyFormatMetadataFormat Древовидная структура метаданных может быть описана, используя интерфейс IIOMetadataFormat. Класс реализации, IIOMetadataFormatImpl, заботится о поддержании "базы данных" информации об элементах, их атрибутах, и отношениях отцов и детей между ними:
package com.mycompany.imageio;

import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadataFormatImpl;

public class MyFormatMetadataFormat extends IIOMetadataFormatImpl {

        // Create a single instance of this class (singleton pattern)
        private static MyFormatMetadataFormat defaultInstance =
                new MyFormatMetadataFormat();

        // Make constructor private to enforce the singleton pattern
        private MyFormatMetadataFormat() {
                // Set the name of the root node
                // The root node has a single child node type that may repeat
                super("com.mycompany.imageio.MyFormatMetadata_1.0",
                      CHILD_POLICY_REPEAT);

                // Set up the "KeywordValuePair" node, which has no children
                addElement("KeywordValuePair",
                           "com.mycompany.imageio.MyFormatMetadata_1.0",
                           CHILD_POLICY_EMPTY);

                // Set up attribute "keyword" which is a String that is required
                // and has no default value
                addAttribute("KeywordValuePair", "keyword", DATATYPE_STRING,
                             true, null);
                // Set up attribute "value" which is a String that is required
                // and has no default value
                addAttribute("KeywordValuePair", "value", DATATYPE_STRING,
                             true, null);
        }

        // Check for legal element name
        public boolean canNodeAppear(String elementName,
                                     ImageTypeSpecifier imageType) {
                return elementName.equals("KeywordValuePair");
        }

        // Return the singleton instance
        public static MyFormatMetadataFormat getDefaultInstance() {
                return defaultInstance;
        }
}


СОДЕРЖАНИЕ | ПРЕДЫДУЩИЙ | NEXT

Oracle и/или его филиалы Авторское право © 1993, 2011, Oracle и/или его филиалы. Все права защищены.
Свяжитесь с Нами