com.mobius.software.telco.protocols.ss7.asn.ASNParser Maven / Gradle / Ivy
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