FileDocCategorySizeDatePackage
PartOfSet.javaAPI DocJaudiotagger 2.0.411304Wed Jun 08 11:59:18 BST 2011org.jaudiotagger.tag.datatype

PartOfSet.java

package org.jaudiotagger.tag.datatype;

import org.jaudiotagger.tag.InvalidDataTypeException;
import org.jaudiotagger.tag.TagOptionSingleton;
import org.jaudiotagger.tag.id3.AbstractTagFrameBody;
import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
import org.jaudiotagger.utils.EqualsUtil;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Represents the form 01/10 whereby the second part is optional. This is used by frame such as TRCK and TPOS
 *
 * Some applications like to prepend the count with a zero to aid sorting, (i.e 02 comes before 10)
 */
@SuppressWarnings({"EmptyCatchBlock"})
public class PartOfSet extends AbstractString
{
    /**
     * Creates a new empty  PartOfSet datatype.
     *
     * @param identifier identifies the frame type
     * @param frameBody
     */
    public PartOfSet(String identifier, AbstractTagFrameBody frameBody)
    {
        super(identifier, frameBody);
    }

    /**
     * Copy constructor
     *
     * @param object
     */
    public PartOfSet(PartOfSet object)
    {
        super(object);
    }

    public boolean equals(Object obj)
    {
        if(obj==this)
        {
            return true;
        }

        if (!(obj instanceof PartOfSet))
        {
            return false;
        }

        PartOfSet that = (PartOfSet) obj;

        return EqualsUtil.areEqual(value, that.value);
    }

    /**
     * Read a 'n' bytes from buffer into a String where n is the framesize - offset
     * so thefore cannot use this if there are other objects after it because it has no
     * delimiter.
     * <p/>
     * Must take into account the text encoding defined in the Encoding Object
     * ID3 Text Frames often allow multiple strings seperated by the null char
     * appropriate for the encoding.
     *
     * @param arr    this is the buffer for the frame
     * @param offset this is where to start reading in the buffer for this field
     * @throws NullPointerException
     * @throws IndexOutOfBoundsException
     */
    public void readByteArray(byte[] arr, int offset) throws InvalidDataTypeException
    {
        logger.finest("Reading from array from offset:" + offset);

        //Get the Specified Decoder
        String charSetName = getTextEncodingCharSet();
        CharsetDecoder decoder = Charset.forName(charSetName).newDecoder();

        //Decode sliced inBuffer
        ByteBuffer inBuffer = ByteBuffer.wrap(arr, offset, arr.length - offset).slice();
        CharBuffer outBuffer = CharBuffer.allocate(arr.length - offset);
        decoder.reset();
        CoderResult coderResult = decoder.decode(inBuffer, outBuffer, true);
        if (coderResult.isError())
        {
            logger.warning("Decoding error:" + coderResult.toString());
        }
        decoder.flush(outBuffer);
        outBuffer.flip();

        //Store value
        String stringValue = outBuffer.toString();
        value = new PartOfSetValue(stringValue);

        //SetSize, important this is correct for finding the next datatype
        setSize(arr.length - offset);
        logger.config("Read SizeTerminatedString:" + value + " size:" + size);
    }

    /**
     * Write String into byte array
     * <p/>
     * It will remove a trailing null terminator if exists if the option
     * RemoveTrailingTerminatorOnWrite has been set.
     *
     * @return the data as a byte array in format to write to file
     */
    public byte[] writeByteArray()
    {
        String value = getValue().toString();
        byte[] data;
        //Try and write to buffer using the CharSet defined by getTextEncodingCharSet()
        try
        {
            if (TagOptionSingleton.getInstance().isRemoveTrailingTerminatorOnWrite())
            {
                if (value.length() > 0)
                {
                    if (value.charAt(value.length() - 1) == '\0')
                    {
                        value = value.substring(0, value.length() - 1);                        
                    }
                }
            }

            String charSetName = getTextEncodingCharSet();
            if (charSetName.equals(TextEncoding.CHARSET_UTF_16))
            {
                charSetName = TextEncoding.CHARSET_UTF_16_LE_ENCODING_FORMAT;
                CharsetEncoder encoder = Charset.forName(charSetName).newEncoder();
                //Note remember LE BOM is ff fe but this is handled by encoder Unicode char is fe ff
                ByteBuffer bb = encoder.encode(CharBuffer.wrap('\ufeff' +  value));
                data = new byte[bb.limit()];
                bb.get(data, 0, bb.limit());

            }
            else
            {
                 CharsetEncoder encoder = Charset.forName(charSetName).newEncoder();
                ByteBuffer bb = encoder.encode(CharBuffer.wrap( value));
                data = new byte[bb.limit()];
                bb.get(data, 0, bb.limit());
            }
        }
        //Should never happen so if does throw a RuntimeException
        catch (CharacterCodingException ce)
        {
            logger.severe(ce.getMessage());
            throw new RuntimeException(ce);
        }
        setSize(data.length);
        return data;
    }

