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.ImmutableMap;
18 import com.google.common.collect.Maps;
19 import java.io.Serializable;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.List;
24 import java.util.Optional;
26 import java.util.TreeMap;
27 import org.opendaylight.mdsal.binding.javav2.generator.context.ModuleContext;
28 import org.opendaylight.mdsal.binding.javav2.generator.util.BindingGeneratorUtil;
29 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
30 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.EnumerationBuilderImpl;
31 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedPropertyBuilderImpl;
32 import org.opendaylight.mdsal.binding.javav2.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
33 import org.opendaylight.mdsal.binding.javav2.model.api.ConcreteType;
34 import org.opendaylight.mdsal.binding.javav2.model.api.Enumeration;
35 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
36 import org.opendaylight.mdsal.binding.javav2.model.api.Restrictions;
37 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
38 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedPropertyBuilder;
39 import org.opendaylight.mdsal.binding.javav2.model.api.type.builder.GeneratedTOBuilder;
40 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingNamespaceType;
41 import org.opendaylight.mdsal.binding.javav2.util.BindingMapping;
42 import org.opendaylight.yangtools.yang.common.Revision;
43 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
44 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
45 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
49 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
51 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.Module;
53 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
54 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
55 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
56 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
57 import org.opendaylight.yangtools.yang.model.api.Status;
58 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
59 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
60 import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
61 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
62 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
63 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
68 * Auxiliary util class for {@link TypeProviderImpl} class
71 public final class TypeGenHelper {
72 private static final Logger LOG = LoggerFactory.getLogger(TypeGenHelper.class);
74 private TypeGenHelper() {
75 throw new UnsupportedOperationException("Util class");
79 * Gets base type definition for <code>extendTypeDef</code>. The method is
80 * recursively called until non <code>ExtendedType</code> type is found.
82 * @param extendTypeDef
83 * type definition for which is the base type definition sought
84 * @return type definition which is base type for <code>extendTypeDef</code>
85 * @throws IllegalArgumentException
86 * if <code>extendTypeDef</code> equal null
88 static TypeDefinition<?> baseTypeDefForExtendedType(final TypeDefinition<?> extendTypeDef) {
89 Preconditions.checkArgument(extendTypeDef != null, "Type Definition reference cannot be NULL!");
91 TypeDefinition<?> ret = extendTypeDef;
92 while (ret.getBaseType() != null) {
93 ret = ret.getBaseType();
100 * Creates generated TO with data about inner extended type
101 * <code>innerExtendedType</code>, about the package name
102 * <code>typedefName</code> and about the generated TO name
103 * <code>typedefName</code>.
105 * It is supposed that <code>innerExtendedType</code> is already present in
106 * {@link TypeProviderImpl#genTypeDefsContextMap genTypeDefsContextMap} to
107 * be possible set it as extended type for the returning generated TO.
111 * @param innerExtendedType
112 * extended type which is part of some other extended type
113 * @param basePackageName
114 * string with the package name of the module
117 * @return generated TO which extends generated TO for
118 * <code>innerExtendedType</code>
119 * @throws IllegalArgumentException
121 * <li>if <code>extendedType</code> equals null</li>
122 * <li>if <code>basePackageName</code> equals null</li>
123 * <li>if <code>typedefName</code> equals null</li>
126 @SuppressWarnings({ "rawtypes", "unchecked" })
127 static GeneratedTransferObject provideGeneratedTOFromExtendedType(final TypeDefinition<?> typedef, final
128 TypeDefinition<?> innerExtendedType, final String basePackageName, final String moduleName, final SchemaContext
129 schemaContext, final Map<String, Map<Optional<Revision>, Map<String, Type>>> genTypeDefsContextMap,
130 final ModuleContext context) {
132 Preconditions.checkArgument(innerExtendedType != null, "Extended type cannot be NULL!");
133 Preconditions.checkArgument(basePackageName != null, "String with base package name cannot be NULL!");
135 final String typedefName = typedef.getQName().getLocalName();
136 final String innerTypeDef = innerExtendedType.getQName().getLocalName();
137 final GeneratedTOBuilderImpl genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typedefName, context);
138 final String typedefDescription = encodeAngleBrackets(typedef.getDescription().orElse(null));
140 genTOBuilder.setDescription(typedefDescription);
141 typedef.getReference().ifPresent(genTOBuilder::setReference);
142 genTOBuilder.setSchemaPath((List) typedef.getPath().getPathFromRoot());
143 genTOBuilder.setModuleName(moduleName);
144 genTOBuilder.setTypedef(true);
145 final Restrictions r = BindingGeneratorUtil.getRestrictions(typedef);
146 genTOBuilder.setRestrictions(r);
147 if (typedef.getStatus() == Status.DEPRECATED) {
148 genTOBuilder.addAnnotation("", "Deprecated");
151 if (baseTypeDefForExtendedType(innerExtendedType) instanceof UnionTypeDefinition) {
152 genTOBuilder.setIsUnion(true);
155 Map<Optional<Revision>, Map<String, Type>> modulesByDate = null;
156 Map<String, Type> typeMap = null;
157 final Module parentModule = findParentModule(schemaContext, innerExtendedType);
158 if (parentModule != null) {
159 modulesByDate = genTypeDefsContextMap.get(parentModule.getName());
160 typeMap = modulesByDate.get(parentModule.getRevision());
163 if (typeMap != null) {
164 final Type type = typeMap.get(innerTypeDef);
165 if (type instanceof GeneratedTransferObject) {
166 genTOBuilder.setExtendsType((GeneratedTransferObject) type);
169 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
170 makeSerializable(genTOBuilder);
172 return genTOBuilder.toInstance();
176 * Wraps base YANG type to generated TO.
178 * @param basePackageName
179 * string with name of package to which the module belongs
181 * type definition which is converted to the TO
183 * JAVA <code>Type</code> to which is <code>typedef</code> mapped
184 * @return generated transfer object which represent<code>javaType</code>
186 static GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition<?> typedef,
187 final Type javaType, final String moduleName, final ModuleContext context) {
188 Preconditions.checkNotNull(javaType, "javaType cannot be null");
189 final String propertyName = "value";
191 final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef, moduleName, context);
192 genTOBuilder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
193 final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty(propertyName);
194 genPropBuilder.setReturnType(javaType);
195 genTOBuilder.addEqualsIdentity(genPropBuilder);
196 genTOBuilder.addHashIdentity(genPropBuilder);
197 genTOBuilder.addToStringProperty(genPropBuilder);
198 if (typedef.getStatus() == Status.DEPRECATED) {
199 genTOBuilder.addAnnotation("", "Deprecated");
201 if (javaType instanceof ConcreteType && "String".equals(javaType.getName()) && typedef.getBaseType() != null) {
202 addStringRegExAsConstant(genTOBuilder, resolveRegExpressionsFromTypedef(typedef));
204 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
205 genTOBuilder.setTypedef(true);
206 makeSerializable((GeneratedTOBuilderImpl) genTOBuilder);
207 return genTOBuilder.toInstance();
211 * Converts the pattern constraints from <code>typedef</code> to the list of
212 * the strings which represents these constraints.
215 * extended type in which are the pattern constraints sought
216 * @return map of strings which represents the constraint patterns
217 * @throws IllegalArgumentException
218 * if <code>typedef</code> equals null
221 static Map<String, String> resolveRegExpressionsFromTypedef(final TypeDefinition<?> typedef) {
222 if (!(typedef instanceof StringTypeDefinition)) {
223 return ImmutableMap.of();
226 // TODO: run diff against base ?
227 return resolveRegExpressions(((StringTypeDefinition) typedef).getPatternConstraints());
231 * Converts the pattern constraints to the list of
232 * the strings which represents these constraints.
234 * @param patternConstraints
235 * list of pattern constraints
236 * @return list of strings which represents the constraint patterns
238 public static Map<String, String> resolveRegExpressions(final List<PatternConstraint> patternConstraints) {
239 if (patternConstraints.isEmpty()) {
240 return ImmutableMap.of();
243 final Map<String, String> regExps = Maps.newHashMapWithExpectedSize(patternConstraints.size());
244 for (PatternConstraint patternConstraint : patternConstraints) {
245 String regEx = patternConstraint.getJavaPatternString();
247 // The pattern can be inverted
248 final Optional<ModifierKind> optModifier = patternConstraint.getModifier();
249 if (optModifier.isPresent()) {
250 regEx = applyModifier(optModifier.get(), regEx);
253 regExps.put(regEx, patternConstraint.getRegularExpressionString());
259 private static String applyModifier(final ModifierKind modifier, final String pattern) {
262 return BindingMapping.negatePatternString(pattern);
264 LOG.warn("Ignoring unhandled modifier {}", modifier);
270 * Finds out for each type definition how many immersion (depth) is
271 * necessary to get to the base type. Every type definition is inserted to
272 * the map which key is depth and value is list of type definitions with
273 * equal depth. In next step are lists from this map concatenated to one
274 * list in ascending order according to their depth. All type definitions
275 * are in the list behind all type definitions on which depends.
277 * @param unsortedTypeDefinitions
278 * list of type definitions which should be sorted by depth
279 * @return list of type definitions sorted according their each other
280 * dependencies (type definitions which are depend on other type
281 * definitions are in list behind them).
283 static List<TypeDefinition<?>> sortTypeDefinitionAccordingDepth(
284 final Collection<TypeDefinition<?>> unsortedTypeDefinitions) {
285 final List<TypeDefinition<?>> sortedTypeDefinition = new ArrayList<>();
287 final Map<Integer, List<TypeDefinition<?>>> typeDefinitionsDepths = new TreeMap<>();
288 for (TypeDefinition<?> unsortedTypeDefinition : unsortedTypeDefinitions) {
289 final int depth = getTypeDefinitionDepth(unsortedTypeDefinition);
290 final List<TypeDefinition<?>> typeDefinitionsConcreteDepth = typeDefinitionsDepths.computeIfAbsent(depth, k -> new ArrayList<>());
291 typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
294 // SortedMap guarantees order corresponding to keys in ascending order
295 typeDefinitionsDepths.values().forEach(sortedTypeDefinition::addAll);
297 return sortedTypeDefinition;
302 * Adds to the <code>genTOBuilder</code> the constant which contains regular
303 * expressions from the <code>regularExpressions</code>
305 * @param genTOBuilder
306 * generated TO builder to which are
307 * <code>regular expressions</code> added
309 * list of string which represent regular expressions
311 static void addStringRegExAsConstant(final GeneratedTOBuilder genTOBuilder, final Map<String, String> expressions) {
312 if (!expressions.isEmpty()) {
313 genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), BindingMapping.PATTERN_CONSTANT_NAME,
314 ImmutableMap.copyOf(expressions));
319 * Returns how many immersion is necessary to get from the type definition
322 * @param typeDefinition
323 * type definition for which is depth sought.
324 * @return number of immersions which are necessary to get from the type
325 * definition to the base type
327 private static int getTypeDefinitionDepth(final TypeDefinition<?> typeDefinition) {
328 if (typeDefinition == null) {
331 final TypeDefinition<?> baseType = typeDefinition.getBaseType();
332 if (baseType == null) {
337 if (baseType.getBaseType() != null) {
338 depth = depth + getTypeDefinitionDepth(baseType);
339 } else if (baseType instanceof UnionTypeDefinition) {
340 final List<TypeDefinition<?>> childTypeDefinitions = ((UnionTypeDefinition) baseType).getTypes();
341 int maxChildDepth = 0;
343 for (TypeDefinition<?> childTypeDefinition : childTypeDefinitions) {
344 childDepth = childDepth + getTypeDefinitionDepth(childTypeDefinition);
345 if (childDepth > maxChildDepth) {
346 maxChildDepth = childDepth;
349 return maxChildDepth;
354 static List<TypeDefinition<?>> getAllTypedefs(final Module module) {
355 final List<TypeDefinition<?>> ret = new ArrayList<>();
357 fillRecursively(ret, module);
359 final Set<NotificationDefinition> notifications = module.getNotifications();
360 for (NotificationDefinition notificationDefinition : notifications) {
361 fillRecursively(ret, notificationDefinition);
364 final Set<RpcDefinition> rpcs = module.getRpcs();
365 for (RpcDefinition rpcDefinition : rpcs) {
366 ret.addAll(rpcDefinition.getTypeDefinitions());
367 final ContainerSchemaNode input = rpcDefinition.getInput();
369 fillRecursively(ret, input);
371 final ContainerSchemaNode output = rpcDefinition.getOutput();
372 if (output != null) {
373 fillRecursively(ret, output);
377 final Collection<DataSchemaNode> potentials = module.getChildNodes();
379 for (DataSchemaNode potential : potentials) {
380 if (potential instanceof ActionNodeContainer) {
381 final Set<ActionDefinition> actions = ((ActionNodeContainer) potential).getActions();
382 for (ActionDefinition action: actions) {
383 final ContainerSchemaNode input = action.getInput();
385 fillRecursively(ret, input);
387 final ContainerSchemaNode output = action.getOutput();
388 if (output != null) {
389 fillRecursively(ret, output);
398 private static void fillRecursively(final List<TypeDefinition<?>> list, final DataNodeContainer container) {
399 final Collection<DataSchemaNode> childNodes = container.getChildNodes();
400 if (childNodes != null) {
401 childNodes.stream().filter(childNode -> !childNode.isAugmenting()).forEach(childNode -> {
402 if (childNode instanceof ContainerSchemaNode) {
403 fillRecursively(list, (ContainerSchemaNode) childNode);
404 } else if (childNode instanceof ListSchemaNode) {
405 fillRecursively(list, (ListSchemaNode) childNode);
406 } else if (childNode instanceof ChoiceSchemaNode) {
407 for (CaseSchemaNode caseNode : ((ChoiceSchemaNode) childNode).getCases().values()) {
408 fillRecursively(list, caseNode);
414 list.addAll(container.getTypeDefinitions());
416 final Set<GroupingDefinition> groupings = container.getGroupings();
417 if (groupings != null) {
418 for (GroupingDefinition grouping : groupings) {
419 fillRecursively(list, grouping);
425 * Add {@link Serializable} to implemented interfaces of this TO. Also
426 * compute and add serialVersionUID property.
429 * transfer object which needs to be serializable
431 static void makeSerializable(final GeneratedTOBuilderImpl gto) {
432 gto.addImplementsType(Types.typeForClass(Serializable.class));
433 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
434 prop.setValue(Long.toString(BindingGeneratorUtil.computeDefaultSUID(gto)));
439 * Converts <code>enumTypeDef</code> to
444 * enumeration type definition which is converted to enumeration
446 * string with name which is used as the enumeration name
447 * @return enumeration type which is built with data (name, enum values)
448 * from <code>enumTypeDef</code>
449 * @throws IllegalArgumentException
451 * <li>if <code>enumTypeDef</code> equals null</li>
452 * <li>if enum values of <code>enumTypeDef</code> equal null</li>
453 * <li>if Q name of <code>enumTypeDef</code> equal null</li>
454 * <li>if name of <code>enumTypeDef</code> equal null</li>
457 @SuppressWarnings({ "rawtypes", "unchecked" })
458 static Enumeration provideTypeForEnum(final EnumTypeDefinition enumTypeDef, final String enumName,
459 final SchemaNode parentNode, final SchemaContext schemaContext, final ModuleContext context) {
460 Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
461 Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
462 "Local Name in EnumTypeDefinition QName cannot be NULL!");
463 final Module module = findParentModule(schemaContext, parentNode);
464 final String basePackageName = BindingMapping.getRootPackageName(module);
465 final String packageName;
467 if (parentNode instanceof TypeDefinition) {
468 packageName = BindingGeneratorUtil.packageNameWithNamespacePrefix(
469 BindingMapping.getRootPackageName(module),
470 BindingNamespaceType.Typedef);
472 packageName = basePackageName;
475 final EnumerationBuilderImpl enumBuilder = new EnumerationBuilderImpl(packageName, enumName, context);
476 final String enumTypedefDescription = encodeAngleBrackets(enumTypeDef.getDescription().orElse(null));
477 enumBuilder.setDescription(enumTypedefDescription);
478 enumBuilder.setReference(enumTypeDef.getReference().orElse(null));
479 enumBuilder.setModuleName(module.getName());
480 enumBuilder.setSchemaPath((List) enumTypeDef.getPath().getPathFromRoot());
481 enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
482 return enumBuilder.toInstance(null);
486 * Converts <code>typedef</code> to the generated TO builder.
488 * @param basePackageName
489 * string with name of package to which the module belongs
491 * type definition from which is the generated TO builder created
492 * @return generated TO builder which contains data from
493 * <code>typedef</code> and <code>basePackageName</code>
495 @SuppressWarnings({ "unchecked", "rawtypes" })
496 private static GeneratedTOBuilderImpl typedefToTransferObject(final String basePackageName,
497 final TypeDefinition<?> typedef, final String moduleName, final ModuleContext context) {
498 final String typeDefTOName = typedef.getQName().getLocalName();
500 if (basePackageName != null && typeDefTOName != null) {
501 final GeneratedTOBuilderImpl newType = new GeneratedTOBuilderImpl(basePackageName, typeDefTOName, context);
502 final String typedefDescription = encodeAngleBrackets(typedef.getDescription().orElse(null));
504 newType.setDescription(typedefDescription);
505 typedef.getReference().ifPresent(newType::setReference);
506 newType.setSchemaPath((List) typedef.getPath().getPathFromRoot());
507 newType.setModuleName(moduleName);
514 static Module getParentModule(final SchemaNode node, final SchemaContext schemaContext) {
515 return schemaContext.findModule(node.getPath().getPathFromRoot().iterator().next().getModule()).orElse(null);