package org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf;
import java.math.BigDecimal;
import java.util.Map;
import javax.xml.namespace.QName;
import org.ebayopensource.turmeric.tools.codegen.external.WSDLUtil;
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.SchemaType;
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.model.ProtobufFieldModifier;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufFieldType;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.SchemaTypeName;
import com.sun.codemodel.JJavaName;
public class MapperUtils {
public static final BigDecimal LONG_MIN = BigDecimal.valueOf(Long.MIN_VALUE);
public static final BigDecimal LONG_MAX = BigDecimal.valueOf(Long.MAX_VALUE);
public static final BigDecimal INT_MIN = BigDecimal.valueOf(Integer.MIN_VALUE);
public static final BigDecimal INT_MAX = BigDecimal.valueOf(Integer.MAX_VALUE);
/**
* This method is used by all types protobuf model mapper to derive the message name.
* @param typeName
* @return
*/
public static String deriveMessageNameFromQName(QName typeName){
return WSDLUtil.getXMLIdentifiersClassName(typeName.getLocalPart());
}
public static String deriveFieldName(String fieldName){
//lets say the element names in WSDL are thisIsXYName and iNAME
//Our aim is create proto field name same as field name in jaxb POJO.
//JAXB first converts each element name into a java property name.( result: ThisIsXYName, INAME )
//it then converts property name into getter method name, setter method name, field name.
//( result of field name : thisIsXYName and iname )
//hence here same util methods used by JAXB are called.
String derivedFieldName = com.sun.xml.bind.api.impl.NameConverter.standard.toPropertyName(fieldName);
derivedFieldName = com.sun.xml.bind.api.impl.NameConverter.standard.toVariableName( derivedFieldName );
if(!JJavaName.isJavaIdentifier ( derivedFieldName )){
derivedFieldName = '_'+derivedFieldName; // avoid colliding with the reserved names like 'abstract'.
}
return derivedFieldName;
}
/**
* This method uses the utility method used by jaxb to derive the enum constant name for the given enum name.
* This is done to achieve the enum name in dot proto file is same as the enum name in jaxb class.
* @param enumName
* @return
*/
public static String deriveEnumConstantName(String enumName){
return com.sun.xml.bind.api.impl.NameConverter.standard.toConstantName(enumName);
}
/**
* This method finds the attribute group type based on the given attribute group ref.
*
* @param schemaTypeMap
* @param attributeGroup
* @return
*/
public static AttributeGroupType findAttributeGroupType( Map<SchemaTypeName, SchemaType> schemaTypeMap, AttributeGroup attributeGroup ){
QName ref = attributeGroup.getGroupRef();
if( ref == null ){
return null;
}
SchemaTypeName refName = new SchemaTypeName( ref );
return (AttributeGroupType)schemaTypeMap.get( refName );
}
/**
* This method can only be used if it is a valid xsd type.
* This method returns the type of field type.
* @param type
* @return
*/
public static ProtobufFieldType getProtobufFieldType(QName type){
ProtobufFieldType fieldType = null;
if(InBuiltType2ProtobufTypeMap.isValidInBuiltType(type)){
if( InBuiltType2ProtobufTypeMap.QNAME_TYPE.equals(type) ){
fieldType = ProtobufFieldType.QNAME_TYPE;
}else if( InBuiltType2ProtobufTypeMap.DURATION_TYPE.equals(type) ){
fieldType = ProtobufFieldType.DURATION_TYPE;
}else if( InBuiltType2ProtobufTypeMap.DATE_TYPE.equals(type)
|| InBuiltType2ProtobufTypeMap.DATETIME_TYPE.equals(type)
|| InBuiltType2ProtobufTypeMap.TIME_TYPE.equals(type)
|| InBuiltType2ProtobufTypeMap.GYEARMONTH_TYPE.equals(type)
|| InBuiltType2ProtobufTypeMap.GYEAR_TYPE.equals(type)
|| InBuiltType2ProtobufTypeMap.GMONTHDAY_TYPE.equals(type)
|| InBuiltType2ProtobufTypeMap.GDAY_TYPE.equals(type)
|| InBuiltType2ProtobufTypeMap.GMONTH_TYPE.equals(type)
){
fieldType = ProtobufFieldType.DATE_TYPE;
}else{
fieldType = ProtobufFieldType.INBUILT_TYPE;
}
}
return fieldType;
}
/**
* This method decides the modifier for the given min occurs and max occurs combination.
* However, this is not final. because an element with minoccurs=1 and maxoccurs=1 is required if it
* is present inside sequence tag but is optional if it is present inside choice tag.
*
* So the complex type mapper, takes care of overwriting the field modifier.
*
* @param minOccurs
* @param maxOccurs
* @return
*/
public static ProtobufFieldModifier getModifier(int minOccurs, int maxOccurs){
if(maxOccurs == 1 && minOccurs == 0){
return ProtobufFieldModifier.OPTIONAL;
}
if(maxOccurs == 1 && minOccurs == 1){
return ProtobufFieldModifier.REQUIRED;
}
if(maxOccurs > 1 && minOccurs >= 0){
return ProtobufFieldModifier.REPEATED;
}
return ProtobufFieldModifier.OPTIONAL;
}
/**
* Returns value only for special in-built types which are by default list like nmtokens.
*
* @param type
* @return
*/
public static ProtobufFieldModifier getModifierForSpecialInBuiltType(QName type){
if( InBuiltType2ProtobufTypeMap.NMTOKENS_TYPE.equals(type) ){
return ProtobufFieldModifier.REPEATED;
}
if( InBuiltType2ProtobufTypeMap.ENTITIES_TYPE.equals(type) ){
return ProtobufFieldModifier.REPEATED;
}
return null;
}
/**
* This method classifies the built in type especially types like Integer or Long.
* If the simple type is configured with facets like minExclusive, maxExclusive, minInclusive or maxInclusive
* then based on the values configured the types are determined.
*
* @param builtInType
* @param surroundingSimpleType
* @return
*/
public static QName classifyBuiltInType(QName builtInType, SimpleType surroundingSimpleType ){
QName INTEGER_TYPE = InBuiltType2ProtobufTypeMap.INTEGER_TYPE;
QName LONG_TYPE = InBuiltType2ProtobufTypeMap.LONG_TYPE;
QName INT_TYPE = InBuiltType2ProtobufTypeMap.INT_TYPE;
QName clasifiedType = builtInType;
if( surroundingSimpleType == null ){
//nothing to do
}else if( INTEGER_TYPE.equals( builtInType ) || LONG_TYPE.equals( builtInType ) ){
SimpleTypeRestriction sTypeRest = surroundingSimpleType.getRestriction();
if( sTypeRest != null ){
BigDecimal xe = null, xi = null;
if( sTypeRest.hasMaxExclusive() ){
xe = sTypeRest.getMaxExclusive().subtract( BigDecimal.ONE ) ;
}
if( sTypeRest.hasMaxInclusive() ){
xi = sTypeRest.getMaxInclusive();
}
BigDecimal max = min(xe,xi); // most restrictive one takes precedence
if(max!=null) {
BigDecimal ne = null, ni = null;
if( sTypeRest.hasMinExclusive() ){
ne = sTypeRest.getMinExclusive().add( BigDecimal.ONE ) ;
}
if( sTypeRest.hasMaxInclusive() ){
ni = sTypeRest.getMinInclusive();
}
BigDecimal min = max(ne,ni);
if(min!=null) {
if(min.compareTo(INT_MIN )>=0 && max.compareTo(INT_MAX )<=0){
clasifiedType = INT_TYPE; //typeLocalName = "int";
}else if(min.compareTo(LONG_MIN)>=0 && max.compareTo(LONG_MAX)<=0){
clasifiedType = LONG_TYPE; //typeLocalName = "long";
}
}
}
}
}
return clasifiedType;
}
/**
* Returns the minimum value between the two.
* @param a
* @param b
* @return
*/
private static BigDecimal min(BigDecimal a, BigDecimal b) {
if(a==null) return b;
if(b==null) return a;
return a.min(b);
}
/**
* Returns the maximum value between the two.
* @param a
* @param b
* @return
*/
private static BigDecimal max(BigDecimal a, BigDecimal b) {
if(a==null) return b;
if(b==null) return a;
return a.max(b);
}
}