All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mobius.software.telco.protocols.ss7.asn.ASNParser Maven / Gradle / Ivy

There is a newer version: 10.0.37-java11
Show newest version
package com.mobius.software.telco.protocols.ss7.asn;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNChoise;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNDecode;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNEncode;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNExclude;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNLength;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNPostprocess;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNPreprocess;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNProperty;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNTag;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNValidate;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNWildcard;
import com.mobius.software.telco.protocols.ss7.asn.annotations.ASNWrappedTag;
import com.mobius.software.telco.protocols.ss7.asn.exceptions.ASNDecodeException;
import com.mobius.software.telco.protocols.ss7.asn.exceptions.ASNException;
import com.mobius.software.telco.protocols.ss7.asn.exceptions.ASNParsingComponentException;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNBitString;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNBoolean;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNEnumerated;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNGeneric;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNIA5String;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNInteger;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNNull;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNObjectIdentifier;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNOctetString;
import com.mobius.software.telco.protocols.ss7.asn.primitives.ASNUTF8String;

/*
 * Mobius Software LTD
 * Copyright 2019, Mobius Software LTD and individual contributors
 * by the @authors tag.
 *
 * This program is free software: you can redistribute it and/or modify
 * under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see 
 */

/**
*
* @author yulian oifa
*
*/
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

public class ASNParser 
{
	private Class defaultClass;
	private ConcurrentHashMap> rootClassMapping=new ConcurrentHashMap>();
	private ConcurrentHashMap cachedElements=new ConcurrentHashMap();
	private ConcurrentHashMap innerParser=new ConcurrentHashMap();
	
	private ConcurrentHashMap> localClassesMapping=new ConcurrentHashMap>();
	private ConcurrentHashMap> defaultLocalClassesMapping=new ConcurrentHashMap>();
	
	private Boolean skipErrors=false;
	private ASNParser parentParser;
	private ASNDecodeHandler handler;
	
	public ASNParser(Class defaultClass,Boolean skipErrors,Boolean loadDefaultPrimitives) {
		this.defaultClass=defaultClass;
		clear(loadDefaultPrimitives);
		this.skipErrors=skipErrors;		
	}
	
	public ASNParser(Boolean skipErrors) {
		clear(true);
		this.skipErrors=skipErrors;		
	}
	
	public ASNParser() {
		clear(true);
	}
	
	protected ASNParser(ASNParser parentParser)
	{
		this.parentParser=parentParser;
		if(this.parentParser.handler!=null)
			this.handler=this.parentParser.handler;
		
		loadPrimitives();
	}
	
	public void setDecodeHandler(ASNDecodeHandler handler) {
		this.handler=handler;
	}
	
	public void clearDecodeHandler() {
		this.handler=null;
	}
	
	private void loadPrimitives() {
		this.loadClass(ASNBitString.class);
		this.loadClass(ASNBoolean.class);
		this.loadClass(ASNEnumerated.class);
		this.loadClass(ASNIA5String.class);
		this.loadClass(ASNInteger.class);
		this.loadClass(ASNNull.class);
		this.loadClass(ASNObjectIdentifier.class);
		this.loadClass(ASNOctetString.class);
		this.loadClass(ASNUTF8String.class);	
	}
	
	public void clear(Boolean loadDefaultPrimitives) {
		rootClassMapping.clear();
		cachedElements.clear();
		if(loadDefaultPrimitives)
			loadPrimitives();
	}
	
	public ASNParser getParser(Class rootClazz) {
		if(parentParser!=null)
			return parentParser.getParser(rootClazz);
		
		ASNParser parser=innerParser.get(rootClazz.getCanonicalName());
		if(parser==null) {
			parser=new ASNParser(this);
			ASNParser oldParser=innerParser.putIfAbsent(rootClazz.getCanonicalName(), parser);
			if(oldParser!=null)
				parser=oldParser;				
		}
		
		return parser;
	}
	
