/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.yangtools.sal.binding.yang.types;
import static org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil.moduleNamespaceToPackageName;
import static org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil.packageNameForGeneratedType;
import static org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil.parseToClassName;
import static org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil.parseToValidParamName;
import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNodeForRelativeXPath;
import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringEscapeUtils;
import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
import org.opendaylight.yangtools.binding.generator.util.TypeConstants;
import org.opendaylight.yangtools.binding.generator.util.Types;
import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.EnumerationBuilderImpl;
import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
import org.opendaylight.yangtools.sal.binding.generator.spi.TypeProvider;
import org.opendaylight.yangtools.sal.binding.model.api.Enumeration;
import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject;
import org.opendaylight.yangtools.sal.binding.model.api.Type;
import org.opendaylight.yangtools.sal.binding.model.api.type.builder.EnumBuilder;
import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.ExtendedType;
import org.opendaylight.yangtools.yang.model.util.StringType;
import org.opendaylight.yangtools.yang.model.util.UnionType;
import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
import com.google.common.base.Preconditions;
public final class TypeProviderImpl implements TypeProvider {
/**
* Contains the schema data red from YANG files.
*/
private final SchemaContext schemaContext;
/**
* The outter map maps module names to the map of the types for the module.
* The inner map maps the name of the concrete type to the JAVA
* Type
(usually it is generated TO).
*/
private Map> genTypeDefsContextMap;
/**
* The map which maps schema paths to JAVA Type
.
*/
private final Map referencedTypes;
/**
* Creates new instance of class TypeProviderImpl
.
*
* @param schemaContext
* contains the schema data red from YANG files
* @throws IllegalArgumentException
* if schemaContext
equal null.
*/
public TypeProviderImpl(final SchemaContext schemaContext) {
Preconditions.checkArgument(schemaContext != null, "Schema Context cannot be null!");
this.schemaContext = schemaContext;
this.genTypeDefsContextMap = new HashMap<>();
this.referencedTypes = new HashMap<>();
resolveTypeDefsFromContext();
}
/**
* Puts refType
to map with key refTypePath
*
* @param refTypePath
* schema path used as the map key
* @param refType
* type which represents the map value
* @throws IllegalArgumentException
*
* - if
refTypePath
equal null
* - if
refType
equal null
*
*
*/
public void putReferencedType(final SchemaPath refTypePath, final Type refType) {
Preconditions.checkArgument(refTypePath != null,
"Path reference of Enumeration Type Definition cannot be NULL!");
Preconditions.checkArgument(refType != null, "Reference to Enumeration Type cannot be NULL!");
referencedTypes.put(refTypePath, refType);
}
/**
*
* Converts basic YANG type type
to JAVA Type
.
*
* @param type
* string with YANG name of type
* @returns JAVA Type
for YANG type type
* @see org.opendaylight.controller.yang.model.type.provider.TypeProvider#
* javaTypeForYangType(java.lang.String)
*/
@Override
public Type javaTypeForYangType(String type) {
return BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForYangType(type);
}
/**
* Converts schema definition type typeDefinition
to JAVA
* Type
*
* @param typeDefinition
* type definition which is converted to JAVA type
* @throws IllegalArgumentException
*
* - if
typeDefinition
equal null
* - if Q name of
typeDefinition
equal null
* - if name of
typeDefinition
equal null
*
*/
@Override
public Type javaTypeForSchemaDefinitionType(final TypeDefinition> typeDefinition, final SchemaNode parentNode) {
Type returnType = null;
Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
if (typeDefinition.getQName() == null) {
throw new IllegalArgumentException(
"Type Definition cannot have non specified QName (QName cannot be NULL!)");
}
Preconditions.checkArgument(typeDefinition.getQName().getLocalName() != null,
"Type Definitions Local Name cannot be NULL!");
if (typeDefinition instanceof ExtendedType) {
returnType = javaTypeForExtendedType(typeDefinition);
} else {
returnType = javaTypeForLeafrefOrIdentityRef(typeDefinition, parentNode);
if (returnType == null) {
returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(typeDefinition,
parentNode);
}
}
// TODO: add throw exception when we will be able to resolve ALL yang
// types!
// if (returnType == null) {
// throw new IllegalArgumentException("Type Provider can't resolve " +
// "type for specified Type Definition " + typedefName);
// }
return returnType;
}
/**
* Returns JAVA Type
for instances of the type
* LeafrefTypeDefinition
or
* IdentityrefTypeDefinition
.
*
* @param typeDefinition
* type definition which is converted to JAVA Type
* @return JAVA Type
instance for typeDefinition
*/
private Type javaTypeForLeafrefOrIdentityRef(TypeDefinition> typeDefinition, SchemaNode parentNode) {
if (typeDefinition instanceof LeafrefTypeDefinition) {
final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) typeDefinition;
return provideTypeForLeafref(leafref, parentNode);
} else if (typeDefinition instanceof IdentityrefTypeDefinition) {
final IdentityrefTypeDefinition idref = (IdentityrefTypeDefinition) typeDefinition;
return provideTypeForIdentityref(idref);
} else {
return null;
}
}
/**
* Returns JAVA Type
for instances of the type
* ExtendedType
.
*
* @param typeDefinition
* type definition which is converted to JAVA Type
* @return JAVA Type
instance for typeDefinition
*/
private Type javaTypeForExtendedType(TypeDefinition> typeDefinition) {
final String typedefName = typeDefinition.getQName().getLocalName();
final TypeDefinition> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
Type returnType = null;
returnType = javaTypeForLeafrefOrIdentityRef(baseTypeDef, typeDefinition);
if (returnType == null) {
if (baseTypeDef instanceof EnumTypeDefinition) {
final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypeDef;
returnType = provideTypeForEnum(enumTypeDef, typedefName, typeDefinition);
} else {
final Module module = findParentModule(schemaContext, typeDefinition);
if (module != null) {
final Map genTOs = genTypeDefsContextMap.get(module.getName());
if (genTOs != null) {
returnType = genTOs.get(typedefName);
}
if (returnType == null) {
returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
baseTypeDef, typeDefinition);
}
}
}
}
return returnType;
// TODO: add throw exception when we will be able to resolve ALL yang
// types!
// if (returnType == null) {
// throw new IllegalArgumentException("Type Provider can't resolve " +
// "type for specified Type Definition " + typedefName);
// }
}
/**
* Seeks for identity reference idref
the JAVA
* type
.
*
*
* Example:
* If identy which is referenced via idref
has name Idn
* then returning type is {@code Class extends Idn>}
*
* @param idref
* identityref type definition for which JAVA Type
* is sought
* @return JAVA Type
of the identity which is refrenced through
* idref
*/
private Type provideTypeForIdentityref(IdentityrefTypeDefinition idref) {
QName baseIdQName = idref.getIdentity();
Module module = schemaContext.findModuleByNamespace(baseIdQName.getNamespace());
IdentitySchemaNode identity = null;
for (IdentitySchemaNode id : module.getIdentities()) {
if (id.getQName().equals(baseIdQName)) {
identity = id;
}
}
Preconditions.checkArgument(identity != null, "Target identity '" + baseIdQName + "' do not exists");
final String basePackageName = moduleNamespaceToPackageName(module);
final String packageName = packageNameForGeneratedType(basePackageName, identity.getPath());
final String genTypeName = parseToClassName(identity.getQName().getLocalName());
Type baseType = Types.typeForClass(Class.class);
Type paramType = Types.wildcardTypeFor(packageName, genTypeName);
return Types.parameterizedTypeFor(baseType, paramType);
}
/**
* Converts typeDefinition
to concrete JAVA Type
.
*
* @param typeDefinition
* type definition which should be converted to JAVA
* Type
* @return JAVA Type
which represents
* typeDefinition
* @throws IllegalArgumentException
*
* - if
typeDefinition
equal null
* - if Q name of
typeDefinition
* - if name of
typeDefinition
*
*/
public Type generatedTypeForExtendedDefinitionType(final TypeDefinition> typeDefinition,
final SchemaNode parentNode) {
Type returnType = null;
Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
if (typeDefinition.getQName() == null) {
throw new IllegalArgumentException(
"Type Definition cannot have non specified QName (QName cannot be NULL!)");
}
Preconditions.checkArgument(typeDefinition.getQName().getLocalName() != null,
"Type Definitions Local Name cannot be NULL!");
final String typedefName = typeDefinition.getQName().getLocalName();
if (typeDefinition instanceof ExtendedType) {
final TypeDefinition> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
if (!(baseTypeDef instanceof LeafrefTypeDefinition) && !(baseTypeDef instanceof IdentityrefTypeDefinition)) {
final Module module = findParentModule(schemaContext, parentNode);
if (module != null) {
final Map genTOs = genTypeDefsContextMap.get(module.getName());
if (genTOs != null) {
returnType = genTOs.get(typedefName);
}
}
}
}
return returnType;
}
/**
* Gets base type definition for extendTypeDef
. The method is
* recursivelly called until non ExtendedType
type is found.
*
* @param extendTypeDef
* type definition for which is the base type definition sought
* @return type definition which is base type for extendTypeDef
* @throws IllegalArgumentException
* if extendTypeDef
equal null
*/
private TypeDefinition> baseTypeDefForExtendedType(final TypeDefinition> extendTypeDef) {
Preconditions.checkArgument(extendTypeDef != null, "Type Definiition reference cannot be NULL!");
final TypeDefinition> baseTypeDef = extendTypeDef.getBaseType();
if (baseTypeDef instanceof ExtendedType) {
return baseTypeDefForExtendedType(baseTypeDef);
} else {
return baseTypeDef;
}
}
/**
* Converts leafrefType
to JAVA Type
.
*
* The path of leafrefType
is followed to find referenced node
* and its Type
is returned.
*
* @param leafrefType
* leafref type definition for which is the type sought
* @return JAVA Type
of data schema node which is referenced in
* leafrefType
* @throws IllegalArgumentException
*
* - if
leafrefType
equal null
* - if path statement of
leafrefType
equal null
*
*
*/
public Type provideTypeForLeafref(final LeafrefTypeDefinition leafrefType, final SchemaNode parentNode) {
Type returnType = null;
Preconditions.checkArgument(leafrefType != null, "Leafref Type Definition reference cannot be NULL!");
Preconditions.checkArgument(leafrefType.getPathStatement() != null,
"The Path Statement for Leafref Type Definition cannot be NULL!");
final RevisionAwareXPath xpath = leafrefType.getPathStatement();
final String strXPath = xpath.toString();
if (strXPath != null) {
if (strXPath.contains("[")) {
returnType = Types.typeForClass(Object.class);
} else {
final Module module = findParentModule(schemaContext, parentNode);
if (module != null) {
final SchemaNode dataNode;
if (xpath.isAbsolute()) {
dataNode = findDataSchemaNode(schemaContext, module, xpath);
} else {
dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, parentNode, xpath);
}
if (leafContainsEnumDefinition(dataNode)) {
returnType = referencedTypes.get(dataNode.getPath());
} else if (leafListContainsEnumDefinition(dataNode)) {
returnType = Types.listTypeFor(referencedTypes.get(dataNode.getPath()));
} else {
returnType = resolveTypeFromDataSchemaNode(dataNode);
}
}
}
}
return returnType;
}
/**
* Checks if dataNode
is LeafSchemaNode
and if it
* so then checks if it is of type EnumTypeDefinition
.
*
* @param dataNode
* data schema node for which is checked if it is leaf and if it
* is of enum type
* @return boolean value
*
* - true - if
dataNode
is leaf of type enumeration
* - false - other cases
*
*/
private boolean leafContainsEnumDefinition(final SchemaNode dataNode) {
if (dataNode instanceof LeafSchemaNode) {
final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
if (leaf.getType() instanceof EnumTypeDefinition) {
return true;
}
}
return false;
}
/**
* Checks if dataNode
is LeafListSchemaNode
and if
* it so then checks if it is of type EnumTypeDefinition
.
*
* @param dataNode
* data schema node for which is checked if it is leaflist and if
* it is of enum type
* @return boolean value
*
* - true - if
dataNode
is leaflist of type
* enumeration
* - false - other cases
*
*/
private boolean leafListContainsEnumDefinition(final SchemaNode dataNode) {
if (dataNode instanceof LeafListSchemaNode) {
final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
if (leafList.getType() instanceof EnumTypeDefinition) {
return true;
}
}
return false;
}
/**
* Converts enumTypeDef
to
* {@link org.opendaylight.yangtools.sal.binding.model.api.Enumeration
* enumeration}.
*
* @param enumTypeDef
* enumeration type definition which is converted to enumeration
* @param enumName
* string with name which is used as the enumeration name
* @return enumeration type which is built with data (name, enum values)
* from enumTypeDef
* @throws IllegalArgumentException
*
* - if
enumTypeDef
equals null
* - if enum values of
enumTypeDef
equal null
* - if Q name of
enumTypeDef
equal null
* - if name of
enumTypeDef
equal null
*
*/
private Enumeration provideTypeForEnum(final EnumTypeDefinition enumTypeDef, final String enumName,
final SchemaNode parentNode) {
Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
Preconditions.checkArgument(enumTypeDef.getValues() != null,
"EnumTypeDefinition MUST contain at least ONE value definition!");
Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
"Local Name in EnumTypeDefinition QName cannot be NULL!");
final String enumerationName = parseToClassName(enumName);
Module module = findParentModule(schemaContext, parentNode);
final String basePackageName = moduleNamespaceToPackageName(module);
final EnumBuilder enumBuilder = new EnumerationBuilderImpl(basePackageName, enumerationName);
enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
return enumBuilder.toInstance(null);
}
/**
* Adds enumeration to typeBuilder
. The enumeration data are
* taken from enumTypeDef
.
*
* @param enumTypeDef
* enumeration type definition is source of enumeration data for
* typeBuilder
* @param enumName
* string with the name of enumeration
* @param typeBuilder
* generated type builder to which is enumeration added
* @return enumeration type which contains enumeration data form
* enumTypeDef
* @throws IllegalArgumentException
*
* - if
enumTypeDef
equals null
* - if enum values of
enumTypeDef
equal null
* - if Q name of
enumTypeDef
equal null
* - if name of
enumTypeDef
equal null
* - if name of
typeBuilder
equal null
*
*
*/
private Enumeration addInnerEnumerationToTypeBuilder(final EnumTypeDefinition enumTypeDef, final String enumName,
final GeneratedTypeBuilderBase> typeBuilder) {
Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
Preconditions.checkArgument(enumTypeDef.getValues() != null,
"EnumTypeDefinition MUST contain at least ONE value definition!");
Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
"Local Name in EnumTypeDefinition QName cannot be NULL!");
Preconditions.checkArgument(typeBuilder != null, "Generated Type Builder reference cannot be NULL!");
final String enumerationName = parseToClassName(enumName);
final EnumBuilder enumBuilder = typeBuilder.addEnumeration(enumerationName);
enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
return enumBuilder.toInstance(enumBuilder);
}
/**
* Converts dataNode
to JAVA Type
.
*
* @param dataNode
* contains information about YANG type
* @return JAVA Type
representation of dataNode
*/
private Type resolveTypeFromDataSchemaNode(final SchemaNode dataNode) {
Type returnType = null;
if (dataNode != null) {
if (dataNode instanceof LeafSchemaNode) {
final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
returnType = javaTypeForSchemaDefinitionType(leaf.getType(), leaf);
} else if (dataNode instanceof LeafListSchemaNode) {
final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
returnType = javaTypeForSchemaDefinitionType(leafList.getType(), leafList);
}
}
return returnType;
}
/**
* Passes through all modules and through all its type definitions and
* convert it to generated types.
*
* The modules are firstly sorted by mutual dependencies. The modules are
* sequentially passed. All type definitions of a module are at the
* beginning sorted so that type definition with less amount of references
* to other type definition are processed first.
* For each module is created mapping record in the map
* {@link TypeProviderImpl#genTypeDefsContextMap genTypeDefsContextMap}
* which map current module name to the map which maps type names to
* returned types (generated types).
*
*/
private void resolveTypeDefsFromContext() {
final Set modules = schemaContext.getModules();
Preconditions.checkArgument(modules != null, "Sef of Modules cannot be NULL!");
final Module[] modulesArray = new Module[modules.size()];
int i = 0;
for (Module modul : modules) {
modulesArray[i++] = modul;
}
final List modulesSortedByDependency = ModuleDependencySort.sort(modulesArray);
for (final Module module : modulesSortedByDependency) {
if (module == null) {
continue;
}
final String moduleName = module.getName();
final String basePackageName = moduleNamespaceToPackageName(module);
final Set> typeDefinitions = module.getTypeDefinitions();
final List> listTypeDefinitions = sortTypeDefinitionAccordingDepth(typeDefinitions);
final Map typeMap = new HashMap<>();
genTypeDefsContextMap.put(moduleName, typeMap);
if ((listTypeDefinitions != null) && (basePackageName != null)) {
for (final TypeDefinition> typedef : listTypeDefinitions) {
typedefToGeneratedType(basePackageName, moduleName, typedef);
}
}
}
}
/**
*
* @param basePackageName
* string with name of package to which the module belongs
* @param moduleName
* string with the name of the module for to which the
* typedef
belongs
* @param typedef
* type definition of the node for which should be creted JAVA
* Type
(usually generated TO)
* @return JAVA Type
representation of typedef
or
* null
value if basePackageName
or
* modulName
or typedef
or Q name of
* typedef
equals null
*/
private Type typedefToGeneratedType(final String basePackageName, final String moduleName,
final TypeDefinition> typedef) {
if ((basePackageName != null) && (moduleName != null) && (typedef != null) && (typedef.getQName() != null)) {
final String typedefName = typedef.getQName().getLocalName();
final TypeDefinition> innerTypeDefinition = typedef.getBaseType();
if (!(innerTypeDefinition instanceof LeafrefTypeDefinition)
&& !(innerTypeDefinition instanceof IdentityrefTypeDefinition)) {
Type returnType = null;
if (innerTypeDefinition instanceof ExtendedType) {
ExtendedType innerExtendedType = (ExtendedType) innerTypeDefinition;
returnType = provideGeneratedTOFromExtendedType(innerExtendedType, basePackageName, typedefName);
} else if (innerTypeDefinition instanceof UnionTypeDefinition) {
final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForUnionTypeDef(basePackageName,
typedef, typedefName, typedef);
returnType = genTOBuilder.toInstance();
} else if (innerTypeDefinition instanceof EnumTypeDefinition) {
final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) innerTypeDefinition;
returnType = provideTypeForEnum(enumTypeDef, typedefName, typedef);
} else if (innerTypeDefinition instanceof BitsTypeDefinition) {
final BitsTypeDefinition bitsTypeDefinition = (BitsTypeDefinition) innerTypeDefinition;
final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForBitsTypeDefinition(
basePackageName, bitsTypeDefinition, typedefName);
returnType = genTOBuilder.toInstance();
} else {
final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
innerTypeDefinition, typedef);
returnType = wrapJavaTypeIntoTO(basePackageName, typedef, javaType);
}
if (returnType != null) {
final Map typeMap = genTypeDefsContextMap.get(moduleName);
if (typeMap != null) {
typeMap.put(typedefName, returnType);
}
return returnType;
}
}
}
return null;
}
/**
* Wraps base YANG type to generated TO.
*
* @param basePackageName
* string with name of package to which the module belongs
* @param typedef
* type definition which is converted to the TO
* @param javaType
* JAVA Type
to which is typedef
mapped
* @return generated transfer object which representjavaType
*/
private GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition> typedef,
final Type javaType) {
if (javaType != null) {
final String propertyName = "value";
final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef);
final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty(propertyName);
genPropBuilder.setReturnType(javaType);
genTOBuilder.addEqualsIdentity(genPropBuilder);
genTOBuilder.addHashIdentity(genPropBuilder);
genTOBuilder.addToStringProperty(genPropBuilder);
if (javaType == BaseYangTypes.STRING_TYPE && typedef instanceof ExtendedType) {
final List regExps = resolveRegExpressionsFromTypedef((ExtendedType) typedef);
addStringRegExAsConstant(genTOBuilder, regExps);
}
return genTOBuilder.toInstance();
}
return null;
}
/**
* Converts output list of generated TO builders to one TO builder (first
* from list) which contains the remaining builders as its enclosing TO.
*
* @param basePackageName
* string with name of package to which the module belongs
* @param typedef
* type definition which should be of type
* UnionTypeDefinition
* @param typeDefName
* string with name for generated TO
* @return generated TO builder with the list of enclosed generated TO
* builders
*/
public GeneratedTOBuilder provideGeneratedTOBuilderForUnionTypeDef(final String basePackageName,
final TypeDefinition> typedef, String typeDefName, SchemaNode parentNode) {
final List genTOBuilders = provideGeneratedTOBuildersForUnionTypeDef(basePackageName,
typedef, typeDefName, parentNode);
GeneratedTOBuilder resultTOBuilder = null;
if (!genTOBuilders.isEmpty()) {
resultTOBuilder = genTOBuilders.get(0);
genTOBuilders.remove(0);
for (GeneratedTOBuilder genTOBuilder : genTOBuilders) {
resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
}
}
return resultTOBuilder;
}
/**
* Converts typedef
to generated TO with
* typeDefName
. Every union type from typedef
is
* added to generated TO builder as property.
*
* @param basePackageName
* string with name of package to which the module belongs
* @param typedef
* type definition which should be of type
* UnionTypeDefinition
* @param typeDefName
* string with name for generated TO
* @return generated TO builder which represents typedef
* @throws IllegalArgumentException
*
* - if
basePackageName
equals null
* - if
typedef
equals null
* - if Q name of
typedef
equals null
*
*/
public List provideGeneratedTOBuildersForUnionTypeDef(final String basePackageName,
final TypeDefinition> typedef, final String typeDefName, final SchemaNode parentNode) {
Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL!");
Preconditions.checkArgument(typedef != null, "Type Definition cannot be NULL!");
Preconditions.checkArgument(typedef.getQName() != null,
"Type Definition cannot have non specified QName (QName cannot be NULL!)");
final List generatedTOBuilders = new ArrayList<>();
final TypeDefinition> baseTypeDefinition = typedef.getBaseType();
if ((baseTypeDefinition != null) && (baseTypeDefinition instanceof UnionTypeDefinition)) {
final UnionTypeDefinition unionTypeDef = (UnionTypeDefinition) baseTypeDefinition;
final List> unionTypes = unionTypeDef.getTypes();
final GeneratedTOBuilder unionGenTOBuilder;
if (typeDefName != null && !typeDefName.isEmpty()) {
final String typeName = parseToClassName(typeDefName);
unionGenTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typeName);
} else {
unionGenTOBuilder = typedefToTransferObject(basePackageName, typedef);
}
generatedTOBuilders.add(unionGenTOBuilder);
unionGenTOBuilder.setIsUnion(true);
final List regularExpressions = new ArrayList();
for (final TypeDefinition> unionType : unionTypes) {
final String unionTypeName = unionType.getQName().getLocalName();
if (unionType instanceof UnionType) {
generatedTOBuilders.addAll(resolveUnionSubtypeAsUnion(unionGenTOBuilder, unionType,
basePackageName, parentNode));
} else if (unionType instanceof ExtendedType) {
resolveExtendedSubtypeAsUnion(unionGenTOBuilder, (ExtendedType) unionType, unionTypeName,
regularExpressions, parentNode);
} else if (unionType instanceof EnumTypeDefinition) {
final Enumeration enumeration = addInnerEnumerationToTypeBuilder((EnumTypeDefinition) unionType,
unionTypeName, unionGenTOBuilder);
updateUnionTypeAsProperty(unionGenTOBuilder, enumeration, unionTypeName);
} else {
final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
unionType, parentNode);
updateUnionTypeAsProperty(unionGenTOBuilder, javaType, unionTypeName);
}
}
if (!regularExpressions.isEmpty()) {
addStringRegExAsConstant(unionGenTOBuilder, regularExpressions);
}
storeGenTO(typedef, unionGenTOBuilder, parentNode);
}
return generatedTOBuilders;
}
/**
* Wraps code which handle case when union subtype is also of the type
* UnionType
.
*
* In this case the new generated TO is created for union subtype (recursive
* call of method
* {@link #provideGeneratedTOBuilderForUnionTypeDef(String, TypeDefinition, String)
* provideGeneratedTOBuilderForUnionTypeDef} and in parent TO builder
* parentUnionGenTOBuilder
is created property which type is
* equal to new generated TO.
*
* @param parentUnionGenTOBuilder
* generated TO builder to which is the property with the child
* union subtype added
* @param basePackageName
* string with the name of the module package
* @param unionSubtype
* type definition which represents union subtype
* @return list of generated TO builders. The number of the builders can be
* bigger one due to recursive call of
* provideGeneratedTOBuildersForUnionTypeDef
method.
*/
private List resolveUnionSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
final TypeDefinition> unionSubtype, final String basePackageName, final SchemaNode parentNode) {
final String newTOBuilderName = provideAvailableNameForGenTOBuilder(parentUnionGenTOBuilder.getName());
final List subUnionGenTOBUilders = provideGeneratedTOBuildersForUnionTypeDef(
basePackageName, unionSubtype, newTOBuilderName, parentNode);
final GeneratedPropertyBuilder propertyBuilder;
propertyBuilder = parentUnionGenTOBuilder.addProperty(BindingGeneratorUtil
.parseToValidParamName(newTOBuilderName));
propertyBuilder.setReturnType(subUnionGenTOBUilders.get(0));
parentUnionGenTOBuilder.addEqualsIdentity(propertyBuilder);
parentUnionGenTOBuilder.addToStringProperty(propertyBuilder);
return subUnionGenTOBUilders;
}
/**
* Wraps code which handle case when union subtype is of the type
* ExtendedType
.
*
* If TO for this type already exists it is used for the creation of the
* property in parentUnionGenTOBuilder
. In other case the base
* type is used for the property creation.
*
* @param parentUnionGenTOBuilder
* generated TO builder in which new property is created
* @param unionSubtype
* type definition of the ExtendedType
type which
* represents union subtype
* @param unionTypeName
* string with the name for unionSubtype
* @param regularExpressions
* list of strings with the regular expressions
*/
private void resolveExtendedSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
final ExtendedType unionSubtype, final String unionTypeName, final List regularExpressions,
final SchemaNode parentNode) {
final Type genTO = findGenTO(unionTypeName, parentNode);
if (genTO != null) {
updateUnionTypeAsProperty(parentUnionGenTOBuilder, genTO, genTO.getName());
} else {
final TypeDefinition> baseType = baseTypeDefForExtendedType(unionSubtype);
if (unionTypeName.equals(baseType.getQName().getLocalName())) {
final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(baseType,
parentNode);
if (javaType != null) {
updateUnionTypeAsProperty(parentUnionGenTOBuilder, javaType, unionTypeName);
}
}
if (baseType instanceof StringType) {
regularExpressions.addAll(resolveRegExpressionsFromTypedef(unionSubtype));
}
}
}
/**
* Searches for generated TO for searchedTypeDef
type
* definition in {@link #genTypeDefsContextMap genTypeDefsContextMap}
*
* @param searchedTypeName
* string with name of searchedTypeDef
* @return generated TO for searchedTypeDef
or
* null
it it doesn't exist
*/
private Type findGenTO(final String searchedTypeName, final SchemaNode parentNode) {
final Module typeModule = findParentModule(schemaContext, parentNode);
if (typeModule != null && typeModule.getName() != null) {
final Map genTOs = genTypeDefsContextMap.get(typeModule.getName());
if (genTOs != null) {
return genTOs.get(searchedTypeName);
}
}
return null;
}
/**
* Stores generated TO created from genTOBuilder
for
* newTypeDef
to {@link #genTypeDefsContextMap
* genTypeDefsContextMap} if the module for newTypeDef
exists
*
* @param newTypeDef
* type definition for which is genTOBuilder
created
* @param genTOBuilder
* generated TO builder which is converted to generated TO and
* stored
*/
private void storeGenTO(TypeDefinition> newTypeDef, GeneratedTOBuilder genTOBuilder, SchemaNode parentNode) {
if (!(newTypeDef instanceof UnionType)) {
Map genTOsMap = null;
final Module parentModule = findParentModule(schemaContext, parentNode);
if (parentModule != null && parentModule.getName() != null) {
genTOsMap = genTypeDefsContextMap.get(parentModule.getName());
genTOsMap.put(newTypeDef.getQName().getLocalName(), genTOBuilder.toInstance());
}
}
}
/**
* Adds a new property with the name propertyName
and with type
* type
to unonGenTransObject
.
*
* @param unionGenTransObject
* generated TO to which should be property added
* @param type
* JAVA type
of the property which should be added
* to unionGentransObject
* @param propertyName
* string with name of property which should be added to
* unionGentransObject
*/
private void updateUnionTypeAsProperty(final GeneratedTOBuilder unionGenTransObject, final Type type,
final String propertyName) {
if (unionGenTransObject != null && type != null && !unionGenTransObject.containsProperty(propertyName)) {
final GeneratedPropertyBuilder propBuilder = unionGenTransObject
.addProperty(parseToValidParamName(propertyName));
propBuilder.setReturnType(type);
unionGenTransObject.addEqualsIdentity(propBuilder);
unionGenTransObject.addHashIdentity(propBuilder);
unionGenTransObject.addToStringProperty(propBuilder);
}
}
/**
* Converts typedef
to the generated TO builder.
*
* @param basePackageName
* string with name of package to which the module belongs
* @param typedef
* type definition from which is the generated TO builder created
* @return generated TO builder which contains data from
* typedef
and basePackageName
*/
private GeneratedTOBuilder typedefToTransferObject(final String basePackageName, final TypeDefinition> typedef) {
final String packageName = packageNameForGeneratedType(basePackageName, typedef.getPath());
final String typeDefTOName = typedef.getQName().getLocalName();
if ((packageName != null) && (typedef != null) && (typeDefTOName != null)) {
final String genTOName = parseToClassName(typeDefTOName);
final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(packageName, genTOName);
newType.addComment(typedef.getDescription());
return newType;
}
return null;
}
/**
* Converts typeDef
which should be of the type
* BitsTypeDefinition
to GeneratedTOBuilder
.
*
* All the bits of the typeDef are added to returning generated TO as
* properties.
*
* @param basePackageName
* string with name of package to which the module belongs
* @param typeDef
* type definition from which is the generated TO builder created
* @param typeDefName
* string with the name for generated TO builder
* @return generated TO builder which represents typeDef
* @throws IllegalArgumentException
*
* - if
typeDef
equals null
* - if
basePackageName
equals null
*
*/
public GeneratedTOBuilder provideGeneratedTOBuilderForBitsTypeDefinition(final String basePackageName,
final TypeDefinition> typeDef, String typeDefName) {
Preconditions.checkArgument(typeDef != null, "typeDef cannot be NULL!");
Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL!");
if (typeDef instanceof BitsTypeDefinition) {
BitsTypeDefinition bitsTypeDefinition = (BitsTypeDefinition) typeDef;
final String typeName = parseToClassName(typeDefName);
final GeneratedTOBuilder genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typeName);
final List bitList = bitsTypeDefinition.getBits();
GeneratedPropertyBuilder genPropertyBuilder;
for (final Bit bit : bitList) {
String name = bit.getName();
genPropertyBuilder = genTOBuilder.addProperty(parseToValidParamName(name));
genPropertyBuilder.setReadOnly(true);
genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
genTOBuilder.addEqualsIdentity(genPropertyBuilder);
genTOBuilder.addHashIdentity(genPropertyBuilder);
genTOBuilder.addToStringProperty(genPropertyBuilder);
}
return genTOBuilder;
}
return null;
}
/**
* Converts the pattern constraints from typedef
to the list of
* the strings which represents these constraints.
*
* @param typedef
* extended type in which are the pattern constraints sought
* @return list of strings which represents the constraint patterns
* @throws IllegalArgumentException
* if typedef
equals null
*
*/
private List resolveRegExpressionsFromTypedef(ExtendedType typedef) {
final List regExps = new ArrayList();
Preconditions.checkArgument(typedef != null, "typedef can't be null");
final TypeDefinition> strTypeDef = baseTypeDefForExtendedType(typedef);
if (strTypeDef instanceof StringType) {
final List patternConstraints = typedef.getPatterns();
if (!patternConstraints.isEmpty()) {
String regEx;
String modifiedRegEx;
for (PatternConstraint patternConstraint : patternConstraints) {
regEx = patternConstraint.getRegularExpression();
modifiedRegEx = StringEscapeUtils.escapeJava(regEx);
regExps.add(modifiedRegEx);
}
}
}
return regExps;
}
/**
*
* Adds to the genTOBuilder
the constant which contains regular
* expressions from the regularExpressions
*
* @param genTOBuilder
* generated TO builder to which are
* regular expressions
added
* @param regularExpressions
* list of string which represent regular expressions
* @throws IllegalArgumentException
*
* - if
genTOBuilder
equals null
* - if
regularExpressions
equals null
*
*/
private void addStringRegExAsConstant(GeneratedTOBuilder genTOBuilder, List regularExpressions) {
if (genTOBuilder == null) {
throw new IllegalArgumentException("Generated transfer object builder can't be null");
}
if (regularExpressions == null) {
throw new IllegalArgumentException("List of regular expressions can't be null");
}
if (!regularExpressions.isEmpty()) {
genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
regularExpressions);
}
}
/**
* Creates generated TO with data about inner extended type
* innerExtendedType
, about the package name
* typedefName
and about the generated TO name
* typedefName
.
*
* It is supposed that innerExtendedType
is already present in
* {@link TypeProviderImpl#genTypeDefsContextMap genTypeDefsContextMap} to
* be possible set it as extended type for the returning generated TO.
*
* @param innerExtendedType
* extended type which is part of some other extended type
* @param basePackageName
* string with the package name of the module
* @param typedefName
* string with the name for the generated TO
* @return generated TO which extends generated TO for
* innerExtendedType
* @throws IllegalArgumentException
*
* - if
extendedType
equals null
* - if
basePackageName
equals null
* - if
typedefName
equals null
*
*/
private GeneratedTransferObject provideGeneratedTOFromExtendedType(final ExtendedType innerExtendedType,
final String basePackageName, final String typedefName) {
Preconditions.checkArgument(innerExtendedType != null, "Extended type cannot be NULL!");
Preconditions.checkArgument(basePackageName != null, "String with base package name cannot be NULL!");
Preconditions.checkArgument(typedefName != null, "String with type definition name cannot be NULL!");
final String classTypedefName = parseToClassName(typedefName);
final String innerTypeDef = innerExtendedType.getQName().getLocalName();
final GeneratedTOBuilder genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, classTypedefName);
Map typeMap = null;
final Module parentModule = findParentModule(schemaContext, innerExtendedType);
if (parentModule != null) {
typeMap = genTypeDefsContextMap.get(parentModule.getName());
}
if (typeMap != null) {
Type type = typeMap.get(innerTypeDef);
if (type instanceof GeneratedTransferObject) {
genTOBuilder.setExtendsType((GeneratedTransferObject) type);
}
}
return genTOBuilder.toInstance();
}
/**
* Finds out for each type definition how many immersion (depth) is
* necessary to get to the base type. Every type definition is inserted to
* the map which key is depth and value is list of type definitions with
* equal depth. In next step are lists from this map concatenated to one
* list in ascending order according to their depth. All type definitions
* are in the list behind all type definitions on which depends.
*
* @param unsortedTypeDefinitions
* list of type definitions which should be sorted by depth
* @return list of type definitions sorted according their each other
* dependencies (type definitions which are depend on other type
* definitions are in list behind them).
*/
private List> sortTypeDefinitionAccordingDepth(
final Set> unsortedTypeDefinitions) {
List> sortedTypeDefinition = new ArrayList<>();
Map>> typeDefinitionsDepths = new TreeMap<>();
for (TypeDefinition> unsortedTypeDefinition : unsortedTypeDefinitions) {
final int depth = getTypeDefinitionDepth(unsortedTypeDefinition);
List> typeDefinitionsConcreteDepth = typeDefinitionsDepths.get(depth);
if (typeDefinitionsConcreteDepth == null) {
typeDefinitionsConcreteDepth = new ArrayList>();
typeDefinitionsDepths.put(depth, typeDefinitionsConcreteDepth);
}
typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
}
// keys are in ascending order
Set depths = typeDefinitionsDepths.keySet();
for (Integer depth : depths) {
sortedTypeDefinition.addAll(typeDefinitionsDepths.get(depth));
}
return sortedTypeDefinition;
}
/**
* Returns how many immersion is necessary to get from the type definition
* to the base type.
*
* @param typeDefinition
* type definition for which is depth sought.
* @return number of immersions which are necessary to get from the type
* definition to the base type
*/
private int getTypeDefinitionDepth(final TypeDefinition> typeDefinition) {
Preconditions.checkArgument(typeDefinition != null, "Type definition can't be null");
int depth = 1;
TypeDefinition> baseType = typeDefinition.getBaseType();
if (baseType instanceof ExtendedType) {
depth = depth + getTypeDefinitionDepth(typeDefinition.getBaseType());
} else if (baseType instanceof UnionType) {
List> childTypeDefinitions = ((UnionType) baseType).getTypes();
int maxChildDepth = 0;
int childDepth = 1;
for (TypeDefinition> childTypeDefinition : childTypeDefinitions) {
childDepth = childDepth + getTypeDefinitionDepth(childTypeDefinition.getBaseType());
if (childDepth > maxChildDepth) {
maxChildDepth = childDepth;
}
}
return maxChildDepth;
}
return depth;
}
/**
* Returns string which contains the same value as name
but
* integer suffix is incremented by one. If name
contains no
* number suffix then number 1 is added.
*
* @param name
* string with name of augmented node
* @return string with the number suffix incremented by one (or 1 is added)
*/
private String provideAvailableNameForGenTOBuilder(String name) {
Pattern searchedPattern = Pattern.compile("[0-9]+\\z");
Matcher mtch = searchedPattern.matcher(name);
if (mtch.find()) {
final int newSuffix = Integer.valueOf(name.substring(mtch.start())) + 1;
return name.substring(0, mtch.start()) + newSuffix;
} else {
return name + 1;
}
}
}