1/*
2 * Copyright 2008 CoreMedia AG, Hamburg
3 *
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.googlecode.mp4parser;
18
19import com.coremedia.iso.BoxParser;
20import com.coremedia.iso.boxes.Box;
21import com.coremedia.iso.boxes.ContainerBox;
22import com.googlecode.mp4parser.util.ByteBufferByteChannel;
23
24import java.io.IOException;
25import java.nio.ByteBuffer;
26import java.nio.channels.ReadableByteChannel;
27import java.nio.channels.WritableByteChannel;
28import java.util.ArrayList;
29import java.util.LinkedList;
30import java.util.List;
31import java.util.logging.Logger;
32
33/**
34 * Abstract base class for a full iso box only containing ither boxes.
35 */
36public abstract class FullContainerBox extends AbstractFullBox implements ContainerBox {
37    protected List<Box> boxes = new LinkedList<Box>();
38    private static Logger LOG = Logger.getLogger(FullContainerBox.class.getName());
39    BoxParser boxParser;
40
41    public void setBoxes(List<Box> boxes) {
42        this.boxes = new LinkedList<Box>(boxes);
43    }
44
45    @SuppressWarnings("unchecked")
46    public <T extends Box> List<T> getBoxes(Class<T> clazz) {
47        return getBoxes(clazz, false);
48    }
49
50    @SuppressWarnings("unchecked")
51    public <T extends Box> List<T> getBoxes(Class<T> clazz, boolean recursive) {
52        List<T> boxesToBeReturned = new ArrayList<T>(2);
53        for (Box boxe : boxes) { //clazz.isInstance(boxe) / clazz == boxe.getClass()?
54            if (clazz == boxe.getClass()) {
55                boxesToBeReturned.add((T) boxe);
56            }
57
58            if (recursive && boxe instanceof ContainerBox) {
59                boxesToBeReturned.addAll((((ContainerBox) boxe).getBoxes(clazz, recursive)));
60            }
61        }
62        // Optimize here! Spare object creation work on arrays directly! System.arrayCopy
63        return boxesToBeReturned;
64        //return (T[]) boxesToBeReturned.toArray();
65    }
66
67    protected long getContentSize() {
68        long contentSize = 4; // flags and version
69        for (Box boxe : boxes) {
70            contentSize += boxe.getSize();
71        }
72        return contentSize;
73    }
74
75    public void addBox(Box b) {
76        b.setParent(this);
77        boxes.add(b);
78    }
79
80    public void removeBox(Box b) {
81        b.setParent(null);
82        boxes.remove(b);
83    }
84
85    public FullContainerBox(String type) {
86        super(type);
87    }
88
89    public List<Box> getBoxes() {
90        return boxes;
91    }
92
93    @Override
94    public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
95        this.boxParser = boxParser;
96        super.parse(readableByteChannel, header, contentSize, boxParser);
97
98    }
99
100    @Override
101    public void _parseDetails(ByteBuffer content) {
102        parseVersionAndFlags(content);
103        parseChildBoxes(content);
104    }
105
106    protected final void parseChildBoxes(ByteBuffer content) {
107        try {
108            while (content.remaining() >= 8) { //  8 is the minimal size for a sane box
109                boxes.add(boxParser.parseBox(new ByteBufferByteChannel(content), this));
110            }
111
112            if (content.remaining() != 0) {
113                setDeadBytes(content.slice());
114                LOG.severe("Some sizes are wrong");
115            }
116        } catch (IOException e) {
117            throw new RuntimeException(e);
118        }
119    }
120
121    public String toString() {
122        StringBuilder buffer = new StringBuilder();
123        buffer.append(this.getClass().getSimpleName()).append("[");
124        for (int i = 0; i < boxes.size(); i++) {
125            if (i > 0) {
126                buffer.append(";");
127            }
128            buffer.append(boxes.get(i).toString());
129        }
130        buffer.append("]");
131        return buffer.toString();
132    }
133
134
135    protected void getContent(ByteBuffer byteBuffer) {
136        writeVersionAndFlags(byteBuffer);
137        writeChildBoxes(byteBuffer);
138    }
139
140    protected final void writeChildBoxes(ByteBuffer bb) {
141        WritableByteChannel wbc = new ByteBufferByteChannel(bb);
142        for (Box box : boxes) {
143            try {
144                box.getBox(wbc);
145            } catch (IOException e) {
146                // cannot happen since my WritableByteChannel won't throw any excpetion
147                throw new RuntimeException("Cannot happen.", e);
148            }
149
150        }
151    }
152
153    public long getNumOfBytesToFirstChild() {
154        long sizeOfChildren = 0;
155        for (Box box : boxes) {
156            sizeOfChildren += box.getSize();
157        }
158        return getSize() - sizeOfChildren;
159    }
160}
161