/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.eas.proto.dom; import com.eas.proto.CoreTags; import com.eas.proto.ProtoReaderException; import com.eas.util.BinaryUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.zip.ZipInputStream; /** * * @author pk */ class ProtoStreamNode implements ProtoNode { private static final String UNEXPECTED_EOF_MSG = "Unexpected EOF"; private static final String BUFFER_OVERFLOW_MSG = "Buffer overflow"; private Map<Integer, ProtoNode> childrenHash = new HashMap<>(); private List<ProtoNode> childrenVector = new ArrayList<>(); private byte[] data; private int nodeTag; private int dataSize; private int startOffset; ProtoStreamNode(int nodeTag, byte[] data) throws ProtoReaderException { this(nodeTag, data, 0, data.length); } ProtoStreamNode(int nodeTag, byte[] data, int startOffset, int dataSize) throws ProtoReaderException { try { this.data = data; this.nodeTag = nodeTag; this.dataSize = dataSize; this.startOffset = startOffset; if (startOffset + dataSize > data.length) { throw new IllegalArgumentException(BUFFER_OVERFLOW_MSG); } int i = startOffset; while (i < startOffset + dataSize) { if (dataSize - 5 < 0) { throw new ProtoReaderException(UNEXPECTED_EOF_MSG); } ProtoNode child; int tag = data[i++] & 0xff; int size = (((data[i] & 0xff) << 24) + ((data[i + 1] & 0xff) << 16) + ((data[i + 2] & 0xff) << 8) + ((data[i + 3] & 0xff))); i += 4; int dataOffset = i; if (dataOffset + size > startOffset + dataSize) { throw new ProtoReaderException(UNEXPECTED_EOF_MSG); } if (isSubStreamAt(dataOffset + size)) { i = dataOffset + size; if (startOffset + dataSize - i < 5) { throw new ProtoReaderException(UNEXPECTED_EOF_MSG); } int nextTag = data[i] & 0xff; assert nextTag == CoreTags.TAG_STREAM || nextTag == CoreTags.TAG_COMPRESSED_STREAM; i += 1; size = (((data[i] & 0xff) << 24) + ((data[i + 1] & 0xff) << 16) + ((data[i + 2] & 0xff) << 8) + ((data[i + 3] & 0xff))); i += 4; dataOffset = i; if (nextTag == CoreTags.TAG_COMPRESSED_STREAM) { try (ZipInputStream zStream = new ZipInputStream(new ByteArrayInputStream(data, dataOffset, size))) { zStream.getNextEntry(); byte[] subStreamData = BinaryUtils.readStream(zStream, -1); child = new ProtoStreamNode(tag, subStreamData, 0, subStreamData.length); } } else { child = new ProtoStreamNode(tag, data, dataOffset, size); } } else { child = new ProtoLeafNode(tag, data, dataOffset, size); } childrenHash.put(tag, child); childrenVector.add(child); i += size; } } catch (IllegalArgumentException | IOException | ArrayIndexOutOfBoundsException | ProtoReaderException ex) { throw new ProtoReaderException(ex); } } @Override public int getNodeTag() { return nodeTag; } @Override public byte getByte() { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public Date getDate() { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public double getDouble() { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public long getEntityID() { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public int getInt() { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public long getLong() { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public String getString() { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public ProtoNode getChild(int tag) { return childrenHash.get(tag); } @Override public List<ProtoNode> getChildren(int tag) { List<ProtoNode> results = new ArrayList<>(); for (ProtoNode node : childrenVector) { if (node.getNodeTag() == tag) { results.add(node); } } return results; } @Override public Iterator<ProtoNode> iterator() { return childrenVector.iterator(); } @Override public boolean containsChild(int tag) { return childrenHash.containsKey(tag); } private boolean isSubStreamAt(int offset) { if (offset >= data.length) { return false; } return CoreTags.TAG_STREAM == (data[offset] & 0xff) || CoreTags.TAG_COMPRESSED_STREAM == (data[offset] & 0xff); } @Override public ProtoNodeType getNodeType() { return ProtoNodeType.STREAM; } @Override public BigDecimal getBigDecimal() throws ProtoReaderException { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public int getOffset() { return startOffset; } @Override public int getSize() { return dataSize; } @Override public byte[] getData() { return data; } @Override public boolean getBoolean() throws ProtoReaderException { throw new UnsupportedOperationException("Unsupported on a stream node"); } @Override public float getFloat() throws ProtoReaderException { throw new UnsupportedOperationException("Unsupported on a stream node"); } }