View Javadoc

1   /*
2    * Entagged Audio Tag library
3    * Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
4    * 
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *  
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   * 
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18   */
19  package entagged.audioformats.ogg.util;
20  
21  import java.io.UnsupportedEncodingException;
22  
23  import entagged.audioformats.generic.TagField;
24  import entagged.audioformats.generic.TagTextField;
25  
26  /***
27   * This class encapsulates the name and content of a tag entry in ogg-files.
28   * <br>
29   * 
30   * @author @author Raphael Slinckx (KiKiDonK)
31   * @author Christian Laireiter (liree)
32   */
33  public class OggTagField implements TagTextField {
34  
35      /***
36       * If <code>true</code>, the id of the current encapsulated tag field is
37       * specified as a common field. <br>
38       * Example is "ARTIST" which should be interpreted by any application as the
39       * artist of the media content. <br>
40       * Will be set during construction with {@link #checkCommon()}.
41       */
42      private boolean common;
43  
44      /***
45       * Stores the content of the tag field. <br>
46       */
47      private String content;
48  
49      /***
50       * Stores the id (name) of the tag field. <br>
51       */
52      private String id;
53  
54      /***
55       * Creates an instance.
56       * 
57       * @param raw
58       *                   Raw byte data of the tagfield.
59       * @throws UnsupportedEncodingException
60       *                    If the data doesn't conform "UTF-8" specification.
61       */
62      public OggTagField(byte[] raw) throws UnsupportedEncodingException {
63          String field = new String(raw, "UTF-8");
64  
65          String[] splitField = field.split("=");
66          if (splitField.length > 1) {
67              this.id = splitField[0].toUpperCase();
68              this.content = splitField[1];
69          } else {
70              //Either we have "XXXXXXX" without "="
71              //Or we have "XXXXXX=" with nothing after the "="
72              int i = field.indexOf("="); 
73              if(i != -1) {
74                  this.id = field.substring(0, i+1);
75                  this.content = "";
76              }
77              else {
78  	            //Beware that ogg ID, must be capitalized and contain no space..
79  	            this.id = "ERRONEOUS";
80  	            this.content = field;
81              }
82          }
83  
84          checkCommon();
85      }
86  
87      /***
88       * Creates an instance.
89       * 
90       * @param fieldId
91       *                   ID (name) of the field.
92       * @param fieldContent
93       *                   Content of the field.
94       */
95      public OggTagField(String fieldId, String fieldContent) {
96          this.id = fieldId.toUpperCase();
97          this.content = fieldContent;
98          checkCommon();
99      }
100 
101     /***
102      * This method examines the ID of the current field and modifies
103      * {@link #common}in order to reflect if the tag id is a commonly used one.
104      * <br>
105      */
106     private void checkCommon() {
107         this.common = id.equals("TITLE") || id.equals("ALBUM")
108                 || id.equals("ARTIST") || id.equals("GENRE")
109                 || id.equals("TRACKNUMBER") || id.equals("DATE")
110                 || id.equals("DESCRIPTION") || id.equals("COMMENT")
111                 || id.equals("TRACK");
112     }
113 
114     /***
115      * This method will copy all bytes of <code>src</code> to <code>dst</code>
116      * at the specified location.
117      * 
118      * @param src
119      *                   bytes to copy.
120      * @param dst
121      *                   where to copy to.
122      * @param dstOffset
123      *                   at which position of <code>dst</code> the data should be
124      *                   copied.
125      */
126     protected void copy(byte[] src, byte[] dst, int dstOffset) {
127         //        for (int i = 0; i < src.length; i++)
128         //            dst[i + dstOffset] = src[i];
129         /*
130          * Heared that this method is optimized and does its job very near of
131          * the system.
132          */
133         System.arraycopy(src, 0, dst, dstOffset, src.length);
134     }
135 
136     /***
137      * (overridden)
138      * 
139      * @see entagged.audioformats.generic.TagField#copyContent(entagged.audioformats.generic.TagField)
140      */
141     public void copyContent(TagField field) {
142         if (field instanceof TagTextField)
143             this.content = ((TagTextField) field).getContent();
144     }
145 
146     /***
147      * This method will try to return the byte representation of the given
148      * string after it has been converted to the given encoding. <br>
149      * 
150      * @param s
151      *                   The string whose converted bytes should be returned.
152      * @param encoding
153      *                   The encoding type to which the string should be converted.
154      * @return If <code>encoding</code> is supported the byte data of the
155      *               given string is returned in that encoding.
156      * @throws UnsupportedEncodingException
157      *                    If the requested encoding is not available.
158      */
159     protected byte[] getBytes(String s, String encoding)
160             throws UnsupportedEncodingException {
161         return s.getBytes(encoding);
162     }
163 
164     /***
165      * (overridden)
166      * 
167      * @see entagged.audioformats.generic.TagTextField#getContent()
168      */
169     public String getContent() {
170         return content;
171     }
172 
173     /***
174      * (overridden)
175      * 
176      * @see entagged.audioformats.generic.TagTextField#getEncoding()
177      */
178     public String getEncoding() {
179         return "UTF-8";
180     }
181 
182     /***
183      * (overridden)
184      * 
185      * @see entagged.audioformats.generic.TagField#getId()
186      */
187     public String getId() {
188         return this.id;
189     }
190 
191     /***
192      * (overridden)
193      * 
194      * @see entagged.audioformats.generic.TagField#getRawContent()
195      */
196     public byte[] getRawContent() throws UnsupportedEncodingException {
197         byte[] size = new byte[4];
198         byte[] idBytes = this.id.getBytes();
199         byte[] contentBytes = getBytes(this.content, "UTF-8");
200         byte[] b = new byte[4 + idBytes.length + 1 + contentBytes.length];
201 
202         int length = idBytes.length + 1 + contentBytes.length;
203         size[3] = (byte) ((length & 0xFF000000) >> 24);
204         size[2] = (byte) ((length & 0x00FF0000) >> 16);
205         size[1] = (byte) ((length & 0x0000FF00) >> 8);
206         size[0] = (byte) (length & 0x000000FF);
207 
208         int offset = 0;
209         copy(size, b, offset);
210         offset += 4;
211         copy(idBytes, b, offset);
212         offset += idBytes.length;
213         b[offset] = (byte) 0x3D;
214         offset++;// "="
215         copy(contentBytes, b, offset);
216 
217         return b;
218     }
219 
220     /***
221      * (overridden)
222      * 
223      * @see entagged.audioformats.generic.TagField#isBinary()
224      */
225     public boolean isBinary() {
226         return false;
227     }
228 
229     /***
230      * (overridden)
231      * 
232      * @see entagged.audioformats.generic.TagField#isBinary(boolean)
233      */
234     public void isBinary(boolean b) {
235         if (b) {
236             // Only throw if binary = true requested.
237             throw new UnsupportedOperationException(
238                     "OggTagFields cannot be changed to binary.\n"
239                             + "binary data should be stored elsewhere"
240                             + " according to Vorbis_I_spec.");
241         }
242     }
243 
244     /***
245      * (overridden)
246      * 
247      * @see entagged.audioformats.generic.TagField#isCommon()
248      */
249     public boolean isCommon() {
250         return common;
251     }
252 
253     /***
254      * (overridden)
255      * 
256      * @see entagged.audioformats.generic.TagField#isEmpty()
257      */
258     public boolean isEmpty() {
259         return this.content.equals("");
260     }
261 
262     /***
263      * (overridden)
264      * 
265      * @see entagged.audioformats.generic.TagTextField#setContent(java.lang.String)
266      */
267     public void setContent(String s) {
268         this.content = s;
269     }
270 
271     /***
272      * (overridden)
273      * 
274      * @see entagged.audioformats.generic.TagTextField#setEncoding(java.lang.String)
275      */
276     public void setEncoding(String s) {
277         if (s == null || !s.equalsIgnoreCase("UTF-8"))
278             throw new UnsupportedOperationException(
279                     "The encoding of OggTagFields cannot be "
280                             + "changed.(specified to be UTF-8)");
281     }
282     
283     public String toString() {
284         return getContent();
285     }
286 }