FileDocCategorySizeDatePackage
ChunkContainerReader.javaAPI DocJaudiotagger 2.0.49124Wed Mar 30 16:11:50 BST 2011org.jaudiotagger.audio.asf.io

ChunkContainerReader

public abstract class ChunkContainerReader extends Object implements ChunkReader
This class represents a reader implementation, which is able to read ASF objects (chunks) which store other objects (chunks) within them.
author
Christian Laireiter
param
The {@link ChunkContainer} instance, the implementation will create.

Fields Summary
protected static final Logger
LOGGER
Logger
public static final int
READ_LIMIT
Within this range, a {@link ChunkReader} should be aware if it fails.
protected final boolean
eachChunkOnce
If true each chunk type will only be read once.
protected boolean
hasFailingReaders
If true due to a {@linkplain #register(Class) registered} chunk reader, all {@link InputStream} objects passed to {@link #read(GUID, InputStream, long)} must support mark/reset.
protected final Map
readerMap
Registers GUIDs to their reader classes.
Constructors Summary
protected ChunkContainerReader(List toRegister, boolean readChunkOnce)
Creates a reader instance, which only utilizes the given list of chunk readers.

param
toRegister List of {@link ChunkReader} class instances, which are to be utilized by the instance.
param
readChunkOnce if true, each chunk type (identified by chunk GUID) will handled only once, if a reader is available, other chunks will be discarded.


                                                                                                                       
     
                
               
        this.eachChunkOnce = readChunkOnce;
        for (final Class<? extends ChunkReader> curr : toRegister) {
            register(curr);
        }
    
Methods Summary
protected voidcheckStream(java.io.InputStream stream)
Checks for the constraints of this class.

param
stream stream to test.
throws
IllegalArgumentException If stream does not meet the requirements.

        if (this.hasFailingReaders && !stream.markSupported()) {
            throw new IllegalArgumentException(
                    "Stream has to support mark/reset.");
        }
    
protected abstract ChunkTypecreateContainer(long streamPosition, java.math.BigInteger chunkLength, java.io.InputStream stream)
This method is called by {@link #read(GUID, InputStream, long)} in order to create the resulting object. Implementations of this class should now return a new instance of their implementation specific result AND all data should be read, until the list of chunks starts. (The {@link ChunkContainer#getChunkEnd()} must return a sane result, too)

param
streamPosition position of the stream, the chunk starts.
param
chunkLength the length of the chunk (from chunk header)
param
stream to read the implementation specific information.
return
instance of the implementations result.
throws
IOException On I/O Errors and Invalid data.

protected ChunkReadergetReader(org.jaudiotagger.audio.asf.data.GUID guid)
Gets a configured {@linkplain ChunkReader reader} instance for ASF objects (chunks) with the specified guid.

param
guid GUID which identifies the chunk to be read.
return
an appropriate reader implementation, null if not {@linkplain #register(Class) registered}.

        return this.readerMap.get(guid);
    
protected booleanisReaderAvailable(org.jaudiotagger.audio.asf.data.GUID guid)
Tests whether {@link #getReader(GUID)} won't return null.

param
guid GUID which identifies the chunk to be read.
return
true if a reader is available.

        return this.readerMap.containsKey(guid);
    
public ChunkTyperead(org.jaudiotagger.audio.asf.data.GUID guid, java.io.InputStream stream, long chunkStart)
This Method implements the reading of a chunk container.

param
guid GUID of the currently read container.
param
stream Stream which contains the chunk container.
param
chunkStart The start of the chunk container from stream start.
For direct file streams one can assume 0 here.
return
null if no valid data found, else a Wrapper containing all supported data.
throws
IOException Read errors.
throws
IllegalArgumentException If one used {@link ChunkReader} could {@linkplain ChunkReader#canFail() fail} and the stream source doesn't support mark/reset.

        checkStream(stream);
        final CountingInputStream cis = new CountingInputStream(stream);
        if (!Arrays.asList(getApplyingIds()).contains(guid)) {
            throw new IllegalArgumentException(
                    "provided GUID is not supported by this reader.");
        }
        // For Know the file pointer pointed to an ASF header chunk.
        final BigInteger chunkLen = Utils.readBig64(cis);
        /*
         * now read implementation specific information until the chunk
         * collection starts and create the resulting object.
         */
        final ChunkType result = createContainer(chunkStart, chunkLen, cis);
        // 16 bytes have already been for providing the GUID
        long currentPosition = chunkStart + cis.getReadCount() + 16;

        final HashSet<GUID> alreadyRead = new HashSet<GUID>();
        /*
         * Now reading header of chuncks.
         */
        while (currentPosition < result.getChunkEnd()) {
            final GUID currentGUID = Utils.readGUID(cis);
            final boolean skip = this.eachChunkOnce
                    && (!isReaderAvailable(currentGUID) || !alreadyRead
                            .add(currentGUID));
            Chunk chunk;
            /*
             * If one reader tells it could fail (new method), then check the
             * input stream for mark/reset. And use it if failed.
             */
            if (!skip && isReaderAvailable(currentGUID)) {
                final ChunkReader reader = getReader(currentGUID);
                if (reader.canFail()) {
                    cis.mark(READ_LIMIT);
                }
                chunk = getReader(currentGUID).read(currentGUID, cis,
                        currentPosition);
            } else {
                chunk = ChunkHeaderReader.getInstance().read(currentGUID, cis,
                        currentPosition);
            }
            if (chunk == null) {
                /*
                 * Reader failed
                 */
                cis.reset();
            } else {
                if (!skip) {
                    result.addChunk(chunk);
                }
                currentPosition = chunk.getChunkEnd();
                // Always take into account, that 16 bytes have been read prior
                // to calling this method
                assert cis.getReadCount() + chunkStart + 16 == currentPosition;
            }
        }

        return result;
    
private voidregister(java.lang.Class toRegister)
Registers the given reader.

param
The actual reader implementation.
param
toRegister chunk reader which is to be registered.

        try {
            final T reader = toRegister.newInstance();
            for (final GUID curr : reader.getApplyingIds()) {
                this.readerMap.put(curr, reader);
            }
        } catch (InstantiationException e) {
            LOGGER.severe(e.getMessage());
        } catch (IllegalAccessException e) {
            LOGGER.severe(e.getMessage());
        }