package org.farng.mp3.id3; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.farng.mp3.AbstractMP3Tag; import org.farng.mp3.TagException; import org.farng.mp3.TagNotFoundException; import org.farng.mp3.TagUtility; /** * Superclass for all ID3v2 tags * * @author Eric Farng * @version $Revision: 2374 $ */ public abstract class AbstractID3v2 extends AbstractID3 { /** * just used to add up the padding. the size written to the file is determined by file pointer */ private static int paddingCounter = 0; private Map frameMap = null; private String duplicateFrameId = null; private byte majorVersion = (byte) 0; private byte revision = (byte) 0; private int duplicateBytes = 0; private int emptyFrameBytes = 0; private int fileReadSize = 0; private int invalidFrameBytes = 0; private int padding = 0; /** * Creates a new AbstractID3v2 object. */ protected AbstractID3v2() { super(); frameMap = new HashMap(); } /** * Creates a new AbstractID3v2 object. */ protected AbstractID3v2(final AbstractID3v2 copyObject) { super(copyObject); frameMap = new HashMap(); duplicateFrameId = copyObject.duplicateFrameId; majorVersion = copyObject.majorVersion; revision = copyObject.revision; duplicateBytes = copyObject.duplicateBytes; emptyFrameBytes = copyObject.emptyFrameBytes; fileReadSize = copyObject.fileReadSize; invalidFrameBytes = copyObject.invalidFrameBytes; padding = copyObject.padding; final Iterator iterator = copyObject.frameMap.keySet().iterator(); String identifier; AbstractID3v2Frame newFrame; while (iterator.hasNext()) { identifier = (String) iterator.next(); newFrame = (AbstractID3v2Frame) TagUtility.copyObject(copyObject.frameMap.get(identifier)); frameMap.put(newFrame.getIdentifier(), newFrame); } } public void setFrame(final AbstractID3v2Frame frame) { if (frame.getBody() != null) { frameMap.put(frame.getIdentifier(), frame); } } public AbstractID3v2Frame getFrame(final String identifier) { return (AbstractID3v2Frame) frameMap.get(identifier); } public int getFrameCount() { int size = 0; if (frameMap != null) { size = frameMap.size(); } return size; } public void clearFrameMap() { this.frameMap.clear(); } public Iterator getFrameIterator() { return this.frameMap.values().iterator(); } public Iterator getFrameOfType(final String identifier) { final Iterator iterator = frameMap.keySet().iterator(); final Set result = new HashSet(frameMap.size()); String hashSetKey; while (iterator.hasNext()) { hashSetKey = (String) iterator.next(); if (hashSetKey.startsWith(identifier)) { result.add(frameMap.get(hashSetKey)); } } return result.iterator(); } public boolean hasFrame(final String identifier) { return frameMap.containsKey(identifier); } public boolean hasFrameOfType(final String identifier) { final Iterator iterator = frameMap.keySet().iterator(); String hashMapKey; boolean found = false; while (iterator.hasNext() && !found) { hashMapKey = (String) iterator.next(); if (hashMapKey.startsWith(identifier)) { found = true; } } return found; } public Iterator iterator() { return frameMap.values().iterator(); } public void removeFrame(final String identifier) { frameMap.remove(identifier); } public void removeFrameOfType(final String identifier) { final Iterator iterator = getFrameOfType(identifier); while (iterator.hasNext()) { final AbstractID3v2Frame frame = (AbstractID3v2Frame) iterator.next(); frameMap.remove(frame.getIdentifier()); } } public Collection values() { return frameMap.values(); } public void append(final AbstractMP3Tag abstractMP3Tag) { final AbstractID3v2 oldTag = this; final AbstractID3v2 newTag; if (abstractMP3Tag != null) { if (abstractMP3Tag instanceof AbstractID3v2) { newTag = (AbstractID3v2) abstractMP3Tag; } else { newTag = new ID3v2_4(abstractMP3Tag); } final Iterator iterator = newTag.getFrameIterator(); AbstractID3v2Frame frame; while (iterator.hasNext()) { frame = (AbstractID3v2Frame) iterator.next(); if (!oldTag.hasFrame(frame.getIdentifier())) { oldTag.setFrame(frame); } } } } public void append(final RandomAccessFile file) throws IOException, TagException { AbstractID3v2 oldTag; try { oldTag = new ID3v2_4(file); oldTag.append(this); oldTag.write(file); } catch (TagNotFoundException ex) { try { oldTag = new ID3v2_3(file); oldTag.append(this); oldTag.write(file); } catch (TagNotFoundException ex2) { try { oldTag = new ID3v2_2(file); oldTag.append(this); oldTag.write(file); } catch (TagNotFoundException ex3) { write(file); } } } } public void delete(final RandomAccessFile file) throws IOException { // this works by just erasing the "TAG" tag at the beginning // of the file final byte[] buffer = new byte[3]; if (seek(file)) { file.seek(0); file.write(buffer); } } public boolean equals(final Object obj) { if (!(obj instanceof AbstractID3v2)) { return false; } final AbstractID3v2 abstractID3v2 = (AbstractID3v2) obj; if (!frameMap.equals(abstractID3v2.frameMap)) { return false; } return super.equals(obj); } public void overwrite(final AbstractMP3Tag abstractMP3Tag) { final AbstractID3v2 oldTag = this; final AbstractID3v2 newTag; if (abstractMP3Tag != null) { if (abstractMP3Tag instanceof AbstractID3v2) { newTag = (AbstractID3v2) abstractMP3Tag; } else { newTag = new ID3v2_4(abstractMP3Tag); } final Iterator iterator = newTag.getFrameIterator(); AbstractID3v2Frame frame; while (iterator.hasNext()) { frame = (AbstractID3v2Frame) iterator.next(); oldTag.setFrame(frame); } } } public void overwrite(final RandomAccessFile file) throws IOException, TagException { AbstractID3v2 oldTag; try { oldTag = new ID3v2_4(file); oldTag.overwrite(this); oldTag.write(file); } catch (TagNotFoundException ex) { try { oldTag = new ID3v2_3(file); oldTag.overwrite(this); oldTag.write(file); } catch (TagNotFoundException ex2) { try { oldTag = new ID3v2_2(file); oldTag.overwrite(this); oldTag.write(file); } catch (TagNotFoundException ex3) { write(file); } } } } public void write(final AbstractMP3Tag abstractMP3Tag) { final AbstractID3v2 oldTag = this; final AbstractID3v2 newTag; if (abstractMP3Tag != null) { if (abstractMP3Tag instanceof AbstractID3v2) { newTag = (AbstractID3v2) abstractMP3Tag; } else { newTag = new ID3v2_4(abstractMP3Tag); } final Iterator iterator = newTag.getFrameIterator(); oldTag.frameMap.clear(); AbstractID3v2Frame frame; while (iterator.hasNext()) { frame = (AbstractID3v2Frame) iterator.next(); oldTag.setFrame(frame); } } } protected static int byteArrayToSize(final byte[] buffer) { /** * the decided not to use the top bit of the 4 bytes so we need to * convert the size back and forth */ return (buffer[0] << 21) + (buffer[1] << 14) + (buffer[2] << 7) + buffer[3]; } protected static byte[] sizeToByteArray(final int size) { final byte[] buffer = new byte[4]; buffer[0] = (byte) ((size & 0x0FE00000) >> 21); buffer[1] = (byte) ((size & 0x001FC000) >> 14); buffer[2] = (byte) ((size & 0x00003F80) >> 7); buffer[3] = (byte) (size & 0x0000007F); return buffer; } protected static void resetPaddingCounter() { paddingCounter = 0; } protected static void incrementPaddingCounter() { paddingCounter++; } protected static void decrementPaddingCounter() { paddingCounter--; } protected static int getPaddingCounter() { return paddingCounter; } public byte getMajorVersion() { return majorVersion; } protected void setMajorVersion(final byte majorVersion) { this.majorVersion = majorVersion; } public byte getRevision() { return revision; } protected void setRevision(final byte revision) { this.revision = revision; } public int getDuplicateBytes() { return duplicateBytes; } public String getDuplicateFrameId() { return duplicateFrameId; } public int getEmptyFrameBytes() { return emptyFrameBytes; } public int getFileReadBytes() { return fileReadSize; } protected void setFileReadBytes(final int fileReadSize) { this.fileReadSize = fileReadSize; } protected void setPaddingSize(final int paddingSize) { this.padding = paddingSize; } protected void incrementDuplicateBytes(final int duplicateBytes) { this.duplicateBytes += duplicateBytes; } protected void incrementEmptyFrameBytes(final int emptyFrameBytes) { this.emptyFrameBytes += emptyFrameBytes; } protected void incrementInvalidFrameBytes() { this.invalidFrameBytes++; } protected void appendDuplicateFrameId(final String duplicateFrameId) { this.duplicateFrameId += duplicateFrameId; } public int getInvalidFrameBytes() { return invalidFrameBytes; } public int getPaddingSize() { return padding; } }