Coverage Report - org.jaudiotagger.audio.asf.io.AsfExtHeaderModifier
 
Classes in this File Line Coverage Branch Coverage Complexity
AsfExtHeaderModifier
100%
50/50
66%
20/30
2.75
 
 1  
 package org.jaudiotagger.audio.asf.io;
 2  
 
 3  
 import org.jaudiotagger.audio.asf.data.GUID;
 4  
 import org.jaudiotagger.audio.asf.util.Utils;
 5  
 
 6  
 import java.io.ByteArrayOutputStream;
 7  
 import java.io.IOException;
 8  
 import java.io.InputStream;
 9  
 import java.io.OutputStream;
 10  
 import java.math.BigInteger;
 11  
 import java.util.ArrayList;
 12  
 import java.util.HashSet;
 13  
 import java.util.List;
 14  
 import java.util.Set;
 15  
 
 16  
 /**
 17  
  * This modifier manipulates an ASF header extension object.
 18  
  * 
 19  
  * @author Christian Laireiter
 20  
  */
 21  4
 public class AsfExtHeaderModifier implements ChunkModifier {
 22  
 
 23  
     /**
 24  
      * List of modifiers which are to be applied to contained chunks.
 25  
      */
 26  
     private final List<ChunkModifier> modifierList;
 27  
 
 28  
     /**
 29  
      * Creates an instance.<br>
 30  
      * 
 31  
      * @param modifiers
 32  
      *            modifiers to apply.
 33  
      */
 34  149
     public AsfExtHeaderModifier(final List<ChunkModifier> modifiers) {
 35  149
         assert modifiers != null;
 36  149
         this.modifierList = new ArrayList<ChunkModifier>(modifiers);
 37  149
     }
 38  
 
 39  
     /**
 40  
      * Simply copies a chunk from <code>source</code> to
 41  
      * <code>destination</code>.<br>
 42  
      * The method assumes, that the GUID has already been read and will write
 43  
      * the provided one to the destination.<br>
 44  
      * The chunk length however will be read and used to determine the amount of
 45  
      * bytes to copy.
 46  
      * 
 47  
      * @param guid
 48  
      *            GUID of the current CHUNK.
 49  
      * @param source
 50  
      *            source of an ASF chunk, which is to be located at the chunk
 51  
      *            length field.
 52  
      * @param destination
 53  
      *            the destination to copy the chunk to.
 54  
      * @throws IOException
 55  
      *             on I/O errors.
 56  
      */
 57  
     private void copyChunk(final GUID guid, final InputStream source,
 58  
             final OutputStream destination) throws IOException {
 59  596
         final long chunkSize = Utils.readUINT64(source);
 60  596
         destination.write(guid.getBytes());
 61  596
         Utils.writeUINT64(chunkSize, destination);
 62  596
         Utils.copy(source, destination, chunkSize - 24);
 63  596
     }
 64  
 
 65  
     /**
 66  
      * {@inheritDoc}
 67  
      */
 68  
     public boolean isApplicable(final GUID guid) {
 69  149
         return GUID.GUID_HEADER_EXTENSION.equals(guid);
 70  
     }
 71  
 
 72  
     /**
 73  
      * {@inheritDoc}
 74  
      */
 75  
     public ModificationResult modify(final GUID guid, final InputStream source,
 76  
             final OutputStream destination) throws IOException {
 77  149
         assert GUID.GUID_HEADER_EXTENSION.equals(guid);
 78  
 
 79  149
         long difference = 0;
 80  149
         final List<ChunkModifier> modders = new ArrayList<ChunkModifier>(
 81  
                 this.modifierList);
 82  149
         final Set<GUID> occuredGuids = new HashSet<GUID>();
 83  149
         occuredGuids.add(guid);
 84  
 
 85  149
         final BigInteger chunkLen = Utils.readBig64(source);
 86  149
         final GUID reserved1 = Utils.readGUID(source);
 87  149
         final int reserved2 = Utils.readUINT16(source);
 88  149
         final long dataSize = Utils.readUINT32(source);
 89  
 
 90  149
         assert dataSize == 0 || dataSize >= 24;
 91  149
         assert chunkLen.subtract(BigInteger.valueOf(46)).longValue() == dataSize;
 92  
 
 93  
         /*
 94  
          * Stream buffer for the chunk list
 95  
          */
 96  149
         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
 97  
         /*
 98  
          * Stream which counts read bytes. Dirty but quick way of implementing
 99  
          * this.
 100  
          */
 101  149
         final CountingInputStream cis = new CountingInputStream(source);
 102  
 
 103  811
         while (cis.getReadCount() < dataSize) {
 104  
             // read GUID
 105  662
             final GUID curr = Utils.readGUID(cis);
 106  662
             boolean handled = false;
 107  1661
             for (int i = 0; i < modders.size() && !handled; i++) {
 108  999
                 if (modders.get(i).isApplicable(curr)) {
 109  66
                     final ModificationResult modRes = modders.get(i).modify(
 110  
                             curr, cis, bos);
 111  66
                     difference += modRes.getByteDifference();
 112  66
                     occuredGuids.addAll(modRes.getOccuredGUIDs());
 113  66
                     modders.remove(i);
 114  66
                     handled = true;
 115  
                 }
 116  
             }
 117  662
             if (!handled) {
 118  596
                 occuredGuids.add(curr);
 119  596
                 copyChunk(curr, cis, bos);
 120  
             }
 121  662
         }
 122  
         // Now apply the left modifiers.
 123  149
         for (final ChunkModifier curr : modders) {
 124  
             // chunks, which were not in the source file, will be added to the
 125  
             // destination
 126  216
             final ModificationResult result = curr.modify(null, null, bos);
 127  216
             difference += result.getByteDifference();
 128  216
             occuredGuids.addAll(result.getOccuredGUIDs());
 129  216
         }
 130  149
         destination.write(GUID.GUID_HEADER_EXTENSION.getBytes());
 131  149
         Utils.writeUINT64(chunkLen.add(BigInteger.valueOf(difference))
 132  
                 .longValue(), destination);
 133  149
         destination.write(reserved1.getBytes());
 134  149
         Utils.writeUINT16(reserved2, destination);
 135  149
         Utils.writeUINT32(dataSize + difference, destination);
 136  149
         destination.write(bos.toByteArray());
 137  149
         return new ModificationResult(0, difference, occuredGuids);
 138  
     }
 139  
 
 140  
 }