/**
*
*/
package org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf;
import javax.xml.namespace.QName;
import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.ComplexType;
import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.ElementType;
import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.SchemaType;
import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.SimpleType;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.exception.ProtobufModelGenerationFailedException;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufField;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufFieldModifier;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufFieldType;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.SchemaTypeMap;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.SchemaTypeName;
/**
* This class parses element tag of the complex type and creates a protobuf field.
* The class understands the various possibilities of value type and creates field from it.
* @author rkulandaivel
*
*/
public class ElementTypeMapper extends BaseSchemaTypeMapper{
public ElementTypeMapper(SchemaTypeMap schemaTypeMap, MapperInstanceProvider instProvider){
super( schemaTypeMap, instProvider );
}
/**
* The method can return null value also.
* It returns null if both minoccurs and max occurs are zero.
*
* @param complexType
* @param seqEl
* @return
* @throws ProtobufModelGenerationFailedException
*/
public ProtobufField createFieldForElement(ComplexType complexType, ElementType seqEl) throws ProtobufModelGenerationFailedException{
if( (seqEl.getMaxOccurs() == 0) && (seqEl.getMinOccurs() == 0) ){
//both values zero is a valid combination
return null;
}
if( seqEl.getRef() != null && seqEl.getTypeName() == null ){
ElementType refElement = getElementType( new SchemaTypeName(seqEl.getRef() ) );
ProtobufField field = createFieldForElement( complexType, (ElementType)refElement );
//update with the modifier of the original element which refers.
//the element which is being referred is definitely "required" modifier, because
//the element tag at root level is not applicable for attributes minoccurs and maxoccurs.
//so blindly overwrite.
field.setFieldModifier( getModifierForElement( seqEl ) );
return field;
}else if( seqEl.getRef() == null && seqEl.getElementType() == null ){
if( seqEl.getSimpleType() == null && seqEl.getComplexType() == null ){
throw new ProtobufModelGenerationFailedException("The type of field cannot be null");
}
}
ProtobufField field = new ProtobufField();
field.setFieldName( seqEl.getTypeName().getLocalPart() );
field.setConvertedFieldName( MapperUtils.deriveFieldName( field.getFieldName() ) );
traceTypeAndPopulateTheField(seqEl, field);
field.setNillable( seqEl.isNillable() );
if( seqEl.getDocumentation() != null){
field.setFieldComments( seqEl.getDocumentation().getContent() );
}
return field;
}
private void traceTypeAndPopulateTheField(ElementType seqEl, ProtobufField field){
field.setFieldModifier( getModifierForElement( seqEl ) );
QName typeName = seqEl.getElementType();
//typeName passed can be an anonymous type.
if( typeName == null && seqEl.hasSimpleType() ){
populateFieldFromSimpleType(seqEl.getSimpleType(), field);
}else if( typeName == null && seqEl.hasComplexType() ){
populateFieldFromComplexType( seqEl.getComplexType(), field );
}else if( typeName != null ){
traceTypeAndPopulateTheField(typeName, field);
}
}
public void populateFieldFromComplexType(ComplexType type, ProtobufField field){
field.setXsdTypeName( type.getTypeName() );
field.setTypeOfField( ProtobufFieldType.COMPLEX_TYPE );
}
/**
* This method traces a particular type and populates the field object with two values
* 1. xsd type, 2. type of field. and 3. FieldModifier
*
* Following algorithmn is used.
* An element might refer to an inbuilt type or an user defined type (simple type or complex type)
* If it is a inbuilt type,
* xsd type name --> in built type name
* type of field --> uses MapperUtils.getProtobufFieldType
* If it is a Complex type
* xsd type name --> type name
* type of field --> USER_DEFINED_TYPE
* If it is simple type and it is enum,
* xsd type name --> type name
* type of field --> ENUM_TYPE
* If it is simple type and it uses restriction but not enums
* xsd type name --> actual inbuilt type name
* type of field --> uses MapperUtils.getProtobufFieldType
* It it is simple type and it uses list base
* xsd type name --> actual inbuilt item base name
* type of field --> uses MapperUtils.getProtobufFieldType
*
*
* Field Modifier is derived from method getModifierForElement.
*/
public void traceTypeAndPopulateTheField(QName typeName, ProtobufField field){
SchemaTypeName schemaTypeName = new SchemaTypeName( typeName );
SchemaType schemaType = getComplexOrSimpleType( schemaTypeName );
//if not null it means either user defined simple type or complex type
if( schemaType != null){
if( schemaType instanceof SimpleType ){
SimpleType simpleType = (SimpleType)schemaType;
populateFieldFromSimpleType(simpleType, field);
}else if( schemaType instanceof ComplexType ){
populateFieldFromComplexType( (ComplexType)schemaType, field );
}
}else{
populateFieldFromSimpleType( typeName, field, null );
}
}
public void populateFieldFromSimpleType(QName simpleType, ProtobufField field, SimpleType surroundingSimpleType){
SchemaTypeName schemaTypeName = new SchemaTypeName( simpleType );
SimpleType schemaType = (SimpleType) getComplexOrSimpleType( schemaTypeName );
if(schemaType != null){
populateFieldFromSimpleType(schemaType, field);
}else{
field.setXsdTypeName( MapperUtils.classifyBuiltInType( simpleType, surroundingSimpleType ) );
field.setTypeOfField( MapperUtils.getProtobufFieldType(simpleType) );
//update the modifier only if it is a special case
ProtobufFieldModifier modifier = MapperUtils.getModifierForSpecialInBuiltType(simpleType);
if( modifier != null){
field.setFieldModifier(modifier);
}
}
}
public void populateFieldFromSimpleType(SimpleType simpleType, ProtobufField field){
//if enumerations defined, then it is a enum type
if( simpleType.getRestriction() != null && simpleType.getRestriction().getEnumerations().size() > 0 ){
field.setTypeOfField( ProtobufFieldType.ENUM_TYPE );
field.setXsdTypeName( simpleType.getTypeName() );
}else if( simpleType.getRestriction() != null){
//get the base type which is gonna be the actual type
//but base type can be either a built-in simple type or a user defined simple type
QName baseType = simpleType.getRestriction().getBase();
populateFieldFromSimpleType( baseType, field, simpleType);
}else if( simpleType.getList() != null ){
//get the item type which is gonna be the actual type
//also if list is used, the element type is repeated
QName itemType = simpleType.getList().getItemType();
populateFieldFromSimpleType( itemType, field, simpleType);
field.setFieldModifier( ProtobufFieldModifier.REPEATED );
}
}
private ProtobufFieldModifier getModifierForElement(ElementType seqEl){
return MapperUtils.getModifier(seqEl.getMinOccurs(), seqEl.getMaxOccurs());
}
}