/*
* Digital Audio Access Protocol (DAAP) Library
* Copyright (C) 2004-2010 Roger Kapsi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ardverk.daap;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.ardverk.daap.chunks.ByteChunk;
import org.ardverk.daap.chunks.Chunk;
import org.ardverk.daap.chunks.ChunkFactory;
import org.ardverk.daap.chunks.ContainerChunk;
import org.ardverk.daap.chunks.DateChunk;
import org.ardverk.daap.chunks.IntChunk;
import org.ardverk.daap.chunks.LongChunk;
import org.ardverk.daap.chunks.ShortChunk;
import org.ardverk.daap.chunks.StringChunk;
import org.ardverk.daap.chunks.VersionChunk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DaapInputStream extends FilterInputStream {
private static final Logger LOG = LoggerFactory
.getLogger(DaapInputStream.class);
private ChunkFactory factory = null;
public DaapInputStream(InputStream in) {
super(in);
}
public int read() throws IOException {
int b = super.read();
if (b < 0) {
throw new EOFException();
}
return b;
}
/*
* Re: skip(length-Chunk.XYZ_LENGTH);
*
* iTunes states in Content-Codes responses that Chunk X is of type Y and
* has hence the length Z. A Byte has for example the length 1. But in some
* cases iTunes uses a different length for Bytes! It's probably a bug in
* iTunes...
*/
public int read(int length) throws IOException {
skip(length - Chunk.BYTE_LENGTH);
return read();
}
public int readShort(int length) throws IOException {
skip(length - Chunk.SHORT_LENGTH);
return (read() << 8) | read();
}
public int readInt(int length) throws IOException {
skip(length - Chunk.INT_LENGTH);
return (read() << 24) | (read() << 16) | (read() << 8) | read();
}
public long readLong(int length) throws IOException {
skip(length - Chunk.LONG_LENGTH);
return (read() << 54l) | (read() << 48l) | (read() << 40l)
| (read() << 32l) | (read() << 24l) | (read() << 16l)
| (read() << 8l) | read();
}
public String readString(int length) throws IOException {
if (length == 0) {
return null;
}
byte[] b = new byte[length];
read(b, 0, b.length);
return new String(b, DaapUtil.UTF_8);
}
public int readContentCode() throws IOException {
return readInt(Chunk.INT_LENGTH);
}
public int readLength() throws IOException {
return readInt(Chunk.INT_LENGTH);
}
public Chunk readChunk() throws IOException {
int contentCode = readContentCode();
int length = readLength();
if (factory == null) {
factory = new ChunkFactory();
}
Chunk chunk = factory.newChunk(contentCode);
if (length > 0) {
if (chunk instanceof ByteChunk) {
checkLength(chunk, Chunk.BYTE_LENGTH, length);
((ByteChunk) chunk).setValue(read(length));
} else if (chunk instanceof ShortChunk) {
checkLength(chunk, Chunk.SHORT_LENGTH, length);
((ShortChunk) chunk).setValue(readShort(length));
} else if (chunk instanceof IntChunk) {
checkLength(chunk, Chunk.INT_LENGTH, length);
((IntChunk) chunk).setValue(readInt(length));
} else if (chunk instanceof LongChunk) {
checkLength(chunk, Chunk.LONG_LENGTH, length);
((LongChunk) chunk).setValue(readLong(length));
} else if (chunk instanceof StringChunk) {
((StringChunk) chunk).setValue(readString(length));
} else if (chunk instanceof DateChunk) {
checkLength(chunk, Chunk.DATE_LENGTH, length);
((DateChunk) chunk).setValue(readInt(length));
} else if (chunk instanceof VersionChunk) {
checkLength(chunk, Chunk.VERSION_LENGTH, length);
((VersionChunk) chunk).setValue(readInt(length));
} else if (chunk instanceof ContainerChunk) {
byte[] b = new byte[length];
read(b, 0, b.length);
DaapInputStream in = new DaapInputStream(
new ByteArrayInputStream(b));
while (in.available() > 0) {
((ContainerChunk) chunk).add(in.readChunk());
}
in.close();
} else {
throw new IOException("Unknown Chunk Type: " + chunk);
}
}
return chunk;
}
/**
* Throws an IOE if expected differs from length
*/
private static void checkLength(Chunk chunk, int expected, int length) {
if (expected != length) {
// throw new IOException("Expected a chunk with length " + expected
// + " but got " + length + " (" + chunk.getContentCodeString() +
// ")");
if (LOG.isWarnEnabled()) {
LOG.warn("Expected a chunk with length " + expected
+ " but got " + length + " ("
+ chunk.getContentCodeString() + ")");
}
}
}
}