Java tutorial
package com.limegroup.gnutella.metadata; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.limegroup.gnutella.util.FileUtils; import com.limegroup.gnutella.xml.LimeXMLReplyCollection; import com.limegroup.gnutella.xml.LimeXMLUtils; import de.vdheide.mp3.ID3v2; import de.vdheide.mp3.ID3v2DecompressionException; import de.vdheide.mp3.ID3v2Exception; import de.vdheide.mp3.ID3v2Frame; import de.vdheide.mp3.NoID3v2TagException; /** * an editor specifically for mp3 files with id3 tags */ public class MP3DataEditor extends AudioMetaDataEditor { private static final Log LOG = LogFactory.getLog(MP3DataEditor.class); private static final String ISO_LATIN_1 = "8859_1"; private static final String UNICODE = "Unicode"; static final String TITLE_ID = "TIT2"; static final String ARTIST_ID = "TPE1"; static final String ALBUM_ID = "TALB"; static final String YEAR_ID = "TYER"; static final String TRACK_ID = "TRCK"; static final String COMMENT_ID = "COMM"; static final String GENRE_ID = "TCON"; static final String LICENSE_ID = "TCOP"; /** * Actually writes the ID3 tags out to the ID3V3 section of the mp3 file */ private int writeID3V2DataToDisk(File file) throws IOException, ID3v2Exception { ID3v2 id3Handler = new ID3v2(file); Vector frames = null; try { frames = (Vector) id3Handler.getFrames().clone(); } catch (NoID3v2TagException ex) {//there are no ID3v2 tags in the file //fall thro' we'll deal with it later -- frames will be null } List framesToUpdate = new ArrayList(); addAllNeededFrames(framesToUpdate); if (framesToUpdate.size() == 0) //we have nothing to update return LimeXMLReplyCollection.NORMAL; if (frames != null) { //old frames present, update the differnt ones for (Iterator iter = frames.iterator(); iter.hasNext();) { ID3v2Frame oldFrame = (ID3v2Frame) iter.next(); //note: equality of ID3v2Frame based on value of id int index = framesToUpdate.indexOf(oldFrame); ID3v2Frame newFrame = null; if (index >= 0) { newFrame = (ID3v2Frame) framesToUpdate.remove(index); if (Arrays.equals(oldFrame.getContent(), newFrame.getContent())) continue;//no need to update, skip this frame } //we are either going to replace it if it was changed, or remove //it since there is no equivalent frame in the ones we need to //update, this means the user probably removed it id3Handler.removeFrame(oldFrame); if (newFrame != null) id3Handler.addFrame(newFrame); } } //now we are left with the ones we need to add only, if there were no //old tags this will be all the frames that need to get updated for (Iterator iter = framesToUpdate.iterator(); iter.hasNext();) { ID3v2Frame frame = (ID3v2Frame) iter.next(); id3Handler.addFrame(frame); } id3Handler.update(); //No Exceptions? We are home return LimeXMLReplyCollection.NORMAL; } private void addAllNeededFrames(List updateList) { add(updateList, title_, TITLE_ID); add(updateList, artist_, ARTIST_ID); add(updateList, album_, ALBUM_ID); add(updateList, year_, YEAR_ID); add(updateList, track_, TRACK_ID); add(updateList, comment_, COMMENT_ID); add(updateList, genre_, GENRE_ID); add(updateList, license_, LICENSE_ID); } private void add(List list, String data, String id) { if (data != null && !data.equals("")) { // genre needs to be updated. if (id == GENRE_ID && getGenreByte() > -1) data = "(" + getGenreByte() + ")" + data; ID3v2Frame frame = makeFrame(id, data); if (frame != null) list.add(frame); } } private ID3v2Frame makeFrame(String frameID, String value) { boolean isISOLatin1 = true; // Basic/ISO-Latin-1: 0x0000 ... 0x00FF // Unicode: > 0x00FF ??? Even with 3byte chars? for (int i = 0; i < value.length(); i++) { if (value.charAt(i) > 0x00FF) { isISOLatin1 = false; break; } } try { return new ID3v2Frame(frameID, value.getBytes((isISOLatin1) ? ISO_LATIN_1 : UNICODE), true, //discard tag if it's altered/unrecognized true, //discard tag if file altered/unrecognized false, //read/write ID3v2Frame.NO_COMPRESSION, //no compression (byte) 0, //no encryption (byte) 0, //no Group isISOLatin1); } catch (ID3v2DecompressionException cx) { return null; } catch (UnsupportedEncodingException err) { return null; } } /** * Actually writes the ID3 tags out to the ID3V1 section of mp3 file. */ private int writeID3V1DataToDisk(RandomAccessFile file) { byte[] buffer = new byte[30];//max buffer length...drop/pickup vehicle //see if there are ID3 Tags in the file String tag = ""; try { file.readFully(buffer, 0, 3); tag = new String(buffer, 0, 3); } catch (EOFException e) { return LimeXMLReplyCollection.RW_ERROR; } catch (IOException e) { return LimeXMLReplyCollection.RW_ERROR; } //We are sure this is an MP3 file.Otherwise this method would never //be called. if (!tag.equals("TAG")) { //Write the TAG try { byte[] tagBytes = "TAG".getBytes();//has to be len 3 file.seek(file.length() - 128);//reset the file-pointer file.write(tagBytes, 0, 3);//write these three bytes into the File } catch (IOException ioe) { return LimeXMLReplyCollection.BAD_ID3; } } LOG.debug("about to start writing to file"); boolean b; b = toFile(title_, 30, file, buffer); if (!b) return LimeXMLReplyCollection.FAILED_TITLE; b = toFile(artist_, 30, file, buffer); if (!b) return LimeXMLReplyCollection.FAILED_ARTIST; b = toFile(album_, 30, file, buffer); if (!b) return LimeXMLReplyCollection.FAILED_ALBUM; b = toFile(year_, 4, file, buffer); if (!b) return LimeXMLReplyCollection.FAILED_YEAR; //comment and track (a little bit tricky) b = toFile(comment_, 28, file, buffer);//28 bytes for comment if (!b) return LimeXMLReplyCollection.FAILED_COMMENT; byte trackByte = (byte) -1;//initialize try { if (track_ == null || track_.equals("")) trackByte = (byte) 0; else trackByte = Byte.parseByte(track_); } catch (NumberFormatException nfe) { return LimeXMLReplyCollection.FAILED_TRACK; } try { file.write(0);//separator b/w comment and track(track is optional) file.write(trackByte); } catch (IOException e) { return LimeXMLReplyCollection.FAILED_TRACK; } //genre byte genreByte = getGenreByte(); try { file.write(genreByte); } catch (IOException e) { return LimeXMLReplyCollection.FAILED_GENRE; } //come this far means we are OK. return LimeXMLReplyCollection.NORMAL; } private boolean toFile(String val, int maxLen, RandomAccessFile file, byte[] buffer) { if (LOG.isDebugEnabled()) LOG.debug("writing value to file " + val); byte[] fromString; if (val == null || val.equals("")) { fromString = new byte[maxLen]; Arrays.fill(fromString, 0, maxLen, (byte) 0);//fill it all with 0 } else { try { fromString = val.getBytes(ISO_LATIN_1); } catch (UnsupportedEncodingException err) { // Should never happen return false; } } int len = fromString.length; if (len < maxLen) { System.arraycopy(fromString, 0, buffer, 0, len); Arrays.fill(buffer, len, maxLen, (byte) 0);//fill the rest with 0s } else//cut off the rest System.arraycopy(fromString, 0, buffer, 0, maxLen); try { file.write(buffer, 0, maxLen); } catch (IOException e) { return false; } return true; } private byte getGenreByte() { if (genre_ == null) return -1; else if (genre_.equals("Blues")) return 0; else if (genre_.equals("Classic Rock")) return 1; else if (genre_.equals("Country")) return 2; else if (genre_.equals("Dance")) return 3; else if (genre_.equals("Disco")) return 4; else if (genre_.equals("Funk")) return 5; else if (genre_.equals("Grunge")) return 6; else if (genre_.equals("Hop")) return 7; else if (genre_.equals("Jazz")) return 8; else if (genre_.equals("Metal")) return 9; else if (genre_.equals("New Age")) return 10; else if (genre_.equals("Oldies")) return 11; else if (genre_.equals("Other")) return 12; else if (genre_.equals("Pop")) return 13; else if (genre_.equals("R & B")) return 14; else if (genre_.equals("Rap")) return 15; else if (genre_.equals("Reggae")) return 16; else if (genre_.equals("Rock")) return 17; else if (genre_.equals("Techno")) return 17; else if (genre_.equals("Industrial")) return 19; else if (genre_.equals("Alternative")) return 20; else if (genre_.equals("Ska")) return 21; else if (genre_.equals("Metal")) return 22; else if (genre_.equals("Pranks")) return 23; else if (genre_.equals("Soundtrack")) return 24; else if (genre_.equals("Euro-Techno")) return 25; else if (genre_.equals("Ambient")) return 26; else if (genre_.equals("Trip-Hop")) return 27; else if (genre_.equals("Vocal")) return 28; else if (genre_.equals("Jazz+Funk")) return 29; else if (genre_.equals("Fusion")) return 30; else if (genre_.equals("Trance")) return 31; else if (genre_.equals("Classical")) return 32; else if (genre_.equals("Instrumental")) return 33; else if (genre_.equals("Acid")) return 34; else if (genre_.equals("House")) return 35; else if (genre_.equals("Game")) return 36; else if (genre_.equals("Sound Clip")) return 37; else if (genre_.equals("Gospel")) return 38; else if (genre_.equals("Noise")) return 39; else if (genre_.equals("AlternRock")) return 40; else if (genre_.equals("Bass")) return 41; else if (genre_.equals("Soul")) return 42; else if (genre_.equals("Punk")) return 43; else if (genre_.equals("Space")) return 44; else if (genre_.equals("Meditative")) return 45; else if (genre_.equals("Instrumental Pop")) return 46; else if (genre_.equals("Instrumental Rock")) return 47; else if (genre_.equals("Ethnic")) return 48; else if (genre_.equals("Gothic")) return 49; else if (genre_.equals("Darkwave")) return 50; else if (genre_.equals("Techno-Industrial")) return 51; else if (genre_.equals("Electronic")) return 52; else if (genre_.equals("Pop-Folk")) return 53; else if (genre_.equals("Eurodance")) return 54; else if (genre_.equals("Dream")) return 55; else if (genre_.equals("Southern Rock")) return 56; else if (genre_.equals("Comedy")) return 57; else if (genre_.equals("Cult")) return 58; else if (genre_.equals("Gangsta")) return 59; else if (genre_.equals("Top 40")) return 60; else if (genre_.equals("Christian Rap")) return 61; else if (genre_.equals("Pop/Funk")) return 62; else if (genre_.equals("Jungle")) return 63; else if (genre_.equals("Native American")) return 64; else if (genre_.equals("Cabaret")) return 65; else if (genre_.equals("New Wave")) return 66; else if (genre_.equals("Psychadelic")) return 67; else if (genre_.equals("Rave")) return 68; else if (genre_.equals("Showtunes")) return 69; else if (genre_.equals("Trailer")) return 70; else if (genre_.equals("Lo-Fi")) return 71; else if (genre_.equals("Tribal")) return 72; else if (genre_.equals("Acid Punk")) return 73; else if (genre_.equals("Acid Jazz")) return 74; else if (genre_.equals("Polka")) return 75; else if (genre_.equals("Retro")) return 76; else if (genre_.equals("Musical")) return 77; else if (genre_.equals("Rock & Roll")) return 78; else if (genre_.equals("Hard Rock")) return 79; else if (genre_.equals("Folk")) return 80; else if (genre_.equals("Folk-Rock")) return 81; else if (genre_.equals("National Folk")) return 82; else if (genre_.equals("Swing")) return 83; else if (genre_.equals("Fast Fusion")) return 84; else if (genre_.equals("Bebob")) return 85; else if (genre_.equals("Latin")) return 86; else if (genre_.equals("Revival")) return 87; else if (genre_.equals("Celtic")) return 88; else if (genre_.equals("Bluegrass")) return 89; else if (genre_.equals("Avantgarde")) return 90; else if (genre_.equals("Gothic Rock")) return 91; else if (genre_.equals("Progressive Rock")) return 92; else if (genre_.equals("Psychedelic Rock")) return 93; else if (genre_.equals("Symphonic Rock")) return 94; else if (genre_.equals("Slow Rock")) return 95; else if (genre_.equals("Big Band")) return 96; else if (genre_.equals("Chorus")) return 97; else if (genre_.equals("Easy Listening")) return 98; else if (genre_.equals("Acoustic")) return 99; else if (genre_.equals("Humour")) return 100; else if (genre_.equals("Speech")) return 101; else if (genre_.equals("Chanson")) return 102; else if (genre_.equals("Opera")) return 103; else if (genre_.equals("Chamber Music")) return 104; else if (genre_.equals("Sonata")) return 105; else if (genre_.equals("Symphony")) return 106; else if (genre_.equals("Booty Bass")) return 107; else if (genre_.equals("Primus")) return 108; else if (genre_.equals("Porn Groove")) return 109; else if (genre_.equals("Satire")) return 110; else if (genre_.equals("Slow Jam")) return 111; else if (genre_.equals("Club")) return 112; else if (genre_.equals("Tango")) return 113; else if (genre_.equals("Samba")) return 114; else if (genre_.equals("Folklore")) return 115; else if (genre_.equals("Ballad")) return 116; else if (genre_.equals("Power Ballad")) return 117; else if (genre_.equals("Rhythmic Soul")) return 118; else if (genre_.equals("Freestyle")) return 119; else if (genre_.equals("Duet")) return 120; else if (genre_.equals("Punk Rock")) return 121; else if (genre_.equals("Drum Solo")) return 122; else if (genre_.equals("A capella")) return 123; else if (genre_.equals("Euro-House")) return 124; else if (genre_.equals("Dance Hall")) return 125; else return -1; } public int commitMetaData(String filename) { if (LOG.isDebugEnabled()) LOG.debug("committing mp3 file"); if (!LimeXMLUtils.isMP3File(filename)) return LimeXMLReplyCollection.INCORRECT_FILETYPE; File f = null; RandomAccessFile file = null; try { try { f = new File(filename); FileUtils.setWriteable(f); file = new RandomAccessFile(f, "rw"); } catch (IOException e) { return LimeXMLReplyCollection.FILE_DEFECTIVE; } long length = 0; try { length = file.length(); if (length < 128) //could not write - file too small return LimeXMLReplyCollection.FILE_DEFECTIVE; file.seek(length - 128); } catch (IOException ee) { return LimeXMLReplyCollection.RW_ERROR; } //1. Try to write out the ID3v2 data first int ret = -1; try { ret = writeID3V2DataToDisk(f); } catch (IOException iox) { return LimeXMLReplyCollection.RW_ERROR; } catch (ID3v2Exception e) { //catches both ID3v2 related exceptions ret = writeID3V1DataToDisk(file); } return ret; } finally { if (file != null) { try { file.close(); } catch (IOException ignored) { } } } } }