Coverage Report - org.jaudiotagger.audio.asf.AsfFileWriter
 
Classes in this File Line Coverage Branch Coverage Complexity
AsfFileWriter
90%
47/52
80%
43/54
0
 
 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 org.jaudiotagger.audio.asf;
 20  
 
 21  
 import org.jaudiotagger.audio.asf.data.AsfHeader;
 22  
 import org.jaudiotagger.audio.asf.data.ExtendedContentDescription;
 23  
 import org.jaudiotagger.audio.asf.data.GUID;
 24  
 import org.jaudiotagger.audio.asf.io.*;
 25  
 import org.jaudiotagger.audio.asf.tag.AsfTag;
 26  
 import org.jaudiotagger.audio.asf.tag.AsfTagField;
 27  
 import org.jaudiotagger.audio.asf.util.TagConverter;
 28  
 import org.jaudiotagger.audio.asf.util.Utils;
 29  
 import org.jaudiotagger.audio.exceptions.CannotWriteException;
 30  
 import org.jaudiotagger.audio.generic.AudioFileWriter;
 31  
 import org.jaudiotagger.tag.Tag;
 32  
 
 33  
 import java.io.IOException;
 34  
 import java.io.RandomAccessFile;
 35  
 import java.util.ArrayList;
 36  
 import java.util.Iterator;
 37  
 import java.util.List;
 38  
 
 39  
 /**
 40  
  * This class writes given tags to ASF files containing WMA content. <br>
 41  
  * <br>
 42  
  * 
 43  
  * @author Christian Laireiter
 44  
  */
 45  42
 public class AsfFileWriter extends AudioFileWriter
 46  
 {
 47  
 
 48  
     /**
 49  
     * This method writes a completely new extended content description to
 50  
     * <code>rafTemp</code>.<br>
 51  
     * Some values of {@link Tag}are nested within this chunk. Those values of
 52  
     * <code>tagChunk</code> which do not belong to tag will be kept, the rest
 53  
     * replaced or even added. <br>
 54  
     * 
 55  
     * @param tag
 56  
     *            contains new Elements.
 57  
     *            File to write to.
 58  
     * @return A new extended content description.
 59  
     */
 60  
     private ExtendedContentDescription createNewExtendedContentDescription(AsfTag tag)
 61  
     {
 62  9
         final ExtendedContentDescription result = new ExtendedContentDescription();
 63  9
         TagConverter.assignCommonTagValues(tag, result);
 64  9
         TagConverter.assignOptionalTagValues(tag, result);
 65  9
         return result;
 66  
     }
 67  
 
 68  
     /**
 69  
      * {@inheritDoc}
 70  
      */
 71  
     protected void deleteTag(RandomAccessFile raf, RandomAccessFile tempRaf) throws CannotWriteException, IOException
 72  
     {
 73  11
         writeTag(new AsfTag(true), raf, tempRaf);
 74  11
     }
 75  
 
 76  
     /**
 77  
      * This method decides if a content description chunk is needed in order to
 78  
      * store selected values of <code>tag</code>.<br>
 79  
      * The selected values are: <br> {@link Tag#getTitle()}<br> {@link Tag#getComment()}<br>
 80  
      * {@link Tag#getArtist()}<br> {@link AsfTag#getCopyright()}<br>
 81  
      * {@link AsfTag#getRating()}<br>
 82  
      * 
 83  
      * @param tag
 84  
      *            Tag which should be written.
 85  
      * @return <code>true</code>, if a property chunk must be written in order
 86  
      *         to store all needed values of tag.
 87  
      */
 88  
     private boolean isContentdescriptionMandatory(AsfTag tag)
 89  
     {
 90  25
         return !Utils.isBlank(tag.getFirstArtist()) || !Utils.isBlank(tag.getFirstComment()) || !Utils.isBlank(tag
 91  
                         .getFirstTitle()) || !Utils.isBlank(tag.getFirstCopyright()) || !Utils.isBlank(tag
 92  
                         .getFirstRating());
 93  
     }
 94  
 
 95  
     /**
 96  
      * This method decides if an extended property chunk is needed in order to
 97  
      * store values of <code>tag</code>.<br>
 98  
      * 
 99  
      * @param tag Tag which should be written.
 100  
      * @return <code>true</code>, if an extended property chunk must be written
 101  
      *         in order to store tag values.
 102  
      */
 103  
     private boolean isExtendedContentDescriptionMandatory(AsfTag tag)
 104  
     {
 105  25
         final Iterator<AsfTagField> asfFields = tag.getAsfFields();
 106  25
         boolean found = false;
 107  
         /*
 108  
          * Search for a field, that is not stored in content description.
 109  
          */
 110  68
         while (asfFields.hasNext() && !found)
 111  
         {
 112  43
             AsfTagField curr = asfFields.next();
 113  43
             found = !AsfTag.storesDescriptor(curr.getDescriptor());
 114  43
         }
 115  25
         return found;
 116  
     }
 117  
 
 118  
     /**
 119  
      * {@inheritDoc}
 120  
      */
 121  
     protected void writeTag(Tag tag, RandomAccessFile raf, RandomAccessFile rafTemp) throws CannotWriteException, IOException
 122  
     {
 123  
         /*
 124  
          * Since this implementation should not change the structure of the ASF file (locations of content description chunks),
 125  
          * we need to read the content description chunk and the extended content description chunk from the source file.
 126  
          * In the second step we need to determine which modifier (asf header or asf extended header) gets the 
 127  
          * appropriate modifiers.
 128  
          * The following policies are applied:
 129  
          * if the source does not contain any descriptor, the necessary descriptors are appended to the header object.
 130  
          * 
 131  
          * if the source contains only one descriptor in the header extension object, and the other type is needed as well,
 132  
          * the other one will be put into the header extension object.
 133  
          * 
 134  
          * for each descriptor type, if an object is found, an updater will be configured.
 135  
          */
 136  25
         final AsfHeader sourceHeader = AsfHeaderReader.readTagHeader(raf);
 137  25
         raf.seek(0); // Reset for the streamer
 138  25
         final boolean headerCD = sourceHeader.getContentDescription() != null;
 139  25
         final boolean headerECD = sourceHeader.getExtendedContentDescription() != null;
 140  25
         final boolean extHeaderCD = sourceHeader.getExtendedHeader() != null && sourceHeader.getExtendedHeader()
 141  
                         .getContentDescription() != null;
 142  25
         final boolean extHeaderECD = sourceHeader.getExtendedHeader() != null && sourceHeader.getExtendedHeader()
 143  
                         .getExtendedContentDescription() != null;
 144  
         /*
 145  
          * Now create modifiers for content descriptor and extended content descriptor as implied by the given Tag.
 146  
          */
 147  
         //TODO not convinced that we need to copy fields here
 148  25
         final AsfTag copy = new AsfTag(tag, true);
 149  
         // Modifiers for the asf header object
 150  25
         final List<ChunkModifier> headerModifier = new ArrayList<ChunkModifier>();
 151  
         // Modifiers for the asf header extension object
 152  25
         final List<ChunkModifier> extHeaderModifier = new ArrayList<ChunkModifier>();
 153  
         // determine content description content
 154  25
         if (isContentdescriptionMandatory(copy))
 155  
         {
 156  13
             final WriteableChunkModifer Modifier = new WriteableChunkModifer(TagConverter
 157  
                             .createContentDescription(copy));
 158  13
             if (headerCD || !extHeaderCD)
 159  
             {
 160  
                 /*
 161  
                  * If header contains object update in any case. Otherwise if not the extension header contains it,
 162  
                  * add it to the header.
 163  
                  */
 164  13
                 headerModifier.add(Modifier);
 165  
             }
 166  13
             if (extHeaderCD)
 167  
             {
 168  0
                 extHeaderModifier.add(Modifier);
 169  
             }
 170  13
         }
 171  
         else
 172  
         {
 173  12
             final ChunkRemover remover = new ChunkRemover(GUID.GUID_CONTENTDESCRIPTION);
 174  12
             if (headerCD)
 175  
             {
 176  8
                 headerModifier.add(remover);
 177  
             }
 178  12
             if (extHeaderCD)
 179  
             {
 180  0
                 extHeaderModifier.add(remover);
 181  
             }
 182  
         }
 183  
         // determine extended content description content        
 184  25
         if (isExtendedContentDescriptionMandatory(copy))
 185  
         {
 186  9
             final WriteableChunkModifer Modifier = new WriteableChunkModifer(createNewExtendedContentDescription(copy));
 187  9
             if (headerECD || !extHeaderECD)
 188  
             {
 189  
                 /*
 190  
                  * If header contains object update in any case. Otherwise if not the extension header contains it,
 191  
                  * add it to the header.
 192  
                  */
 193  9
                 headerModifier.add(Modifier);
 194  
             }
 195  9
             if (extHeaderECD)
 196  
             {
 197  0
                 extHeaderModifier.add(Modifier);
 198  
             }
 199  9
         }
 200  
         else
 201  
         {
 202  16
             final ChunkRemover remover = new ChunkRemover(GUID.GUID_EXTENDED_CONTENT_DESCRIPTION);
 203  16
             if (headerECD)
 204  
             {
 205  4
                headerModifier.add(remover);
 206  
             }
 207  16
             if (extHeaderECD)
 208  
             {
 209  0
                 extHeaderModifier.add(remover);
 210  
             }
 211  
         }
 212  
         // only add an AsfExtHeaderModifier, if there is actually something to change (performance)
 213  25
         if (!extHeaderModifier.isEmpty())
 214  
         {
 215  0
             headerModifier.add(new AsfExtHeaderModifier(extHeaderModifier));
 216  
         }
 217  25
         new AsfStreamer()
 218  
                         .createModifiedCopy(new RandomAccessFileInputstream(raf), new RandomAccessFileOutputStream(rafTemp), headerModifier);
 219  25
     }
 220  
 
 221  
 }