	public ASNParser getParser(Class rootClazz,Class defaultClass) {
		if(parentParser!=null)
			return parentParser.getParser(rootClazz);
		
		ASNParser parser=innerParser.get(rootClazz.getCanonicalName());
		if(parser==null) {
			parser=new ASNParser(this);
			parser.defaultClass=defaultClass;
			ASNParser oldParser=innerParser.putIfAbsent(rootClazz.getCanonicalName(), parser);
			if(oldParser!=null)
				parser=oldParser;	
		}
		
		return parser;
	}
	
	public void clearClassMapping(Class rootClazz) {
		getParser(rootClazz).clear(true);
	}
	
	public void registerAlternativeClassMapping(Class rootClazz, Class clazz) {
		getParser(rootClazz).replaceClass(clazz);
	}
	
	public void registerLocalMapping(Class rootClazz, Object key, Class clazz) {
		if(parentParser!=null) {
			parentParser.registerLocalMapping(rootClazz, key, clazz);
			return;
		}
		
		LocalMappingKey localKey=new LocalMappingKey(key, rootClazz.getCanonicalName());
		localClassesMapping.put(localKey, clazz);
	}
	
	public Class getLocalMapping(Class rootClazz,Object key) {
		if(parentParser!=null)
			return parentParser.getLocalMapping(rootClazz, key);
		
		LocalMappingKey localKey=new LocalMappingKey(key, rootClazz.getCanonicalName());
		return localClassesMapping.get(localKey);
	}
	
	public void registerDefaultLocalMapping(Class rootClazz, Class clazz) {
		if(parentParser!=null) {
			parentParser.registerDefaultLocalMapping(rootClazz, clazz);
			return;
		}
		
		defaultLocalClassesMapping.put(rootClazz.getCanonicalName(), clazz);
	}
	
	public Class getDefaultLocalMapping(Class rootClazz) {
		if(parentParser!=null)
			return parentParser.getDefaultLocalMapping(rootClazz);
		
		return defaultLocalClassesMapping.get(rootClazz.getCanonicalName());
	}
	
	public void loadClass(Class newClass) {
		ASNTag tag=newClass.getAnnotation(ASNTag.class);
		if(tag==null) {
			loadWrappedClass(newClass,false);
			return;
		}
		
		try
		{
			newClass.getConstructor();
		}
		catch(Exception ex) {
			throw new RuntimeException("only classes with empty constructor are supported");			
		}
		
		ASNHeader newHeader=new ASNHeader(tag,tag.asnClass(),tag.tag(),tag.constructed(),null);
		if(rootClassMapping.containsKey(newHeader))
			throw new RuntimeException("class with this ASNTag already registered");
		
		rootClassMapping.put(newHeader, newClass);
	}
	
	public void replaceClass(Class newClass) {
		ASNTag tag=newClass.getAnnotation(ASNTag.class);
		if(tag==null) {
			loadWrappedClass(newClass,true);
			return;
		}
		
		try
		{
			newClass.getConstructor();
		}
		catch(Exception ex) {
			throw new RuntimeException("only classes with empty constructor are supported");			
		}
		
		ASNHeader newHeader=new ASNHeader(tag,tag.asnClass(),tag.tag(),tag.constructed(),null);
		rootClassMapping.put(newHeader, newClass);
	}
	
	private void loadWrappedClass(Class newClass, boolean replace) {
		ASNWrappedTag tag=newClass.getAnnotation(ASNWrappedTag.class);
		if(tag==null) {
			return;
		}
		
		try
		{
			newClass.getConstructor();
		}
		catch(Exception ex) {
			throw new RuntimeException("only classes with empty constructor are supported");			
		}
		
		ParserClassData cachedData=cachedElements.get(newClass.getCanonicalName());
		if(cachedData==null) {
			cachedData=processField(newClass,cachedElements);			
		}
		
		ConcurrentHashMap fieldsMap=cachedData.getFieldsMap();
		Iterator> iterator=fieldsMap.entrySet().iterator();		
		//in cassed we have wrapped at this level for this class we should clear all the items first
		//rootClassMapping.clear();
		while(iterator.hasNext()) {
			ASNHeader newHeader=iterator.next().getKey();
			if(!replace && rootClassMapping.containsKey(newHeader))
				throw new RuntimeException("class with this ASNTag already registered");
			
			rootClassMapping.put(newHeader, newClass);
		}
	}
	