    /**
     * Get the text encoding being used.
     * <p/>
     * The text encoding is defined by the frame body that the text field belongs to.
     *
     * @return the text encoding charset
     */
    protected String getTextEncodingCharSet()
    {
        byte textEncoding = this.getBody().getTextEncoding();
        String charSetName = TextEncoding.getInstanceOf().getValueForId(textEncoding);
        logger.finest("text encoding:" + textEncoding + " charset:" + charSetName);
        return charSetName;
    }

    /**
     * Holds data
     *
      */
    public static class PartOfSetValue
    {
        private static final Pattern trackNoPatternWithTotalCount;
        private static final Pattern trackNoPattern;

        static
        {
            //Match track/total pattern allowing for extraneous nulls ectera at the end
            trackNoPatternWithTotalCount  = Pattern.compile("([0-9]+)/([0-9]+)(.*)", Pattern.CASE_INSENSITIVE);
            trackNoPattern                = Pattern.compile("([0-9]+)(.*)", Pattern.CASE_INSENSITIVE);
        }

        private static final String SEPARATOR = "/";
        private Integer count;
        private Integer total;
        private String  extra;   //Any extraneous info such as null chars
        public PartOfSetValue()
        {

        }

        /** When constructing from data
         *
         * @param value
         */
        public PartOfSetValue(String value)
        {
            try
            {
                Matcher m = trackNoPatternWithTotalCount.matcher(value);
                if (m.matches())
                {
                    this.extra = m.group(3);
                    this.count = Integer.parseInt(m.group(1));
                    this.total = Integer.parseInt(m.group(2));
                    return;
                }

                m = trackNoPattern.matcher(value);
                if (m.matches())
                {
                    this.extra = m.group(2);
                    this.count = Integer.parseInt(m.group(1));
                }
            }
            catch(NumberFormatException nfe)
            {
                //#JAUDIOTAGGER-366 Could occur if actually value is a long not an int
                this.count = 0;
            }
        }

        /**
         * Newly created
         *
         * @param count
         * @param total
         */
        public PartOfSetValue(Integer count,Integer total)
        {
            this.count = count;
            this.total = total;
        }



        public Integer getCount()
        {
            return count;
        }

        public Integer getTotal()
        {
            return total;
        }

        public void setCount(Integer count)
        {
            this.count=count;
        }

        public void setTotal(Integer total)
        {
            this.total=total;

        }

        public void setCount(String count)
        {
            try
            {
                this.count=Integer.parseInt(count);
            }
            catch(NumberFormatException nfe)
            {

            }
        }

        public void setTotal(String total)
        {
            try
            {
                this.total=Integer.parseInt(total);
            }
            catch(NumberFormatException nfe)
            {

            }
        }

        public String toString()
        {
            //Don't Pad
            StringBuffer sb = new StringBuffer();
            if(!TagOptionSingleton.getInstance().isPadNumbers())
            {
                if(count!=null)
                {
                    sb.append(count.intValue());
                }
                else if(total!=null)
                {
                    sb.append('0');
                }
                if(total!=null)
                {
                    sb.append(SEPARATOR).append(total);
                }
                if(extra!=null)
                {
                    sb.append(extra);
                }
            }
            else
            {
                if(count!=null)
                {
                    if(count>0 && count<10)
                    {
                        sb.append("0").append(count);
                    }
                    else
                    {
                        sb.append(count.intValue());
                    }
                }
                else if(total!=null)
                {
                    sb.append('0');
                }
                if(total!=null)
                {
                    if(total>0 && total<10)
                    {
                        sb.append(SEPARATOR + "0").append(total);
                    }
                    else
                    {
                        sb.append(SEPARATOR).append(total);
                    }
                }
                if(extra!=null)
                {
                    sb.append(extra);
                }
            }
            return sb.toString();
        }


        public boolean equals(Object obj)
        {
            if(obj==this)
            {
                return true;
            }

            if (!(obj instanceof PartOfSetValue))
            {
                return false;
            }

            PartOfSetValue that = (PartOfSetValue) obj;

            return
                  EqualsUtil.areEqual(getCount(), that.getCount()) &&
                  EqualsUtil.areEqual(getTotal(), that.getTotal());
        }

    }


    public PartOfSetValue getValue()
    {
        return (PartOfSetValue)value;
    }

    public String toString()
    {
        return value.toString();
    }
}