/*************************************************************************** * Copyright (C) 2010 by Mirco Gamberini * * Copyright (C) 2010 by Fabrizio Montesi <famontesi@gmail.com> * * Copyright (C) 2012 by Claudio Guidi <cguidi@italianasoftware.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * For details about the authors of this software, see the AUTHORS file. * ***************************************************************************/ package jolie.xml.xsd.impl; import com.sun.xml.xsom.XSComplexType; import com.sun.xml.xsom.XSContentType; import com.sun.xml.xsom.XSElementDecl; import com.sun.xml.xsom.XSModelGroup; import com.sun.xml.xsom.XSModelGroupDecl; import com.sun.xml.xsom.XSParticle; import com.sun.xml.xsom.XSRestrictionSimpleType; import com.sun.xml.xsom.XSSchemaSet; import com.sun.xml.xsom.XSSimpleType; import com.sun.xml.xsom.XSTerm; import com.sun.xml.xsom.XSType; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import jolie.lang.Constants; import jolie.lang.NativeType; import jolie.lang.parse.OLVisitor; import jolie.lang.parse.context.ParsingContext; import jolie.lang.parse.ast.types.TypeDefinition; import jolie.lang.parse.ast.types.TypeDefinitionLink; import jolie.lang.parse.ast.types.TypeInlineDefinition; import jolie.lang.parse.context.URIParsingContext; import jolie.util.Range; import jolie.xml.xsd.XsdToJolieConverter; import jolie.xml.xsd.XsdUtils; /** * @author Mirco Gamberini * @author Fabrizio Montesi */ public class XsdToJolieConverterImpl implements XsdToJolieConverter { private final Logger logger; private final List<TypeDefinition> jolieTypes = new ArrayList<TypeDefinition>(); private final static ParsingContext parsingContext = URIParsingContext.DEFAULT; private final boolean strict; private final XSSchemaSet schemaSet; private final Map<String, TypeDefinition> complexTypes = new HashMap<String, TypeDefinition>(); private final Map<String, TypeDefinition> simpleTypes = new HashMap<String, TypeDefinition>(); private final ArrayList<String> complexTypeNames = new ArrayList<String>(); //private final ArrayList<String> simpleTypeNames = new ArrayList<String>() private static final String XMLSCHEMA_URI = "http://www.w3.org/2001/XMLSchema"; private static final String XMLSOAPSCHEMA_URI = "http://schemas.xmlsoap.org/soap/encoding/"; private static final String WARNING_1 = "Element type does not exist."; private static final String WARNING_2 = "The referred type is not defined or is not supported by JOLIE."; private static final String ERROR_SIMPLE_TYPE = "\nERROR: ConversionException\n"; private static final String WARNING_SIMPLE_TYPE = "The following simple type can not be converted to a suitable JOLIE type: "; private static final String WARNING_CONVERT_STRING = "Simple type converted to \"string\" for compatibility reasons."; private static final String ERROR_CHOICE = "Element \"choice\" is unsupported by JOLIE. Consider using \"all\" instead."; private static final String WARNING_SEQUENCE = "Element \"sequence\" is unsupported by JOLIE."; private static final String WARNING_DEFAULT_ATTRIBUTE = "Attribute \"default\" is unsupported by JOLIE."; private static final String WARNING_FIXED_ATTRIBUTE = "Attribute \"fixed\" is unsupported by JOLIE."; public static final String TYPE_SUFFIX = "Type"; /* * Constructor. * @param schemaSet the schema set to convert. * @param strict {@code true} if encountering elements unsupported by JOLIE should raise an exception, {@code false} if they should just raise a warning message. */ public XsdToJolieConverterImpl( XSSchemaSet schemaSet, boolean strict, Logger logger ) { this.strict = strict; this.schemaSet = schemaSet; this.logger = logger; } private boolean checkSkippedTypes( String typeName, String targetNameSpace ) { boolean flag = false; if ( targetNameSpace.equals( XMLSCHEMA_URI ) || targetNameSpace.equals( XMLSOAPSCHEMA_URI )) { if ( typeName.equals( "unsignedShort" ) || typeName.equals( "long" ) || typeName.equals( "date" ) || typeName.equals( "float" ) || typeName.equals( "short" ) || typeName.equals( "nonNegativeInteger" ) || typeName.equals( "time" ) || typeName.equals( "base64Binary" ) || typeName.equals( "unsignedShort" ) || typeName.equals( "gMonthDay" ) || typeName.equals( "gYeardDay" ) || typeName.equals( "gDay" ) || typeName.equals( "gMonth" ) || typeName.equals( "gYearMonth" ) || typeName.equals( "gYear" ) || typeName.equals( "dateTime" ) || typeName.equals( "nonPositiveInteger" ) || typeName.equals( "anyURI" ) || typeName.equals( "byte" ) || typeName.equals( "hexBinary" ) || typeName.equals( "boolean" ) || typeName.equals( "negativeInteger" ) || typeName.equals( "long" ) || typeName.equals( "unsignedByte" ) || typeName.equals( "integer" ) || typeName.equals( "int" ) || typeName.equals( "unsignedInt" ) || typeName.equals( "normalizedString" ) || typeName.equals( "double" ) || typeName.equals( "decimal" ) || typeName.equals( "positiveInteger" ) || typeName.equals( "duration" ) || typeName.equals( "string" ) || typeName.equals( "unsignedLong" ) || typeName.equals( "base64" ) || typeName.equals( "anyType" ) || typeName.equals( "anySimpleType" ) || typeName.equals( "ENTITIES" ) || typeName.equals( "ENTITY" ) || typeName.equals( "ID" ) || typeName.equals( "IDREF" ) || typeName.equals( "IDREFS" ) || typeName.equals( "language" ) || typeName.equals( "Name" ) || typeName.equals( "NCName" ) || typeName.equals( "NMTOKEN" ) || typeName.equals( "NMTOKENS" ) || typeName.equals( "normalizedString" ) || typeName.equals( "QName" ) || typeName.equals( "token" ) ) { flag = true; } } return flag; } public List<TypeDefinition> convert() throws ConversionException { // creating type name lists Iterator complexNameIter = schemaSet.iterateComplexTypes(); while( complexNameIter.hasNext() ) { XSComplexType complexType = (XSComplexType) complexNameIter.next(); if ( complexType.getContentType().asSimpleType() == null && !checkSkippedTypes( complexType.getName(), complexType.getTargetNamespace() ) ) { // avoinding simple type insertion complexTypeNames.add( complexType.getName() + TYPE_SUFFIX ); } } // Load simple types Iterator simpleIter = schemaSet.iterateSimpleTypes(); while( simpleIter.hasNext() ) { XSSimpleType simpleType = (XSSimpleType) simpleIter.next(); if ( !checkSkippedTypes( simpleType.getName(), simpleType.getTargetNamespace() ) ) { TypeDefinition jolieSimpleType; jolieSimpleType = loadSimpleType( simpleType, false, simpleTypes.get( simpleType.getName() + TYPE_SUFFIX ) ); simpleTypes.put( jolieSimpleType.id(), jolieSimpleType ); jolieTypes.add( jolieSimpleType ); } } // Load complex types Iterator complexIter = schemaSet.iterateComplexTypes(); while( complexIter.hasNext() ) { XSComplexType complexType = (XSComplexType) complexIter.next(); if ( complexType.getContentType().asSimpleType() == null && !checkSkippedTypes( complexType.getName(), complexType.getTargetNamespace() ) ) { TypeDefinition jolieComplexType; if ( complexTypes.containsKey( complexType.getName() + TYPE_SUFFIX ) ) { // lazy type jolieComplexType = loadComplexType( complexType, true, complexTypes.get( complexType.getName() + TYPE_SUFFIX ) ); } else { jolieComplexType = loadComplexType( complexType, false, complexTypes.get( complexType.getName() + TYPE_SUFFIX ) ); if ( jolieComplexType != null ) { complexTypes.put( jolieComplexType.id(), jolieComplexType ); } } if ( jolieComplexType != null ) { jolieTypes.add( jolieComplexType ); } } } // Load element types Iterator elementDeclIter = schemaSet.iterateElementDecls(); while( elementDeclIter.hasNext() ) { XSElementDecl element = (XSElementDecl) elementDeclIter.next(); XSType type = element.getType(); checkDefaultAndFixed( element ); if ( type == null ) { continue; } if ( type.getName() != null && type.getName().equals( "anyType" ) ) { continue; } if ( type.getName() != null ) { String fullName = type.getName() + TYPE_SUFFIX; // The element refers to a previously defined complex type if ( complexTypes.get( fullName ) != null && complexTypes.containsKey( element.getName() ) ) { TypeDefinition jolieType = new TypeDefinitionLink( parsingContext, element.getName(), Constants.RANGE_ONE_TO_ONE, complexTypes.get( fullName ) ); jolieTypes.add( jolieType ); continue; } // The element refers to a previously defined simple type if ( simpleTypes.get( fullName ) != null && simpleTypes.containsKey( element.getName() ) == false ) { TypeDefinition jolieType = new TypeDefinitionLink( parsingContext, element.getName(), Constants.RANGE_ONE_TO_ONE, simpleTypes.get( fullName ) ); jolieTypes.add( jolieType ); continue; } } if ( type.isSimpleType() ) { // Element is a simple type checkForNativeType( type, WARNING_1 ); } else if ( type.isComplexType() ) { // Element is a complex type XSComplexType complexType = type.asComplexType(); XSParticle particle; XSContentType contentType; contentType = complexType.getContentType(); // The complex type does not contain sub elements if ( (particle = contentType.asParticle()) == null ) { //jolieTypes.add( createAnyOrUndefined( element.getName(), complexType ) ); continue; } if ( contentType.asSimpleType() != null ) { // Unsupported by JOLIE checkStrictModeForSimpleType( contentType ); } else if ( (particle = contentType.asParticle()) != null ) { XSTerm term = particle.getTerm(); XSModelGroupDecl modelGroupDecl = null; XSModelGroup modelGroup = null; modelGroup = getModelGroup( modelGroupDecl, term ); if ( modelGroup != null ) { TypeInlineDefinition jolieComplexType = createComplexType( complexType, element.getName(), particle ); groupProcessing( modelGroup, particle, jolieComplexType ); jolieTypes.add( jolieComplexType ); } } } else { log( Level.WARNING, "Found a type that is not simple nor complex." ); } } return jolieTypes; } private void groupProcessing( XSModelGroup modelGroup, XSParticle particle, TypeInlineDefinition jolieType ) throws ConversionException { XSModelGroup.Compositor compositor = modelGroup.getCompositor(); // We handle "all" and "sequence", but not "choice" if ( compositor.equals( XSModelGroup.ALL ) || compositor.equals( XSModelGroup.SEQUENCE ) ) { if ( compositor.equals( XSModelGroup.SEQUENCE ) ) { log( Level.WARNING, WARNING_SEQUENCE ); } XSParticle[] children = modelGroup.getChildren(); XSTerm currTerm; for( int i = 0; i < children.length; i++ ) { currTerm = children[i].getTerm(); if ( currTerm.isModelGroup() ) { groupProcessing( currTerm.asModelGroup(), particle, jolieType ); } else { // Create the new complex type for root types navigateSubTypes( children[i], jolieType ); } } } else if ( compositor.equals( XSModelGroup.CHOICE ) ) { throw new ConversionException( ERROR_CHOICE ); } } private void log( Level level, String message ) { if ( logger != null ) { logger.log( level, message ); } } private void navigateSubTypes( XSParticle parentParticle, TypeInlineDefinition jolieType ) throws ConversionException { XSTerm currTerm; currTerm = parentParticle.getTerm(); if ( currTerm.isElementDecl() ) { XSElementDecl currElementDecl; currElementDecl = currTerm.asElementDecl(); XSType type = currElementDecl.getType(); if ( type != null && (type.getName()) != null && complexTypeNames.contains( type.getName() + TYPE_SUFFIX ) ) { if ( complexTypes.get( type.getName() + TYPE_SUFFIX ) == null ) { // create lazy type TypeDefinition jolieLazyType = new TypeInlineDefinition( parsingContext, type.getName() + TYPE_SUFFIX, NativeType.ANY, Constants.RANGE_ONE_TO_ONE ); complexTypes.put( type.getName() + TYPE_SUFFIX, jolieLazyType ); } TypeDefinition jolieSimpleType = new TypeDefinitionLink( parsingContext, currElementDecl.getName(), getRange( parentParticle ), complexTypes.get( type.getName() + TYPE_SUFFIX ) ); jolieType.putSubType( jolieSimpleType ); } else if ( type != null && type.getName() != null && simpleTypes.get( type.getName() + TYPE_SUFFIX ) != null ) { /*if ( simpleTypes.get( type.getName() + TYPE_SUFFIX ) == null ) { // create lazy type TypeDefinition jolieLazyType = new TypeInlineDefinition( parsingContext, type.getName() + TYPE_SUFFIX, NativeType.ANY, Constants.RANGE_ONE_TO_ONE ); simpleTypes.put( type.getName() + TYPE_SUFFIX, jolieLazyType ); }*/ TypeDefinition jolieSimpleType = new TypeDefinitionLink( parsingContext, currElementDecl.getName(), getRange( parentParticle ), simpleTypes.get( type.getName() + TYPE_SUFFIX ) ); jolieType.putSubType( jolieSimpleType ); } else { checkDefaultAndFixed( currElementDecl ); if ( type.isSimpleType() ) { checkForNativeType( type, WARNING_2 ); if ( type.getName() != null && XsdUtils.xsdToNativeType( type.getName() ) != null ) { jolieType.putSubType( createSimpleType( type, currElementDecl, getRange( parentParticle ) ) ); } } else if ( type.isComplexType() ) { XSComplexType complexType = type.asComplexType(); XSParticle particle; XSContentType contentType; contentType = complexType.getContentType(); if ( (particle = contentType.asParticle()) == null ) { //jolieType.putSubType( createAnyOrUndefined( currElementDecl.getName(), complexType ) ); } if ( contentType.asSimpleType() != null ) { checkStrictModeForSimpleType( contentType ); } else if ( (particle = contentType.asParticle()) != null ) { XSTerm term = particle.getTerm(); XSModelGroupDecl modelGroupDecl = null; XSModelGroup modelGroup = null; modelGroup = getModelGroup( modelGroupDecl, term ); if ( modelGroup != null ) { TypeInlineDefinition jolieComplexType = createComplexType( complexType, currElementDecl.getName(), particle ); groupProcessing( modelGroup, particle, jolieComplexType ); jolieType.putSubType( jolieComplexType ); } } } } } } private TypeDefinition loadSimpleType( XSSimpleType simpleType, boolean lazy, TypeDefinition lazyType ) { // processing restrictions TypeInlineDefinition jolietype; if ( lazy ) { jolietype = (TypeInlineDefinition) lazyType; } else { if ( simpleType.isRestriction() ) { XSRestrictionSimpleType restriction = simpleType.asRestriction(); checkType( restriction.getBaseType() ); jolietype = new TypeInlineDefinition( parsingContext, simpleType.getName() + TYPE_SUFFIX, XsdUtils.xsdToNativeType( restriction.getBaseType().getName() ), Constants.RANGE_ONE_TO_ONE ); } else { log( Level.WARNING, "SimpleType not processed:" + simpleType.getName() ); jolietype = new TypeInlineDefinition( parsingContext, simpleType.getName(), NativeType.VOID, Constants.RANGE_ONE_TO_ONE ); } } return jolietype; } private TypeDefinition loadComplexType( XSComplexType complexType, boolean lazy, TypeDefinition lazyType ) throws ConversionException { XSParticle particle; XSContentType contentType; contentType = complexType.getContentType(); if ( (particle = contentType.asParticle()) == null ) { return null;//createAnyOrUndefined( complexType.getName(), complexType ); } TypeInlineDefinition jolieType; if ( lazy ) { jolieType = (TypeInlineDefinition) lazyType; } else { jolieType = createComplexType( complexType, complexType.getName() + TYPE_SUFFIX, particle ); } if ( contentType.asSimpleType() != null ) { checkStrictModeForSimpleType( contentType ); } else if ( (particle = contentType.asParticle()) != null ) { XSTerm term = particle.getTerm(); XSModelGroupDecl modelGroupDecl = null; XSModelGroup modelGroup = null; modelGroup = getModelGroup( modelGroupDecl, term ); if ( modelGroup != null ) { groupProcessing( modelGroup, particle, jolieType ); } } return jolieType; } private TypeInlineDefinition createAnyOrUndefined( String typeName, XSComplexType complexType ) { TypeInlineDefinition jolieType = new TypeInlineDefinition( parsingContext, typeName, NativeType.ANY, Constants.RANGE_ONE_TO_ONE ); if ( !complexType.isMixed() ) { jolieType.setUntypedSubTypes( true ); } return jolieType; } private void checkType( XSType type ) { if ( type.getName() != null && (type.getName().contains( "date" ) || type.getName().contains( "time" ) || type.getName().contains( "boolean" )) ) { log( Level.WARNING, WARNING_CONVERT_STRING + " Type: " + type.getName() ); } } /** * Emit an alert in case we find a "default" or "fixed" attribute */ private void checkDefaultAndFixed( XSElementDecl element ) { if ( element.getDefaultValue() != null ) { log( Level.WARNING, WARNING_DEFAULT_ATTRIBUTE + " Element: " + element.getName() ); } if ( element.getFixedValue() != null ) { log( Level.WARNING, WARNING_FIXED_ATTRIBUTE + " Element: " + element.getName() ); } } private TypeInlineDefinition createSimpleType( XSType type, XSElementDecl element, Range range ) { checkType( type ); return new TypeInlineDefinition( parsingContext, element.getName(), XsdUtils.xsdToNativeType( type.getName() ), range ); } private TypeInlineDefinition createComplexType( XSComplexType complexType, String typeName, XSParticle particle ) { if ( complexType.isMixed() ) { return new TypeInlineDefinition( parsingContext, typeName, NativeType.ANY, getRange( particle ) ); } else { return new TypeInlineDefinition( parsingContext, typeName, NativeType.VOID, getRange( particle ) ); } } private XSModelGroup getModelGroup( XSModelGroupDecl modelGroupDecl, XSTerm term ) { if ( (modelGroupDecl = term.asModelGroupDecl()) != null ) { return modelGroupDecl.getModelGroup(); } else if ( term.isModelGroup() ) { return term.asModelGroup(); } else { return null; } } private boolean strict() { return strict; } /** * Checks whether a native type for a given simple type is defined. */ private void checkForNativeType( XSType type, String msg ) throws ConversionException { if ( XsdUtils.xsdToNativeType( type.getName() ) == null ) { if ( !strict() ) { log( Level.WARNING, msg + " Name: " + type.getName() ); } else { throw new ConversionException( ERROR_SIMPLE_TYPE + msg + " Name: " + type.getName() ); } } } private void checkStrictModeForSimpleType( XSContentType contentType ) throws ConversionException { if ( !strict() ) { log( Level.WARNING, WARNING_SIMPLE_TYPE + contentType.asSimpleType().getName() ); } else { throw new ConversionException( ERROR_SIMPLE_TYPE + WARNING_SIMPLE_TYPE + contentType.asSimpleType().getName() ); } } private Range getRange( XSParticle part ) { int min = 1; int max = Integer.MAX_VALUE; if ( part.getMinOccurs() != -1 ) { min = part.getMinOccurs(); } if ( part.getMaxOccurs() != -1 ) { max = part.getMaxOccurs(); } return new Range( min, max ); } }