/** * */ package org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.Attribute; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.AttributeGroup; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.AttributeGroupType; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.Choice; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.ComplexContent; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.ComplexType; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.Extension; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.Group; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.GroupType; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.Restriction; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.SchemaAll; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.SchemaType; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.Sequence; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.SequenceElement; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.SimpleContent; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.SimpleType; import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.SimpleTypeRestriction; 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.ProtobufMessage; import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.SchemaTypeMap; import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.SchemaTypeName; /** * The class parses the complex type schema object and creates protobuf message object. * The classes understands combinations of various child elements like simple content, * complex content, sequence, choice, group and attributes. * * @author rkulandaivel * */ public class ComplexTypeMapper extends BaseSchemaTypeMapper{ private static final String FIELD_NAME_FOR_SIMPLE_CONTENT_EXTENSION_TYPE = "value"; public ComplexTypeMapper(SchemaTypeMap schemaTypeMap, MapperInstanceProvider instProvider){ super( schemaTypeMap, instProvider ); } /** * The method creates protobuf message from the given complex type. * This method understands various combinations of simple content, complex content, * restriction, extension, group, choice and sequence at all level. * Based on the combinations used, the elements are created. * * In case of attributes, all the attributes in the entire hierarchy are collected into single map. * So that two attributes defined with same name are resolved as single attribute. * * If the simple content uses extension, then there might be a chance that two attributes defined with same. * * Finally, the method validates the uniqueness in the field names. * The validation is required because if both super type and sub type defines field with same name * then proto model cannot differentiate it. Hence this validation is required. * * @param complexType * @return * @throws ProtobufModelGenerationFailedException */ public ProtobufMessage createProtobufMessage(ComplexType complexType) throws ProtobufModelGenerationFailedException{ ProtobufMessage protoMessage = new ProtobufMessage(); String messageName = MapperUtils.deriveMessageNameFromQName( complexType.getTypeName() ); protoMessage.setMessageName( messageName ); protoMessage.setSchemaTypeName( new SchemaTypeName( complexType.getTypeName() ) ); List<ProtobufField> fields = new ArrayList<ProtobufField>(); //a map if created here, because when this class goes down to each leaf of the type, //all the attributes would be collected into map. //finally when all fields are identified, attributes would be converted into fields. //the reason for doing this is a simple content can define an attribute with the same name its base type defines. Map<QName, Attribute> mapOfAttributes = new HashMap<QName, Attribute>(); fields.addAll( handleComplexTypeForFields(complexType, mapOfAttributes) ); List<Attribute> attributes = new ArrayList<Attribute>(mapOfAttributes.values()); List<ProtobufField> attrs = getInstanceProvider().getAttributesMapper().createProtobufFields(complexType, attributes); fields.addAll( attrs ); validateUniquenessInFields( complexType, fields ); protoMessage.getFields().addAll( fields ); if( complexType.getDocumentation() != null ){ protoMessage.setMessageComments( complexType.getDocumentation().getContent() ); } return protoMessage; } /** * This validation is required because if a complex type extends another type. * Both type defines fields with same name irrespective of field type. * This case is not supported in protobuf. Hence exception would be thrown here. * @param fields * @throws ProtobufModelGenerationFailedException */ private void validateUniquenessInFields(ComplexType complexType, List<ProtobufField> fields) throws ProtobufModelGenerationFailedException{ Map<String, ProtobufField> mapOfFields = new HashMap<String, ProtobufField>(); for(ProtobufField field : fields){ String fieldName = field.getFieldName(); if(mapOfFields.get( fieldName ) != null){ String message = "Identified duplicate field name '"+ fieldName+"' in type '"+complexType.getTypeName()+"'."; message = message + " This could be due to fields declared with same name in base type and sub type"; throw new ProtobufModelGenerationFailedException(message); }else{ mapOfFields.put(fieldName, field); } } } /** * The method understands the possible child elements the complex type has like group, sequence etc * and handles it. * * @param complexType * @param mapOfAttributes * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleComplexTypeForFields(ComplexType complexType, Map<QName, Attribute> mapOfAttributes) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); if(complexType.hasSimpleContent()){ fields.addAll( handleSimpleContent(complexType, mapOfAttributes) ); } if(complexType.hasComplexContent() ){ fields.addAll( handleComplexContent(complexType, mapOfAttributes) ); } if(complexType.hasGroup()){ fields.addAll( handleGroup(complexType, complexType.getGroup()) ); } if( complexType.hasChoice() ){ fields.addAll( handleChoice(complexType, complexType.getChoice()) ); } if( complexType.hasSequence() ){ fields.addAll( handleSequence(complexType, complexType.getSequence()) ); } if( complexType.hasAll() ){ fields.addAll( handleAll(complexType, complexType.getAll()) ); } List<Attribute> attrs = complexType.getAttributes(); populateAttributesInMap(mapOfAttributes, attrs); List<AttributeGroup> attrGps = complexType.getAttributeGroup(); populateAttributeGroupsInMap(mapOfAttributes, attrGps); return fields; } /** * This method handles the complex content of the complex type. * * @param complexType * @param mapOfAttributes * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleComplexContent(ComplexType complexType, Map<QName, Attribute> mapOfAttributes) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); ComplexContent complexContent = complexType.getComplexContent(); if( complexContent.getExtension() != null ){ fields.addAll( handleComplexContentExtension(complexType, mapOfAttributes) ); } if( complexContent.getRestriction() != null ){ fields.addAll( handleComplexContentRestriction(complexType, mapOfAttributes) ); } return fields; } /** * This method handles the element node. * It uses ElementTypeMapper to create the field. * * @param complexType * @param seqEl * @return * @throws ProtobufModelGenerationFailedException */ private ProtobufField handleElement(ComplexType complexType, SequenceElement seqEl) throws ProtobufModelGenerationFailedException{ return getInstanceProvider().getElementTypeMapper().createFieldForElement(complexType, seqEl); } /** * This method handles the choice node. * For each element the choice has, protobuf Field is created and then * the field modifier is overwritten. * Because, a field can never be required inside the choice tag. * See method compareAndUpdateModifier for algorithmn of overwriting the modifier. * * @param complexType * @param choiceEl * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleChoice(ComplexType complexType, Choice choiceEl) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); for( SequenceElement element : choiceEl.getElements() ){ ProtobufField field = handleElement(complexType, element); if(field != null){ //compareAndUpdateModifier(choiceEl, field); fields.add( field ); } } for ( Choice choice : choiceEl.getChoices() ){ fields.addAll( handleChoice(complexType, choice) ); } for ( Sequence sequence : choiceEl.getSequences() ){ fields.addAll( handleSequence(complexType, sequence) ); } for ( Group gp : choiceEl.getGroups() ){ fields.addAll( handleGroup(complexType, gp) ); } for(ProtobufField field : fields){ compareAndUpdateModifier(choiceEl, field); } return fields; } /** * This method handles the sequence node. * For each element the sequence has, protobuf Field is created and then * the field modifier is overwritten. * Because, the sequence node can also define min occurs and max occurs attribute. * * See method compareAndUpdateModifier for algorithmn of overwriting the modifier. * It assigns lesser modifier. For example if the element is required and sequence is optional, then field modifier is optional. * * @param complexType * @param seqEl * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleSequence(ComplexType complexType, Sequence seqEl) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); for( Sequence.SequenceEntry entry : seqEl.getEntries() ){ if(entry.isChoice()){ fields.addAll( handleChoice(complexType, entry.getChoice()) ); } if(entry.isElement()){ ProtobufField field = handleElement(complexType, entry.getElement()); if(field != null){ compareAndUpdateModifier(seqEl, field); fields.add( field ); } } if(entry.isGroup()){ fields.addAll( handleGroup(complexType, entry.getGroup()) ); } if(entry.isSequence()){ fields.addAll( handleSequence(complexType, entry.getSequence()) ); } } return fields; } /** * This method identifies the group ref type and reads the elements from the corresponding group type. * * @param complexType * @param groupEl * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleGroup(ComplexType complexType, Group groupEl) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); QName groupRef = groupEl.getGroupRef(); SchemaTypeName groupRefName = new SchemaTypeName( groupRef ); GroupType groupType = getGroupType( groupRefName ); if(groupType != null){ if( groupType.getChoice() != null ){ fields.addAll( handleChoice(complexType, groupType.getChoice()) ); } if( groupType.getSequence() != null ){ fields.addAll( handleSequence(complexType, groupType.getSequence()) ); } if( groupType.getAll() != null ){ fields.addAll( handleAll(complexType, groupType.getAll()) ); } } return fields; } private List<ProtobufField> handleAll(ComplexType complexType, SchemaAll allEl) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); for( SequenceElement element : allEl.getElements() ){ ProtobufField field = handleElement(complexType, element); if(field != null){ fields.add( field ); } } for(ProtobufField field : fields){ compareAndUpdateModifier(allEl, field); } return fields; } /** * This method handles the complex content restriction. * If restriction is used at complex content, then the base type would be another complex type. * So all the fields from the base complex type would be flattened to the current complex type. * It also handles if sequence, group are used. * * @param complexType * @param mapOfAttributes * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleComplexContentRestriction(ComplexType complexType, Map<QName, Attribute> mapOfAttributes) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); Restriction restriction = complexType.getComplexContent().getRestriction(); //handle base here QName complexTypeBase = restriction.getBase(); //complex content restriction base would definitely be a complex type which can have simple or complex content SchemaTypeName schemaTypeName = new SchemaTypeName( complexTypeBase ); ComplexType baseComplexType = (ComplexType)getComplexOrSimpleType( schemaTypeName ); fields.addAll( handleComplexTypeForFields(baseComplexType,mapOfAttributes) ); // if(restriction.getChoice() != null ){ // fields.addAll( handleChoice(complexType, restriction.getChoice()) ); // } // if( restriction.getGroup() != null){ // fields.addAll( handleGroup(complexType, restriction.getGroup()) ); // } // if( restriction.getSequence() != null){ // fields.addAll( handleSequence(complexType, restriction.getSequence()) ); // } // if( restriction.getAll() != null ){ // fields.addAll( handleAll(complexType, restriction.getAll()) ); // } //no need to populate attributes from a complex content restriction //because the jaxb does not generate fields corresponding to attributes present in complex content restriction //jaxb generates fields corresponding to attributes only in base types. //consider the below example. //jaxb generated for baseType1 has fields corresponding to attributes //whereas jaxb generated for restrictedType1 does not have fields corresponding to attributes /* * e.g. * <xs:complexType name="baseType1"> <xs:attributeGroup ref="tns:attgroup1"></xs:attributeGroup> </xs:complexType> <xs:complexType name="restrictedType1"> <xs:complexContent> <xs:restriction base ="tns:baseType1"> <xs:attribute name = "att1" type ="xs:string" use = "required"/> <xs:attribute name = "att2" type ="xs:string" use = "prohibited"/> <xs:attribute name = "att3" type ="xs:string" use = "optional"/> </xs:restriction> </xs:complexContent> </xs:complexType> <xs:attributeGroup name="attgroup1"> <xs:attribute name = "att1" type ="xs:string" use = "optional"/> <xs:attribute name = "att2" type ="xs:string" use = "optional"/> <xs:attribute name = "att3" type ="xs:string" use = "optional"/> <xs:attribute name = "att4" type ="xs:string" use = "optional"/> </xs:attributeGroup> */ // List<Attribute> attrs = restriction.getAttributeList(); // populateAttributesInMap(mapOfAttributes, attrs); // // List<AttributeGroup> attrGps = restriction.getAttributeGroup(); // populateAttributeGroupsInMap(mapOfAttributes, attrGps); return fields; } /** * This method handles the complex content extension. * If extension is used at complex content, then the base type would be another complex type. * So all the fields from the base complex type would be flattened to the current complex type. * It also handles if sequence, group are used. * * @param complexType * @param mapOfAttributes * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleComplexContentExtension(ComplexType complexType, Map<QName, Attribute> mapOfAttributes) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); Extension extension = complexType.getComplexContent().getExtension(); //handle base here QName complexTypeBase = extension.getBase(); //complex content extension base would definitely be a complex type which can have simple or complex content SchemaTypeName schemaTypeName = new SchemaTypeName( complexTypeBase ); ComplexType baseComplexType = (ComplexType) getComplexOrSimpleType( schemaTypeName ); fields.addAll( handleComplexTypeForFields(baseComplexType,mapOfAttributes) ); if(extension.getChoice() != null ){ fields.addAll( handleChoice(complexType, extension.getChoice()) ); } if( extension.getGroup() != null){ fields.addAll( handleGroup(complexType, extension.getGroup()) ); } if( extension.getSequence() != null){ fields.addAll( handleSequence(complexType, extension.getSequence()) ); } if( extension.getAll() != null ){ fields.addAll( handleAll(complexType, extension.getAll()) ); } List<Attribute> attrs = extension.getAttributeList(); populateAttributesInMap(mapOfAttributes, attrs); List<AttributeGroup> attrGps = extension.getAttributeGroup(); populateAttributeGroupsInMap(mapOfAttributes, attrGps); return fields; } /** * This method handles the simple content. * It checks the simple content uses restriction or extension. * Accordingly it handles restriction and extension. * * @param complexType * @param mapOfAttributes * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleSimpleContent(ComplexType complexType, Map<QName, Attribute> mapOfAttributes) throws ProtobufModelGenerationFailedException{ SimpleContent simpleContent = complexType.getSimpleContent(); List<ProtobufField> fields = new ArrayList<ProtobufField>(); if( simpleContent.getExtension() != null ){ fields.addAll( handleSimpleContentExtension(complexType, mapOfAttributes) ); } if( simpleContent.getRestriction() != null ){ fields.addAll( handleSimpleContentRestriction(complexType, mapOfAttributes) ); } return fields; } /** * This method handles Simple Content restriction. * The restriction base would definitely be a complex type with simple content * * @param complexType * @param mapOfAttributes * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleSimpleContentRestriction(ComplexType complexType, Map<QName, Attribute> mapOfAttributes) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); SimpleContent simpleContent = complexType.getSimpleContent(); SimpleTypeRestriction restriction = simpleContent.getRestriction(); QName complexTypeBase = restriction.getBase(); //simple content restriction base would definitely be a complex type which has simple content SchemaTypeName schemaTypeName = new SchemaTypeName( complexTypeBase ); ComplexType baseComplexType = (ComplexType)getComplexOrSimpleType( schemaTypeName ); if( baseComplexType != null ){ fields.addAll( handleComplexTypeForFields( baseComplexType, mapOfAttributes) ); } //no need to populate attributes from a simple content restriction //because the jaxb does not generate fields corresponding to attributes present in simple content restriction //jaxb generates fields corresponding to attributes only in base types. //consider the below example. //jaxb generated for baseComplex has fields corresponding to attributes //whereas jaxb generated for ActualComplex does not have fields corresponding to attributes /* * e.g. * <xs:complexType name="baseComplex"> <xs:simpleContent> <xs:extension base="tns:baseSimple"> <xs:attribute name = "att1" type ="xs:string" use = "required"/> <xs:attribute name = "att2" type ="xs:string" use = "optional"/> <xs:attribute name = "att3" type ="xs:string" use = "optional"/> <xs:attribute name = "att4" type ="xs:string" use = "optional"/> </xs:extension> </xs:simpleContent> </xs:complexType> <xs:complexType name="ActualComplex"> <xs:simpleContent> <xs:restriction base="tns:baseComplex"> <xs:attribute name = "att1" type ="xs:string" use = "required"/> <xs:attribute name = "att2" type ="xs:string" use = "prohibited"/> <xs:attribute name = "att3" type ="xs:string" use = "optional"/> </xs:restriction> </xs:simpleContent> </xs:complexType> */ // List<Attribute> attrs = simpleContent.getRestriction().getAttributes(); // populateAttributesInMap(mapOfAttributes, attrs); // // List<AttributeGroup> attrGps = simpleContent.getRestriction().getAttributeGroups(); // populateAttributeGroupsInMap(mapOfAttributes, attrGps); return fields; } /** * This method handles simple content extension. * The extension base would either be a complex type or an inbuilt xsd type. * * @param complexType * @param mapOfAttributes * @return * @throws ProtobufModelGenerationFailedException */ private List<ProtobufField> handleSimpleContentExtension(ComplexType complexType, Map<QName, Attribute> mapOfAttributes) throws ProtobufModelGenerationFailedException{ List<ProtobufField> fields = new ArrayList<ProtobufField>(); SimpleContent simpleContent = complexType.getSimpleContent(); QName base = complexType.getSimpleContent().getExtension().getBase(); //the base type can either be a complex type with simple content or a primitive type if(InBuiltType2ProtobufTypeMap.isValidInBuiltType(base)){ ProtobufField baseField = new ProtobufField(); baseField.setFieldModifier( ProtobufFieldModifier.OPTIONAL ); baseField.setFieldName( FIELD_NAME_FOR_SIMPLE_CONTENT_EXTENSION_TYPE ); baseField.setConvertedFieldName( MapperUtils.deriveFieldName( FIELD_NAME_FOR_SIMPLE_CONTENT_EXTENSION_TYPE ) ); baseField.setXsdTypeName( base ); baseField.setTypeOfField( MapperUtils.getProtobufFieldType( base ) ); fields.add( baseField ); }else{ SchemaTypeName baseType = new SchemaTypeName( base ); SchemaType baseSchemaType = getComplexOrSimpleType(baseType); if( baseSchemaType instanceof ComplexType){ ComplexType baseComplexType = (ComplexType)baseSchemaType; fields.addAll( handleComplexTypeForFields(baseComplexType, mapOfAttributes) ); }else if( baseSchemaType instanceof SimpleType ){ SimpleType baseSimpleType = (SimpleType)baseSchemaType; fields.addAll( handleSimpleType( baseSimpleType ) ); } } List<Attribute> attrs = simpleContent.getExtension().getAttributes(); populateAttributesInMap(mapOfAttributes, attrs); List<AttributeGroup> attrGps = simpleContent.getExtension().getAttributeGroups(); populateAttributeGroupsInMap(mapOfAttributes, attrGps); return fields; } private List<ProtobufField> handleSimpleType(SimpleType simpleType){ List<ProtobufField> fields = new ArrayList<ProtobufField>(); ProtobufField field = new ProtobufField(); field.setFieldModifier( ProtobufFieldModifier.OPTIONAL ); field.setFieldName( FIELD_NAME_FOR_SIMPLE_CONTENT_EXTENSION_TYPE ); field.setConvertedFieldName( MapperUtils.deriveFieldName( FIELD_NAME_FOR_SIMPLE_CONTENT_EXTENSION_TYPE ) ); getInstanceProvider().getElementTypeMapper().populateFieldFromSimpleType(simpleType, field); fields.add(field); return fields; } private void populateAttributeGroupsInMap(Map<QName, Attribute> mapOfAttributes, List<AttributeGroup> attributeGps){ for( AttributeGroup attr : attributeGps){ AttributeGroupType gpType = getArrtibuteGroupType( attr ); populateAttributesInMap(mapOfAttributes, gpType.getAttributes()); } } private void populateAttributesInMap(Map<QName, Attribute> mapOfAttributes, List<Attribute> attributes){ for( Attribute attr : attributes){ if( attr.getUse() == Attribute.AttributeUse.PROHIBHITED ){ continue; } if( attr.getAttributeQName() != null ){ mapOfAttributes.put(attr.getAttributeQName(), attr); }else{ mapOfAttributes.put( attr.getAttributeRef() , attr); } } } /** * Following method compares choice tag and element tag for attributes like min occurs and maxoccurs * and decides the modifier for field. * If an element is defined inside choice tag, then the modifier can only be either repeated or optional. * It can never be Required. * * Uses following logic to compare. * If either element tag or choice tag is repeated then the final value taken is repeated. * If none of them is repeated, then value taken is optional. * @param choiceEl * @param field */ private void compareAndUpdateModifier(Choice choiceEl, ProtobufField field){ ProtobufFieldModifier oldValue = field.getFieldModifier(); ProtobufFieldModifier newValue = MapperUtils.getModifier(choiceEl.getMinOccurs(), choiceEl.getMaxOccurs()); if ( (ProtobufFieldModifier.REPEATED == newValue) || (ProtobufFieldModifier.REPEATED == oldValue) ) { field.setFieldModifier( ProtobufFieldModifier.REPEATED ); }else { field.setFieldModifier( ProtobufFieldModifier.OPTIONAL ); } } /** * Following method compares sequence tag and element tag for attributes like min occurs and maxoccurs * and decides the modifier for the field. * * Uses following logic to compare. * If either element tag or choice tag is repeated then the final value taken is repeated. * If none of them is repeated and if either of them is optional then final value is optional. * Final value would be required only if both of them is required. * * @param seqEl * @param field */ private void compareAndUpdateModifier(Sequence seqEl, ProtobufField field){ ProtobufFieldModifier oldValue = field.getFieldModifier(); ProtobufFieldModifier newValue = MapperUtils.getModifier(seqEl.getMinOccurs(), seqEl.getMaxOccurs()); if ( (ProtobufFieldModifier.REPEATED == newValue) || (ProtobufFieldModifier.REPEATED == oldValue) ) { field.setFieldModifier( ProtobufFieldModifier.REPEATED ); }else if( (ProtobufFieldModifier.OPTIONAL == newValue) || (ProtobufFieldModifier.OPTIONAL == oldValue) ){ field.setFieldModifier( ProtobufFieldModifier.OPTIONAL ); }else if( (ProtobufFieldModifier.REQUIRED == newValue) && (ProtobufFieldModifier.REQUIRED == oldValue) ){ field.setFieldModifier( ProtobufFieldModifier.REQUIRED ); } } /** * Updates the modifier for an element present inside all. * Schema definition says, an element which is inside all, cannot have maxoccurs > 1. * So the modifier of an element can either be Required or OPTIONAL. * If it is required, all has minoccurs=0, then make the modifier as OPTIONAL * * @param all * @param field */ private void compareAndUpdateModifier(SchemaAll all, ProtobufField field){ ProtobufFieldModifier oldValue = field.getFieldModifier(); ProtobufFieldModifier newValue = MapperUtils.getModifier(all.getMinOccurs(), all.getMaxOccurs()); if ( (ProtobufFieldModifier.OPTIONAL == newValue) && (ProtobufFieldModifier.REQUIRED == oldValue) ) { field.setFieldModifier( ProtobufFieldModifier.OPTIONAL ); } } }