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 java.util.Objects.requireNonNull;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableMap.Builder;
14 import com.google.common.collect.ImmutableSet;
15 import com.google.common.collect.Iterables;
16 import java.util.HashMap;
17 import java.util.HashSet;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.opendaylight.mdsal.binding.model.api.Enumeration;
23 import org.opendaylight.mdsal.binding.model.api.Enumeration.Pair;
24 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
25 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
26 import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
27 import org.opendaylight.mdsal.binding.model.api.Type;
28 import org.opendaylight.mdsal.binding.model.api.WildcardType;
31 * Abstract class representing a generated type, either top-level or nested. It takes care of tracking references
32 * to other Java types and resolving them as best as possible. This class is NOT thread-safe.
34 * @author Robert Varga
37 abstract class AbstractJavaGeneratedType {
38 private final Map<JavaTypeName, @Nullable String> nameCache = new HashMap<>();
39 private final ImmutableMap<String, NestedJavaGeneratedType> enclosedTypes;
40 private final ImmutableSet<String> conflictingNames;
42 private final JavaTypeName name;
44 AbstractJavaGeneratedType(final GeneratedType genType) {
45 name = genType.getIdentifier();
46 final Builder<String, NestedJavaGeneratedType> b = ImmutableMap.builder();
47 for (GeneratedType type : Iterables.concat(genType.getEnclosedTypes(), genType.getEnumerations())) {
48 b.put(type.getIdentifier().simpleName(), new NestedJavaGeneratedType(this, type));
50 enclosedTypes = b.build();
52 final Set<String> cb = new HashSet<>();
53 if (genType instanceof Enumeration) {
54 ((Enumeration) genType).getValues().stream().map(Pair::getMappedName).forEach(cb::add);
56 // TODO: perhaps we can do something smarter to actually access the types
57 collectAccessibleTypes(cb, genType);
59 conflictingNames = ImmutableSet.copyOf(cb);
62 private void collectAccessibleTypes(final Set<String> set, final GeneratedType type) {
63 for (Type impl : type.getImplements()) {
64 if (impl instanceof GeneratedType) {
65 final GeneratedType genType = (GeneratedType) impl;
66 for (GeneratedType inner : Iterables.concat(genType.getEnclosedTypes(), genType.getEnumerations())) {
67 set.add(inner.getIdentifier().simpleName());
69 collectAccessibleTypes(set, genType);
74 final JavaTypeName getName() {
78 final String getSimpleName() {
79 return name.simpleName();
82 final String getReferenceString(final Type type, final String... annotations) {
83 final String ref = getReferenceString(type.getIdentifier());
84 if (!(type instanceof ParameterizedType)) {
85 return annotations.length == 0 ? ref : annotate(ref, annotations).toString();
88 final StringBuilder sb = annotate(ref, annotations).append('<');
89 final Type[] types = ((ParameterizedType) type).getActualTypeArguments();
90 if (types.length == 0) {
91 return sb.append("?>").toString();
94 for (int i = 0; i < types.length; i++) {
95 final Type t = types[i];
96 if (t instanceof WildcardType) {
97 sb.append("? extends ");
99 sb.append(getReferenceString(t));
100 if (i != types.length - 1) {
105 return sb.append('>').toString();
108 final String getReferenceString(final JavaTypeName type) {
109 if (type.packageName().isEmpty()) {
110 // This is a packageless primitive type, refer to it directly
111 return type.simpleName();
114 // Self-reference, return simple name
115 if (name.equals(type)) {
116 return name.simpleName();
119 // Fast path: we have already resolved how to refer to this type
120 final String existing = nameCache.get(type);
121 if (existing != null) {
125 // Fork based on whether the class is in this compilation unit, package or neither
127 if (name.topLevelClass().equals(type.topLevelClass())) {
128 result = localTypeName(type);
129 } else if (name.packageName().equals(type.packageName())) {
130 result = packageTypeName(type);
132 result = foreignTypeName(type);
135 nameCache.put(type, result);
139 final NestedJavaGeneratedType getEnclosedType(final JavaTypeName type) {
140 return requireNonNull(enclosedTypes.get(type.simpleName()));
143 final boolean checkAndImportType(final JavaTypeName type) {
144 // We can import the type only if it does not conflict with us or our immediately-enclosed types
145 final String simpleName = type.simpleName();
146 return !simpleName.equals(getSimpleName()) && !enclosedTypes.containsKey(simpleName)
147 && !conflictingNames.contains(simpleName) && importCheckedType(type);
150 abstract boolean importCheckedType(JavaTypeName type);
152 abstract String localTypeName(JavaTypeName type);
154 private String foreignTypeName(final JavaTypeName type) {
155 return checkAndImportType(type) ? type.simpleName() : type.toString();
158 private String packageTypeName(final JavaTypeName type) {
159 // Try to anchor the top-level type and use a local reference
160 return checkAndImportType(type.topLevelClass()) ? type.localName() : type.toString();
163 private static StringBuilder annotate(final String ref, final String... annotations) {
164 final StringBuilder sb = new StringBuilder();
165 if (annotations.length == 0) {
166 return sb.append(ref);
169 final int dot = ref.lastIndexOf('.');
171 sb.append(ref, 0, dot + 1);
173 for (String annotation : annotations) {
174 sb.append('@').append(annotation).append(' ');
177 return sb.append(ref, dot + 1, ref.length());