be.fedict.commons.eid.consumer.tlv.TlvParser.java Source code

Java tutorial

Introduction

Here is the source code for be.fedict.commons.eid.consumer.tlv.TlvParser.java

Source

/*
 * Commons eID Project.
 * Copyright (C) 2008-2013 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.commons.eid.consumer.tlv;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Tag-Length-Value parser. The TLV-format is used in the eID card for encoding
 * of the identity and address files.
 * 
 * @author Frank Cornelis
 * 
 */
public class TlvParser {

    private static final Log LOG = LogFactory.getLog(TlvParser.class);

    private TlvParser() {
        super();
    }

    /**
     * Parses the given file using the meta-data annotations within the tlvClass
     * parameter.
     * 
     * @param <T>
     * @param file
     * @param tlvClass
     * @return
     */
    public static <T> T parse(final byte[] file, final Class<T> tlvClass) {
        T t;
        try {
            t = parseThrowing(file, tlvClass);
        } catch (final Exception ex) {
            throw new RuntimeException("error parsing file: " + tlvClass.getName(), ex);
        }
        return t;
    }

    private static byte[] copy(final byte[] source, final int idx, final int count) {
        final byte[] result = new byte[count];
        System.arraycopy(source, idx, result, 0, count);
        return result;
    }

    private static <T> T parseThrowing(final byte[] file, final Class<T> tlvClass) throws InstantiationException,
            IllegalAccessException, DataConvertorException, UnsupportedEncodingException {
        final Field[] fields = tlvClass.getDeclaredFields();
        final Map<Integer, Field> tlvFields = new HashMap<Integer, Field>();
        final T tlvObject = tlvClass.newInstance();
        for (Field field : fields) {
            final TlvField tlvFieldAnnotation = field.getAnnotation(TlvField.class);
            if (null != tlvFieldAnnotation) {
                final int tagId = tlvFieldAnnotation.value();
                if (tlvFields.containsKey(new Integer(tagId))) {
                    throw new IllegalArgumentException("TLV field duplicate: " + tagId);
                }
                tlvFields.put(new Integer(tagId), field);
            }
            final OriginalData originalDataAnnotation = field.getAnnotation(OriginalData.class);
            if (null != originalDataAnnotation) {
                field.setAccessible(true);
                field.set(tlvObject, file);
            }
        }

        int idx = 0;
        while (idx < file.length - 1) {
            final byte tag = file[idx];
            idx++;
            byte lengthByte = file[idx];
            int length = lengthByte & 0x7f;
            while ((lengthByte & 0x80) == 0x80) {
                idx++;
                lengthByte = file[idx];
                length = (length << 7) + (lengthByte & 0x7f);
            }
            idx++;
            if (0 == tag) {
                idx += length;
                continue;
            }
            if (tlvFields.containsKey(new Integer(tag))) {
                final Field tlvField = tlvFields.get(new Integer(tag));
                final Class<?> tlvType = tlvField.getType();
                final ConvertData convertDataAnnotation = tlvField.getAnnotation(ConvertData.class);
                final byte[] tlvValue = copy(file, idx, length);
                Object fieldValue;
                if (null != convertDataAnnotation) {
                    final Class<? extends DataConvertor<?>> dataConvertorClass = convertDataAnnotation.value();
                    final DataConvertor<?> dataConvertor = dataConvertorClass.newInstance();
                    fieldValue = dataConvertor.convert(tlvValue);
                } else if (String.class == tlvType) {
                    fieldValue = new String(tlvValue, "UTF-8");
                } else if (Boolean.TYPE == tlvType) {
                    fieldValue = true;
                } else if (tlvType.isArray() && Byte.TYPE == tlvType.getComponentType()) {
                    fieldValue = tlvValue;
                } else {
                    throw new IllegalArgumentException("unsupported field type: " + tlvType.getName());
                }
                if (null != tlvField.get(tlvObject) && false == tlvField.getType().isPrimitive()) {
                    throw new RuntimeException("field was already set: " + tlvField.getName());
                }
                tlvField.setAccessible(true);
                tlvField.set(tlvObject, fieldValue);
            } else {
                LOG.debug("unknown tag: " + (tag & 0xff) + ", length: " + length);
            }
            idx += length;
        }
        return tlvObject;
    }
}