/**************************************************************************** * Copyright (C) 2012 ecsec GmbH. * All rights reserved. * Contact: ecsec GmbH (info@ecsec.de) * * This file is part of the Open eCard App. * * GNU General Public License Usage * This file may be used under the terms of the GNU General Public * License version 3.0 as published by the Free Software Foundation * and appearing in the file LICENSE.GPL included in the packaging of * this file. Please review the following information to ensure the * GNU General Public License version 3.0 requirements will be met: * http://www.gnu.org/copyleft/gpl.html. * * Other Usage * Alternatively, this file may be used in accordance with the terms * and conditions contained in a signed written agreement between * you and ecsec GmbH. * ***************************************************************************/ package org.openecard.common.tlv; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.LinkedList; import java.util.List; import org.openecard.common.util.ByteUtils; /** * * @author Tobias Wich <tobias.wich@ecsec.de> */ public class TLV { TagLengthValue tag; //protected TLV parent = null; protected TLV next = null; protected TLV child = null; public TLV() { tag = new TagLengthValue(); } public TLV(TLV obj) { this.tag = obj.tag; this.next = (obj.next != null) ? new TLV(obj.next) : null; this.child = (obj.child != null) ? new TLV(obj.child) : null; } /// /// deferred setters for TLV container /// public TagClass getTagClass() { return tag.getTagClass(); } public void setTagClass(TagClass tagClass) { tag.setTagClass(tagClass); } public boolean isPrimitive() { return tag.isPrimitive(); } public void setPrimitive(boolean primitive) { tag.setPrimitive(primitive); } public long getTagNum() { return tag.getTagNum(); } public void setTagNum(byte tagNum) { setTagNum(tagNum & 0xFF); } public void setTagNum(long tagNum) { tag.setTagNum(tagNum); } public long getTagNumWithClass() { return tag.getTagNumWithClass(); } public void setTagNumWithClass(byte tagNumWithClass) throws TLVException { setTagNumWithClass(tagNumWithClass & 0xFF); } public void setTagNumWithClass(long tagNumWithClass) throws TLVException { tag.setTagNumWithClass(tagNumWithClass); } public int getValueLength() { return tag.getValueLength(); } public byte[] getValue() { return tag.getValue(); } public void setValue(byte[] value) { tag.setValue(value); } /// /// modification functions /// public void addToEnd(TLV sibling) { if (next == null) { next = sibling; } else { next.addToEnd(sibling); } } /** * Remove next which is indicated by n. 0 means direct sibling. */ public TLV remove(int n) { if (n == 0) { TLV tmp = next; next = null; return tmp; } else if (n > 0 && next != null) { return next.remove(n - 1); } else { return null; } } public TLV removeNext() { return remove(0); } public void setChild(TLV child) { this.child = child; } public boolean hasChild() { return child != null; } public TLV getChild() { return child; } public boolean hasNext() { return next != null; } public TLV getNext() { return next; } public List<TLV> asList() { LinkedList<TLV> result = new LinkedList<TLV>(); TLV nextTag = this; while (nextTag != null) { TLV toAdd = new TLV(nextTag); toAdd.next = null; // delete reference to next result.add(toAdd); nextTag = nextTag.next; } return result; } public List<TLV> findNextTags(long num) { List<TLV> all = asList(); LinkedList<TLV> result = new LinkedList<TLV>(); for (TLV nextTLV : all) { if (nextTLV.getTagNumWithClass() == num) { result.add(nextTLV); } } return result; } public List<TLV> findChildTags(long num) { if (hasChild()) { return getChild().findNextTags(num); } else { return new LinkedList<TLV>(); } } /// /// TLV construction from and to different encodings /// public static TLV fromBER(byte[] input) throws TLVException { byte[] rest = input; TLV first = new TLV(); boolean isFirst = true; TLV last = first; // build as long as there is input left while (rest.length > 0) { TLV next; if (isFirst) { next = first; } else { next = new TLV(); } // break execution when 0 tag encountered if (rest[0] == (byte) 0) { return first; } // convert bytes to flat TLV data next.tag = TagLengthValue.fromBER(rest); // if constructed build child structure if (!next.tag.isPrimitive() && next.tag.getValueLength() > 0) { next.child = fromBER(next.tag.getValue()); } // set next as sibling in last if (isFirst) { isFirst = false; } else { last.next = next; } last = next; // get rest of the bytes for next iteration rest = last.tag.extractRest(rest); } return first; } public byte[] toBER() throws TLVException { return toBER(false); } public byte[] toBER(boolean withSuccessors) throws TLVException { ByteArrayOutputStream out = new ByteArrayOutputStream(); // value calculated from child if any toBER(out, withSuccessors); return out.toByteArray(); } private void toBER(ByteArrayOutputStream out, boolean withSuccessors) throws TLVException { if (child != null) { byte[] childBytes = child.toBER(withSuccessors); tag.setPrimitive(false); tag.setValue(childBytes); } else { tag.setPrimitive(true); } // write child to output stream try { out.write(tag.toBER()); } catch (IOException ex) { throw new TLVException(ex); } if (withSuccessors && next != null) { next.toBER(out, withSuccessors); } } @Override public String toString() { return toString(""); } public String toString(String prefix) { String result = prefix + String.format("%02X", getTagNumWithClass()); if (!hasChild()) { result += " " + tag.getValueLength() + " " + ByteUtils.toHexString(tag.getValue()); } else { result += "\n" + getChild().toString(prefix + " "); } if (hasNext()) { result += "\n" + getNext().toString(prefix); } return result; } }