Coverage Report - org.jaudiotagger.tag.datatype.PartOfSet
 
Classes in this File Line Coverage Branch Coverage Complexity
PartOfSet
88%
45/51
64%
9/14
2.444
PartOfSet$PartOfSetValue
79%
46/58
73%
22/30
2.444
 
 1  
 package org.jaudiotagger.tag.datatype;
 2  
 
 3  
 import org.jaudiotagger.tag.InvalidDataTypeException;
 4  
 import org.jaudiotagger.tag.TagOptionSingleton;
 5  
 import org.jaudiotagger.tag.id3.AbstractTagFrameBody;
 6  
 import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
 7  
 
 8  
 import java.nio.ByteBuffer;
 9  
 import java.nio.CharBuffer;
 10  
 import java.nio.charset.*;
 11  
 import java.util.regex.Matcher;
 12  
 import java.util.regex.Pattern;
 13  
 
 14  
 /**
 15  
  * Represents the form 01/10 whereby the second part is optional. This is used by frame such as TRCK and TPOS
 16  
  *
 17  
  * Some applications like to prepend the count with a zero to aid sorting, (i.e 02 comes before 10)
 18  
  */
 19  
 @SuppressWarnings({"EmptyCatchBlock"})
 20  497
 public class PartOfSet extends AbstractString
 21  
 {
 22  
     /**
 23  
      * Creates a new empty  PartOfSet datatype.
 24  
      *
 25  
      * @param identifier identifies the frame type
 26  
      * @param frameBody
 27  
      */
 28  
     public PartOfSet(String identifier, AbstractTagFrameBody frameBody)
 29  
     {
 30  670
         super(identifier, frameBody);
 31  670
     }
 32  
 
 33  
     /**
 34  
      * Copy constructor
 35  
      *
 36  
      * @param object
 37  
      */
 38  
     public PartOfSet(PartOfSet object)
 39  
     {
 40  434
         super(object);
 41  434
     }
 42  
 
 43  
     public boolean equals(Object obj)
 44  
     {
 45  0
         return obj instanceof PartOfSet && super.equals(obj);
 46  
     }
 47  
 
 48  
     /**
 49  
      * Read a 'n' bytes from buffer into a String where n is the framesize - offset
 50  
      * so thefore cannot use this if there are other objects after it because it has no
 51  
      * delimiter.
 52  
      * <p/>
 53  
      * Must take into account the text encoding defined in the Encoding Object
 54  
      * ID3 Text Frames often allow multiple strings seperated by the null char
 55  
      * appropriate for the encoding.
 56  
      *
 57  
      * @param arr    this is the buffer for the frame
 58  
      * @param offset this is where to start reading in the buffer for this field
 59  
      * @throws NullPointerException
 60  
      * @throws IndexOutOfBoundsException
 61  
      */
 62  
     public void readByteArray(byte[] arr, int offset) throws InvalidDataTypeException
 63  
     {
 64  514
         logger.finest("Reading from array from offset:" + offset);
 65  
 
 66  
         //Get the Specified Decoder
 67  514
         String charSetName = getTextEncodingCharSet();
 68  514
         CharsetDecoder decoder = Charset.forName(charSetName).newDecoder();
 69  
 
 70  
         //Decode sliced inBuffer
 71  514
         ByteBuffer inBuffer = ByteBuffer.wrap(arr, offset, arr.length - offset).slice();
 72  514
         CharBuffer outBuffer = CharBuffer.allocate(arr.length - offset);
 73  514
         decoder.reset();
 74  514
         CoderResult coderResult = decoder.decode(inBuffer, outBuffer, true);
 75  514
         if (coderResult.isError())
 76  
         {
 77  0
             logger.warning("Decoding error:" + coderResult.toString());
 78  
         }
 79  514
         decoder.flush(outBuffer);
 80  514
         outBuffer.flip();
 81  
 
 82  
         //Store value
 83  514
         String stringValue = outBuffer.toString();
 84  514
         value = new PartOfSetValue(stringValue);
 85  
 
 86  
         //SetSize, important this is correct for finding the next datatype
 87  514
         setSize(arr.length - offset);
 88  514
         logger.info("Read SizeTerminatedString:" + value + " size:" + size);
 89  514
     }
 90  
 
 91  
     /**
 92  
      * Write String into byte array
 93  
      * <p/>
 94  
      * It will remove a trailing null terminator if exists if the option
 95  
      * RemoveTrailingTerminatorOnWrite has been set.
 96  
      *
 97  
      * @return the data as a byte array in format to write to file
 98  
      */
 99  
     public byte[] writeByteArray()
 100  
     {
 101  312
         String value = getValue().toString();
 102  
         byte[] data;
 103  
         //Try and write to buffer using the CharSet defined by getTextEncodingCharSet()
 104  
         try
 105  
         {
 106  312
             if (TagOptionSingleton.getInstance().isRemoveTrailingTerminatorOnWrite())
 107  
             {
 108  304
                 if (value.length() > 0)
 109  
                 {
 110  284
                     if (value.charAt(value.length() - 1) == '\0')
 111  
                     {
 112  34
                         value = value.substring(0, value.length() - 1);                        
 113  
                     }
 114  
                 }
 115  
             }
 116  
 
 117  312
             String charSetName = getTextEncodingCharSet();
 118  312
             if (charSetName.equals(TextEncoding.CHARSET_UTF_16))
 119  
             {
 120  17
                 charSetName = TextEncoding.CHARSET_UTF_16_ENCODING_FORMAT;
 121  17
                 CharsetEncoder encoder = Charset.forName(charSetName).newEncoder();
 122  
                 //Note remember LE BOM is ff fe but this is handled by encoder Unicode char is fe ff
 123  17
                 ByteBuffer bb = encoder.encode(CharBuffer.wrap('\ufeff' +  value));
 124  17
                 data = new byte[bb.limit()];
 125  17
                 bb.get(data, 0, bb.limit());
 126  
 
 127  17
             }
 128  
             else
 129  
             {
 130  295
                  CharsetEncoder encoder = Charset.forName(charSetName).newEncoder();
 131  295
                 ByteBuffer bb = encoder.encode(CharBuffer.wrap( value));
 132  295
                 data = new byte[bb.limit()];
 133  295
                 bb.get(data, 0, bb.limit());
 134  
             }
 135  
         }
 136  
         //Should never happen so if does throw a RuntimeException
 137  0
         catch (CharacterCodingException ce)
 138  
         {
 139  0
             logger.severe(ce.getMessage());
 140  0
             throw new RuntimeException(ce);
 141  312
         }
 142  312
         setSize(data.length);
 143  312
         return data;
 144  
     }
 145  
 
 146  
     /**
 147  
      * Get the text encoding being used.
 148  
      * <p/>
 149  
      * The text encoding is defined by the frame body that the text field belongs to.
 150  
      *
 151  
      * @return the text encoding charset
 152  
      */
 153  
     protected String getTextEncodingCharSet()
 154  
     {
 155  826
         byte textEncoding = this.getBody().getTextEncoding();
 156  826
         String charSetName = TextEncoding.getInstanceOf().getValueForId(textEncoding);
 157  826
         logger.finest("text encoding:" + textEncoding + " charset:" + charSetName);
 158  826
         return charSetName;
 159  
     }
 160  
 
 161  
     /**
 162  
      * Holds data
 163  
      *
 164  
       */
 165  
     public static class PartOfSetValue
 166  
     {
 167  
         private static final Pattern trackNoPatternWithTotalCount;
 168  
         private static final Pattern trackNoPattern;
 169  
 
 170  
         static
 171  
         {
 172  
             //Match track/total pattern allowing for extraneous nulls ectera at the end
 173  4
             trackNoPatternWithTotalCount  = Pattern.compile("([0-9]+)/([0-9]+)(.*)", Pattern.CASE_INSENSITIVE);
 174  4
             trackNoPattern                = Pattern.compile("([0-9]+)(.*)", Pattern.CASE_INSENSITIVE);
 175  4
         }
 176  
 
 177  
         private static final String SEPARATOR = "/";
 178  
         private Integer count;
 179  
         private Integer total;
 180  
         private String  extra;   //Any extraneous info such as null chars
 181  
         public PartOfSetValue()
 182  108
         {
 183  
 
 184  108
         }
 185  
 
 186  
         /** When constructing from data
 187  
          *
 188  
          * @param value
 189  
          */
 190  
         public PartOfSetValue(String value)
 191  538
         {
 192  
             
 193  538
             Matcher m = trackNoPatternWithTotalCount.matcher(value);
 194  538
             if (m.matches())
 195  
             {
 196  140
                 this.count = Integer.parseInt(m.group(1));
 197  140
                 this.total = Integer.parseInt(m.group(2));
 198  140
                 this.extra = m.group(3);
 199  140
                 return;
 200  
             }
 201  
 
 202  398
             m = trackNoPattern.matcher(value);
 203  398
             if (m.matches())
 204  
             {
 205  370
                 this.count = Integer.parseInt(m.group(1));
 206  370
                 this.extra = m.group(2);
 207  
             }
 208  398
         }
 209  
 
 210  
         /**
 211  
          * Newly created
 212  
          *
 213  
          * @param count
 214  
          * @param total
 215  
          */
 216  
         public PartOfSetValue(Integer count,Integer total)
 217  24
         {
 218  24
             this.count = count;
 219  24
             this.total = total;
 220  24
         }
 221  
 
 222  
 
 223  
 
 224  
         public Integer getCount()
 225  
         {
 226  163
             return count;
 227  
         }
 228  
 
 229  
         public Integer getTotal()
 230  
         {
 231  137
             return total;
 232  
         }
 233  
 
 234  
         public void setCount(Integer count)
 235  
         {
 236  40
             this.count=count;
 237  40
         }
 238  
 
 239  
         public void setTotal(Integer total)
 240  
         {
 241  56
             this.total=total;
 242  
 
 243  56
         }
 244  
 
 245  
         public void setCount(String count)
 246  
         {
 247  
             try
 248  
             {
 249  0
                 this.count=Integer.parseInt(count);
 250  
             }
 251  0
             catch(NumberFormatException nfe)
 252  
             {
 253  
 
 254  0
             }
 255  0
         }
 256  
 
 257  
         public void setTotal(String total)
 258  
         {
 259  
             try
 260  
             {
 261  0
                 this.total=Integer.parseInt(total);
 262  
             }
 263  0
             catch(NumberFormatException nfe)
 264  
             {
 265  
 
 266  0
             }
 267  0
         }
 268  
 
 269  
         public String toString()
 270  
         {
 271  
             //Dont Pad
 272  927
             StringBuffer sb = new StringBuffer();
 273  927
             if(!TagOptionSingleton.getInstance().isPadNumbers())
 274  
             {
 275  911
                 if(count!=null)
 276  
                 {
 277  847
                     sb.append(count.intValue());
 278  
                 }
 279  64
                 else if(total!=null)
 280  
                 {
 281  8
                     sb.append('0');
 282  
                 }
 283  911
                 if(total!=null)
 284  
                 {
 285  285
                     sb.append(SEPARATOR).append(total);
 286  
                 }
 287  911
                 if(extra!=null)
 288  
                 {
 289  799
                     sb.append(extra);
 290  
                 }
 291  
             }
 292  
             else
 293  
             {
 294  16
                 if(count!=null)
 295  
                 {
 296  16
                     if(count>0 && count<10)
 297  
                     {
 298  16
                         sb.append("0").append(count);
 299  
                     }
 300  
                     else
 301  
                     {
 302  0
                         sb.append(count.intValue());
 303  
                     }
 304  
                 }
 305  0
                 else if(total!=null)
 306  
                 {
 307  0
                     sb.append('0');
 308  
                 }
 309  16
                 if(total!=null)
 310  
                 {
 311  16
                     if(total>0 && total<10)
 312  
                     {
 313  8
                         sb.append(SEPARATOR + "0").append(total);
 314  
                     }
 315  
                     else
 316  
                     {
 317  8
                         sb.append(SEPARATOR).append(total);
 318  
                     }
 319  
                 }
 320  16
                 if(extra!=null)
 321  
                 {
 322  0
                     sb.append(extra);
 323  
                 }
 324  
             }
 325  927
             return sb.toString();
 326  
         }
 327  
 
 328  
     }
 329  
 
 330  
     public PartOfSetValue getValue()
 331  
     {
 332  809
         return (PartOfSetValue)value;
 333  
     }
 334  
 
 335  
     public String toString()
 336  
     {
 337  0
         return value.toString();
 338  
     }
 339  
 }