Coverage Report - org.jaudiotagger.tag.id3.framebody.AbstractID3v2FrameBody
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractID3v2FrameBody
94%
49/52
93%
13/14
0
 
 1  
 /**
 2  
  *  @author : Paul Taylor
 3  
  *  @author : Eric Farng
 4  
  *
 5  
  *  Version @version:$Id: AbstractID3v2FrameBody.java,v 1.21 2008/07/21 10:45:42 paultaylor Exp $
 6  
  *
 7  
  *  MusicTag Copyright (C)2003,2004
 8  
  *
 9  
  *  This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
 10  
  *  General Public  License as published by the Free Software Foundation; either version 2.1 of the License,
 11  
  *  or (at your option) any later version.
 12  
  *
 13  
  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 14  
  *  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15  
  *  See the GNU Lesser General Public License for more details.
 16  
  *
 17  
  *  You should have received a copy of the GNU Lesser General Public License along with this library; if not,
 18  
  *  you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
 19  
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 20  
  *
 21  
  * Description:
 22  
  * Abstract Superclass of all Frame Bodys
 23  
  *
 24  
  */
 25  
 package org.jaudiotagger.tag.id3.framebody;
 26  
 
 27  
 import org.jaudiotagger.audio.mp3.MP3File;
 28  
 import org.jaudiotagger.tag.InvalidDataTypeException;
 29  
 import org.jaudiotagger.tag.InvalidFrameException;
 30  
 import org.jaudiotagger.tag.InvalidTagException;
 31  
 import org.jaudiotagger.tag.datatype.AbstractDataType;
 32  
 import org.jaudiotagger.tag.id3.AbstractTagFrameBody;
 33  
 
 34  
 import java.io.ByteArrayOutputStream;
 35  
 import java.io.IOException;
 36  
 import java.nio.ByteBuffer;
 37  
 
 38  
 /**
 39  
  * Contains the content for an ID3v2 frame, (the header is held directly within the frame
 40  
  */
 41  
 public abstract class AbstractID3v2FrameBody extends AbstractTagFrameBody
 42  
 {
 43  
     protected static final String TYPE_BODY = "body";
 44  
 
 45  
 
 46  
     /**
 47  
      * Frame Body Size, originally this is size as indicated in frame header
 48  
      * when we come to writing data we recalculate it.
 49  
      */
 50  
     private int size;
 51  
 
 52  
 
 53  
     /**
 54  
      * Create Empty Body. Super Constructor sets up Object list
 55  
      */
 56  
     protected AbstractID3v2FrameBody()
 57  570
     {
 58  570
     }
 59  
 
 60  
     /**
 61  
      * Create Body based on another body
 62  
      */
 63  
     protected AbstractID3v2FrameBody(AbstractID3v2FrameBody copyObject)
 64  
     {
 65  1182
         super(copyObject);
 66  1182
     }
 67  
 
 68  
     /**
 69  
      * Creates a new FrameBody datatype from file. The super
 70  
      * Constructor sets up the Object list for the frame.
 71  
      *
 72  
      * @param byteBuffer from where to read the frame body from
 73  
      */
 74  
     protected AbstractID3v2FrameBody(ByteBuffer byteBuffer, int frameSize) throws InvalidTagException
 75  
     {
 76  1565
         super();
 77  1565
         setSize(frameSize);
 78  1565
         this.read(byteBuffer);
 79  
 
 80  1562
     }
 81  
 
 82  
     /**
 83  
      * Return the ID3v2 Frame Identifier, must be implemented by concrete subclasses
 84  
      *
 85  
      * @return the frame identifier
 86  
      */
 87  
     public abstract String getIdentifier();
 88  
 
 89  
 
 90  
     /**
 91  
      * Return size of frame body,if framebody already exist will take this value from the frame header
 92  
      * but it is always recalculated before writing any changes back to disk.
 93  
      *
 94  
      * @return size in bytes of this frame body
 95  
      */
 96  
     public int getSize()
 97  
     {
 98  2365
         return size;
 99  
     }
 100  
 
 101  
     /**
 102  
      * Set size based on size passed as parameter from frame header,
 103  
      * done before read
 104  
      */
 105  
     public void setSize(int size)
 106  
     {
 107  1565
         this.size = size;
 108  1565
     }
 109  
 
 110  
     /**
 111  
      * Set size based on size of the DataTypes making up the body,done after write
 112  
      */
 113  
     public void setSize()
 114  
     {
 115  958
         size = 0;
 116  958
         for (AbstractDataType object : objectList)
 117  
         {
 118  2388
             size += object.getSize();
 119  
         }
 120  
         ;
 121  958
     }
 122  
 
 123  
     /**
 124  
      * Are two bodies equal
 125  
      *
 126  
      * @param obj
 127  
      */
 128  
     public boolean equals(Object obj)
 129  
     {
 130  1
         if ((obj instanceof AbstractID3v2FrameBody) == false)
 131  
         {
 132  0
             return false;
 133  
         }
 134  1
         return super.equals(obj);
 135  
     }
 136  
 
 137  
     /**
 138  
      * This reads a frame body from a ByteBuffer into the appropriate FrameBody class and update the position of the
 139  
      * buffer to be just after the end of this framebody
 140  
      * <p/>
 141  
      * The ByteBuffer represents the tag and its position should be at the start of this framebody. The size as
 142  
      * indicated in the header is passed to the frame constructor when reading from file.
 143  
      *
 144  
      * @param byteBuffer file to read
 145  
      * @throws InvalidFrameException if unable to construct a framebody from the ByteBuffer
 146  
      */
 147  
     public void read(ByteBuffer byteBuffer) throws InvalidTagException
 148  
     {
 149  1565
         int size = getSize();
 150  1565
         logger.info("Reading body for" + this.getIdentifier() + ":" + size);
 151  
 
 152  
         //Allocate a buffer to the size of the Frame Body and read from file
 153  1565
         byte[] buffer = new byte[size];
 154  1565
         byteBuffer.get(buffer);
 155  
 
 156  
         //Offset into buffer, incremented by length of previous Datatype
 157  
         //this offset is only used internally to decide where to look for the next
 158  
         //datatype within a framebody, it does not decide where to look for the next frame body
 159  1565
         int offset = 0;
 160  
 
 161  
         //Go through the ObjectList of the Frame reading the data into the
 162  
         //correct datatype.
 163  1565
         for (AbstractDataType object : objectList)
 164  
         {
 165  3892
             logger.finest("offset:" + offset);
 166  
 
 167  
             //The read has extended further than the defined frame size (ok to extend upto
 168  
             //size because the next datatype may be of length 0.)
 169  3892
             if (offset > (size))
 170  
             {
 171  1
                 logger.warning("Invalid Size for FrameBody");
 172  1
                 throw new InvalidFrameException("Invalid size for Frame Body");
 173  
             }
 174  
 
 175  
             //Try and load it with data from the Buffer
 176  
             //if it fails frame is invalid
 177  
             try
 178  
             {
 179  3891
                 object.readByteArray(buffer, offset);
 180  
             }
 181  2
             catch (InvalidDataTypeException e)
 182  
             {
 183  2
                 logger.warning("Invalid DataType for Frame Body:" + e.getMessage());
 184  2
                 throw new InvalidFrameException("Invalid DataType for Frame Body:" + e.getMessage());
 185  3889
             }
 186  
             //Increment Offset to start of next datatype.
 187  3889
             offset += object.getSize();
 188  
         }
 189  1562
     }
 190  
 
 191  
     /**
 192  
      * Write the contents of this datatype to the byte array
 193  
      *
 194  
      * @throws IOException on any I/O error
 195  
      */
 196  
     public void write(ByteArrayOutputStream tagBuffer)
 197  
 
 198  
     {
 199  958
         logger.info("Writing frame body for" + this.getIdentifier() + ":Est Size:" + size);
 200  
         //Write the various fields to file in order
 201  958
         for (AbstractDataType object : objectList)
 202  
         {
 203  2388
             byte[] objectData = object.writeByteArray();
 204  2388
             if (objectData != null)
 205  
             {
 206  
                 try
 207  
                 {
 208  2380
                     tagBuffer.write(objectData);
 209  
                 }
 210  0
                 catch (IOException ioe)
 211  
                 {
 212  
                     //This could never happen coz not writing to file, so convert to RuntimeException
 213  0
                     throw new RuntimeException(ioe);
 214  2380
                 }
 215  
             }
 216  2388
         }
 217  958
         setSize();
 218  958
         logger.info("Written frame body for" + this.getIdentifier() + ":Real Size:" + size);
 219  
 
 220  958
     }
 221  
 
 222  
     /**
 223  
      * Return String Representation of Datatype     *
 224  
      */
 225  
     public void createStructure()
 226  
     {
 227  95
         MP3File.getStructureFormatter().openHeadingElement(TYPE_BODY, "");
 228  95
         for (AbstractDataType nextObject : objectList)
 229  
         {
 230  249
             nextObject.createStructure();
 231  
         }
 232  95
         MP3File.getStructureFormatter().closeHeadingElement(TYPE_BODY);
 233  95
     }
 234  
 }