	public ASNDecodeResult decode(ByteBuf buffer)  throws ASNException {
		return decode(buffer,new ConcurrentHashMap(),skipErrors);
	}
	
	public ASNDecodeResult decode(ByteBuf buffer, ConcurrentHashMap mappedData, Boolean skipErrors) throws ASNException {
		try {
			return decode(null, buffer,skipErrors, null, null, rootClassMapping,cachedElements,null,mappedData, defaultClass);
		}
		catch(ASNException ex) {
			throw ex;
		}
		catch(Exception ex) {
			throw new ASNException(ex.getMessage());
		}
	}
	
	public void merge(Object first,Object second) {
		Class effectiveClass=first.getClass();
		ParserClassData cachedData=cachedElements.get(effectiveClass.getCanonicalName());
		if(cachedData==null) {
			cachedData=processField(effectiveClass,cachedElements);			
		}
		
		for(FieldData currField:cachedData.getFields()) {
			try {
				if(currField.getField().isAccessible() && currField.getField().get(second)!=null) {
					currField.getField().set(first, currField.getField().get(second));
				}
			}
			catch(Exception ex) {
				
			}
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private DecodeResult decode(Object parent,ByteBuf buffer,Boolean skipErrors,Field wildcardField, ConcurrentHashMap fieldsMap,ConcurrentHashMap> classMapping,ConcurrentHashMap cachedElements,Integer index,ConcurrentHashMap mappedData,Class defaultClass) throws ASNException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, InstantiationException {
		int oldIndex=buffer.readerIndex();
		buffer.markReaderIndex();
		ASNHeaderWithLength header=readHeader(buffer);
		ASNHeader currHeader=new ASNHeader(header.getAsnClass(), header.getIsConstructed(), header.getAsnTag(), header.getIndefiniteLength(),index);
		Class effectiveClass=classMapping.get(currHeader);
		
		if(effectiveClass==null) {
			currHeader=new ASNHeader(header.getAsnClass(), header.getIsConstructed(), header.getAsnTag(), header.getIndefiniteLength(),null);
			effectiveClass=classMapping.get(currHeader);
			if(effectiveClass==null) {
				if(wildcardField!=null) {
					header.setLength(header.getLength() + buffer.readerIndex()-oldIndex);
					buffer.resetReaderIndex();
					if(wildcardField.getType().isAssignableFrom(List.class) && !wildcardField.getType().equals(Object.class)) {
						Type[] innerTypes = ((ParameterizedType) wildcardField.getGenericType()).getActualTypeArguments();
						effectiveClass=(Class)innerTypes[0];
					}
					else
						effectiveClass=wildcardField.getType();
				} else {
					if(defaultClass!=null)
						effectiveClass=defaultClass;
					else {
						if(skipErrors) {
							if(buffer.readableBytes()>=header.getLength())
								buffer.skipBytes(header.getLength());
							else
								buffer.skipBytes(buffer.readableBytes());
							
							return new DecodeResult(null,parent,header.getAsnClass(), header.getAsnTag(), header.getIsConstructed(),true, true, new ASNDecodeResult(null, parent, true, true, null));
						}
						else
							throw new ASNDecodeException("no class found for matching tag:" + currHeader.getAsnClass() + "," + currHeader.getAsnTag() + "," + currHeader.getIsConstructed() + "," + currHeader.getIndefiniteLength(),currHeader.getAsnTag(),currHeader.getAsnClass(),currHeader.getIsConstructed(),parent);						
					}
				}
			}						
		}
		
		Boolean isChoise = false;
		FieldData fieldData = null;
		if(fieldsMap!=null) {
			fieldData=fieldsMap.get(currHeader);
			
			if(fieldData!=null && fieldData.getFieldType()==FieldType.CHOISE) {
				isChoise = true;
				header.setLength(header.getLength() + buffer.readerIndex()-oldIndex);
				buffer.resetReaderIndex();	
				
				//if(fieldData.getField().getType().isAssignableFrom(List.class) && !fieldData.getField().getType().equals(Object.class)) {
				//	Type[] innerTypes = ((ParameterizedType) fieldData.getField().getGenericType()).getActualTypeArguments();
				//	effectiveClass=(Class)innerTypes[0];					
				//}
				//else
					effectiveClass=fieldData.getDefaultClass();
			}
		}
		
		ParserClassData cachedData=cachedElements.get(effectiveClass.getCanonicalName());
		if(cachedData==null) {
			cachedData=processField(effectiveClass,cachedElements);			
		}

		Boolean hadErrors=false;
		ASNDecodeResult errorResults=null;
		
		Constructor ctor = effectiveClass.getConstructor();
		Object currObject = ctor.newInstance(new Object[] {  });		
		if(cachedData.getHasWrappedTag())
			buffer.resetReaderIndex();					
		
		if(handler!=null) {
			ASNPreprocess preprocessAnnotation=effectiveClass.getAnnotation(ASNPreprocess.class);
			if(preprocessAnnotation!=null) {
				handler.preProcessElement(parent, currObject, mappedData);
			}
		}
		
		if(!cachedData.getSubFieldsFound()) {
			Method[] methods=effectiveClass.getMethods();
			for(Method method:methods) {
				ASNDecode asnDecode=method.getAnnotation(ASNDecode.class);
				if(asnDecode!=null) {
					if(parent!=null && wildcardField!=null && wildcardField.isAccessible() &&!wildcardField.getType().isAssignableFrom(List.class) && wildcardField.get(parent)!=null) {
						//patching in case multiple items get through wildcard to same item
						currObject=wildcardField.get(parent);
					}
					
					try {
						Object value=method.invoke(currObject,new Object[] { this, parent, buffer.slice(buffer.readerIndex(), header.getLength()), mappedData, skipErrors });
						if(value instanceof Boolean)
							hadErrors|=(Boolean)value;
						else if(value instanceof ASNDecodeResult) {
							if(errorResults==null)
								errorResults=(ASNDecodeResult)value;
							
							if(value!=null)
								hadErrors|=((ASNDecodeResult)value).getHadErrors();
						}
					}
					catch(Exception ex) {
						if(skipErrors) {
							return new DecodeResult(null,parent,header.getAsnClass(), header.getAsnTag(), header.getIsConstructed(), true, false, new ASNDecodeResult(null, parent, true, false, null));
						}
						else
							throw ex;
					}
					
					buffer.skipBytes(header.getLength());
					break;
				}
			}
		}
		else {
			int remainingBytes=0;
			//for parent level wrapped tag may have multiple entries , especially in Huawei
			if(parent==null && cachedData.getHasWrappedTag())
				remainingBytes=buffer.readableBytes();
			else
				remainingBytes=header.getLength();
			
			int innerIndex=0;
			if(isChoise)
				innerIndex=index;
			
			int originalIndex=buffer.readerIndex();
			while((buffer.readerIndex()-originalIndex)>6) & 0x03);
		boolean constructed=(currData & 0x20)!=0;
		int asnTag=currData & 0x1F;
		if(asnTag==0x1F) {
			asnTag=0;
			Boolean gotEOF=false;
			
			while(buffer.readableBytes()>0 && !gotEOF)
			{
				currData=buffer.readByte();
				if((currData & 0x80)==0)
					gotEOF=true;
				
				asnTag=((asnTag<<7) | (currData & 0x7F));				
			}
			
			if(!gotEOF)
				throw new ASNException("Invalid tag encoding found");
		}
				
		currData=buffer.readByte();
		int length=0;
		boolean indefiniteLength=false;
		if((currData & 0x080)==0x080 && currData !=-128) {
			int lengthLength=currData & 0x7F;
			for(int i=0;i0) {
				indefiniteLength=true;
				buffer.markReaderIndex();
				Boolean previousWasZero=false;
				Boolean gotEOF=false;
				while(!gotEOF && buffer.readableBytes()>0) {
					length++;
					if(buffer.readByte()!=0x00) {
						previousWasZero=false;
					} else {
						if(previousWasZero) {
							length-=2;
							gotEOF=true;
						}
						else
							previousWasZero=true;
					}
				}
				
				if(!gotEOF) {					
					//throw new ASNException("Invalid length encoding found");
					length=0;
					indefiniteLength=false;
				}
				
				buffer.resetReaderIndex();				
			}
		}				
		
		return new ASNHeaderWithLength(asnClass, constructed, asnTag, indefiniteLength, null,length);
	}
	
	public ASNParsingComponentException validateObject(Object value) throws ASNException {
		ASNTag tag=value.getClass().getAnnotation(ASNTag.class);
		ASNWrappedTag wrappedTag=value.getClass().getAnnotation(ASNWrappedTag.class);
		if(tag==null && wrappedTag==null)
			throw new ASNException("only entities annotated with ASNTag or ASNWrappedTag annotation are supported");

		try
		{
			ParserClassData cachedData=cachedElements.get(value.getClass().getCanonicalName());
			if(cachedData==null) {
				cachedData=processField(value.getClass(),cachedElements);			
			}
			
			return validateObject(value,cachedElements);			
		}
		catch(Exception ex) {			
			throw new ASNException(ex.getMessage());
		}
	}
	
	@SuppressWarnings("rawtypes")
	private ASNParsingComponentException validateObject(Object value,ConcurrentHashMap cachedElements) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ASNException {
		ASNParsingComponentException result=null;
		
		ParserClassData cachedData=cachedElements.get(value.getClass().getCanonicalName());
		if(cachedData==null) {
			cachedData=processField(value.getClass(),cachedElements);			
		}

		Method[] methods=value.getClass().getMethods();
		for(Method method:methods) {
			ASNValidate asnValidate=method.getAnnotation(ASNValidate.class);
			if(asnValidate!=null) {
				try {
					method.invoke(value,new Object[] { });
				}
				catch(InvocationTargetException ex) {
					if(ex.getTargetException()!=null) {
						if(ex.getTargetException() instanceof ASNParsingComponentException)
							return (ASNParsingComponentException)ex.getTargetException();
						
						throw new ASNException(ex.getTargetException().getMessage());
					}
					else
						throw new ASNException("An error occured while validating the ASN.1 Object format");
				}
				break;
			}
		}
		
		for(int i=0;i cachedElements) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ASNException {
		if(asnTag!=null && asnWrappedTag==null) {
			encodeTagAndHeader(buffer,realClass,realTag,realConstructed);
			if(!asnTag.lengthIndefinite())
				encodeLength(buffer,false,length);
			else
				encodeLength(buffer,true,0);
		}
		
		encodeWithoutHeader(buffer, value, cachedElements,false);
		
		if(asnTag!=null && asnWrappedTag==null) {
			if(asnTag.lengthIndefinite()) {
				buffer.writeByte(0x00);
				buffer.writeByte(0x00);
			}
		}
	}
	
	@SuppressWarnings("rawtypes")
	private void encodeWithoutHeader(ByteBuf buffer,Object value,ConcurrentHashMap cachedElements,Boolean isChoise) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, ASNException {
		//encode content
		ParserClassData cachedData=cachedElements.get(value.getClass().getCanonicalName());
		if(cachedData==null) {
			cachedData=processField(value.getClass(),cachedElements);			
		}
		
		Boolean hadData=false;
		for(int i=0;i cachedElements) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ASNException {
		Integer length=0;
		if(value==null)
			return length;
		
		ASNTag asnTag=value.getClass().getAnnotation(ASNTag.class);
		ASNWrappedTag asnWrappedTag=value.getClass().getAnnotation(ASNWrappedTag.class);
		//calculating itself header
		if(asnTag!=null) {
			ParserClassData cachedData=cachedElements.get(value.getClass().getCanonicalName());
			if(cachedData==null) {
				cachedData=processField(value.getClass(),cachedElements);			
			}
			
			//get internal content/subtags length
			length=getLength(cachedData, value, cachedElements,false);
			
			if(asnTag.lengthIndefinite())
				//2 bytes end of tag and one length
				length+=3;
			else {
				Integer lengthLength=getLengthLength(length);
				length += lengthLength + 1;				
			}
			
			if(realTag!=null)
				length+=getTagLength(realTag)+1;
			else
				length+=getTagLength(asnTag.tag())+1;
		} else if(asnWrappedTag!=null) {
			ParserClassData cachedData=cachedElements.get(value.getClass().getCanonicalName());
			if(cachedData==null) {
				cachedData=processField(value.getClass(),cachedElements);			
			}
			
			//get internal content/subtags length
			length=getLength(cachedData, value, cachedElements,false);
			//no outer header here
		}
		
		return length;
	}
	
	public Integer getLength(Object value) throws ASNException {
		ParserClassData cachedData=cachedElements.get(value.getClass().getCanonicalName());
		if(cachedData==null) {
			cachedData=processField(value.getClass(),cachedElements);			
		}
		
		try {
			return getLengthWithHeader(null, value, cachedElements);			
		}
		catch(Exception ex) {
			throw new ASNException(ex.getMessage());
		}
	}
	
	@SuppressWarnings("rawtypes")
	private Integer getLength(ParserClassData parserData,Object value,ConcurrentHashMap cachedElements,Boolean isChoise) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, ASNException {
		Integer length=0;
		Boolean hadData=false;
		for(int i=0;i0)
		{
			result+=1;
			testValue=((testValue>>7) & Integer.MAX_VALUE);			
		}
		
		if(result==0)
			result=1;
		
		return result;
	}
	
	public static Integer getLengthLength(Integer value) {
		if(value<128)
			return 0;
		
		Integer testValue=value;
		int result=0;
		while(testValue>0)
		{
			result+=1;
			testValue=((testValue>>8) & Integer.MAX_VALUE);			
		}
		
		if(result==0)
			result=1;
		
		return result;
	}
	
	private void encodeTagAndHeader(ByteBuf buffer, ASNClass realClass,Integer realTag,Boolean realConstructed) {
		byte header;
		if(realTag<31)
		{
			if(realConstructed)
				header=(byte)(((realClass.getValue()<<6) & 0xC0) | 0x20 | (realTag & 0x1F));
			else
				header=(byte)(((realClass.getValue()<<6) & 0xC0) | (realTag & 0x1F));
			
			buffer.writeByte(header);
		}
		else
		{
			if(realConstructed)
				header=(byte)(((realClass.getValue()<<6) & 0xC0) | 0x3F);
			else
				header=(byte)(((realClass.getValue()<<6) & 0xC0) | 0x1F);
			
			buffer.writeByte(header);
			
			Integer testValue=realTag;
			while(testValue>0)
			{
				int currByte=testValue & 0x07F;
				testValue=((testValue>>7) & Integer.MAX_VALUE);
				if(testValue==0)
					buffer.writeByte(currByte);
				else
					buffer.writeByte(0x80 | currByte);
			}
		}
	}
	
	public static void encodeLength(ByteBuf buffer, Boolean lengthIndefinite,Integer value) {
		if(lengthIndefinite)
			buffer.writeByte((byte)0x80);
		else if(value<128)
			buffer.writeByte(value);
		else
		{
			int totalBytes=getLengthLength(value);
			buffer.writeByte(0x80 | totalBytes);
			
			for(int i=0,size=(totalBytes-1)*8;i>size) & 0x0FF);
		}		
	}
	
	private ParserClassData processField(Class effectiveClass,ConcurrentHashMap cachedElements) {
		ParserClassData cachedData=cachedElements.get(effectiveClass.getCanonicalName());
		if(cachedData==null) {
			Class currentPath=effectiveClass;
			List> classesPath=new ArrayList>();
			do {
				classesPath.add(0,currentPath);
				currentPath=currentPath.getSuperclass();				
			}
			while(currentPath!=null);
			
			List annotatedFields=new ArrayList();
			Boolean hasWrappedTag=false;
			for(Class currentClass:classesPath)
			{
				ASNWrappedTag wrappedTag=currentClass.getAnnotation(ASNWrappedTag.class);
				if(wrappedTag!=null)
					hasWrappedTag=true;
				
				Field[] fields=currentClass.getDeclaredFields();
				for(int i=0;i realType=fields[i].getType();
							if(fields[i].getType().isAssignableFrom(List.class) && !fields[i].getType().equals(Object.class)) {
								Type[] innerTypes = ((ParameterizedType) fields[i].getGenericType()).getActualTypeArguments();
								if(innerTypes.length==1) {
									realType=(Class)innerTypes[0];
									innerTag=((Class)innerTypes[0]).getAnnotation(ASNTag.class);
								}
							}
							else if(fields[i].getType().isAssignableFrom(ASNGeneric.class) && !fields[i].getType().equals(Object.class)) {
								innerTag=((Class)((ParameterizedType)effectiveClass.getGenericSuperclass()).getActualTypeArguments()[0]).getAnnotation(ASNTag.class);
							}	
							else
								innerTag=fields[i].getType().getAnnotation(ASNTag.class);		
							
							if(innerTag!=null) {
								annotatedFields.add(new FieldData(FieldType.STANDARD,fields[i],realType));
							}
						}
					}
				}
			}
			
			cachedData=new ParserClassData(annotatedFields,hasWrappedTag);
			storeFields(effectiveClass,cachedData,annotatedFields,null,null);
			cachedElements.put(effectiveClass.getCanonicalName(), cachedData);			
		}
		
		return cachedData;
	}
	
	private void processChoiseField(Class effectiveClass,ParserClassData cachedData,Field parentField,Class parentType) {
		Class currentPath=effectiveClass;
		List> classesPath=new ArrayList>();
		do {
			classesPath.add(0,currentPath);
			currentPath=currentPath.getSuperclass();				
		}
		while(currentPath!=null);
		
		List annotatedFields=new ArrayList();
		for(Class currentClass:classesPath)
		{
			Field[] fields=currentClass.getDeclaredFields();
			for(int i=0;i realType=fields[i].getType();
						if(fields[i].getType().isAssignableFrom(List.class) && !fields[i].getType().equals(Object.class)) {
							Type[] innerTypes = ((ParameterizedType) fields[i].getGenericType()).getActualTypeArguments();
							if(innerTypes.length==1) {
								realType=(Class)innerTypes[0];
								innerTag=((Class)innerTypes[0]).getAnnotation(ASNTag.class);
							}
						}
						else if(fields[i].getType().isAssignableFrom(ASNGeneric.class) && !fields[i].getType().equals(Object.class)) {
							innerTag=((Class)((ParameterizedType)effectiveClass.getGenericSuperclass()).getActualTypeArguments()[0]).getAnnotation(ASNTag.class);
						}	
						else {
							innerTag=fields[i].getType().getAnnotation(ASNTag.class);							
						}
						
						if(innerTag!=null) {
							annotatedFields.add(new FieldData(FieldType.STANDARD,fields[i],realType));
						}
					}
				}
			}
		}
		
