View Javadoc

1   /*
2    * Entagged Audio Tag library
3    * Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
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.asf.data;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.math.BigInteger;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  
29  import entagged.audioformats.Tag;
30  import entagged.audioformats.asf.util.Utils;
31  
32  /***
33   * This structure represents the data of a chunk, wich contains extended content
34   * description. <br>
35   * These properties are simply represented by
36   * {@link entagged.audioformats.asf.data.ContentDescriptor}
37   * 
38   * @author Christian Laireiter
39   */
40  public class ExtendedContentDescription extends Chunk {
41  
42      /***
43       * Contains the properties. <br>
44       */
45      private final ArrayList descriptors;
46  
47      /***
48       * This map stores the ids (names) of inserted content descriptors. <br>
49       * If {@link #getDescriptor(String)}is called this field will be filled if
50       * <code>null</code>. Any modification of the contents of this object
51       * will set this field to <code>null</code>.
52       */
53      private HashMap indexMap = null;
54  
55      /***
56       * Creates an instance.
57       *  
58       */
59      public ExtendedContentDescription() {
60          this(0, BigInteger.valueOf(0));
61      }
62  
63      /***
64       * Creates an instance.
65       * 
66       * @param pos
67       *                   Position of header object within file or stream.
68       * @param chunkLen
69       *                   Length of the represented chunck.
70       */
71      public ExtendedContentDescription(long pos, BigInteger chunkLen) {
72          super(GUID.GUID_EXTENDED_CONTENT_DESCRIPTION, pos, chunkLen);
73          this.descriptors = new ArrayList();
74      }
75  
76      /***
77       * This method inserts the given ContentDescriptor.
78       * 
79       * @param toAdd
80       *                   ContentDescriptor to insert.
81       */
82      public void addDescriptor(ContentDescriptor toAdd) {
83          assert toAdd != null : "Argument must not be null.";
84          if (getDescriptor(toAdd.getName()) != null) {
85              throw new RuntimeException(toAdd.getName() + " is already present");
86          }
87          this.descriptors.add(toAdd);
88          this.indexMap.put(toAdd.getName(), new Integer(descriptors.size() - 1));
89      }
90  
91      /***
92       * This method adds or replaces an existing content descriptor.
93       * 
94       * @param descriptor
95       *                   Descriptor to be added or replaced.
96       */
97      public void addOrReplace(ContentDescriptor descriptor) {
98          assert descriptor != null : "Argument must not be null";
99          if (getDescriptor(descriptor.getName()) != null) {
100             /*
101              * Just remove if exists. Will prevent the indexmap being rebuild.
102              */
103             remove(descriptor.getName());
104         }
105         addDescriptor(descriptor);
106     }
107 
108     /***
109      * Returns the album entered in the content descriptor chunk.
110      * 
111      * @return Album, <code>""</code> if not defined.
112      */
113     public String getAlbum() {
114         ContentDescriptor result = getDescriptor(ContentDescriptor.ID_ALBUM);
115         if (result == null)
116             return "";
117 
118         return result.getString();
119     }
120 
121     /***
122      * Returns the "WM/AlbumArtist" entered in the extended content description.
123      * 
124      * @return Title, <code>""</code> if not defined.
125      */
126     public String getArtist() {
127         ContentDescriptor result = getDescriptor(ContentDescriptor.ID_ARTIST);
128         if (result == null)
129             return "";
130         return result.getString();
131     }
132 
133     /***
134      * This method creates a byte array which can be written to asf files.
135      * 
136      * @return asf file representation of the current object.
137      */
138     public byte[] getBytes() {
139         ByteArrayOutputStream result = new ByteArrayOutputStream();
140         try {
141             ByteArrayOutputStream content = new ByteArrayOutputStream();
142             // Write the number of descriptors.
143             content.write(Utils.getBytes(this.descriptors.size(), 2));
144             Iterator it = this.descriptors.iterator();
145             while (it.hasNext()) {
146                 ContentDescriptor current = (ContentDescriptor) it.next();
147                 content.write(current.getBytes());
148             }
149             byte[] contentBytes = content.toByteArray();
150             // Write the guid
151             result.write(GUID.GUID_EXTENDED_CONTENT_DESCRIPTION.getBytes());
152             // Write the length + 24.
153             result.write(Utils.getBytes(contentBytes.length + 24, 8));
154             // Write the content
155             result.write(contentBytes);
156         } catch (Exception e) {
157             e.printStackTrace();
158         }
159         return result.toByteArray();
160     }
161 
162     /***
163      * Returns a previously inserted content descriptor.
164      * 
165      * @param name
166      *                   name of the content descriptor.
167      * @return <code>null</code> if not present.
168      */
169     public ContentDescriptor getDescriptor(String name) {
170         if (this.indexMap == null) {
171             this.indexMap = new HashMap();
172             for (int i = 0; i < descriptors.size(); i++) {
173                 ContentDescriptor current = (ContentDescriptor) descriptors
174                         .get(i);
175                 indexMap.put(current.getName(), new Integer(i));
176             }
177         }
178         Integer pos = (Integer) indexMap.get(name);
179         if (pos != null) {
180             return (ContentDescriptor) descriptors.get(pos.intValue());
181         }
182         return null;
183     }
184 
185     /***
186      * @return Returns the descriptorCount.
187      */
188     public long getDescriptorCount() {
189         return descriptors.size();
190     }
191 
192     /***
193      * Returns a collection of all {@link ContentDescriptor}objects stored in
194      * this extended content description.
195      * 
196      * @return An enumeration of {@link ContentDescriptor}objects.
197      */
198     public Collection getDescriptors() {
199         return new ArrayList(this.descriptors);
200     }
201 
202     /***
203      * Returns the Genre entered in the content descriptor chunk.
204      * 
205      * @return Genre, <code>""</code> if not defined.
206      */
207     public String getGenre() {
208         String result = null;
209         ContentDescriptor prop = getDescriptor(ContentDescriptor.ID_GENRE);
210         if (prop == null) {
211             prop = getDescriptor(ContentDescriptor.ID_GENREID);
212             if (prop == null)
213                 result = "";
214             else {
215                 result = prop.getString();
216                 if (result.startsWith("(") && result.endsWith(")")) {
217                     result = result.substring(1, result.length() - 1);
218                     try {
219                         int genreNum = Integer.parseInt(result);
220                         if (genreNum >= 0
221                                 && genreNum < Tag.DEFAULT_GENRES.length) {
222                             result = Tag.DEFAULT_GENRES[genreNum];
223                         }
224                     } catch (NumberFormatException e) {
225                         // Do nothing
226                     }
227                 }
228             }
229         } else {
230             result = prop.getString();
231         }
232         return result;
233     }
234 
235     /***
236      * Returns the Track entered in the content descriptor chunk.
237      * 
238      * @return Track, <code>""</code> if not defined.
239      */
240     public String getTrack() {
241         ContentDescriptor result = getDescriptor(ContentDescriptor.ID_TRACKNUMBER);
242         if (result == null)
243             return "";
244 
245         return result.getString();
246     }
247 
248     /***
249      * Returns the Year entered in the extended content descripion.
250      * 
251      * @return Year, <code>""</code> if not defined.
252      */
253     public String getYear() {
254         ContentDescriptor result = getDescriptor(ContentDescriptor.ID_YEAR);
255         if (result == null)
256             return "";
257 
258         return result.getString();
259     }
260 
261     /***
262      * This method creates a String containing the tag elements an their values
263      * for printing. <br>
264      * 
265      * @return nice string.
266      */
267     public String prettyPrint() {
268         StringBuffer result = new StringBuffer(super.prettyPrint());
269         result.insert(0, "\nExtended Content Description:\n");
270         ContentDescriptor[] list = (ContentDescriptor[]) descriptors
271                 .toArray(new ContentDescriptor[descriptors.size()]);
272         Arrays.sort(list);
273         for (int i = 0; i < list.length; i++) {
274             result.append("   ");
275             result.append(list[i]);
276             result.append(Utils.LINE_SEPARATOR);
277         }
278         return result.toString();
279     }
280 
281     /***
282      * This method removes the content descriptor with the given name. <br>
283      * 
284      * @param id
285      *                   The id (name) of the descriptor which should be removed.
286      * @return The descriptor which is removed. If not present <code>null</code>.
287      */
288     public ContentDescriptor remove(String id) {
289         ContentDescriptor result = getDescriptor(id);
290         if (result != null) {
291             descriptors.remove(result);
292         }
293         this.indexMap = null;
294         return result;
295     }
296 }