2 * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.mdsal.binding.javav2.generator.yang.types;
11 import static org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil.encodeAngleBrackets;
12 import static org.opendaylight.mdsal.binding.javav2.generator.yang.types.TypeProviderImpl.addUnitsToGenTO;
13 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
15 import com.google.common.annotations.Beta;
16 import com.google.common.base.Preconditions;
17 import com.google.common.collect.ImmutableList;
18 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Date;
23 import java.util.List;
26 import java.util.TreeMap;
27 import org.apache.commons.lang3.StringEscapeUtils;
28 import org.opendaylight.mdsal.binding.javav2.generator.context.ModuleContext;
29 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil;
30 import org.opendaylight.mdsal.binding.javav2.generator.util.TypeConstants;
31 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
32 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.EnumerationBuilderImpl;
33 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedPropertyBuilderImpl;
34 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
35 import org.opendaylight.mdsal.binding.javav2.model.api.ConcreteType;
36 import org.opendaylight.mdsal.binding.javav2.model.api.Enumeration;
37 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
38 import org.opendaylight.mdsal.binding.javav2.model.api.Restrictions;
39 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
40 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedPropertyBuilder;
41 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTOBuilder;
42 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
43 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
44 import org.opendaylight.yangtools.yang.common.QName;
45 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
46 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
47 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
48 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
51 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
53 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.Module;
55 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
56 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
57 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
58 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
59 import org.opendaylight.yangtools.yang.model.api.Status;
60 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
61 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
62 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
63 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
64 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
67 * Auxiliary util class for {@link TypeProviderImpl} class
70 final class TypeGenHelper {
72 private TypeGenHelper() {
73 throw new UnsupportedOperationException("Util class");
77 * Gets base type definition for <code>extendTypeDef</code>. The method is
78 * recursively called until non <code>ExtendedType</code> type is found.
80 * @param extendTypeDef
81 * type definition for which is the base type definition sought
82 * @return type definition which is base type for <code>extendTypeDef</code>
83 * @throws IllegalArgumentException
84 * if <code>extendTypeDef</code> equal null
86 static TypeDefinition<?> baseTypeDefForExtendedType(final TypeDefinition<?> extendTypeDef) {
87 Preconditions.checkArgument(extendTypeDef != null, "Type Definition reference cannot be NULL!");
89 TypeDefinition<?> ret = extendTypeDef;
90 while (ret.getBaseType() != null) {
91 ret = ret.getBaseType();
98 * Creates generated TO with data about inner extended type
99 * <code>innerExtendedType</code>, about the package name
100 * <code>typedefName</code> and about the generated TO name
101 * <code>typedefName</code>.
103 * It is supposed that <code>innerExtendedType</code> is already present in
104 * {@link TypeProviderImpl#genTypeDefsContextMap genTypeDefsContextMap} to
105 * be possible set it as extended type for the returning generated TO.
109 * @param innerExtendedType
110 * extended type which is part of some other extended type
111 * @param basePackageName
112 * string with the package name of the module
115 * @return generated TO which extends generated TO for
116 * <code>innerExtendedType</code>
117 * @throws IllegalArgumentException
119 * <li>if <code>extendedType</code> equals null</li>
120 * <li>if <code>basePackageName</code> equals null</li>
121 * <li>if <code>typedefName</code> equals null</li>
124 @SuppressWarnings({ "rawtypes", "unchecked" })
125 static GeneratedTransferObject provideGeneratedTOFromExtendedType(final TypeDefinition<?> typedef, final
126 TypeDefinition<?> innerExtendedType, final String basePackageName, final String moduleName, final SchemaContext
127 schemaContext, final Map<String, Map<Date, Map<String, Type>>> genTypeDefsContextMap,
128 ModuleContext context) {
130 Preconditions.checkArgument(innerExtendedType != null, "Extended type cannot be NULL!");
131 Preconditions.checkArgument(basePackageName != null, "String with base package name cannot be NULL!");
133 final String typedefName = typedef.getQName().getLocalName();
134 final String innerTypeDef = innerExtendedType.getQName().getLocalName();
135 final GeneratedTOBuilderImpl genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typedefName, context);
136 final String typedefDescription = encodeAngleBrackets(typedef.getDescription());
138 genTOBuilder.setDescription(typedefDescription);
139 genTOBuilder.setReference(typedef.getReference());
140 genTOBuilder.setSchemaPath((List) typedef.getPath().getPathFromRoot());
141 genTOBuilder.setModuleName(moduleName);
142 genTOBuilder.setTypedef(true);
143 final Restrictions r = BindingGeneratorUtil.getRestrictions(typedef);
144 genTOBuilder.setRestrictions(r);
145 if (typedef.getStatus() == Status.DEPRECATED) {
146 genTOBuilder.addAnnotation("", "Deprecated");
149 if (baseTypeDefForExtendedType(innerExtendedType) instanceof UnionTypeDefinition) {
150 genTOBuilder.setIsUnion(true);
153 Map<Date, Map<String, Type>> modulesByDate = null;
154 Map<String, Type> typeMap = null;
155 final Module parentModule = findParentModule(schemaContext, innerExtendedType);
156 if (parentModule != null) {
157 modulesByDate = genTypeDefsContextMap.get(parentModule.getName());
158 typeMap = modulesByDate.get(parentModule.getRevision());
161 if (typeMap != null) {
162 final Type type = typeMap.get(innerTypeDef);
163 if (type instanceof GeneratedTransferObject) {
164 genTOBuilder.setExtendsType((GeneratedTransferObject) type);
167 addUnitsToGenTO(genTOBuilder, typedef.getUnits());
168 makeSerializable(genTOBuilder);
170 return genTOBuilder.toInstance();
174 * Wraps base YANG type to generated TO.
176 * @param basePackageName
177 * string with name of package to which the module belongs
179 * type definition which is converted to the TO
181 * JAVA <code>Type</code> to which is <code>typedef</code> mapped
182 * @return generated transfer object which represent<code>javaType</code>
184 static GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition<?> typedef,
185 final Type javaType, final String moduleName, ModuleContext context) {
186 Preconditions.checkNotNull(javaType, "javaType cannot be null");
187 final String propertyName = "value";
189 final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef, moduleName, context);
190 genTOBuilder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
191 final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty(propertyName);
192 genPropBuilder.setReturnType(javaType);
193 genTOBuilder.addEqualsIdentity(genPropBuilder);
194 genTOBuilder.addHashIdentity(genPropBuilder);
195 genTOBuilder.addToStringProperty(genPropBuilder);
196 if (typedef.getStatus() == Status.DEPRECATED) {
197 genTOBuilder.addAnnotation("", "Deprecated");
199 if (javaType instanceof ConcreteType && "String".equals(javaType.getName()) && typedef.getBaseType() != null) {
200 final List<String> regExps = resolveRegExpressionsFromTypedef(typedef);
201 addStringRegExAsConstant(genTOBuilder, regExps);
203 addUnitsToGenTO(genTOBuilder, typedef.getUnits());
204 genTOBuilder.setTypedef(true);
205 makeSerializable((GeneratedTOBuilderImpl) genTOBuilder);
206 return genTOBuilder.toInstance();
210 * Converts the pattern constraints from <code>typedef</code> to the list of
211 * the strings which represents these constraints.
214 * extended type in which are the pattern constraints sought
215 * @return list of strings which represents the constraint patterns
216 * @throws IllegalArgumentException
217 * if <code>typedef</code> equals null
220 static List<String> resolveRegExpressionsFromTypedef(final TypeDefinition<?> typedef) {
221 Preconditions.checkArgument(typedef != null, "typedef can't be null");
223 final List<PatternConstraint> patternConstraints;
224 if (typedef instanceof StringTypeDefinition) {
225 patternConstraints = ((StringTypeDefinition) typedef).getPatternConstraints();
227 patternConstraints = ImmutableList.of();
230 final List<String> regExps = new ArrayList<>(patternConstraints.size());
231 for (final PatternConstraint patternConstraint : patternConstraints) {
232 final String regEx = patternConstraint.getRegularExpression();
233 final String modifiedRegEx = StringEscapeUtils.escapeJava(regEx);
234 regExps.add(modifiedRegEx);
241 * Finds out for each type definition how many immersion (depth) is
242 * necessary to get to the base type. Every type definition is inserted to
243 * the map which key is depth and value is list of type definitions with
244 * equal depth. In next step are lists from this map concatenated to one
245 * list in ascending order according to their depth. All type definitions
246 * are in the list behind all type definitions on which depends.
248 * @param unsortedTypeDefinitions
249 * list of type definitions which should be sorted by depth
250 * @return list of type definitions sorted according their each other
251 * dependencies (type definitions which are depend on other type
252 * definitions are in list behind them).
254 static List<TypeDefinition<?>> sortTypeDefinitionAccordingDepth(
255 final Collection<TypeDefinition<?>> unsortedTypeDefinitions) {
256 final List<TypeDefinition<?>> sortedTypeDefinition = new ArrayList<>();
258 final Map<Integer, List<TypeDefinition<?>>> typeDefinitionsDepths = new TreeMap<>();
259 for (final TypeDefinition<?> unsortedTypeDefinition : unsortedTypeDefinitions) {
260 final int depth = getTypeDefinitionDepth(unsortedTypeDefinition);
261 final List<TypeDefinition<?>> typeDefinitionsConcreteDepth = typeDefinitionsDepths.computeIfAbsent(depth, k -> new ArrayList<>());
262 typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
265 // SortedMap guarantees order corresponding to keys in ascending order
266 typeDefinitionsDepths.values().forEach(sortedTypeDefinition::addAll);
268 return sortedTypeDefinition;
273 * Adds to the <code>genTOBuilder</code> the constant which contains regular
274 * expressions from the <code>regularExpressions</code>
276 * @param genTOBuilder
277 * generated TO builder to which are
278 * <code>regular expressions</code> added
279 * @param regularExpressions
280 * list of string which represent regular expressions
281 * @throws IllegalArgumentException
283 * <li>if <code>genTOBuilder</code> equals null</li>
284 * <li>if <code>regularExpressions</code> equals null</li>
287 static void addStringRegExAsConstant(final GeneratedTOBuilder genTOBuilder, final List<String> regularExpressions) {
288 if (genTOBuilder == null) {
289 throw new IllegalArgumentException("Generated transfer object builder can't be null");
291 if (regularExpressions == null) {
292 throw new IllegalArgumentException("List of regular expressions can't be null");
294 if (!regularExpressions.isEmpty()) {
295 genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
301 * Returns how many immersion is necessary to get from the type definition
304 * @param typeDefinition
305 * type definition for which is depth sought.
306 * @return number of immersions which are necessary to get from the type
307 * definition to the base type
309 private static int getTypeDefinitionDepth(final TypeDefinition<?> typeDefinition) {
310 if (typeDefinition == null) {
313 final TypeDefinition<?> baseType = typeDefinition.getBaseType();
314 if (baseType == null) {
319 if (baseType.getBaseType() != null) {
320 depth = depth + getTypeDefinitionDepth(baseType);
321 } else if (baseType instanceof UnionTypeDefinition) {
322 final List<TypeDefinition<?>> childTypeDefinitions = ((UnionTypeDefinition) baseType).getTypes();
323 int maxChildDepth = 0;
325 for (final TypeDefinition<?> childTypeDefinition : childTypeDefinitions) {
326 childDepth = childDepth + getTypeDefinitionDepth(childTypeDefinition);
327 if (childDepth > maxChildDepth) {
328 maxChildDepth = childDepth;
331 return maxChildDepth;
336 static List<TypeDefinition<?>> getAllTypedefs(final Module module) {
337 final List<TypeDefinition<?>> ret = new ArrayList<>();
339 fillRecursively(ret, module);
341 final Set<NotificationDefinition> notifications = module.getNotifications();
342 for (final NotificationDefinition notificationDefinition : notifications) {
343 fillRecursively(ret, notificationDefinition);
346 final Set<RpcDefinition> rpcs = module.getRpcs();
347 for (final RpcDefinition rpcDefinition : rpcs) {
348 ret.addAll(rpcDefinition.getTypeDefinitions());
349 final ContainerSchemaNode input = rpcDefinition.getInput();
351 fillRecursively(ret, input);
353 final ContainerSchemaNode output = rpcDefinition.getOutput();
354 if (output != null) {
355 fillRecursively(ret, output);
359 final Collection<DataSchemaNode> potentials = module.getChildNodes();
361 for (final DataSchemaNode potential : potentials) {
362 if (potential instanceof ActionNodeContainer) {
363 final Set<ActionDefinition> actions = ((ActionNodeContainer) potential).getActions();
364 for (final ActionDefinition action: actions) {
365 final ContainerSchemaNode input = action.getInput();
367 fillRecursively(ret, input);
369 final ContainerSchemaNode output = action.getOutput();
370 if (output != null) {
371 fillRecursively(ret, output);
380 private static void fillRecursively(final List<TypeDefinition<?>> list, final DataNodeContainer container) {
381 final Collection<DataSchemaNode> childNodes = container.getChildNodes();
382 if (childNodes != null) {
383 childNodes.stream().filter(childNode -> !childNode.isAugmenting()).forEach(childNode -> {
384 if (childNode instanceof ContainerSchemaNode) {
385 fillRecursively(list, (ContainerSchemaNode) childNode);
386 } else if (childNode instanceof ListSchemaNode) {
387 fillRecursively(list, (ListSchemaNode) childNode);
388 } else if (childNode instanceof ChoiceSchemaNode) {
389 final Set<ChoiceCaseNode> cases = ((ChoiceSchemaNode) childNode).getCases();
391 for (final ChoiceCaseNode caseNode : cases) {
392 fillRecursively(list, caseNode);
399 list.addAll(container.getTypeDefinitions());
401 final Set<GroupingDefinition> groupings = container.getGroupings();
402 if (groupings != null) {
403 for (final GroupingDefinition grouping : groupings) {
404 fillRecursively(list, grouping);
410 * Add {@link Serializable} to implemented interfaces of this TO. Also
411 * compute and add serialVersionUID property.
414 * transfer object which needs to be serializable
416 static void makeSerializable(final GeneratedTOBuilderImpl gto) {
417 gto.addImplementsType(Types.typeForClass(Serializable.class));
418 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
419 prop.setValue(Long.toString(BindingGeneratorUtil.computeDefaultSUID(gto)));
424 * Converts <code>enumTypeDef</code> to
429 * enumeration type definition which is converted to enumeration
431 * string with name which is used as the enumeration name
432 * @return enumeration type which is built with data (name, enum values)
433 * from <code>enumTypeDef</code>
434 * @throws IllegalArgumentException
436 * <li>if <code>enumTypeDef</code> equals null</li>
437 * <li>if enum values of <code>enumTypeDef</code> equal null</li>
438 * <li>if Q name of <code>enumTypeDef</code> equal null</li>
439 * <li>if name of <code>enumTypeDef</code> equal null</li>
442 @SuppressWarnings({ "rawtypes", "unchecked" })
443 static Enumeration provideTypeForEnum(final EnumTypeDefinition enumTypeDef, final String enumName,
444 final SchemaNode parentNode, final SchemaContext schemaContext, ModuleContext context) {
445 Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
446 Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
447 "Local Name in EnumTypeDefinition QName cannot be NULL!");
448 final Module module = findParentModule(schemaContext, parentNode);
449 final String basePackageName = BindingMapping.getRootPackageName(module);
450 final String packageName;
452 if (parentNode instanceof TypeDefinition) {
453 packageName = BindingGeneratorUtil.packageNameWithNamespacePrefix(
454 BindingMapping.getRootPackageName(module),
455 BindingNamespaceType.Typedef);
457 packageName = basePackageName;
460 final EnumerationBuilderImpl enumBuilder = new EnumerationBuilderImpl(packageName, enumName, context);
461 final String enumTypedefDescription = encodeAngleBrackets(enumTypeDef.getDescription());
462 enumBuilder.setDescription(enumTypedefDescription);
463 enumBuilder.setReference(enumTypeDef.getReference());
464 enumBuilder.setModuleName(module.getName());
465 enumBuilder.setSchemaPath((List) enumTypeDef.getPath().getPathFromRoot());
466 enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
467 return enumBuilder.toInstance(null);
471 * Converts <code>typedef</code> to the generated TO builder.
473 * @param basePackageName
474 * string with name of package to which the module belongs
476 * type definition from which is the generated TO builder created
477 * @return generated TO builder which contains data from
478 * <code>typedef</code> and <code>basePackageName</code>
480 @SuppressWarnings({ "unchecked", "rawtypes" })
481 private static GeneratedTOBuilderImpl typedefToTransferObject(final String basePackageName,
482 final TypeDefinition<?> typedef, final String moduleName, ModuleContext context) {
483 final String typeDefTOName = typedef.getQName().getLocalName();
485 if ((basePackageName != null) && (typeDefTOName != null)) {
486 final GeneratedTOBuilderImpl newType = new GeneratedTOBuilderImpl(basePackageName, typeDefTOName, context);
487 final String typedefDescription = encodeAngleBrackets(typedef.getDescription());
489 newType.setDescription(typedefDescription);
490 newType.setReference(typedef.getReference());
491 newType.setSchemaPath((List) typedef.getPath().getPathFromRoot());
492 newType.setModuleName(moduleName);
499 static Module getParentModule(final SchemaNode node, final SchemaContext schemaContext) {
500 final QName qname = node.getPath().getPathFromRoot().iterator().next();
501 final URI namespace = qname.getNamespace();
502 final Date revision = qname.getRevision();
503 return schemaContext.findModuleByNamespaceAndRevision(namespace, revision);