Walker.java
package zserio.runtime.walker;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import zserio.runtime.ZserioError;
import zserio.runtime.typeinfo.FieldInfo;
import zserio.runtime.typeinfo.TypeInfo;
import zserio.runtime.typeinfo.TypeInfoUtil;
/**
* Walker through zserio objects, based on generated type info (see -withTypeInfoCode).
*/
public final class Walker
{
/**
* Constructor from walk observer.
*
* @param walkObserver Observer to use during walking.
*/
public Walker(WalkObserver walkObserver)
{
this(walkObserver, new DefaultWalkFilter());
}
/**
* Constructor from walk observer and walk filter.
*
* @param walkObserver Observer to use during walking.
* @param walkFilter Walk filter to use.
*/
public Walker(WalkObserver walkObserver, WalkFilter walkFilter)
{
this.walkObserver = walkObserver;
this.walkFilter = walkFilter;
}
/**
* Walks given zserio object which must be generated with type_info (see -withTypeInfoCode options).
*
* @param zserioObject Zserio object to walk.
*/
public void walk(Object zserioObject)
{
final TypeInfo typeInfo = callTypeInfoMethod(zserioObject);
if (!TypeInfoUtil.isCompound(typeInfo.getSchemaType()))
{
throw new ZserioError(
"Walker: Root object '" + typeInfo.getSchemaName() + "' is not a compound type!");
}
walkObserver.beginRoot(zserioObject);
walkFields(zserioObject, typeInfo);
walkObserver.endRoot(zserioObject);
}
private TypeInfo callTypeInfoMethod(Object zserioObject)
{
try
{
final Method typeInfoMethod = zserioObject.getClass().getMethod("typeInfo");
if (!typeInfoMethod.getReturnType().equals(TypeInfo.class))
throw new ZserioError("Walker: Zserio object has wrong typeInfo method!");
final TypeInfo typeInfo = (TypeInfo)typeInfoMethod.invoke(zserioObject);
return typeInfo;
}
catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException e)
{
throw new ZserioError(
"Walker: Zserio object must have type info enabled (see zserio option -withTypeInfoCode)!");
}
}
private int callChoiceTagMethod(Object zserioObject)
{
try
{
final Method choiceTagMethod = zserioObject.getClass().getMethod("choiceTag");
if (!choiceTagMethod.getReturnType().equals(Integer.TYPE))
throw new ZserioError("Walker: Zserio object has wrong choiceTag method!");
final int choiceTag = (Integer)choiceTagMethod.invoke(zserioObject);
return choiceTag;
}
catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException e)
{
throw new ZserioError("Walker: Zserio object does not have choiceTag() method!");
}
}
private int getUndefinedChoiceField(Object zserioObject)
{
try
{
final Field undefinedChoiceField = zserioObject.getClass().getField("UNDEFINED_CHOICE");
final int undefinedChoice = undefinedChoiceField.getInt(zserioObject);
return undefinedChoice;
}
catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e)
{
throw new ZserioError("Walker: Zserio object does not have UNDEFINED_CHOICE field!");
}
}
private Object callGetterMethod(Object zserioObject, FieldInfo fieldInfo)
{
final String getterName = fieldInfo.getGetterName();
try
{
final Method getterMethod = zserioObject.getClass().getMethod(getterName);
final Object result = getterMethod.invoke(zserioObject);
return result;
}
catch (NoSuchMethodException | SecurityException | IllegalArgumentException | IllegalAccessException |
InvocationTargetException e)
{
throw new ZserioError("Walker: Zserio object does not have " + getterName + "() method!");
}
}
private void walkFields(Object zserioObject, TypeInfo typeInfo)
{
final List<FieldInfo> fields = typeInfo.getFields();
if (TypeInfoUtil.hasChoice(typeInfo.getSchemaType()))
{
// union or choice
final int choiceTag = callChoiceTagMethod(zserioObject);
final int undefinedChoice = getUndefinedChoiceField(zserioObject);
if (choiceTag != undefinedChoice)
{
final FieldInfo field = fields.get(choiceTag);
walkField(callGetterMethod(zserioObject, field), field);
}
// else: uninitialized or empty branch
}
else
{
// structure
for (FieldInfo field : fields)
{
if (!walkField(callGetterMethod(zserioObject, field), field))
break;
}
}
}
private boolean walkField(Object field, FieldInfo fieldInfo)
{
if (field != null && fieldInfo.isArray())
{
if (walkFilter.beforeArray(field, fieldInfo))
{
walkObserver.beginArray(field, fieldInfo);
final int length = Array.getLength(field);
for (int i = 0; i < length; i++)
{
final Object element = Array.get(field, i);
if (!walkFieldValue(element, fieldInfo, i))
break;
}
walkObserver.endArray(field, fieldInfo);
}
return walkFilter.afterArray(field, fieldInfo);
}
else
{
return walkFieldValue(field, fieldInfo, WalkerConst.NOT_ELEMENT);
}
}
private boolean walkFieldValue(Object field, FieldInfo fieldInfo, int elementIndex)
{
final TypeInfo typeInfo = fieldInfo.getTypeInfo();
if (field != null && TypeInfoUtil.isCompound(typeInfo.getSchemaType()))
{
if (walkFilter.beforeCompound(field, fieldInfo, elementIndex))
{
walkObserver.beginCompound(field, fieldInfo, elementIndex);
walkFields(field, typeInfo);
walkObserver.endCompound(field, fieldInfo, elementIndex);
}
return walkFilter.afterCompound(field, fieldInfo, elementIndex);
}
else
{
if (walkFilter.beforeValue(field, fieldInfo, elementIndex))
walkObserver.visitValue(field, fieldInfo, elementIndex);
return walkFilter.afterValue(field, fieldInfo, elementIndex);
}
}
private final WalkObserver walkObserver;
private final WalkFilter walkFilter;
};