1
package org.jaudiotagger.audio.asf.io;
3
import org.jaudiotagger.audio.asf.data.GUID;
4
import org.jaudiotagger.audio.asf.util.Utils;
6
import java.io.ByteArrayOutputStream;
7
import java.io.IOException;
8
import java.io.InputStream;
9
import java.io.OutputStream;
10
import java.math.BigInteger;
11
import java.util.ArrayList;
12
import java.util.HashSet;
13
import java.util.List;
17
* This modifier manipulates an ASF header extension object.
19
* @author Christian Laireiter
21
public class AsfExtHeaderModifier implements ChunkModifier
25
* List of modifiers which are to be applied to contained chunks.
27
private final List<ChunkModifier> modifierList;
30
* Creates an instance.<br>
32
* @param modifiers modifiers to apply.
34
public AsfExtHeaderModifier(List<ChunkModifier> modifiers)
36
assert modifiers != null;
37
this.modifierList = new ArrayList<ChunkModifier>(modifiers);
41
* Simply copies a chunk from <code>source</code> to <code>destination</code>.<br>
42
* The method assumes, that the GUID has already been read and will write the provided one to
43
* the destination.<br>
44
* The chunk length however will be read and used to determine the amount of bytes to copy.
46
* @param guid GUID of the current CHUNK.
47
* @param source source of an ASF chunk, which is to be located at the chunk length field.
48
* @param destination the destination to copy the chunk to.
49
* @throws IOException on I/O errors.
51
private void copyChunk(GUID guid, InputStream source, OutputStream destination) throws IOException
53
long chunkSize = Utils.readUINT64(source);
54
destination.write(guid.getBytes());
55
Utils.writeUINT64(chunkSize, destination);
56
Utils.copy(source, destination, chunkSize - 24);
62
public boolean isApplicable(GUID guid)
64
return GUID.GUID_HEADER_EXTENSION.equals(guid);
70
public ModificationResult modify(GUID guid, InputStream source, OutputStream destination) throws IOException
72
assert GUID.GUID_HEADER_EXTENSION.equals(guid);
75
final List<ChunkModifier> modders = new ArrayList<ChunkModifier>(this.modifierList);
76
final Set<GUID> occuredGuids = new HashSet<GUID>();
77
occuredGuids.add(guid);
79
BigInteger chunkLen = Utils.readBig64(source);
80
final GUID reserved1 = Utils.readGUID(source);
81
final int reserved2 = Utils.readUINT16(source);
82
final long dataSize = Utils.readUINT32(source);
84
assert dataSize == 0 || dataSize >= 24;
85
assert chunkLen.subtract(BigInteger.valueOf(46)).longValue() == dataSize;
88
* Stream buffer for the chunk list
90
ByteArrayOutputStream bos = new ByteArrayOutputStream();
92
* Stream which counts read bytes. Dirty but quick way of implementing this.
94
final CountingInputStream cis = new CountingInputStream(source);
96
while (cis.getReadCount() < dataSize)
99
GUID curr = Utils.readGUID(cis);
100
boolean handled = false;
101
for (int i = 0; i < modders.size() && !handled; i++)
103
if (modders.get(i).isApplicable(curr))
105
final ModificationResult modRes = modders.get(i).modify(curr, cis, bos);
106
difference += modRes.getByteDifference();
107
occuredGuids.addAll(modRes.getOccuredGUIDs());
114
occuredGuids.add(curr);
115
copyChunk(curr, cis, bos);
118
// Now apply the left modifiers.
119
for (ChunkModifier curr : modders)
121
// chunks, which were not in the source file, will be added to the destination
122
final ModificationResult result = curr.modify(null, null, bos);
123
difference += result.getByteDifference();
124
occuredGuids.addAll(result.getOccuredGUIDs());
126
destination.write(GUID.GUID_HEADER_EXTENSION.getBytes());
127
Utils.writeUINT64(chunkLen.add(BigInteger.valueOf(difference)).longValue(), destination);
128
destination.write(reserved1.getBytes());
129
Utils.writeUINT16(reserved2, destination);
130
Utils.writeUINT32(dataSize + difference, destination);
131
destination.write(bos.toByteArray());
132
return new ModificationResult(0, difference, occuredGuids);