/**
*
*/
package org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager;
import org.ebayopensource.turmeric.tools.codegen.CodeGenContext;
import org.ebayopensource.turmeric.tools.codegen.InputOptions;
import org.ebayopensource.turmeric.tools.codegen.external.WSDLOperationType;
import org.ebayopensource.turmeric.tools.codegen.external.WSDLUtil;
import org.ebayopensource.turmeric.tools.codegen.external.wsdl.parser.schema.AttributeGroupType;
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.GroupType;
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.ProtobufEnumMessage;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufField;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufFieldType;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufMessage;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufOption;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufOptionType;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.ProtobufSchema;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.SchemaTypeMap;
import org.ebayopensource.turmeric.tools.codegen.fastserformat.protobuf.model.SchemaTypeName;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenConstants;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenUtil;
/**
* @author rkulandaivel
*
*/
public class ProtobufSchemaMapper {
private static final String DOT_PROTO_FILE_EXTENSION = ".proto";
private static final String PROTO_RELATIVE_PATH = CodeGenUtil.toOSFilePath("META-INF/soa/services/proto/");
private static final ProtobufSchemaMapper s_instance = new ProtobufSchemaMapper();
private static Logger s_logger = LogManager.getInstance(ProtobufSchemaMapper.class);
private ProtobufSchemaMapper(){
}
public static ProtobufSchemaMapper getInstance(){
return s_instance;
}
private static Logger getLogger(){
return s_logger;
}
public ProtobufSchema createProtobufSchema(List<SchemaType> schemaTypes,
CodeGenContext codeGenContext) throws ProtobufModelGenerationFailedException{
ProtobufSchema schema = new ProtobufSchema();
schema.setDotprotoFileName( codeGenContext.getServiceAdminName() + DOT_PROTO_FILE_EXTENSION);
Map<String, String> ns2PkgMap = getNS2PkgMap(codeGenContext);
String serviceNamesapce = codeGenContext.getNamespace();
String pkfFromServiceNamespace = getPackageFromNamespace(serviceNamesapce, ns2PkgMap);
schema.setDotprotoFilePackage( pkfFromServiceNamespace );
SchemaTypeMap schemaTypeMap = SchemaTypeMap.createSchemaTypeMapFromList(schemaTypes);
MapperInstanceProvider mapperInstanceProvider = MapperInstanceProvider
.createMapperInstanceProvider(schemaTypeMap);
Map<SchemaTypeName, ProtobufMessage> protoMessageMap = createProtoMessageForEachSchemaType(
schemaTypeMap, mapperInstanceProvider, codeGenContext);
populateClassNamesForAllMessages(schema, protoMessageMap,
pkfFromServiceNamespace, codeGenContext, ns2PkgMap);
schema.getMessages().addAll( protoMessageMap.values() );
ProtobufOption option = new ProtobufOption();
option.setOptionType( ProtobufOptionType.JAVA_PACKAGE_NAME );
option.setOptionValue(pkfFromServiceNamespace + ".proto");
schema.getDotprotoOptions().add( option );
option = new ProtobufOption();
option.setOptionType( ProtobufOptionType.JAVA_OUTER_CLASS_NAME );
option.setOptionValue(codeGenContext.getServiceAdminName());
schema.getDotprotoOptions().add( option );
option = new ProtobufOption();
option.setOptionType( ProtobufOptionType.OPTIMIZE_FOR );
option.setOptionValue( "SPEED" );
schema.getDotprotoOptions().add( option );
//Following block finds the destination directory for dot proto file.
//The path is <ProjectRoot>/meta-src/META-INF/soa/services/proto/<ServiceAdminName>/ServiceAdminName.proto.
String destRootPath = null;
InputOptions inputOptions = codeGenContext.getInputOptions();
String projectRoot = inputOptions.getProjectRoot();
if(!CodeGenUtil.isEmptyString(projectRoot)){
destRootPath = projectRoot;
}
else {
destRootPath = inputOptions.getDestLocation();
}
//inputOptions.getDestLocation() would never be empty
//this block of code can be depricated but required for turmeric
if(CodeGenUtil.isEmptyString(destRootPath) ){
String mDestRootPath = CodeGenUtil.toOSFilePath( codeGenContext.getMetaSrcDestLocation() );
String genMetaSrcPath = CodeGenUtil.toOSFilePath( CodeGenConstants.GEN_META_SRC_FOLDER );
String metaSrcPath = CodeGenUtil.toOSFilePath( CodeGenConstants.META_SRC_FOLDER );
//the mdest path might end with "gen-meta-src" so remove it
int indexOfMetaSrcPath = mDestRootPath.indexOf( genMetaSrcPath );
//the mdest path might end with "meta-src" so remove it
if( indexOfMetaSrcPath < 0 ){
indexOfMetaSrcPath = mDestRootPath.indexOf( metaSrcPath );
}
//impossible case. still have a check to avoid runtime exception
//the mdest path does not end with gen-meta-src or meta-src. so ignore it
if( indexOfMetaSrcPath < 0 ){
indexOfMetaSrcPath = mDestRootPath.length();
}
destRootPath = mDestRootPath.substring(0, indexOfMetaSrcPath);
}
String metaSrcPath = CodeGenUtil.toOSFilePath(destRootPath) + CodeGenConstants.META_SRC_FOLDER;
String inputPath = CodeGenUtil.toOSFilePath( metaSrcPath );// CodeGenUtil.toOSFilePath( codeGenContext.getMetaSrcDestLocation() );
inputPath = inputPath + PROTO_RELATIVE_PATH + inputOptions.getServiceAdminName() ;
inputPath = CodeGenUtil.toOSFilePath( inputPath ) ;
try {
CodeGenUtil.createDir(inputPath);
schema.setDotprotoTargetDir(inputPath);
} catch (IOException e) {
throw new ProtobufModelGenerationFailedException("Target Directory creation failed for proto destination path", e);
}
return schema;
}
private Map<SchemaTypeName, ProtobufMessage> createProtoMessageForEachSchemaType(
SchemaTypeMap schemaTypeMap,
MapperInstanceProvider mapperInstanceProvider,
CodeGenContext codeGenContext) throws ProtobufModelGenerationFailedException{
Map<SchemaTypeName, ProtobufMessage> protoMessageMap = new HashMap<SchemaTypeName, ProtobufMessage>();
createProtoMessageForEachSchemaType(schemaTypeMap
.getAllComplexAndSimpleTypes(), mapperInstanceProvider,
codeGenContext, protoMessageMap);
createProtoMessageForEachSchemaType(schemaTypeMap.getAllElementTypes(), mapperInstanceProvider,
codeGenContext, protoMessageMap);
return protoMessageMap;
}
private void createProtoMessageForEachSchemaType(
Map<SchemaTypeName, SchemaType> schemaTypeMap,
MapperInstanceProvider mapperInstanceProvider,
CodeGenContext codeGenContext,
Map<SchemaTypeName, ProtobufMessage> protoMessageMap) throws ProtobufModelGenerationFailedException{
for(Map.Entry<SchemaTypeName, SchemaType> entry : schemaTypeMap.entrySet()){
SchemaTypeName schemaTypeName = entry.getKey();
ProtobufMessage protoMessage = createProtoMessage( entry.getKey(), entry.getValue(), mapperInstanceProvider );
if(protoMessage != null){
protoMessageMap.put(schemaTypeName, protoMessage);
}
}
}
private ProtobufMessage createProtoMessage(SchemaTypeName schemaTypeName, SchemaType schemaType, MapperInstanceProvider mapperInstanceProvider) throws ProtobufModelGenerationFailedException{
ProtobufMessage protoMessage = null;
if( (schemaType instanceof AttributeGroupType) ||
(schemaType instanceof GroupType) ){
return null;
}
if(schemaType instanceof ComplexType){
ComplexType complexType = (ComplexType)schemaType;
if( complexType.isAbstract() ){
return null;
}
protoMessage = mapperInstanceProvider.getComplexTypeMapper().createProtobufMessage(complexType);
}else if( schemaType instanceof SimpleType){
SimpleType simpleType = (SimpleType)schemaType;
if( (simpleType.getRestriction() != null) && (simpleType.getRestriction().getEnumerations().size() > 0) ){
protoMessage = mapperInstanceProvider.getSimpleTypeMapper().createEnumProtoMessage(schemaTypeName, simpleType);
}
}else if( schemaType instanceof ElementType ){
ElementType elementType = (ElementType)schemaType;
if(elementType.getElementType() == null){
if( elementType.hasSimpleType() ){
protoMessage = createProtoMessage( schemaTypeName, elementType.getSimpleType(), mapperInstanceProvider);
}else if( elementType.hasComplexType() ){
protoMessage = createProtoMessage( schemaTypeName, elementType.getComplexType(), mapperInstanceProvider);
}
}
}
return protoMessage;
}
private void populateClassNamesForAllMessages(ProtobufSchema schema,
Map<SchemaTypeName, ProtobufMessage> protoMessageMap,
String pkfFromServiceNamespace,
CodeGenContext codeGenContext, Map<String, String> ns2PkgMap) {
String jprotoPackage = pkfFromServiceNamespace + ".proto";
String eprotoPackage = jprotoPackage + ".extended";
String jprotoOuterClassName = jprotoPackage + "." + codeGenContext.getServiceAdminName();
schema.setJProtoOuterClassName(jprotoOuterClassName);
Set<QName> rootTypes = new HashSet<QName>();
getLogger().log(Level.INFO, "Input file path "+codeGenContext.getInputOptions());
Map<String, WSDLOperationType> wsdlOperations = WSDLUtil
.getWSDLOparations(codeGenContext.getInputOptions().getInputFile(), codeGenContext);
for( WSDLOperationType operationType : wsdlOperations.values() ){
if( operationType.getInMessage() != null ){
QName typeName = operationType.getInMessage().getSchemaTypeQName();
rootTypes.add(typeName);
}
if( operationType.getOutMessage() != null ){
QName typeName = operationType.getOutMessage().getSchemaTypeQName();
rootTypes.add(typeName);
}
//TODO what about faults?
}
for(Map.Entry<SchemaTypeName, ProtobufMessage> entry : protoMessageMap.entrySet()){
SchemaTypeName typeName = entry.getKey();
ProtobufMessage message = entry.getValue();
QName currTypeQName = typeName.getTypeName();
String NS = currTypeQName.getNamespaceURI();
String pkg = getPackageFromNamespace(NS, ns2PkgMap );
if (CodeGenUtil.isEmptyString(pkg)) {
pkg = WSDLUtil.getPackageFromNamespace(NS);
}
String messageName = message.getMessageName();
String javaTypeFullName = pkg + "." + messageName ;
message.setJaxbClassName(javaTypeFullName);
message.setEprotoClassName(eprotoPackage + ".E"+ messageName);
message.setJprotoClassName(jprotoOuterClassName + "$" + messageName);
if(message.isEnumType()){
message.setJprotoClassName( message.getJprotoClassName() + "Enum$" + messageName);
}
if(rootTypes.contains(currTypeQName) ){
message.setRootType(true);
}
for(ProtobufField field : message.getFields()){
QName xsdType = field.getXsdTypeName();
if(field.getTypeOfField() == ProtobufFieldType.COMPLEX_TYPE){
SchemaTypeName complexTypeName = new SchemaTypeName(xsdType);
ProtobufMessage complexTypeMessage = protoMessageMap.get(complexTypeName);
field.setProtobufTypeName( complexTypeMessage.getMessageName() );
}else if(field.getTypeOfField() == ProtobufFieldType.ENUM_TYPE){
SchemaTypeName enumTypeName = new SchemaTypeName(xsdType);
ProtobufEnumMessage enumTypeMessage = (ProtobufEnumMessage)protoMessageMap.get(enumTypeName);
field.setProtobufTypeName( enumTypeMessage.getEnumMessageName() + "." + enumTypeMessage.getMessageName() );
}else if(InBuiltType2ProtobufTypeMap.isValidInBuiltType( xsdType ) ){
field.setProtobufTypeName( InBuiltType2ProtobufTypeMap.getProtoType(xsdType) );
}
}
}
}
private Map<String, String> getNS2PkgMap(CodeGenContext codeGenCtx) {
return WSDLUtil.getNS2PkgMappings(codeGenCtx.getInputOptions());
}
private String getPackageFromNamespace( String nameSpace, Map<String, String> ns2PkgMap){
String pkg = ns2PkgMap.get(nameSpace);
if (CodeGenUtil.isEmptyString(pkg)) {
pkg = WSDLUtil.getPackageFromNamespace(nameSpace);
}
return pkg;
}
}