package org.jaudiotagger.tag.datatype; import org.jaudiotagger.tag.InvalidDataTypeException; import org.jaudiotagger.tag.id3.AbstractTagFrameBody; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import java.util.logging.Level; /** * Represents a data type that supports multiple terminated Strings (there may only be one) */ public class MultipleTextEncodedStringNullTerminated extends AbstractDataType { /** * Creates a new ObjectStringSizeTerminated datatype. * * @param identifier identifies the frame type * @param frameBody */ public MultipleTextEncodedStringNullTerminated(String identifier, AbstractTagFrameBody frameBody) { super(identifier, frameBody); value = new MultipleTextEncodedStringNullTerminated.Values(); } public MultipleTextEncodedStringNullTerminated(TextEncodedStringSizeTerminated object) { super(object); value = new MultipleTextEncodedStringNullTerminated.Values(); } public MultipleTextEncodedStringNullTerminated(MultipleTextEncodedStringNullTerminated object) { super(object); } public boolean equals(Object obj) { return obj instanceof MultipleTextEncodedStringNullTerminated && super.equals(obj); } /** * Returns the size in bytes of this datatype when written to file * * @return size of this datatype */ public int getSize() { return size; } /** * Check the value can be encoded with the specified encoding * * @return */ public boolean canBeEncoded() { for (ListIterator<String> li = ((Values) value).getList().listIterator(); li.hasNext(); ) { TextEncodedStringNullTerminated next = new TextEncodedStringNullTerminated(identifier, frameBody, li.next()); if (!next.canBeEncoded()) { return false; } } return true; } /** * Read Null Terminated Strings from the array starting at offset, continue until unable to find any null terminated * Strings or until reached the end of the array. The offset should be set to byte after the last null terminated * String found. * * @param arr to read the Strings from * @param offset in the array to start reading from * @throws InvalidDataTypeException if unable to find any null terminated Strings */ public void readByteArray(byte[] arr, int offset) throws InvalidDataTypeException { // logger.finer("Reading MultipleTextEncodedStringNullTerminated from array from offset:" + offset); //Continue until unable to read a null terminated String while (true) { try { //Read String TextEncodedStringNullTerminated next = new TextEncodedStringNullTerminated(identifier, frameBody); next.readByteArray(arr, offset); if (next.getSize() == 0) { break; } else { //Add to value ((Values) value).add((String) next.getValue()); //Add to size calculation size += next.getSize(); //Increment Offset to start of next datatype. offset += next.getSize(); } } catch (InvalidDataTypeException idte) { break; } if (size == 0) { //logger.warning("No null terminated Strings found"); throw new InvalidDataTypeException("No null terminated Strings found"); } } // logger.finer("Read MultipleTextEncodedStringNullTerminated:" + value + " size:" + size); } /** * For every String write to bytebuffer * * @return bytebuffer that should be written to file to persist this datatype. */ public byte[] writeByteArray() { // logger.finer("Writing MultipleTextEncodedStringNullTerminated"); int localSize = 0; ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try { for (ListIterator<String> li = ((Values) value).getList().listIterator(); li.hasNext(); ) { TextEncodedStringNullTerminated next = new TextEncodedStringNullTerminated(identifier, frameBody, li.next()); buffer.write(next.writeByteArray()); localSize += next.getSize(); } } catch (IOException ioe) { //This should never happen because the write is internal with the JVM it is not to a file logger.log(Level.SEVERE, "IOException in MultipleTextEncodedStringNullTerminated when writing byte array", ioe); throw new RuntimeException(ioe); } //Update size member variable size = localSize; // //logger.finer("Written MultipleTextEncodedStringNullTerminated"); return buffer.toByteArray(); } /** * This holds the values held by a MultipleTextEncodedData type */ public static class Values { private List<String> valueList = new ArrayList<String>(); public Values() { } /** * Add String Data type to the value list * * @param value to add to the list */ public void add(String value) { valueList.add(value); } /** * Return the list of values * * @return the list of values */ public List<String> getList() { return valueList; } /** * @return no of values */ public int getNumberOfValues() { return valueList.size(); } /** * Return the list of values as a single string separated by a comma * * @return a string representation of the value */ public String toString() { StringBuffer sb = new StringBuffer(); for (ListIterator<String> li = valueList.listIterator(); li.hasNext(); ) { String next = li.next(); sb.append(next); if (li.hasNext()) { sb.append(","); } } return sb.toString(); } } }