| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.johnzon.jsonschema.generator; |
| |
| import javax.json.JsonObject; |
| import javax.json.JsonString; |
| import javax.json.JsonValue; |
| import javax.json.bind.annotation.JsonbProperty; |
| import java.time.Duration; |
| import java.time.LocalDate; |
| import java.time.LocalTime; |
| import java.time.OffsetDateTime; |
| import java.util.AbstractMap; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import java.util.TreeSet; |
| import java.util.function.Function; |
| import java.util.stream.IntStream; |
| import java.util.stream.Stream; |
| |
| import static java.util.Collections.singletonMap; |
| import static java.util.Comparator.comparing; |
| import static java.util.function.Function.identity; |
| import static java.util.stream.Collectors.joining; |
| import static java.util.stream.Collectors.toList; |
| import static java.util.stream.Collectors.toMap; |
| |
| // todo: support ref resolution for schema and avoid to generate as much classes as attributes |
| public class PojoGenerator { |
| private final PojoConfiguration configuration; |
| |
| protected final Set<String> imports = new TreeSet<>(String::compareTo); |
| protected final List<Attribute> attributes = new ArrayList<>(); |
| protected final Map<String, String> nested = new TreeMap<>(String::compareTo); |
| |
| public PojoGenerator(final PojoConfiguration configuration) { |
| this.configuration = configuration; |
| } |
| |
| public PojoGenerator setNested(final Map<String, String> nested) { |
| this.nested.putAll(nested); |
| return this; |
| } |
| |
| public Map<String, String> generate() { |
| final String name = configuration.getPackageName() + '.' + configuration.getClassName(); |
| final String path = name.replace('.', '/') + ".java"; |
| attributes.sort(comparing(a -> a.javaName)); |
| if (!attributes.isEmpty()) { |
| imports.add(Objects.class.getName()); |
| } |
| final String content = "" + |
| "package " + configuration.getPackageName() + ";\n" + |
| "\n" + |
| (imports.isEmpty() ? "" : imports.stream() |
| .sorted() |
| .map(it -> "import " + it + ";") |
| .collect(joining("\n", "", "\n\n"))) + |
| "public class " + configuration.getClassName() + afterClassName() + " {\n" + |
| (attributes.isEmpty() ? |
| ("" + |
| " @Override\n" + |
| " public int hashCode() {\n" + |
| " return 0;\n" + |
| " }\n" + |
| "\n" + |
| " @Override\n" + |
| " public boolean equals(final Object other) {\n" + |
| " return other instanceof " + configuration.getClassName() + ";\n" + |
| " }\n" + |
| "}\n") : |
| (attributes.stream() |
| .map(a -> "" + |
| (configuration.isAddJsonbProperty() && !Objects.equals(a.javaName, a.jsonName) ? |
| " @JsonbProperty(\"" + a.jsonName.replace("\"", "\\\"") + "\")\n" : |
| "") + |
| " private " + a.type + " " + a.javaName + ";") |
| .collect(joining("\n", "", "\n\n")) + |
| (configuration.isAddAllArgsConstructor() ? |
| " public " + configuration.getClassName() + "() {\n" + |
| " // no-op\n" + |
| " }\n" + |
| "\n" + |
| " public " + configuration.getClassName() + "(" + |
| attributes.stream() |
| .map(a -> "final " + a.type + " " + a.javaName) |
| .collect(joining( |
| ",\n" + IntStream.range( |
| 0, |
| " public (".length() + |
| configuration.getClassName().length()) |
| .mapToObj(i -> " ") |
| .collect(joining()), |
| "", |
| ") {\n" + |
| " // no-op\n" + |
| " }\n\n")) : |
| "") + |
| attributes.stream() |
| .map(a -> { |
| final String marker = Character.toUpperCase(a.javaName.charAt(0)) + a.javaName.substring(1); |
| return "" + |
| " public " + a.type + " get" + Character.toUpperCase(a.javaName.charAt(0)) + a.javaName.substring(1) + "() {\n" + |
| " return " + a.javaName + ";\n" + |
| " }\n" + |
| "\n" + |
| " public " + |
| (configuration.isFluentSetters() ? a.type : "void") + |
| " set" + marker + "(final " + a.type + " " + a.javaName + ") {\n" + |
| " this." + a.javaName + " = " + a.javaName + ";\n" + |
| (configuration.isFluentSetters() ? " return this;\n" : "") + |
| " }\n" + |
| ""; |
| }) |
| .collect(joining("\n", "", "\n")) + |
| " @Override\n" + |
| " public int hashCode() {\n" + |
| " return Objects.hash(\n" + |
| attributes.stream() |
| .map(a -> a.javaName) |
| .collect(joining(",\n ", " ", ");\n")) + |
| " }\n" + |
| "\n" + |
| " @Override\n" + |
| " public boolean equals(final Object __other) {\n" + |
| " if (!(__other instanceof " + configuration.getClassName() + ")) {\n" + |
| " return false;\n" + |
| " }\n" + |
| " final " + configuration.getClassName() + " __otherCasted = (" + configuration.getClassName() + ") __other;\n" + |
| " return " + attributes.stream() |
| .map(a -> a.javaName) |
| .map(it -> "Objects.equals(" + it + ", __otherCasted." + it + ")") |
| .collect(joining(" &&\n ")) + ";\n" + |
| " }\n" |
| )) + |
| beforeClassEnd() + |
| "}\n"; |
| if (nested.isEmpty()) { |
| return singletonMap(path, content); |
| } |
| return Stream.concat( |
| nested.entrySet().stream(), |
| Stream.of(new AbstractMap.SimpleImmutableEntry<>(path, content))) |
| .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, () -> new TreeMap<>(String::compareTo))); |
| } |
| |
| public PojoGenerator visitSchema(final JsonObject schema) { |
| if (!schema.containsKey("properties")) { |
| throw new IllegalArgumentException("Unsupported schema since it does not contain any properties: " + schema); |
| } |
| |
| final JsonObject properties = getValueAs(schema, "properties", JsonObject.class); |
| final JsonValue required = schema.get("required"); |
| final List<String> requiredAttrs = required != null && required.getValueType() == JsonValue.ValueType.ARRAY ? |
| required.asJsonArray().stream().map(JsonString.class::cast).map(JsonString::getString).collect(toList()) : |
| null; |
| attributes.addAll(properties.entrySet().stream() |
| .map(e -> { |
| final String javaName = toJavaName(e.getKey()); |
| return new Attribute( |
| javaName, e.getKey(), |
| asType(javaName, e.getValue().asJsonObject(), requiredAttrs != null && requiredAttrs.contains(e.getKey()))); |
| }) |
| .collect(toList())); |
| |
| return this; |
| } |
| |
| /** |
| * @param ref the reference to resolve. |
| * @return the reference class name if resolved else null. |
| */ |
| protected String onRef(final Ref ref) { |
| if (configuration.getOnRef() != null) { |
| return configuration.getOnRef().apply(ref); |
| } |
| return null; // todo: check if already in nested for ex |
| } |
| |
| protected String beforeClassEnd() { |
| return ""; |
| } |
| |
| protected String afterClassName() { |
| return ""; |
| } |
| |
| protected String asType(final String javaName, final JsonObject schema, final boolean required) { |
| final JsonValue ref = schema.get("$ref"); |
| if (ref != null && ref.getValueType() == JsonValue.ValueType.STRING) { |
| final String name = onRef(new Ref( |
| JsonString.class.cast(ref).getString(), configuration.getClassName(), imports, attributes, nested)); |
| if (name != null) { |
| return name; |
| } |
| } |
| |
| final JsonValue value = schema.get("type"); |
| String type; |
| if (value == null) { |
| if (schema.containsKey("properties") || schema.containsKey("additionalProperties")) { |
| type = "object"; |
| } else if (schema.containsKey("items")) { |
| type = "array"; |
| } else { // unknown, don't fail for wrongly written schema |
| imports.add(JsonValue.class.getName()); |
| return JsonValue.class.getSimpleName(); |
| } |
| } else { |
| type = JsonString.class.cast(value).getString(); |
| } |
| final JsonValue formatValue = schema.get("date-time"); |
| if (formatValue != null && formatValue.getValueType() == JsonValue.ValueType.STRING) { |
| type = JsonString.class.cast(formatValue).getString(); |
| } |
| |
| switch (type) { |
| case "array": |
| final JsonObject items = getValueAs(schema, "items", JsonObject.class); |
| final String itemType = onItemSchema(javaName, items); |
| imports.add(List.class.getName()); |
| return List.class.getSimpleName() + "<" + itemType + ">"; |
| case "object": |
| return onObjectAttribute(javaName, schema); |
| case "null": |
| imports.add(JsonValue.class.getName()); |
| return JsonValue.class.getSimpleName(); |
| case "boolean": |
| return required ? "boolean" : "Boolean"; |
| case "string": |
| final JsonValue enumList = schema.get("enum"); |
| if (enumList != null && enumList.getValueType() == JsonValue.ValueType.ARRAY) { |
| return onEnum(javaName, enumList); |
| } |
| return "String"; |
| case "number": |
| case "double": // openapi |
| return required ? "double" : "Double"; |
| // openapi types |
| case "int": |
| case "int32": |
| case "integer": |
| return required ? "int" : "Integer"; |
| case "int64": |
| case "long": |
| return required ? "long" : "Long"; |
| case "float": |
| return required ? "float" : "Float"; |
| case "date": |
| imports.add(LocalDate.class.getName()); |
| return LocalDate.class.getSimpleName(); |
| case "duration": |
| imports.add(Duration.class.getName()); |
| return Duration.class.getSimpleName(); |
| case "date-time": |
| case "dateTime": |
| imports.add(OffsetDateTime.class.getName()); |
| return OffsetDateTime.class.getSimpleName(); |
| case "time": |
| imports.add(LocalTime.class.getName()); |
| return LocalTime.class.getSimpleName(); |
| case "byte": |
| return "byte[]"; |
| case "uuid": |
| case "hostname": |
| case "idn-hostname": |
| case "email": |
| case "idn-email": |
| case "ipv4": |
| case "ipv6": |
| case "uri": |
| case "uri-reference": |
| case "iri": |
| case "iri-reference": |
| case "uri-template": |
| case "json-pointer": |
| case "relative-json-pointer": |
| case "regex": |
| case "binary": // base64 |
| case "password": |
| return "String"; |
| default: |
| throw new IllegalArgumentException("Unsupported type: " + type); |
| } |
| } |
| |
| protected String onEnum(final String javaName, final JsonValue enumList) { |
| final String className = configuration.getClassName() + Character.toUpperCase(javaName.charAt(0)) + javaName.substring(1); |
| final Map<String, String> values = enumList.asJsonArray().stream() |
| .map(JsonString.class::cast) |
| .map(JsonString::getString) |
| .collect(toMap(identity(), this::toJavaName)); |
| final boolean injectValues = !values.keySet().equals(new HashSet<>(values.values())); // java != json |
| nested.put( |
| configuration.getPackageName().replace('.', '/') + '/' + className + ".java", "" + |
| "package " + configuration.getPackageName() + ";\n" + |
| "\n" + |
| "public enum " + className + " {\n" + |
| values.entrySet().stream() |
| .map(it -> "" + |
| (injectValues && configuration.isAddJsonbProperty() ? |
| " @JsonbProperty(\"" + it.getKey().replace("\"", "\\\"") + "\")\n" : |
| "") + |
| " " + it.getValue() + |
| (injectValues ? "(\"" + it.getKey().replace("\"", "\\\"") + "\")" : "")) |
| .collect(joining(",\n", "", injectValues ? ";\n\n" : "\n")) + |
| (injectValues ? |
| "" + |
| " private String value;\n" + |
| "\n" + |
| " " + className + "(final String value) {\n" + |
| " this.value = value;\n" + |
| " }\n" + |
| "\n" + |
| " public String toString() {\n" + |
| " return value;\n" + |
| " }\n" + |
| "" : |
| "") + |
| "}\n"); |
| return className; |
| } |
| |
| protected String onObjectAttribute(final String javaName, final JsonObject schema) { |
| final JsonValue additionalProperties = schema.get("additionalProperties"); |
| final JsonValue properties = schema.get("properties"); |
| final boolean hasProperties = properties != null && properties.getValueType() == JsonValue.ValueType.OBJECT; |
| if (!hasProperties && |
| additionalProperties != null && |
| additionalProperties.getValueType() == JsonValue.ValueType.OBJECT) { |
| final JsonObject propSchema = additionalProperties.asJsonObject(); |
| final JsonValue propTypeValue = propSchema.get("type"); |
| if (propTypeValue != null && propTypeValue.getValueType() == JsonValue.ValueType.STRING) { |
| String propType = JsonString.class.cast(propTypeValue).getString(); |
| final JsonValue formatValue = schema.get("date-time"); |
| if (formatValue != null && formatValue.getValueType() == JsonValue.ValueType.STRING) { |
| propType = JsonString.class.cast(formatValue).getString(); |
| } |
| switch (propType) { |
| case "uuid": |
| case "hostname": |
| case "idn-hostname": |
| case "email": |
| case "idn-email": |
| case "ipv4": |
| case "ipv6": |
| case "uri": |
| case "uri-reference": |
| case "iri": |
| case "iri-reference": |
| case "uri-template": |
| case "json-pointer": |
| case "relative-json-pointer": |
| case "regex": |
| case "string": |
| case "binary": |
| case "password": |
| imports.add(Map.class.getName()); |
| return "Map<String, String>"; |
| case "boolean": |
| imports.add(Map.class.getName()); |
| return "Map<String, Boolean>"; |
| case "number": |
| case "double": |
| imports.add(Map.class.getName()); |
| return "Map<String, Double>"; |
| case "int": |
| case "int32": |
| case "integer": |
| imports.add(Map.class.getName()); |
| return "Map<String, Integer>"; |
| case "int64": |
| case "long": |
| imports.add(Map.class.getName()); |
| return "Map<String, Long>"; |
| case "float": |
| imports.add(Map.class.getName()); |
| return "Map<String, Float>"; |
| case "date": |
| imports.add(Map.class.getName()); |
| imports.add(LocalDate.class.getName()); |
| return "Map<String, LocalDate>"; |
| case "dateTime": |
| case "date-time": |
| imports.add(Map.class.getName()); |
| imports.add(OffsetDateTime.class.getName()); |
| return "Map<String, OffsetDateTime>"; |
| case "duration": |
| imports.add(Map.class.getName()); |
| imports.add(Duration.class.getName()); |
| return "Map<String, Duration>"; |
| case "time": |
| imports.add(Map.class.getName()); |
| imports.add(LocalTime.class.getName()); |
| return "Map<String, LocalTime>"; |
| default: |
| // todo: case array, object |
| } |
| } |
| } else if (hasProperties) { |
| final String className = configuration.getClassName() + Character.toUpperCase(javaName.charAt(0)) + javaName.substring(1); |
| nested.putAll(newSubPojoGenerator(new PojoConfiguration() |
| .setPackageName(configuration.getPackageName()) |
| .setClassName(className) |
| .setAddJsonbProperty(configuration.isAddJsonbProperty()) |
| .setAddAllArgsConstructor(configuration.isAddAllArgsConstructor()) |
| .setOnRef(configuration.getOnRef())) |
| .visitSchema(schema) |
| .generate()); |
| return className; |
| } |
| |
| imports.add(JsonObject.class.getName()); |
| return JsonObject.class.getSimpleName(); |
| } |
| |
| protected PojoGenerator newSubPojoGenerator(final PojoConfiguration pojoConfiguration) { |
| return new PojoGenerator(pojoConfiguration); |
| } |
| |
| protected String onItemSchema(final String javaName, final JsonObject schema) { |
| final JsonValue ref = schema.get("$ref"); |
| if (ref != null && ref.getValueType() == JsonValue.ValueType.STRING) { |
| final String name = onRef(new Ref( |
| JsonString.class.cast(ref).getString(), configuration.getClassName(), |
| imports, attributes, nested)); |
| if (name != null) { |
| return name; |
| } |
| } |
| |
| final JsonValue propTypeValue = schema.get("type"); |
| if (propTypeValue != null && propTypeValue.getValueType() == JsonValue.ValueType.STRING) { |
| String type = JsonString.class.cast(propTypeValue).getString(); |
| final JsonValue formatValue = schema.get("date-time"); |
| if (formatValue != null && formatValue.getValueType() == JsonValue.ValueType.STRING) { |
| type = JsonString.class.cast(formatValue).getString(); |
| } |
| switch (type) { |
| case "array": |
| throw new IllegalStateException("Array of array unsupported"); |
| case "object": |
| final String className = configuration.getClassName() + Character.toUpperCase(javaName.charAt(0)) + javaName.substring(1); |
| nested.putAll(newSubPojoGenerator(new PojoConfiguration() |
| .setPackageName(configuration.getPackageName()) |
| .setClassName(className) |
| .setAddJsonbProperty(configuration.isAddJsonbProperty()) |
| .setAddAllArgsConstructor(configuration.isAddAllArgsConstructor()) |
| .setOnRef(configuration.getOnRef())) |
| .visitSchema(schema) |
| .generate()); |
| return className; |
| case "null": |
| imports.add(JsonValue.class.getName()); |
| return JsonValue.class.getSimpleName(); |
| case "boolean": |
| return "Boolean"; |
| case "uuid": |
| case "hostname": |
| case "idn-hostname": |
| case "email": |
| case "idn-email": |
| case "ipv4": |
| case "ipv6": |
| case "uri": |
| case "uri-reference": |
| case "iri": |
| case "iri-reference": |
| case "uri-template": |
| case "json-pointer": |
| case "relative-json-pointer": |
| case "regex": |
| case "string": |
| case "binary": |
| case "password": |
| return "String"; |
| case "number": |
| case "double": |
| return "Double"; |
| case "int": |
| case "int32": |
| case "integer": |
| return "Integer"; |
| case "int64": |
| case "long": |
| return "Long"; |
| case "float": |
| return "Float"; |
| case "date": |
| imports.add(LocalDate.class.getName()); |
| return LocalDate.class.getSimpleName(); |
| case "dateTime": |
| imports.add(OffsetDateTime.class.getName()); |
| return OffsetDateTime.class.getSimpleName(); |
| case "duration": |
| imports.add(Duration.class.getName()); |
| return Duration.class.getSimpleName(); |
| case "time": |
| imports.add(LocalTime.class.getName()); |
| return LocalTime.class.getSimpleName(); |
| case "byte": |
| return "byte[]"; |
| default: |
| throw new IllegalArgumentException("Unsupported type: " + type); |
| } |
| } |
| |
| imports.add(JsonValue.class.getName()); |
| return JsonValue.class.getSimpleName(); |
| } |
| |
| private String toJavaName(final String key) { |
| String name = key.chars() |
| .mapToObj(i -> Character.toString(!Character.isJavaIdentifierPart(i) ? '_' : (char) i)) |
| .collect(joining()); |
| if (Character.isDigit(name.charAt(0))) { |
| name = "a" + name; |
| } |
| while (!name.isEmpty() && (!Character.isJavaIdentifierStart(name.charAt(0)) || name.charAt(0) == '_' || name.charAt(0) == '$')) { |
| name = name.substring(1); |
| } |
| if (name.isEmpty()) { |
| throw new IllegalArgumentException("Can't find a name for '" + key + "'"); |
| } |
| |
| if (isReserved(name)) { |
| name += "Value"; |
| } |
| |
| if (name.contains("_")) { |
| name = toCamelCase(name); |
| } |
| |
| if (!Objects.equals(key, name) && configuration.isAddJsonbProperty()) { |
| imports.add(JsonbProperty.class.getName()); |
| } |
| return name; |
| } |
| |
| protected String toCamelCase(final String name) { |
| final StringBuilder out = new StringBuilder(name.length()); |
| boolean up = false; |
| for (final char c : name.toCharArray()) { |
| if (up) { |
| out.append(Character.toUpperCase(c)); |
| up = false; |
| } else if (c == '_') { |
| up = true; |
| } else { |
| out.append(c); |
| } |
| } |
| return out.toString(); |
| } |
| |
| protected boolean isReserved(final String name) { |
| return "continue".equals(name) || "break".equals(name) || "default".equals(name) || |
| "do".equals(name) || "while".equals(name) || |
| "for".equals(name) || |
| "if".equals(name) || "else".equals(name) || |
| "enum".equals(name) || |
| "int".equals(name) || |
| "long".equals(name) || |
| "float".equals(name) || |
| "double".equals(name) || |
| "boolean".equals(name) || |
| "byte".equals(name) || |
| "char".equals(name) || |
| "short".equals(name) || |
| "String".equals(name); |
| } |
| |
| private static <T> T getValueAs(final JsonObject schema, final String attribute, final Class<T> type) { |
| final JsonValue value = schema.get(attribute); |
| if (value == null) { |
| throw new IllegalArgumentException("No \"" + attribute + "\" value in " + schema); |
| } |
| return valueAs(schema, type, value); |
| } |
| |
| private static <T> T valueAs(final JsonObject schema, final Class<T> type, final JsonValue value) { |
| if (!type.isInstance(value)) { |
| throw new IllegalArgumentException("\"items\" not an object: " + schema); |
| } |
| return type.cast(value); |
| } |
| |
| public static class PojoConfiguration { |
| private String packageName = "org.apache.johnzon.generated.pojo"; |
| private String className; |
| private boolean addJsonbProperty = true; |
| private boolean addAllArgsConstructor = true; |
| private boolean fluentSetters = false; |
| private Function<Ref, String> onRef; |
| |
| public Function<Ref, String> getOnRef() { |
| return onRef; |
| } |
| |
| public PojoConfiguration setOnRef(final Function<Ref, String> onRef) { |
| this.onRef = onRef; |
| return this; |
| } |
| |
| public boolean isFluentSetters() { |
| return fluentSetters; |
| } |
| |
| public PojoConfiguration setFluentSetters(final boolean fluentSetters) { |
| this.fluentSetters = fluentSetters; |
| return this; |
| } |
| |
| public boolean isAddAllArgsConstructor() { |
| return addAllArgsConstructor; |
| } |
| |
| public PojoConfiguration setAddAllArgsConstructor(final boolean addAllArgsConstructor) { |
| this.addAllArgsConstructor = addAllArgsConstructor; |
| return this; |
| } |
| |
| public boolean isAddJsonbProperty() { |
| return addJsonbProperty; |
| } |
| |
| public PojoConfiguration setAddJsonbProperty(final boolean addJsonbProperty) { |
| this.addJsonbProperty = addJsonbProperty; |
| return this; |
| } |
| |
| public String getClassName() { |
| return className; |
| } |
| |
| public PojoConfiguration setClassName(final String className) { |
| this.className = className; |
| return this; |
| } |
| |
| public String getPackageName() { |
| return packageName; |
| } |
| |
| public PojoConfiguration setPackageName(final String packageName) { |
| this.packageName = packageName; |
| return this; |
| } |
| } |
| |
| protected static class Attribute { |
| protected final String javaName; |
| protected final String jsonName; |
| protected final String type; |
| |
| protected Attribute(final String javaName, final String jsonName, final String type) { |
| this.javaName = javaName; |
| this.jsonName = jsonName; |
| this.type = type; |
| } |
| |
| public String getJavaName() { |
| return javaName; |
| } |
| |
| public String getJsonName() { |
| return jsonName; |
| } |
| |
| public String getType() { |
| return type; |
| } |
| } |
| |
| public static class Ref { |
| private final String ref; |
| private final String enclosingClass; |
| private final Set<String> imports; |
| private final List<Attribute> attributes; |
| private final Map<String, String> nested; |
| |
| private Ref(final String ref, final String enclosingClass, final Set<String> imports, |
| final List<Attribute> attributes, final Map<String, String> nested) { |
| this.ref = ref; |
| this.enclosingClass = enclosingClass; |
| this.imports = imports; |
| this.attributes = attributes; |
| this.nested = nested; |
| } |
| |
| public String getEnclosingClass() { |
| return enclosingClass; |
| } |
| |
| public String getRef() { |
| return ref; |
| } |
| |
| public Set<String> getImports() { |
| return imports; |
| } |
| |
| public List<Attribute> getAttributes() { |
| return attributes; |
| } |
| |
| public Map<String, String> getNested() { |
| return nested; |
| } |
| } |
| } |