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.model.api;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.Beta;
14 import com.google.common.collect.ImmutableList;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.Optional;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.yangtools.concepts.Identifier;
22 import org.opendaylight.yangtools.concepts.Immutable;
25 * A type name. This class encloses Java type naming rules laid down in
26 * <a href="https://docs.oracle.com/javase/specs/jls/se9/html/index.html">The Java Language Specification</a>, notably
27 * sections 4 and 8. It deals with primitive, array and reference types.
29 * @author Robert Varga
33 public abstract class JavaTypeName implements Identifier, Immutable {
34 private static final class Primitive extends JavaTypeName {
35 private static final long serialVersionUID = 1L;
37 Primitive(final String simpleName) {
42 public String packageName() {
47 public Optional<JavaTypeName> immediatelyEnclosingClass() {
48 return Optional.empty();
52 public boolean canCreateEnclosed(final String simpleName) {
53 throw new UnsupportedOperationException("Primitive type " + simpleName() + " cannot enclose type "
58 public JavaTypeName createEnclosed(final String simpleName) {
59 throw new UnsupportedOperationException("Primitive type " + simpleName() + " cannot enclose type "
64 public String localName() {
69 public List<String> localNameComponents() {
70 return ImmutableList.of(simpleName());
74 public JavaTypeName createSibling(final String simpleName) {
75 return new Primitive(simpleName);
79 public JavaTypeName topLevelClass() {
84 public String toString() {
89 private abstract static class Reference extends JavaTypeName {
90 private static final long serialVersionUID = 1L;
92 Reference(final String simpleName) {
97 public boolean canCreateEnclosed(final String simpleName) {
98 return !simpleName.equals(simpleName());
102 public final JavaTypeName createEnclosed(final String simpleName) {
103 checkValidName(requireNonNull(simpleName));
104 return new Nested(this, simpleName);
108 public final String toString() {
109 return appendClass(new StringBuilder()).toString();
112 void checkValidName(final String nestedName) {
113 checkArgument(canCreateEnclosed(nestedName), "Nested class name %s conflicts with enclosing class %s",
117 abstract StringBuilder appendClass(StringBuilder sb);
120 private static final class TopLevel extends Reference {
121 private static final long serialVersionUID = 1L;
123 private final String packageName;
125 TopLevel(final String packageName, final String simpleName) {
127 checkArgument(!packageName.isEmpty());
128 this.packageName = packageName;
132 public String packageName() {
137 public JavaTypeName createSibling(final String simpleName) {
138 return new TopLevel(packageName, simpleName);
142 public Optional<JavaTypeName> immediatelyEnclosingClass() {
143 return Optional.empty();
147 public String localName() {
152 public List<String> localNameComponents() {
153 final List<String> ret = new ArrayList<>();
154 ret.add(simpleName());
159 public JavaTypeName topLevelClass() {
164 StringBuilder appendClass(final StringBuilder sb) {
165 return sb.append(packageName).append('.').append(simpleName());
169 private static final class Nested extends Reference {
170 private static final long serialVersionUID = 1L;
172 private final Reference immediatelyEnclosingClass;
174 Nested(final Reference immediatelyEnclosingClass, final String simpleName) {
176 this.immediatelyEnclosingClass = requireNonNull(immediatelyEnclosingClass);
180 public String packageName() {
181 return immediatelyEnclosingClass.packageName();
185 public JavaTypeName createSibling(final String simpleName) {
186 return immediatelyEnclosingClass.createEnclosed(simpleName);
190 public Optional<JavaTypeName> immediatelyEnclosingClass() {
191 return Optional.of(immediatelyEnclosingClass);
195 StringBuilder appendClass(final StringBuilder sb) {
196 return immediatelyEnclosingClass.appendClass(sb).append('.').append(simpleName());
200 public boolean canCreateEnclosed(final String simpleName) {
201 return super.canCreateEnclosed(simpleName) && immediatelyEnclosingClass.canCreateEnclosed(simpleName);
205 public String localName() {
206 return immediatelyEnclosingClass.localName() + "." + simpleName();
210 public List<String> localNameComponents() {
211 final List<String> ret = immediatelyEnclosingClass.localNameComponents();
212 ret.add(simpleName());
217 public JavaTypeName topLevelClass() {
218 return immediatelyEnclosingClass.topLevelClass();
222 private static final long serialVersionUID = 1L;
224 private final String simpleName;
226 JavaTypeName(final String simpleName) {
227 checkArgument(!simpleName.isEmpty());
228 this.simpleName = simpleName;
232 * Create a TypeName for an existing class.
234 * @param clazz Class instance
235 * @return A new TypeName
236 * @throws NullPointerException if clazz is null
238 public static JavaTypeName create(final Class<?> clazz) {
239 final Class<?> enclosing = clazz.getEnclosingClass();
240 if (enclosing != null) {
241 return create(enclosing).createEnclosed(clazz.getSimpleName());
243 final Package pkg = clazz.getPackage();
244 return pkg == null ? new Primitive(clazz.getSimpleName()) : new TopLevel(pkg.getName(), clazz.getSimpleName());
248 * Create a TypeName for a top-level class.
250 * @param packageName Class package name
251 * @param simpleName Class simple name
252 * @return A new TypeName.
253 * @throws NullPointerException if any of the arguments is null
254 * @throws IllegalArgumentException if any of the arguments is empty
256 public static JavaTypeName create(final String packageName, final String simpleName) {
257 return new TopLevel(packageName, simpleName);
261 * Check if an enclosed type with specified name can be created.
263 * @param simpleName Simple name of the enclosed class
264 * @return True if the proposed simple name does not conflict with any enclosing types
265 * @throws IllegalArgumentException if the simpleName is empty
266 * @throws UnsupportedOperationException if this type name does not support nested type
268 public abstract boolean canCreateEnclosed(final String simpleName);
271 * Create a TypeName for a class immediately enclosed by this class.
273 * @param simpleName Simple name of the enclosed class
274 * @return A new TypeName.
275 * @throws NullPointerException if simpleName is null
276 * @throws IllegalArgumentException if the simpleName hides any of the enclosing types or if it is empty
277 * @throws UnsupportedOperationException if this type name does not support nested type
279 public abstract JavaTypeName createEnclosed(String simpleName);
282 * Create a TypeName for a class that is a sibling of this class. A sibling has the same package name, and the same
283 * immediately enclosing class.
285 * @param simpleName Simple name of the sibling class
286 * @return A new TypeName.
287 * @throws NullPointerException if simpleName is null
288 * @throws IllegalArgumentException if the simpleName is empty
290 // TODO: we could detect/allocate names in this method such that they don't conflict.
291 public abstract JavaTypeName createSibling(String simpleName);
294 * Return the simple name of the class.
296 * @return Simple name of the class.
298 public final String simpleName() {
303 * Return the package name in which this class resides. This does not account for any class nesting, i.e. for nested
304 * classes this returns the package name of the top-level class in naming hierarchy.
306 * @return Package name of the class.
308 public abstract String packageName();
311 * Return the enclosing class JavaTypeName, if present.
313 * @return Enclosing class JavaTypeName.
315 public abstract Optional<JavaTypeName> immediatelyEnclosingClass();
318 * Return the top-level class JavaTypeName which is containing this type, or self if this type is a top-level
321 * @return Top-level JavaTypeName
323 public abstract JavaTypeName topLevelClass();
326 * Return the package-local name by which this type can be referenced by classes living in the same package.
328 * @return Local name.
330 public abstract String localName();
333 * Return broken-down package-local name components.
335 * @return List of package-local components.
337 public abstract List<String> localNameComponents();
340 public final int hashCode() {
341 return Objects.hash(simpleName, packageName(), immediatelyEnclosingClass());
345 public final boolean equals(final @Nullable Object obj) {
349 if (!(obj instanceof JavaTypeName)) {
352 final JavaTypeName other = (JavaTypeName) obj;
353 return simpleName.equals(other.simpleName) && packageName().equals(other.packageName())
354 && immediatelyEnclosingClass().equals(other.immediatelyEnclosingClass());
358 * Return the Fully-Qualified Class Name string of this TypeName.
360 * @return Fully-Qualified Class Name string of this TypeName.
363 public abstract String toString();