001    /**
002     * iFish - An iRiver iHP jukebox database creation tool
003     *
004     * Copyright (C) 2009 Richard "Shred" Körber
005     *   http://ifish.shredzone.org
006     *
007     * This program is free software: you can redistribute it and/or modify
008     * it under the terms of the GNU General Public License as published by
009     * the Free Software Foundation, either version 3 of the License, or
010     * (at your option) any later version.
011     *
012     * This program is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015     * GNU General Public License for more details.
016     *
017     * You should have received a copy of the GNU General Public License
018     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
019     */
020    package net.shredzone.ifish.db;
021    
022    import java.io.*;
023    
024    /**
025     * Abstract class for an entry in the iRiver database.
026     *
027     * @author    Richard Körber &lt;dev@shredzone.de&gt;
028     * @version   $Id: Entry.java 291 2009-04-28 21:29:27Z shred $
029     */
030    public abstract class Entry implements Serializable, Comparable<Entry> {
031      private static final long serialVersionUID = -2272263266038018131L;
032    
033      /**
034       * Get the file behind this entry. Returns null if there is no matching
035       * file in the file system.
036       *
037       * @return    File represented by this entry
038       */
039      public File getFile() {
040        return null;
041      }
042    
043      /**
044       * Get the file name of this entry. This is the path and file name
045       * relative to the iHP root directory mount point, and always
046       * using wrongslashes (even on unixoid systems). 
047       *
048       * @return    File name
049       */
050      public abstract String getFileName();
051      
052      /**
053       * Get the title of the track, as given by the file's tag. Does not
054       * return null.
055       *
056       * @return    Title
057       */
058      public abstract String getTitle();
059      
060      /**
061       * Get the artist of the track, as given by the file's tag. Does not
062       * return null.
063       *
064       * @return    Artist
065       */
066      public abstract String getArtist();
067      
068      /**
069       * Get the album of the track, as given by the file's tag. Does not
070       * return null.
071       *
072       * @return    Album
073       */
074      public abstract String getAlbum();
075      
076      /**
077       * Get the genre of the track, as given by the file's tag. This is
078       * always a word, not a number. Does not return null.
079       *
080       * @return    Genre
081       */
082      public abstract String getGenre();
083      
084      /**
085       * Get the byte size of this entry when saved to the database
086       *
087       * @param   charset     Charset the iRivNavi.db shall be written in
088       * @return  Size
089       * @throws  UnsupportedEncodingException
090       */
091      public int size( String charset )
092      throws UnsupportedEncodingException {
093        int size = 10;                      // 5x 2 byte length
094        size += getBytesTerm( getFileName(), charset ).length;
095        size += getBytesTerm( getTitle(),    charset ).length;
096        size += getBytesTerm( getArtist(),   charset ).length;
097        size += getBytesTerm( getAlbum(),    charset ).length;
098        size += getBytesTerm( getGenre(),    charset ).length;
099        return size;
100      }
101      
102      /**
103       * Write the entry to an OutputStream in iRivDB format.
104       *
105       * @param   out         OutputStream to write to
106       * @param   charset     Charset the iRivNavi.db shall be written in
107       * @throws  IOException, UnsupportedEncodingException
108       */
109      public void write( OutputStream out, String charset )
110      throws IOException, UnsupportedEncodingException {
111        //--- Get the strings ---
112        final byte[] bFileName = getBytesTerm( getFileName(), charset );
113        final byte[] bTitle    = getBytesTerm( getTitle(),    charset );
114        final byte[] bArtist   = getBytesTerm( getArtist(),   charset );
115        final byte[] bAlbum    = getBytesTerm( getAlbum(),    charset );
116        final byte[] bGenre    = getBytesTerm( getGenre(),    charset );
117    
118        //--- Write the string lengths ---
119        writeShort( out, bFileName.length );
120        writeShort( out, bTitle.length    );
121        writeShort( out, bArtist.length   );
122        writeShort( out, bAlbum.length    );
123        writeShort( out, bGenre.length    );
124        
125        //--- Write the strings itself ---
126        out.write( bFileName );   // they are already 0 terminated
127        out.write( bTitle    );
128        out.write( bArtist   );
129        out.write( bAlbum    );
130        out.write( bGenre    );
131      }
132      
133      /**
134       * Get a byte array of a string, using the given charset. The byte
135       * array will be properly null terminated.
136       * 
137       * @param   str         String to convert
138       * @param   charset     Charset to be used
139       * @return  A byte array of the string, null terminated.
140       */
141      protected byte[] getBytesTerm( String str, String charset )
142      throws UnsupportedEncodingException {
143        if( charset.equals("UTF-16") ) {
144          final byte[] data = (str+'\0').getBytes( "UTF-16LE" );
145          final byte[] rv = new byte[ data.length+2 ];
146          rv[0] = (byte) 0xFF;    // Marker: Little Endian
147          rv[1] = (byte) 0xFE;    //  The Jukebox is unable to handle big endian!
148          System.arraycopy( data, 0, rv, 2, data.length );
149          return rv;
150        }else {
151          return (str+'\0').getBytes( charset );
152        }
153      }
154       
155      /**
156       * Write out a short value (16 bit) in little endian order.
157       *
158       * @param   out       OutputStream to write to
159       * @param   val       Value
160       */
161      private void writeShort( OutputStream out, int val )
162      throws IOException {
163        out.write( (val   ) & 0xFF );
164        out.write( (val>>8) & 0xFF );
165      }
166      
167      /**
168       * Compare one Entry to another.
169       *
170       * @param   o       Other Entry to compare this entry to
171       * @return  positive, 0 or negative value according to the result
172       */
173      @Override
174      public int compareTo( Entry o ) {
175        return getFileName().compareToIgnoreCase( o.getFileName() );
176      }
177      
178      /**
179       * Check if this Entry is equal to another object. An Entry is never
180       * equal to null or foreign objects. Two Entry are considered equal
181       * if they have the same file name.
182       *
183       * @param   o           Object to compare with
184       * @return  true: same, false: different
185       */
186      @Override
187      public boolean equals( Object o ) {
188        if( o==null || !(o instanceof Entry) )
189          return false;
190        Entry e2 = (Entry) o;
191        return getFileName().equalsIgnoreCase( e2.getFileName() );
192      }
193      
194      /**
195       * Return a hash code for this Entry.
196       *
197       * @return  int     Hash Code
198       */
199      @Override
200      public int hashCode() {
201        return getFileName().hashCode();
202      }
203      
204    }
205