/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.extension.internal.capability.xml.schema.builder;
import static java.lang.String.format;
import static java.math.BigInteger.ONE;
import static java.math.BigInteger.ZERO;
import static java.util.Collections.singleton;
import static org.apache.commons.lang.StringUtils.EMPTY;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.mule.runtime.api.meta.ExpressionSupport.NOT_SUPPORTED;
import static org.mule.runtime.api.meta.ExpressionSupport.SUPPORTED;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.MAX_ONE;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.MULE_ABSTRACT_EXTENSION_TYPE;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.MULE_EXTENSION_NAMESPACE;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.MULE_EXTENSION_OPERATION_TRANSACTIONAL_ACTION_TYPE;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.MULE_EXTENSION_SCHEMA_LOCATION;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.MULE_SCHEMA_LOCATION;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.MULE_TLS_NAMESPACE;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.MULE_TLS_SCHEMA_LOCATION;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.SPRING_FRAMEWORK_NAMESPACE;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.SPRING_FRAMEWORK_SCHEMA_LOCATION;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.STRING;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.TLS_CONTEXT_TYPE;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.USE_OPTIONAL;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.USE_REQUIRED;
import static org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants.XML_NAMESPACE;
import static org.mule.runtime.extension.api.ExtensionConstants.TLS_PARAMETER_NAME;
import static org.mule.runtime.extension.api.declaration.type.TypeUtils.isContent;
import static org.mule.runtime.extension.api.util.ExtensionMetadataTypeUtils.getId;
import static org.mule.runtime.extension.api.util.ExtensionMetadataTypeUtils.isMap;
import static org.mule.runtime.extension.api.util.ExtensionModelUtils.isContent;
import static org.mule.runtime.extension.api.util.NameUtils.sanitizeName;
import static org.mule.runtime.internal.dsl.DslConstants.CORE_NAMESPACE;
import static org.mule.runtime.internal.dsl.DslConstants.NAME_ATTRIBUTE_NAME;
import static org.mule.runtime.internal.dsl.DslConstants.VALUE_ATTRIBUTE_NAME;
import static org.mule.runtime.module.extension.internal.capability.xml.schema.builder.ObjectTypeSchemaDelegate.getAbstractElementName;
import org.mule.metadata.api.ClassTypeLoader;
import org.mule.metadata.api.annotation.EnumAnnotation;
import org.mule.metadata.api.model.ArrayType;
import org.mule.metadata.api.model.MetadataType;
import org.mule.metadata.api.model.ObjectType;
import org.mule.metadata.api.model.StringType;
import org.mule.metadata.api.visitor.MetadataTypeVisitor;
import org.mule.runtime.api.dsl.DslResolvingContext;
import org.mule.runtime.api.meta.ExpressionSupport;
import org.mule.runtime.api.meta.model.ExtensionModel;
import org.mule.runtime.api.meta.model.ImportedTypeModel;
import org.mule.runtime.api.meta.model.ParameterDslConfiguration;
import org.mule.runtime.api.meta.model.XmlDslModel;
import org.mule.runtime.api.meta.model.config.ConfigurationModel;
import org.mule.runtime.api.meta.model.connection.ConnectionProviderModel;
import org.mule.runtime.api.meta.model.operation.OperationModel;
import org.mule.runtime.api.meta.model.parameter.ParameterGroupModel;
import org.mule.runtime.api.meta.model.parameter.ParameterModel;
import org.mule.runtime.api.meta.model.parameter.ParameterizedModel;
import org.mule.runtime.api.meta.model.source.SourceModel;
import org.mule.runtime.api.meta.type.TypeCatalog;
import org.mule.runtime.config.spring.dsl.api.xml.SchemaConstants;
import org.mule.runtime.core.util.StringUtils;
import org.mule.runtime.extension.api.declaration.type.ExtensionsTypeLoaderFactory;
import org.mule.runtime.extension.api.dsl.syntax.DslElementSyntax;
import org.mule.runtime.extension.api.dsl.syntax.resolver.DslSyntaxResolver;
import org.mule.runtime.extension.api.tx.OperationTransactionalAction;
import org.mule.runtime.extension.internal.property.InfrastructureParameterModelProperty;
import org.mule.runtime.extension.internal.property.QNameModelProperty;
import org.mule.runtime.extension.internal.util.ParameterModelComparator;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.Annotation;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.Attribute;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.Documentation;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.ExplicitGroup;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.ExtensionType;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.FormChoice;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.Import;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.LocalComplexType;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.LocalSimpleType;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.NoFixedFacet;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.ObjectFactory;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.Restriction;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.Schema;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.SchemaTypeConversion;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.TopLevelElement;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.TopLevelSimpleType;
import org.mule.runtime.module.extension.internal.capability.xml.schema.model.Union;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
/**
* Builder class to generate a XSD schema that describes a {@link ExtensionModel}
*
* @since 3.7.0
*/
public final class SchemaBuilder {
private static final String GLOBAL_ABSTRACT_ELEMENT_MASK = "global-%s";
private final Set<StringType> registeredEnums = new LinkedHashSet<>();
private final ObjectFactory objectFactory = new ObjectFactory();
private final ClassTypeLoader typeLoader = ExtensionsTypeLoaderFactory.getDefault().createTypeLoader();
private Schema schema;
private boolean requiresTls = false;
private ExtensionModel extensionModel;
private DslSyntaxResolver dslResolver;
private Set<ImportedTypeModel> importedTypes;
private TypeCatalog typesMapping;
private DslResolvingContext dslContext;
private ConfigurationSchemaDelegate configurationSchemaDelegate;
private ConnectionProviderSchemaDelegate connectionProviderSchemaDelegate;
private OperationSchemaDelegate operationSchemaDelegate;
private SourceSchemaDelegate sourceSchemaDelegate;
private CollectionSchemaDelegate collectionDelegate;
private ObjectTypeSchemaDelegate objectTypeDelegate;
private MapSchemaDelegate mapDelegate;
public static SchemaBuilder newSchema(ExtensionModel extensionModel, XmlDslModel xmlDslModel,
DslResolvingContext dslContext) {
SchemaBuilder builder = new SchemaBuilder();
builder.extensionModel = extensionModel;
builder.schema = new Schema();
builder.schema.setTargetNamespace(xmlDslModel.getNamespace());
builder.schema.setElementFormDefault(FormChoice.QUALIFIED);
builder.schema.setAttributeFormDefault(FormChoice.UNQUALIFIED);
builder.withDslSyntaxResolver(extensionModel, dslContext)
.importXmlNamespace()
.importSpringFrameworkNamespace()
.importMuleNamespace()
.importMuleExtensionNamespace();
builder.initialiseDelegates();
builder.withImportedTypes(extensionModel.getImportedTypes());
builder.withTypeMapping(extensionModel);
builder.withTypes(extensionModel.getTypes());
return builder;
}
private void initialiseDelegates() {
configurationSchemaDelegate = new ConfigurationSchemaDelegate(this);
connectionProviderSchemaDelegate = new ConnectionProviderSchemaDelegate(this);
operationSchemaDelegate = new OperationSchemaDelegate(this);
sourceSchemaDelegate = new SourceSchemaDelegate(this);
collectionDelegate = new CollectionSchemaDelegate(this);
objectTypeDelegate = new ObjectTypeSchemaDelegate(this);
mapDelegate = new MapSchemaDelegate(this, typeLoader);
}
private SchemaBuilder withDslSyntaxResolver(ExtensionModel model, DslResolvingContext dslContext) {
this.dslContext = dslContext;
this.dslResolver = DslSyntaxResolver.getDefault(model, dslContext);
return this;
}
private SchemaBuilder withTypeMapping(ExtensionModel model) {
this.typesMapping = TypeCatalog.getDefault(singleton(model));
model.getSubTypes().forEach(objectTypeDelegate::registerPojoSubtypes);
return this;
}
private SchemaBuilder withImportedTypes(Set<ImportedTypeModel> importedTypes) {
this.importedTypes = importedTypes;
importedTypes.forEach(type -> dslContext.getExtension(type.getOriginExtensionName())
.ifPresent(this::registerExtensionImport));
return this;
}
private SchemaBuilder withTypes(Collection<ObjectType> types) {
types.stream().filter(t -> {
Optional<DslElementSyntax> typeDsl = dslResolver.resolve(t);
return typeDsl.isPresent() && typeDsl.get().supportsTopLevelDeclaration();
}).forEach(t -> objectTypeDelegate.registerPojoType(t, t.getDescription().orElse(EMPTY)));
return this;
}
DslSyntaxResolver getDslResolver() {
return dslResolver;
}
public Schema build() {
return schema;
}
private SchemaBuilder importXmlNamespace() {
Import xmlImport = new Import();
xmlImport.setNamespace(XML_NAMESPACE);
schema.getIncludeOrImportOrRedefine().add(xmlImport);
return this;
}
private SchemaBuilder importSpringFrameworkNamespace() {
Import springFrameworkImport = new Import();
springFrameworkImport.setNamespace(SPRING_FRAMEWORK_NAMESPACE);
springFrameworkImport.setSchemaLocation(SPRING_FRAMEWORK_SCHEMA_LOCATION);
schema.getIncludeOrImportOrRedefine().add(springFrameworkImport);
return this;
}
private SchemaBuilder importMuleNamespace() {
Import muleSchemaImport = new Import();
muleSchemaImport.setNamespace(CORE_NAMESPACE);
muleSchemaImport.setSchemaLocation(MULE_SCHEMA_LOCATION);
schema.getIncludeOrImportOrRedefine().add(muleSchemaImport);
return this;
}
private SchemaBuilder importMuleExtensionNamespace() {
Import muleExtensionImport = new Import();
muleExtensionImport.setNamespace(MULE_EXTENSION_NAMESPACE);
muleExtensionImport.setSchemaLocation(MULE_EXTENSION_SCHEMA_LOCATION);
schema.getIncludeOrImportOrRedefine().add(muleExtensionImport);
return this;
}
private SchemaBuilder importTlsNamespace() {
Import tlsImport = new Import();
tlsImport.setNamespace(MULE_TLS_NAMESPACE);
tlsImport.setSchemaLocation(MULE_TLS_SCHEMA_LOCATION);
schema.getIncludeOrImportOrRedefine().add(tlsImport);
return this;
}
public SchemaBuilder registerConnectionProviderElement(ConnectionProviderModel providerModel) {
connectionProviderSchemaDelegate.registerConnectionProviderElement(providerModel, dslResolver.resolve(providerModel));
return this;
}
public SchemaBuilder registerConfigElement(ConfigurationModel configurationModel) {
configurationSchemaDelegate.registerConfigElement(schema, configurationModel, dslResolver.resolve(configurationModel));
return this;
}
Attribute createNameAttribute(boolean required) {
return createAttribute(NAME_ATTRIBUTE_NAME, load(String.class), required, NOT_SUPPORTED);
}
public SchemaBuilder registerOperation(OperationModel operationModel) {
operationSchemaDelegate.registerOperation(operationModel, dslResolver.resolve(operationModel));
return this;
}
public SchemaBuilder registerMessageSource(SourceModel sourceModel) {
sourceSchemaDelegate.registerMessageSource(sourceModel, dslResolver.resolve(sourceModel));
return this;
}
List<TopLevelElement> registerParameters(ExtensionType type, Collection<ParameterModel> parameterModels) {
List<TopLevelElement> all = new ArrayList<>(parameterModels.size());
getSortedParameterModels(parameterModels).stream()
.filter(p -> !p.getModelProperty(QNameModelProperty.class).isPresent())
.forEach(parameterModel -> {
DslElementSyntax paramDsl = dslResolver.resolve(parameterModel);
declareAsParameter(parameterModel.getType(), type, parameterModel, paramDsl, all);
});
return all;
}
/**
* Sorts the given {@code parameterModels} so that infrastructure ones are firsts and the rest maintain their relative order. If
* more than one infrastructure parameter is found, the subgroup is sorted alphabetically.
* <p>
* The {@link InfrastructureParameterModelProperty} is used to identify those parameters which are infrastructure
*
* @param parameterModels a {@link Collection} of {@link ParameterModel parameter models}
* @return a sorted {@link List}
*/
private List<ParameterModel> getSortedParameterModels(Collection<ParameterModel> parameterModels) {
List<ParameterModel> sortedParameters = new ArrayList<>(parameterModels);
sortedParameters.sort(new ParameterModelComparator(true));
return sortedParameters;
}
public SchemaBuilder registerEnums() {
registeredEnums.forEach(enumType -> registerEnum(schema, enumType));
return this;
}
private void registerEnum(Schema schema, StringType enumType) {
TopLevelSimpleType enumSimpleType = new TopLevelSimpleType();
enumSimpleType.setName(sanitizeName(getId(enumType)) + SchemaConstants.ENUM_TYPE_SUFFIX);
Union union = new Union();
union.getSimpleType().add(createEnumSimpleType(enumType));
union.getSimpleType().add(createExpressionAndPropertyPlaceHolderSimpleType());
enumSimpleType.setUnion(union);
schema.getSimpleTypeOrComplexTypeOrGroup().add(enumSimpleType);
}
private LocalSimpleType createExpressionAndPropertyPlaceHolderSimpleType() {
LocalSimpleType expression = new LocalSimpleType();
Restriction restriction = new Restriction();
expression.setRestriction(restriction);
restriction.setBase(SchemaConstants.MULE_PROPERTY_PLACEHOLDER_TYPE);
return expression;
}
private LocalSimpleType createEnumSimpleType(MetadataType enumType) {
LocalSimpleType enumValues = new LocalSimpleType();
Restriction restriction = new Restriction();
enumValues.setRestriction(restriction);
restriction.setBase(STRING);
EnumAnnotation<String> enumAnnotation = enumType.getAnnotation(EnumAnnotation.class)
.orElseThrow(() -> new IllegalArgumentException("Cannot obtain enum values for the given type"));
for (String value : enumAnnotation.getValues()) {
NoFixedFacet noFixedFacet = objectFactory.createNoFixedFacet();
noFixedFacet.setValue(value);
JAXBElement<NoFixedFacet> enumeration = objectFactory.createEnumeration(noFixedFacet);
enumValues.getRestriction().getFacets().add(enumeration);
}
return enumValues;
}
Attribute createAttribute(String name, MetadataType type, boolean required, ExpressionSupport expressionSupport) {
return createAttribute(name, EMPTY, type, null, required, expressionSupport);
}
private Attribute createAttribute(final String name, String description, final MetadataType type, Object defaultValue,
boolean required, final ExpressionSupport expressionSupport) {
final Attribute attribute = new Attribute();
attribute.setUse(required ? USE_REQUIRED : USE_OPTIONAL);
attribute.setAnnotation(createDocAnnotation(description));
if (defaultValue instanceof String && isNotBlank(defaultValue.toString())) {
attribute.setDefault(defaultValue.toString());
}
type.accept(new MetadataTypeVisitor() {
@Override
public void visitString(StringType stringType) {
Optional<EnumAnnotation> enumAnnotation = stringType.getAnnotation(EnumAnnotation.class);
if (enumAnnotation.isPresent()) {
visitEnum(stringType);
} else {
defaultVisit(stringType);
}
}
private void visitEnum(StringType enumType) {
attribute.setName(name);
String typeName = getId(enumType);
if (OperationTransactionalAction.class.getName().equals(typeName)) {
attribute.setType(MULE_EXTENSION_OPERATION_TRANSACTIONAL_ACTION_TYPE);
} else {
attribute.setType(new QName(schema.getTargetNamespace(), sanitizeName(typeName) + SchemaConstants.ENUM_TYPE_SUFFIX));
registeredEnums.add(enumType);
}
}
@Override
protected void defaultVisit(MetadataType metadataType) {
attribute.setName(name);
attribute.setType(SchemaTypeConversion.convertType(type, expressionSupport));
}
});
return attribute;
}
Attribute createValueAttribute(MetadataType genericType) {
return createAttribute(VALUE_ATTRIBUTE_NAME, genericType, true, SUPPORTED);
}
/**
* Creates a {@link ExplicitGroup Choice} group that supports {@code refs} to both the {@code global} and {@code local}
* abstract elements for the given {@code type}.
* This is required in order to allow subtypes that support top-level declaration along with other subtypes that
* support only local declarations as childs.
* <p/>
* For example, a resulting choice group for a type of name {@code TypeName} will look like:
* <p>
* <xs:complexType>
* <xs:choice minOccurs="1" maxOccurs="1">
* <xs:element minOccurs="0" maxOccurs="1" ref="ns:abstract-type-name"></xs:element>
* <xs:element minOccurs="0" maxOccurs="1" ref="ns:global-abstract-type-name"></xs:element>
* </xs:choice>
* </xs:complexType>
* <p/>
*
* @param typeDsl {@link DslElementSyntax} of the referenced type
* @param type the {@link MetadataType type} of the base element that will be referenced
* @param minOccurs {@link BigInteger#ZERO} if the {@code group} is optional or {@link BigInteger#ONE} if required
* @param maxOccurs the maximum number of occurrences for this group
* @return a {@link ExplicitGroup Choice} group with the necessary options for this case
*/
ExplicitGroup createTypeRefChoiceLocalOrGlobal(DslElementSyntax typeDsl, MetadataType type, BigInteger minOccurs,
String maxOccurs) {
if (!isImported(type)) {
objectTypeDelegate.registerPojoType(type, EMPTY);
objectTypeDelegate.registerAbstractElement(type, typeDsl);
if (typeDsl.supportsTopLevelDeclaration() || (typeDsl.supportsChildDeclaration() && typeDsl.isWrapped())) {
objectTypeDelegate.registerConcreteGlobalElement(typeDsl, EMPTY, getAbstractElementName(typeDsl),
objectTypeDelegate.getTypeQName(typeDsl, type));
}
}
final ExplicitGroup choice = new ExplicitGroup();
choice.setMinOccurs(minOccurs);
choice.setMaxOccurs(maxOccurs);
QName refAbstract = new QName(typeDsl.getNamespace(), getAbstractElementName(typeDsl), typeDsl.getPrefix());
TopLevelElement localAbstractElementRef = createRefElement(refAbstract, true);
choice.getParticle().add(objectFactory.createElement(localAbstractElementRef));
QName refGlobal = new QName(typeDsl.getNamespace(), format(GLOBAL_ABSTRACT_ELEMENT_MASK, getAbstractElementName(typeDsl)),
typeDsl.getPrefix());
TopLevelElement topLevelElementRef = createRefElement(refGlobal, true);
choice.getParticle().add(objectFactory.createElement(topLevelElementRef));
return choice;
}
boolean isImported(MetadataType type) {
return importedTypes.stream()
.anyMatch(t -> Objects.equals(getTypeId(t.getImportedType()), getTypeId(type)));
}
private String getTypeId(MetadataType type) {
return getId(type);
}
/**
* Creates a {@code ref} to the abstract element of the given {@code type} based on its {@code typeDsl} declaration.
* Anywhere this ref element is used, the schema will allow to declare an xml element with a substitution group that matches
* the referenced abstract-element
* <p/>
* For example, if we create this ref element to the type of name {@code TypeName}:
* <p>
* <xs:element minOccurs="0" maxOccurs="1" ref="ns:abstract-type-name"></xs:element>
* <p>
* <p/>
* Then, any of the following elements are allowed to be used where the ref exists:
* <p>
* <xs:element type="ns:org.mule.test.SomeType" substitutionGroup="ns:abstract-type-name" name="some-type"></xs:element>
* <p>
* <xs:element type="ns:org.mule.test.OtherType" substitutionGroup="ns:abstract-type-name" name="other-type"></xs:element>
*
* @param typeDsl {@link DslElementSyntax} of the referenced {@code type}
* @param type {@link MetadataType} of the referenced {@code type}
* @param isRequired whether or not the element element is required
* @return the {@link TopLevelElement element} representing the reference
*/
TopLevelElement createTypeRef(DslElementSyntax typeDsl, ObjectType type, boolean isRequired) {
if (!isImported(type)) {
objectTypeDelegate.registerPojoType(type, EMPTY);
objectTypeDelegate.registerAbstractElement(type, typeDsl);
objectTypeDelegate.registerConcreteGlobalElement(typeDsl, EMPTY, getAbstractElementName(typeDsl),
objectTypeDelegate.getTypeQName(typeDsl, type));
}
QName refQName = new QName(typeDsl.getNamespace(), getAbstractElementName(typeDsl), typeDsl.getPrefix());
return createRefElement(refQName, isRequired);
}
void declareAsParameter(MetadataType type, ExtensionType extensionType, ParameterModel parameterModel,
DslElementSyntax paramDsl, List<TopLevelElement> childElements) {
if (isContent(parameterModel) || isContent(type)) {
childElements.add(generateTextElement(paramDsl, parameterModel.getDescription(), parameterModel.isRequired()));
} else {
type.accept(getParameterDeclarationVisitor(extensionType, childElements, parameterModel));
}
}
private MetadataTypeVisitor getParameterDeclarationVisitor(final ExtensionType extensionType,
final List<TopLevelElement> childElements,
final ParameterModel parameterModel) {
final DslElementSyntax paramDsl = dslResolver.resolve(parameterModel);
return getParameterDeclarationVisitor(extensionType, childElements,
parameterModel.getName(), parameterModel.getDescription(),
parameterModel.getExpressionSupport(), parameterModel.isRequired(),
parameterModel.getDefaultValue(), paramDsl,
parameterModel.getDslConfiguration());
}
private MetadataTypeVisitor getParameterDeclarationVisitor(final ExtensionType extensionType,
final List<TopLevelElement> childElements,
final String name, final String description,
ExpressionSupport expressionSupport, boolean required,
Object defaultValue, DslElementSyntax paramDsl,
ParameterDslConfiguration dslModel) {
return new MetadataTypeVisitor() {
private boolean forceOptional = paramDsl.supportsChildDeclaration() || !required;
@Override
public void visitArrayType(ArrayType arrayType) {
defaultVisit(arrayType);
if (paramDsl.supportsChildDeclaration()) {
collectionDelegate.generateCollectionElement(arrayType, paramDsl, description,
!paramDsl.supportsAttributeDeclaration(),
childElements);
}
}
@Override
public void visitObject(ObjectType objectType) {
defaultVisit(objectType);
if (isMap(objectType)) {
if (paramDsl.supportsChildDeclaration()) {
mapDelegate.generateMapElement(objectType, paramDsl, description,
!paramDsl.supportsAttributeDeclaration(),
childElements);
}
} else {
objectTypeDelegate.generatePojoElement(objectType, paramDsl, dslModel, description, childElements);
}
}
@Override
public void visitString(StringType stringType) {
if (paramDsl.supportsChildDeclaration()) {
childElements.add(generateTextElement(paramDsl, description, isRequired(forceOptional, required)));
} else {
defaultVisit(stringType);
}
}
@Override
protected void defaultVisit(MetadataType metadataType) {
if (paramDsl.supportsAttributeDeclaration()) {
extensionType.getAttributeOrAttributeGroup().add(createAttribute(name, description, metadataType, defaultValue,
isRequired(forceOptional, required),
expressionSupport));
}
}
private boolean isRequired(boolean forceOptional, boolean required) {
return !forceOptional && required;
}
};
}
private XmlDslModel registerExtensionImport(ExtensionModel extension) {
XmlDslModel languageModel = extension.getXmlDslModel();
Import schemaImport = new Import();
schemaImport.setNamespace(languageModel.getNamespace());
schemaImport.setSchemaLocation(languageModel.getSchemaLocation());
if (!schema.getIncludeOrImportOrRedefine().contains(schemaImport)) {
schema.getIncludeOrImportOrRedefine().add(schemaImport);
}
return languageModel;
}
void addTlsSupport(ExtensionType extensionType) {
if (!requiresTls) {
importTlsNamespace();
requiresTls = true;
}
extensionType.getAttributeOrAttributeGroup()
.add(createAttribute(TLS_PARAMETER_NAME, load(String.class), false, NOT_SUPPORTED));
}
void addTlsSupport(ExtensionType extensionType, List<TopLevelElement> childElements) {
addTlsSupport(extensionType);
childElements.add(createRefElement(TLS_CONTEXT_TYPE, false));
}
TopLevelElement createRefElement(QName elementRef, boolean isRequired) {
TopLevelElement element = new TopLevelElement();
element.setRef(elementRef);
element.setMinOccurs(isRequired ? ONE : ZERO);
element.setMaxOccurs(MAX_ONE);
return element;
}
Attribute createAttribute(String name, String description, boolean optional, QName type) {
Attribute attr = new Attribute();
attr.setName(name);
attr.setUse(optional ? USE_OPTIONAL : USE_REQUIRED);
attr.setType(type);
if (description != null) {
attr.setAnnotation(createDocAnnotation(description));
}
return attr;
}
Schema getSchema() {
return schema;
}
TypeCatalog getTypesMapping() {
return typesMapping;
}
Annotation createDocAnnotation(String content) {
if (StringUtils.isBlank(content)) {
return null;
}
Annotation annotation = new Annotation();
Documentation doc = new Documentation();
doc.getContent().add(content);
annotation.getAppinfoOrDocumentation().add(doc);
return annotation;
}
MetadataType load(Class<?> clazz) {
return typeLoader.load(clazz);
}
TopLevelElement createTopLevelElement(String name, BigInteger minOccurs, String maxOccurs) {
TopLevelElement element = new TopLevelElement();
element.setName(name);
element.setMinOccurs(minOccurs);
element.setMaxOccurs(maxOccurs);
return element;
}
TopLevelElement createTopLevelElement(String name, BigInteger minOccurs, String maxOccurs, LocalComplexType type) {
TopLevelElement element = createTopLevelElement(name, minOccurs, maxOccurs);
element.setComplexType(type);
return element;
}
ExtensionModel getExtensionModel() {
return extensionModel;
}
private TopLevelElement generateTextElement(DslElementSyntax paramDsl, String description, boolean isRequired) {
TopLevelElement textElement = createTopLevelElement(paramDsl.getElementName(), isRequired ? ONE : ZERO, MAX_ONE);
textElement.setAnnotation(createDocAnnotation(description));
textElement.setType(STRING);
return textElement;
}
boolean isRequired(TopLevelElement element) {
return element.getMinOccurs().equals(ONE);
}
void addParameterToSequence(List<TopLevelElement> parameters, ExplicitGroup sequence) {
parameters.forEach(parameter -> {
sequence.getParticle().add(objectFactory.createElement(parameter));
if (isRequired(parameter)) {
sequence.setMinOccurs(ONE);
}
});
}
void addInfrastructureParameters(ExtensionType extensionType, ParameterizedModel model, ExplicitGroup sequence) {
// TODO MULE-11608 (alegmarra): remove this TLS custom handling
model.getAllParameterModels().stream()
.filter(p -> p.getModelProperty(InfrastructureParameterModelProperty.class).isPresent())
.filter(p -> !p.getName().equals(TLS_PARAMETER_NAME))
.forEach(parameter -> parameter.getModelProperty(QNameModelProperty.class)
.map(QNameModelProperty::getValue)
.ifPresent(qName -> sequence.getParticle()
.add(objectFactory.createElement(createRefElement(qName, false)))));
model.getAllParameterModels().stream()
.filter(p -> p.getModelProperty(InfrastructureParameterModelProperty.class).isPresent())
.filter(p -> p.getName().equals(TLS_PARAMETER_NAME))
.findFirst()
.ifPresent(p -> {
p.getModelProperty(QNameModelProperty.class).map(QNameModelProperty::getValue)
.ifPresent(qName -> sequence.getParticle()
.add(objectFactory.createElement(createRefElement(qName, false))));
addTlsSupport(extensionType);
});
}
void addInlineParameterGroup(ParameterGroupModel group, ExplicitGroup parentSequence) {
DslElementSyntax groupDsl = dslResolver.resolveInline(group);
LocalComplexType complexType = objectTypeDelegate.createTypeExtension(MULE_ABSTRACT_EXTENSION_TYPE);
ExplicitGroup groupSequence = new ExplicitGroup();
List<ParameterModel> groupParameters = group.getParameterModels();
List<TopLevelElement> parameterElements =
registerParameters(complexType.getComplexContent().getExtension(), groupParameters);
addParameterToSequence(parameterElements, groupSequence);
BigInteger minOccurs = groupParameters.stream().anyMatch(ParameterModel::isRequired) ? ONE : ZERO;
TopLevelElement groupElement = createTopLevelElement(groupDsl.getElementName(), minOccurs, MAX_ONE);
groupElement.setComplexType(complexType);
complexType.getComplexContent().getExtension().setSequence(groupSequence);
parentSequence.getParticle().add(objectFactory.createElement(groupElement));
}
}