		storeFields(effectiveClass,cachedData,annotatedFields,parentField,parentType);		
	}
	
	private void storeFields(Class effectiveClass,ParserClassData cachedData,List fields,Field parentField,Class parentType) {
		for(int i=0;i realType=fields.get(i).getField().getType();
					if(fields.get(i).getField().getType().isAssignableFrom(List.class) && !fields.get(i).getField().getType().equals(Object.class)) {
						Type[] innerTypes = ((ParameterizedType) fields.get(i).getField().getGenericType()).getActualTypeArguments();
						if(innerTypes.length==1) {
							realType=((Class)innerTypes[0]);
							innerTag=realType.getAnnotation(ASNTag.class);
						}
					}
					else if(fields.get(i).getField().getType().isAssignableFrom(ASNGeneric.class) && !fields.get(i).getField().getType().equals(Object.class)) {
						realType=((Class)((ParameterizedType)effectiveClass.getGenericSuperclass()).getActualTypeArguments()[0]);
						innerTag=((Class)((ParameterizedType)effectiveClass.getGenericSuperclass()).getActualTypeArguments()[0]).getAnnotation(ASNTag.class);
					}	
					else {
						innerTag=realType.getAnnotation(ASNTag.class);					
					}
					
					int realInnerTag=innerTag.tag();
					ASNClass realClass=innerTag.asnClass();
					Boolean realConstructed=innerTag.constructed();
					Integer index=null;
					ASNProperty property=fields.get(i).getField().getAnnotation(ASNProperty.class);
					if(property!=null)
					{
						realInnerTag=property.tag();
						realClass=property.asnClass();
						realConstructed=property.constructed();
						if(property.index()>=0)
							index=property.index();
					}
						
					ASNHeader asnHeader=new ASNHeader(innerTag, realClass, realInnerTag,realConstructed,index);
					if(parentField==null || parentType==null) {
						Class defaultClass=realType;						
						if(fields.get(i).getField().getType().isInterface() && property!=null && !property.defaultImplementation().getCanonicalName().equals(Void.class.getCanonicalName()))
							defaultClass = property.defaultImplementation();
						
						cachedData.addInnerMapElement(asnHeader, defaultClass);
						cachedData.addFieldsMapElement(asnHeader, new FieldData(FieldType.STANDARD,fields.get(i).getField(), defaultClass));
					} else {
						cachedData.addInnerMapElement(asnHeader, parentType);
						cachedData.addFieldsMapElement(asnHeader, new FieldData(FieldType.CHOISE, parentField, parentType));						
					}
					break;
				case CHOISE:
					realType=fields.get(i).getField().getType();
					Class choiceDefaultClass=fields.get(i).getDefaultClass();
					if(!choiceDefaultClass.getCanonicalName().equals(realType.getCanonicalName()))
						realType=choiceDefaultClass;
					else {
						if(fields.get(i).getField().getType().isAssignableFrom(List.class) && !fields.get(i).getField().getType().equals(Object.class)) {
							Type[] innerTypes = ((ParameterizedType) fields.get(i).getField().getGenericType()).getActualTypeArguments();
							if(innerTypes.length==1)
								realType=((Class)innerTypes[0]);							
						}
						else if(fields.get(i).getField().getType().isAssignableFrom(ASNGeneric.class) && !fields.get(i).getField().getType().equals(Object.class)) {
							realType=((Class)((ParameterizedType)effectiveClass.getGenericSuperclass()).getActualTypeArguments()[0]);
						}
					}
					
					if(parentField!=null && parentType!=null)
						processChoiseField(realType, cachedData, parentField, parentType);
					else
						processChoiseField(realType, cachedData, fields.get(i).getField(), realType);
					break;
				case WILDCARD:
				default:
					break;			
			}						
		}
	}
	
	private class DecodeResult extends ASNDecodeResult
	{
		private Integer realTag;
		private ASNClass realClass;
		private Boolean realConstructed;
		
		private DecodeResult(Object result,Object parent,ASNClass realClass,Integer realTag,Boolean realConstructed,Boolean hadErrors,Boolean isTagError,ASNDecodeResult firstError) {
			super(result,parent,hadErrors,isTagError,firstError);
			this.realClass=realClass;
			this.realTag=realTag;
			this.realConstructed=realConstructed;
			this.firstError=firstError;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy