package org.infinispan.objectfilter.impl.syntax.parser;
import java.util.ArrayList;
import java.util.List;
import org.infinispan.objectfilter.impl.logging.Log;
import org.infinispan.objectfilter.impl.syntax.IndexedFieldProvider;
import org.infinispan.objectfilter.impl.util.StringHelper;
import org.infinispan.protostream.SerializationContext;
import org.infinispan.protostream.descriptors.Descriptor;
import org.infinispan.protostream.descriptors.EnumDescriptor;
import org.infinispan.protostream.descriptors.EnumValueDescriptor;
import org.infinispan.protostream.descriptors.FieldDescriptor;
import org.infinispan.protostream.descriptors.JavaType;
import org.jboss.logging.Logger;
/**
* @author anistor@redhat.com
* @since 7.0
*/
public final class ProtobufPropertyHelper extends ObjectPropertyHelper<Descriptor> {
private static final Log log = Logger.getMessageLogger(Log.class, ProtobufPropertyHelper.class.getName());
private final SerializationContext serializationContext;
private final IndexedFieldProvider<Descriptor> indexedFieldProvider;
public ProtobufPropertyHelper(SerializationContext serializationContext, IndexedFieldProvider<Descriptor> indexedFieldProvider) {
this.serializationContext = serializationContext;
this.indexedFieldProvider = indexedFieldProvider != null ? indexedFieldProvider : super.getIndexedFieldProvider();
}
@Override
public IndexedFieldProvider<Descriptor> getIndexedFieldProvider() {
return indexedFieldProvider;
}
@Override
public Descriptor getEntityMetadata(String typeName) {
return serializationContext.getMessageDescriptor(typeName);
}
@Override
public List<?> mapPropertyNamePathToFieldIdPath(Descriptor messageDescriptor, String[] propertyPath) {
List<Integer> translatedPath = new ArrayList<>(propertyPath.length);
Descriptor md = messageDescriptor;
for (String prop : propertyPath) {
FieldDescriptor fd = md.findFieldByName(prop);
translatedPath.add(fd.getNumber());
if (fd.getJavaType() == JavaType.MESSAGE) {
md = fd.getMessageType();
} else {
md = null; // iteration is expected to stop here
}
}
return translatedPath;
}
@Override
public Class<?> getPrimitivePropertyType(Descriptor entityType, String[] propertyPath) {
FieldDescriptor field = getField(entityType, propertyPath);
if (field == null) {
throw log.getNoSuchPropertyException(entityType.getFullName(), StringHelper.join(propertyPath));
}
switch (field.getJavaType()) {
case INT:
return Integer.class;
case LONG:
return Long.class;
case FLOAT:
return Float.class;
case DOUBLE:
return Double.class;
case BOOLEAN:
return Boolean.class;
case STRING:
return String.class;
case BYTE_STRING:
return byte[].class;
case ENUM:
return Integer.class;
}
return null;
}
/**
* @param entityType
* @param propertyPath
* @return the field descriptor or null if not found
*/
private FieldDescriptor getField(Descriptor entityType, String[] propertyPath) {
Descriptor messageDescriptor = entityType;
int i = 0;
for (String p : propertyPath) {
FieldDescriptor field = messageDescriptor.findFieldByName(p);
if (field == null || ++i == propertyPath.length) {
return field;
}
if (field.getJavaType() == JavaType.MESSAGE) {
messageDescriptor = field.getMessageType();
} else {
break;
}
}
return null;
}
@Override
public boolean hasProperty(Descriptor entityType, String[] propertyPath) {
Descriptor messageDescriptor = entityType;
int i = 0;
for (String p : propertyPath) {
i++;
FieldDescriptor field = messageDescriptor.findFieldByName(p);
if (field == null) {
return false;
}
if (field.getJavaType() == JavaType.MESSAGE) {
messageDescriptor = field.getMessageType();
} else {
break;
}
}
return i == propertyPath.length;
}
@Override
public boolean hasEmbeddedProperty(Descriptor entityType, String[] propertyPath) {
Descriptor messageDescriptor = entityType;
for (String p : propertyPath) {
FieldDescriptor field = messageDescriptor.findFieldByName(p);
if (field == null) {
return false;
}
if (field.getJavaType() == JavaType.MESSAGE) {
messageDescriptor = field.getMessageType();
} else {
return false;
}
}
return true;
}
@Override
public boolean isRepeatedProperty(Descriptor entityType, String[] propertyPath) {
Descriptor messageDescriptor = entityType;
for (String p : propertyPath) {
FieldDescriptor field = messageDescriptor.findFieldByName(p);
if (field == null) {
break;
}
if (field.isRepeated()) {
return true;
}
if (field.getJavaType() != JavaType.MESSAGE) {
break;
}
messageDescriptor = field.getMessageType();
}
return false;
}
@Override
public Object convertToPropertyType(Descriptor entityType, String[] propertyPath, String value) {
FieldDescriptor field = getField(entityType, propertyPath);
if (field == null) {
throw log.getNoSuchPropertyException(entityType.getFullName(), StringHelper.join(propertyPath));
}
//todo [anistor] this is just for remote query because enums are handled as integers for historical reasons.
if (field.getJavaType() == JavaType.BOOLEAN) {
try {
return Integer.parseInt(value) != 0;
} catch (NumberFormatException e) {
return super.convertToPropertyType(entityType, propertyPath, value);
}
} else if (field.getJavaType() == JavaType.ENUM) {
EnumDescriptor enumType = field.getEnumType();
EnumValueDescriptor enumValue;
try {
enumValue = enumType.findValueByNumber(Integer.parseInt(value));
} catch (NumberFormatException e) {
enumValue = enumType.findValueByName(value);
}
if (enumValue == null) {
throw log.getInvalidEnumLiteralException(value, enumType.getFullName());
}
return enumValue.getNumber();
}
return super.convertToPropertyType(entityType, propertyPath, value);
}
}