import org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil;
import org.opendaylight.mdsal.binding.model.util.BindingTypes;
import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl;
+import org.opendaylight.mdsal.binding.model.util.TypeConstants;
import org.opendaylight.mdsal.binding.model.util.Types;
import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder;
import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedPropertyBuilderImpl;
import org.opendaylight.mdsal.binding.yang.types.AbstractTypeProvider;
+import org.opendaylight.mdsal.binding.yang.types.BaseYangTypes;
import org.opendaylight.mdsal.binding.yang.types.GroupingDefinitionDependencySort;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
import org.opendaylight.yangtools.yang.binding.BindingMapping;
import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+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.DataNodeIterator;
import org.opendaylight.yangtools.yang.model.util.ModuleDependencySort;
return false;
}
+ private void addPatternConstant(final GeneratedTypeBuilder typeBuilder, final String leafName,
+ final List<PatternConstraint> patternConstraints) {
+ if (!patternConstraints.isEmpty()) {
+ final StringBuilder field = new StringBuilder().append(TypeConstants.PATTERN_CONSTANT_NAME).append("_")
+ .append(BindingMapping.getPropertyName(leafName).toUpperCase());
+ typeBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), field.toString(),
+ typeProvider.resolveRegExpressions(patternConstraints));
+ }
+ }
+
/**
* Converts <code>leaf</code> to the getter method which is added to
* <code>typeBuilder</code>.
final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
returnType = typeProvider.javaTypeForSchemaDefinitionType(getBaseOrDeclaredType(typeDef), leaf,
restrictions);
+ addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints());
}
} else {
final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, restrictions);
+ addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints());
}
if (returnType == null) {
} else {
final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
+ addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints());
}
} else {
final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
+ addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints());
}
final ParameterizedType listType = Types.listTypeFor(returnType);
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
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.StringTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.ModuleDependencySort;
public abstract GeneratedTypeBuilder newGeneratedTypeBuilder(String packageName, String name);
+ /**
+ * Converts the pattern constraints to the list of the strings which represents these constraints.
+ *
+ * @param patternConstraints list of pattern constraints
+ * @return list of strings which represents the constraint patterns
+ */
+ public abstract Map<String, String> resolveRegExpressions(List<PatternConstraint> patternConstraints);
+
abstract void addCodegenInformation(GeneratedTypeBuilderBase<?> genTOBuilder, TypeDefinition<?> typeDef);
/**
* if <code>typedef</code> equals null
*
*/
- abstract Map<String, String> resolveRegExpressionsFromTypedef(TypeDefinition<?> typedef);
+ private Map<String, String> resolveRegExpressionsFromTypedef(final TypeDefinition<?> typedef) {
+ if (!(typedef instanceof StringTypeDefinition)) {
+ return ImmutableMap.of();
+ }
+
+ // TODO: run diff against base ?
+ return resolveRegExpressions(((StringTypeDefinition) typedef).getPatternConstraints());
+ }
/**
* Converts <code>dataNode</code> to JAVA <code>Type</code>.
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
-import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
typeDef.getReference().ifPresent(genTOBuilder::setReference);
}
+ /**
+ * Converts the pattern constraints to the list of
+ * the strings which represents these constraints.
+ *
+ * @param patternConstraints
+ * list of pattern constraints
+ * @return list of strings which represents the constraint patterns
+ */
@Override
- Map<String, String> resolveRegExpressionsFromTypedef(final TypeDefinition<?> typedef) {
- if (!(typedef instanceof StringTypeDefinition)) {
+ public Map<String, String> resolveRegExpressions(final List<PatternConstraint> patternConstraints) {
+ if (patternConstraints.isEmpty()) {
return ImmutableMap.of();
}
- // TODO: run diff against base ?
- final List<PatternConstraint> patternConstraints = ((StringTypeDefinition) typedef).getPatternConstraints();
final Map<String, String> regExps = Maps.newHashMapWithExpectedSize(patternConstraints.size());
for (PatternConstraint patternConstraint : patternConstraints) {
String regEx = patternConstraint.getJavaPatternString();
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableMap;
+import java.util.List;
import java.util.Map;
import org.opendaylight.mdsal.binding.model.api.type.builder.EnumBuilder;
import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
/**
* {@link AbstractTypeProvider} which generates enough type information for runtime support. For a codegen-compatible
}
@Override
- Map<String, String> resolveRegExpressionsFromTypedef(final TypeDefinition<?> typedef) {
+ public Map<String, String> resolveRegExpressions(final List<PatternConstraint> patternConstraints) {
return ImmutableMap.of();
}
public final class TypeConstants {
/**
- * Name of the class constant which holds the map of regular expressions that need to be enforced on the string
- * value. The map is keyed by Pattern-compatible string and values are XSD-compatible strings.
+ * Name or prefix (multiple patterns in builder class as composed with '_'
+ * and upper case of the field name) of the class constant which holds the map
+ * of regular expressions that need to be enforced on the string value.
+ * The map is keyed by Pattern-compatible string and values are XSD-compatible
+ * strings.
*/
public static final String PATTERN_CONSTANT_NAME = "PATTERN_CONSTANTS";
private static final LoadingCache<Class<?>, ConcreteType> TYPE_CACHE =
CacheBuilder.newBuilder().weakKeys().build(TYPE_LOADER);
- private static final Type SET_TYPE = typeForClass(Set.class);
- private static final Type LIST_TYPE = typeForClass(List.class);
- private static final Type MAP_TYPE = typeForClass(Map.class);
+ public static final Type SET_TYPE = typeForClass(Set.class);
+ public static final Type LIST_TYPE = typeForClass(List.class);
+ public static final Type MAP_TYPE = typeForClass(Map.class);
public static final ConcreteType BOOLEAN = typeForClass(Boolean.class);
public static final ConcreteType FUTURE = typeForClass(Future.class);
*/
package org.opendaylight.mdsal.binding.java.api.generator
+import static extension org.apache.commons.text.StringEscapeUtils.escapeJava;
+
import com.google.common.base.MoreObjects
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSortedSet
+import com.google.common.collect.ImmutableList
import java.util.ArrayList
import java.util.Arrays
import java.util.Collection
import java.util.Map
import java.util.Objects
import java.util.Set
+import java.util.regex.Pattern
import org.opendaylight.mdsal.binding.model.api.ConcreteType
import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
import org.opendaylight.mdsal.binding.model.api.GeneratedType
import org.opendaylight.mdsal.binding.model.api.MethodSignature
import org.opendaylight.mdsal.binding.model.api.Type
+import org.opendaylight.mdsal.binding.model.api.ParameterizedType
+import org.opendaylight.mdsal.binding.model.api.Restrictions
import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl
import org.opendaylight.mdsal.binding.model.util.Types
import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder
+import org.opendaylight.mdsal.binding.model.util.TypeConstants
import org.opendaylight.yangtools.concepts.Builder
import org.opendaylight.yangtools.yang.binding.Augmentable
import org.opendaylight.yangtools.yang.binding.AugmentationHolder
«generateFields(false)»
+ «constantsDeclarations()»
+
«generateAugmentField(false)»
«generateConstructorsFromIfcs(type)»
«ENDIF»
'''
+ def private constantsDeclarations() '''
+ «FOR c : type.getConstantDefinitions»
+ «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
+ «val cValue = c.value as Map<String, String>»
+ «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length).toLowerCase»
+ public static final «List.importedName»<String> «c.getName» = «ImmutableList.importedName».of(
+ «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
+ «IF cValue.size == 1»
+ private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile(«c.getName».get(0));
+ private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
+ «ELSE»
+ private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«c.getName»);
+ private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
+ FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
+ «ENDIF»
+ «ELSE»
+ «emitConstant(c)»
+ «ENDIF»
+ «ENDFOR»
+ '''
+
+ def private generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
+ «IF restrictions.rangeConstraint.present»
+ «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
+ restrictions.rangeConstraint.get, this)»
+ «ENDIF»
+ «IF restrictions.lengthConstraint.present»
+ «LengthGenerator.generateLengthChecker(field.fieldName.toString, actualType, restrictions.lengthConstraint.get, this)»
+ «ENDIF»
+ '''
+
+ def private checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType) '''
+ «IF restrictions.getRangeConstraint.isPresent»
+ «IF actualType instanceof ConcreteType»
+ «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value")»
+ «ELSE»
+ «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value.getValue()")»
+ «ENDIF»
+ «ENDIF»
+ «IF restrictions.getLengthConstraint.isPresent»
+ «IF actualType instanceof ConcreteType»
+ «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value")»
+ «ELSE»
+ «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value.getValue()")»
+ «ENDIF»
+ «ENDIF»
+
+ «val fieldUpperCase = property.fieldName.toString.toUpperCase()»
+ «FOR currentConstant : type.getConstantDefinitions»
+ «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
+ && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
+ «CodeHelpers.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«property.fieldName», «Constants.MEMBER_REGEX_LIST»«property.fieldName»);
+ «ENDIF»
+ «ENDFOR»
+ '''
+
+ def private Restrictions restrictionsForSetter(Type actualType) {
+ if (actualType instanceof GeneratedType) {
+ return null;
+ }
+ return actualType.restrictions;
+ }
+
+ def private generateListSetter(GeneratedProperty field, Type actualType) '''
+ «val restrictions = restrictionsForSetter(actualType)»
+ «IF restrictions !== null»
+ «generateCheckers(field, restrictions, actualType)»
+ «ENDIF»
+ public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
+ «IF restrictions !== null»
+ if (values != null) {
+ for («actualType.getFullyQualifiedName» value : values) {
+ «checkArgument(field, restrictions, actualType)»
+ }
+ }
+ «ENDIF»
+ this.«field.fieldName.toString» = values;
+ return this;
+ }
+
+ '''
+
+ def private generateSetter(GeneratedProperty field, Type actualType) '''
+ «val restrictions = restrictionsForSetter(actualType)»
+ «IF restrictions !== null»
+ «generateCheckers(field, restrictions, actualType)»
+ «ENDIF»
+
+ public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
+ «IF restrictions !== null»
+ if (value != null) {
+ «checkArgument(field, restrictions, actualType)»
+ }
+ «ENDIF»
+ this.«field.fieldName.toString» = value;
+ return this;
+ }
+ '''
+
+ private def Type getActualType(ParameterizedType ptype) {
+ return ptype.getActualTypeArguments.get(0)
+ }
+
/**
* Template method which generates setter methods
*
* @return string with the setter methods
*/
def private generateSetters() '''
- «FOR field : properties SEPARATOR '\n'»
- «/* FIXME: generate checkers as simple blocks and embed them directly in setters */»
- «val restrictions = field.returnType.restrictions»
- «IF !(field.returnType instanceof GeneratedType) && restrictions !== null»
- «IF restrictions.rangeConstraint.present»
- «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)»
- «rangeGenerator.generateRangeChecker(field.name.toFirstUpper, restrictions.rangeConstraint.get, this)»
-
- «ENDIF»
- «IF restrictions.lengthConstraint.present»
- «LengthGenerator.generateLengthChecker(field.fieldName.toString, field.returnType, restrictions.lengthConstraint.get, this)»
-
- «ENDIF»
+ «FOR property : properties»
+ «IF property.returnType instanceof ParameterizedType
+ && (property.returnType as ParameterizedType).rawType.equals(Types.LIST_TYPE)»
+ «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
+ «ELSE»
+ «generateSetter(property, property.returnType)»
«ENDIF»
- public «type.name»«BUILDER» set«field.name.toFirstUpper»(final «field.returnType.importedName» value) {
- «IF !(field.returnType instanceof GeneratedType) && restrictions !== null»
- «IF restrictions !== null && (restrictions.rangeConstraint.present || restrictions.lengthConstraint.present)»
- if (value != null) {
- «IF restrictions.rangeConstraint.present»
- «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)»
- «IF field.returnType instanceof ConcreteType»
- «rangeGenerator.generateRangeCheckerCall(field.name.toFirstUpper, "value")»
- «ELSE»
- «rangeGenerator.generateRangeCheckerCall(field.name.toFirstUpper, "value.getValue()")»
- «ENDIF»
- «ENDIF»
- «IF restrictions.lengthConstraint.present»
- «IF field.returnType instanceof ConcreteType»
- «LengthGenerator.generateLengthCheckerCall(field.fieldName.toString, "value")»
- «ELSE»
- «LengthGenerator.generateLengthCheckerCall(field.fieldName.toString, "value.getValue()")»
- «ENDIF»
- «ENDIF»
- }
- «ENDIF»
- «ENDIF»
- this.«field.fieldName» = value;
- return this;
- }
«ENDFOR»
- «IF augmentField !== null»
+ «IF augmentField !== null»
public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentationValue) {
if (augmentationValue == null) {
return remove«augmentField.name.toFirstUpper»(augmentationType);
«CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
«ENDFOR»
«IF augmentField !== null»
- «CodeHelpers.importedName».appendValue(helper, "«augmentField.name»", «augmentField.name».values());
+ «CodeHelpers.importedName».appendValue(helper, "«augmentField.name»", «augmentField.name».values());
«ENDIF»
return helper.toString();
}
}
def private generateRestrictions(Type type, String paramName, Type returnType) '''
- «val restrictions = type.getRestrictions»
+ «val restrictions = type.restrictions»
«IF restrictions !== null»
«IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
if («paramName» != null) {
public static final String DOT = ".";
/**
- * Name of the class constant which contains list of <code>Pattern</code> instances. The type of this constant
- * is Pattern[] for more than one pattern, or Pattern if there is only a single one.
+ * Name or prefix (multiple patterns in builder class as composed with '_'
+ * and upper case of the field name) of the class constant which contains list
+ * of <code>Pattern</code> instances. The type of this constant is Pattern[]
+ * for more than one pattern, or Pattern if there is only a single one.
*/
public static final String MEMBER_PATTERN_LIST = "patterns";
/**
- * Name of the class constant which contains a list of XSD regular expression strings. The type of this constant
- * is String[] (or String for single strings) and it corresponds to {@link #MEMBER_PATTERN_LIST} in both size and
- * ordering.
+ * Name or prefix (multiple patterns in builder class as composed with '_'
+ * and upper case of the field name) of the class constant which contains a list
+ * of XSD regular expression strings. The type of this constant is String[]
+ * (or String for single strings) and it corresponds to {@link #MEMBER_PATTERN_LIST}
+ * in both size and ordering.
*/
public static final String MEMBER_REGEX_LIST = "regexes";
def private generateConstants() '''
«IF !consts.empty»
«FOR c : consts»
- «IF c.name != TypeConstants.PATTERN_CONSTANT_NAME»
+ «IF !c.name.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
«emitConstant(c)»
«ENDIF»
«ENDFOR»
assertEquals("@Override\n" +
"public java.lang.String toString() {\n" +
" final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" +
- " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values()); \n" +
+ " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values());\n" +
" return helper.toString();\n" +
"}\n", genToString(mockAugment(mockGenType(TEST))).toString());
}
"public java.lang.String toString() {\n" +
" final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" +
" CodeHelpers.appendValue(helper, \"_test\", _test);\n" +
- " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values()); \n" +
+ " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values());\n" +
" return helper.toString();\n" +
"}\n", genToString(mockAugment(mockGenType("get" + TEST))).toString());
}
" final MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(\"test\");\n" +
" CodeHelpers.appendValue(helper, \"_test1\", _test1);\n" +
" CodeHelpers.appendValue(helper, \"_test2\", _test2);\n" +
- " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values()); \n" +
+ " CodeHelpers.appendValue(helper, \"augmentation\", augmentation.values());\n" +
" return helper.toString();\n" +
"}\n", genToString(mockAugment(mockGenTypeMoreMeth("get" + TEST))).toString());
}
--- /dev/null
+module test-pattern {
+ yang-version 1.1;
+ namespace "urn:test:pattern";
+ prefix pattern;
+ revision 2017-01-01;
+
+ container cont {
+ leaf test {
+ type string {
+ pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
+ pattern '[xX][mM][lL].*';
+ }
+ }
+
+ leaf test2 {
+ type string {
+ pattern '[0-9]*';
+ }
+ }
+
+
+ leaf-list test3 {
+ type string {
+ pattern '[a-zA-Z_]*';
+ pattern '[xX][mM][lL].*' {
+ modifier invert-match;
+ }
+ }
+ }
+
+ leaf-list test4 {
+ type string {
+ pattern '[a-z]*';
+ }
+ }
+ }
+}
\ No newline at end of file