2 * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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
8 package org.opendaylight.mdsal.binding.java.api.generator;
10 import static com.google.common.collect.ImmutableSet.toImmutableSet;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableMap.Builder;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.collect.Iterables;
17 import java.util.HashMap;
20 import javax.annotation.concurrent.NotThreadSafe;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.mdsal.binding.model.api.Enumeration;
24 import org.opendaylight.mdsal.binding.model.api.Enumeration.Pair;
25 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
26 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
27 import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
28 import org.opendaylight.mdsal.binding.model.api.Type;
29 import org.opendaylight.mdsal.binding.model.api.WildcardType;
32 * Abstract class representing a generated type, either top-level or nested. It takes care of tracking references
33 * to other Java types and resolving them as best as possible.
35 * @author Robert Varga
39 abstract class AbstractJavaGeneratedType {
40 private final Map<JavaTypeName, @Nullable String> nameCache = new HashMap<>();
41 private final Map<String, NestedJavaGeneratedType> enclosedTypes;
42 private final Set<String> conflictingNames;
44 private final JavaTypeName name;
46 AbstractJavaGeneratedType(final GeneratedType genType) {
47 name = genType.getIdentifier();
48 final Builder<String, NestedJavaGeneratedType> b = ImmutableMap.builder();
49 for (GeneratedType type : Iterables.concat(genType.getEnclosedTypes(), genType.getEnumerations())) {
50 b.put(type.getIdentifier().simpleName(), new NestedJavaGeneratedType(this, type));
52 collectInheritedEnclosedTypes(b, genType);
54 enclosedTypes = b.build();
56 if (genType instanceof Enumeration) {
57 conflictingNames = ((Enumeration) genType).getValues().stream().map(Pair::getMappedName)
58 .collect(toImmutableSet());
60 conflictingNames = ImmutableSet.of();
64 private void collectInheritedEnclosedTypes(Builder<String, NestedJavaGeneratedType> builder,
66 for (Type impl : type.getImplements()) {
67 if (impl instanceof GeneratedType) {
68 final GeneratedType genType = (GeneratedType) impl;
69 for (GeneratedType inner : Iterables.concat(genType.getEnclosedTypes(), genType.getEnumerations())) {
70 builder.put(inner.getIdentifier().simpleName(), new NestedJavaGeneratedType(this, inner));
72 collectInheritedEnclosedTypes(builder, genType);
77 final JavaTypeName getName() {
81 final String getSimpleName() {
82 return name.simpleName();
85 final String getReferenceString(final Type type) {
86 if (!(type instanceof ParameterizedType)) {
87 return getReferenceString(type.getIdentifier());
90 final StringBuilder sb = new StringBuilder();
91 sb.append(getReferenceString(type.getIdentifier())).append('<');
92 final Type[] types = ((ParameterizedType) type).getActualTypeArguments();
93 if (types.length == 0) {
94 return sb.append("?>").toString();
97 for (int i = 0; i < types.length; i++) {
98 final Type t = types[i];
99 if (t instanceof WildcardType) {
100 sb.append("? extends ");
102 sb.append(getReferenceString(t));
103 if (i != types.length - 1) {
108 return sb.append('>').toString();
111 final String getReferenceString(final JavaTypeName type) {
112 if (type.packageName().isEmpty()) {
113 // This is a packageless primitive type, refer to it directly
114 return type.simpleName();
117 // Self-reference, return simple name
118 if (name.equals(type)) {
119 return name.simpleName();
122 // Fast path: we have already resolved how to refer to this type
123 final String existing = nameCache.get(type);
124 if (existing != null) {
128 // Fork based on whether the class is in this compilation unit, package or neither
130 if (name.topLevelClass().equals(type.topLevelClass())) {
131 result = localTypeName(type);
132 } else if (name.packageName().equals(type.packageName())) {
133 result = packageTypeName(type);
135 result = foreignTypeName(type);
138 nameCache.put(type, result);
142 final NestedJavaGeneratedType getEnclosedType(final JavaTypeName type) {
143 return requireNonNull(enclosedTypes.get(type.simpleName()));
146 final boolean checkAndImportType(final JavaTypeName type) {
147 // We can import the type only if it does not conflict with us or our immediately-enclosed types
148 final String simpleName = type.simpleName();
149 return !simpleName.equals(getSimpleName()) && !enclosedTypes.containsKey(simpleName)
150 && !conflictingNames.contains(simpleName) && importCheckedType(type);
153 abstract boolean importCheckedType(JavaTypeName type);
155 abstract String localTypeName(JavaTypeName type);
157 private String foreignTypeName(final JavaTypeName type) {
158 return checkAndImportType(type) ? type.simpleName() : type.toString();
161 private String packageTypeName(final JavaTypeName type) {
162 // Try to anchor the top-level type and use a local reference
163 return checkAndImportType(type.topLevelClass()) ? type.localName() : type.toString();