Coverage Report - org.jaudiotagger.audio.asf.data.ChunkContainer
 
Classes in this File Line Coverage Branch Coverage Complexity
ChunkContainer
78%
36/46
81%
18/22
2.111
 
 1  
 package org.jaudiotagger.audio.asf.data;
 2  
 
 3  
 import org.jaudiotagger.audio.asf.util.ChunkPositionComparator;
 4  
 import org.jaudiotagger.audio.asf.util.Utils;
 5  
 
 6  
 import java.math.BigInteger;
 7  
 import java.util.*;
 8  
 
 9  
 /**
 10  
  * Stores multiple ASF objects (chunks) in form of {@link Chunk} objects, and is
 11  
  * itself an ASF object (chunk).<br>
 12  
  * <br>
 13  
  * Because current implementation is solely used for ASF metadata, all chunks
 14  
  * (except for {@link StreamChunk}) may only be {@linkplain #addChunk(Chunk)
 15  
  * inserted} once.
 16  
  * 
 17  
  * @author Christian Laireiter
 18  
  */
 19  4
 public class ChunkContainer extends Chunk {
 20  
 
 21  
     /**
 22  
      * Stores the {@link GUID} instances, which are allowed multiple times
 23  
      * within an ASF header.
 24  
      */
 25  
     private final static Set<GUID> MULTI_CHUNKS;
 26  
 
 27  
     static {
 28  4
         MULTI_CHUNKS = new HashSet<GUID>();
 29  4
         MULTI_CHUNKS.add(GUID.GUID_STREAM);
 30  4
     }
 31  
 
 32  
     /**
 33  
      * Tests whether all stored chunks have a unique starting position among
 34  
      * their brothers.
 35  
      * 
 36  
      * @param container
 37  
      *            the container to test.
 38  
      * 
 39  
      * @return <code>true</code> if all chunks are located at an unique
 40  
      *         position. However, no intersection is tested.
 41  
      */
 42  
     protected static boolean chunkstartsUnique(final ChunkContainer container) {
 43  2155
         boolean result = true;
 44  2155
         final Set<Long> chunkStarts = new HashSet<Long>();
 45  2155
         final Collection<Chunk> chunks = container.getChunks();
 46  2155
         for (final Chunk curr : chunks) {
 47  7389
             result &= chunkStarts.add(curr.getPosition());
 48  
         }
 49  2155
         return result;
 50  
     }
 51  
 
 52  
     /**
 53  
      * Stores the {@link Chunk} objects to their {@link GUID}.
 54  
      */
 55  
     private final Map<GUID, List<Chunk>> chunkTable;
 56  
 
 57  
     /**
 58  
      * Creates an instance.
 59  
      * 
 60  
      * @param chunkGUID
 61  
      *            the GUID which identifies the chunk.
 62  
      * @param pos
 63  
      *            the position of the chunk within the stream.
 64  
      * @param length
 65  
      *            the length of the chunk.
 66  
      */
 67  
     public ChunkContainer(final GUID chunkGUID, final long pos,
 68  
             final BigInteger length) {
 69  878
         super(chunkGUID, pos, length);
 70  862
         this.chunkTable = new Hashtable<GUID, List<Chunk>>();
 71  862
     }
 72  
 
 73  
     /**
 74  
      * Adds a chunk to the container.<br>
 75  
      * 
 76  
      * @param toAdd
 77  
      *            The chunk which is to be added.
 78  
      * @throws IllegalArgumentException
 79  
      *             If a chunk of same type is already added, except for
 80  
      *             {@link StreamChunk}.
 81  
      */
 82  
     public void addChunk(final Chunk toAdd) {
 83  2215
         final List<Chunk> list = assertChunkList(toAdd.getGuid());
 84  2215
         if (!list.isEmpty() && !MULTI_CHUNKS.contains(toAdd.getGuid())) {
 85  68
             throw new IllegalArgumentException(
 86  
                     "The GUID of the given chunk indicates, that there is no more instance allowed."); //$NON-NLS-1$
 87  
         }
 88  2147
         list.add(toAdd);
 89  2147
         assert chunkstartsUnique(this) : "Chunk has equal start position like an already inserted one."; //$NON-NLS-1$
 90  2143
     }
 91  
 
 92  
     /**
 93  
      * This method asserts that a {@link List} exists for the given {@link GUID}
 94  
      * , in {@link #chunkTable}.<br>
 95  
      * 
 96  
      * @param lookFor
 97  
      *            The GUID to get list for.
 98  
      * @return an already existing, or newly created list.
 99  
      */
 100  
     protected List<Chunk> assertChunkList(final GUID lookFor) {
 101  3403
         List<Chunk> result = this.chunkTable.get(lookFor);
 102  3403
         if (result == null) {
 103  2131
             result = new ArrayList<Chunk>();
 104  2131
             this.chunkTable.put(lookFor, result);
 105  
         }
 106  3403
         return result;
 107  
     }
 108  
 
 109  
     /**
 110  
      * Returns a collection of all contained chunks.<br>
 111  
      * 
 112  
      * @return all contained chunks
 113  
      */
 114  
     public Collection<Chunk> getChunks() {
 115  2175
         final List<Chunk> result = new ArrayList<Chunk>();
 116  2175
         for (final List<Chunk> curr : this.chunkTable.values()) {
 117  7585
             result.addAll(curr);
 118  
         }
 119  2175
         return result;
 120  
     }
 121  
 
 122  
     /**
 123  
      * Looks for the first stored chunk which has the given GUID.
 124  
      * 
 125  
      * @param lookFor
 126  
      *            GUID to look up.
 127  
      * @param instanceOf
 128  
      *            The class which must additionally be matched.
 129  
      * @return <code>null</code> if no chunk was found, or the stored instance
 130  
      *         doesn't match.
 131  
      */
 132  
     protected Chunk getFirst(final GUID lookFor,
 133  
             final Class<? extends Chunk> instanceOf) {
 134  4329
         Chunk result = null;
 135  4329
         final List<Chunk> list = this.chunkTable.get(lookFor);
 136  4329
         if (list != null && !list.isEmpty()) {
 137  2681
             final Chunk chunk = list.get(0);
 138  2681
             if (instanceOf.isAssignableFrom(chunk.getClass())) {
 139  2673
                 result = chunk;
 140  
             }
 141  
         }
 142  4329
         return result;
 143  
     }
 144  
 
 145  
     /**
 146  
      * This method checks if a chunk has been {@linkplain #addChunk(Chunk)
 147  
      * added} with specified {@linkplain Chunk#getGuid() GUID}.<br>
 148  
      * 
 149  
      * @param lookFor
 150  
      *            GUID to look up.
 151  
      * @return <code>true</code> if chunk with specified GUID has been added.
 152  
      */
 153  
     public boolean hasChunkByGUID(final GUID lookFor) {
 154  1474
         return this.chunkTable.containsKey(lookFor);
 155  
     }
 156  
 
 157  
     /**
 158  
      * {@inheritDoc}
 159  
      */
 160  
     @Override
 161  
     public String prettyPrint(final String prefix) {
 162  0
         return prettyPrint(prefix, "");
 163  
     }
 164  
 
 165  
     /**
 166  
      * Nearly the same as {@link #prettyPrint(String)} however, additional
 167  
      * information can be injected below the {@link Chunk#prettyPrint(String)}
 168  
      * output and the listing of the contained chunks.<br>
 169  
      * 
 170  
      * @param prefix
 171  
      *            The prefix to prepend.
 172  
      * @param containerInfo
 173  
      *            Information to inject.
 174  
      * @return Information of current Chunk Object.
 175  
      */
 176  
     public String prettyPrint(final String prefix, final String containerInfo) {
 177  0
         final StringBuilder result = new StringBuilder(super.prettyPrint(prefix));
 178  0
         result.append(containerInfo);
 179  0
         result.append(prefix).append("  |").append(Utils.LINE_SEPARATOR);
 180  0
         final ArrayList<Chunk> list = new ArrayList<Chunk>(getChunks());
 181  0
         Collections.sort(list, new ChunkPositionComparator());
 182  
 
 183  0
         for (Chunk curr : list) {
 184  0
             result.append(curr.prettyPrint(prefix + "  |"));
 185  0
             result.append(prefix).append("  |").append(Utils.LINE_SEPARATOR);
 186  
         }
 187  0
         return result.toString();
 188  
     }
 189  
 }