/* * 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 * * */ 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 * */ @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}
* * @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; } } }