/*
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.mdsal.binding.java.api.generator;
import static com.google.common.base.Preconditions.checkArgument;
import static org.opendaylight.mdsal.binding.java.api.generator.Constants.COMMA;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.opendaylight.mdsal.binding.model.api.AnnotationType;
import org.opendaylight.mdsal.binding.model.api.Constant;
import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
import org.opendaylight.mdsal.binding.model.api.GeneratedType;
import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
import org.opendaylight.mdsal.binding.model.api.MethodSignature;
import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
import org.opendaylight.mdsal.binding.model.api.Type;
import org.opendaylight.mdsal.binding.model.api.WildcardType;
import org.opendaylight.mdsal.binding.model.util.TypeConstants;
import org.opendaylight.mdsal.binding.model.util.Types;
public final class GeneratorUtil {
private GeneratorUtil() {
throw new UnsupportedOperationException();
}
/**
* Returns the map of imports. The map maps the type name to the package name. To the map are added packages
* for genType
and for all enclosed types, constants, methods (parameter types, return values),
* implemented types.
*
* @param genType generated type for which the map of the imports is created
* @return map of the necessary imports
* @throws IllegalArgumentException if genType
equals null
*/
static Map createImports(final GeneratedType genType) {
if (genType == null) {
throw new IllegalArgumentException("Generated Type cannot be NULL!");
}
final Map imports = new LinkedHashMap<>();
List childGeneratedTypes = genType.getEnclosedTypes();
if (!childGeneratedTypes.isEmpty()) {
for (GeneratedType genTypeChild : childGeneratedTypes) {
imports.putAll(createImports(genTypeChild));
}
}
// REGULAR EXPRESSION
if (genType instanceof GeneratedTransferObject
&& isConstantInTO(TypeConstants.PATTERN_CONSTANT_NAME, (GeneratedTransferObject) genType)) {
putTypeIntoImports(genType, Types.typeForClass(java.util.regex.Pattern.class), imports);
}
final List methods = genType.getMethodDefinitions();
// METHODS
if (methods != null) {
for (final MethodSignature method : methods) {
final Type methodReturnType = method.getReturnType();
putTypeIntoImports(genType, methodReturnType, imports);
for (final MethodSignature.Parameter methodParam : method.getParameters()) {
putTypeIntoImports(genType, methodParam.getType(), imports);
}
for (final AnnotationType at : method.getAnnotations()) {
putTypeIntoImports(genType, at, imports);
}
}
}
// PROPERTIES
if (genType instanceof GeneratedTransferObject) {
final GeneratedTransferObject genTO = (GeneratedTransferObject) genType;
final List properties = genTO.getProperties();
if (properties != null) {
for (GeneratedProperty property : properties) {
final Type propertyType = property.getReturnType();
putTypeIntoImports(genType, propertyType, imports);
}
}
}
return imports;
}
/**
* Evaluates if it is necessary to add the package name for type
to the map of imports for
* parentGenType
. If it is so the package name is saved to the map imports
.
*
* @param parentGenType generated type for which is the map of the necessary imports built
* @param type JAVA Type
for which is the necessary of the package import evaluated
* @param imports map of the imports for parentGenType
* @throws IllegalArgumentException
*
* - if the
parentGenType
equals
* null
* - if the name of
parentGenType
equals
* null
* - if the name of the package of
parentGenType
* equals null
* - if the
type
equals null
* - if the name of
type
equals null
*
* - if the name of the package of
type
equals
* null
*
*/
static void putTypeIntoImports(final GeneratedType parentGenType, final Type type,
final Map imports) {
checkArgument(parentGenType != null, "Parent Generated Type parameter MUST be specified and cannot be "
+ "NULL!");
checkArgument(parentGenType.getName() != null, "Parent Generated Type name cannot be NULL!");
checkArgument(parentGenType.getPackageName() != null,
"Parent Generated Type cannot have Package Name referenced as NULL!");
checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
checkArgument(type.getName() != null, "Type name cannot be NULL!");
checkArgument(type.getPackageName() != null, "Type cannot have Package Name referenced as NULL!");
final String typeName = type.getName();
final String typePackageName = type.getPackageName();
final String parentTypeName = parentGenType.getName();
if (typeName.equals(parentTypeName) || typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
return;
}
if (!imports.containsKey(typeName)) {
imports.put(typeName, type.getIdentifier());
}
if (type instanceof ParameterizedType) {
final ParameterizedType paramType = (ParameterizedType) type;
final Type[] params = paramType.getActualTypeArguments();
if (params != null) {
for (Type param : params) {
putTypeIntoImports(parentGenType, param, imports);
}
}
}
}
/**
* Checks if the constant with the name constName
is in the list of the constant definition for
* genTO
.
*
* @param constName string with the name of constant which is sought
* @param genTO generated transfer object in which is constName
sought
* @return boolean value
*
* - true - if
constName
is in the list of the
* constant definition for genTO
* - false - in other cases
*
* @throws IllegalArgumentException
*
* - if
constName
equals null
* - if
genTO
equals null
*
*/
static boolean isConstantInTO(final String constName, final GeneratedTransferObject genTO) {
if (constName == null || genTO == null) {
throw new IllegalArgumentException();
}
List consts = genTO.getConstantDefinitions();
for (Constant cons : consts) {
if (cons.getName().equals(constName)) {
return true;
}
}
return false;
}
/**
* Creates the map which maps the type name to package name and contains only package names for enclosed types
* of genType
and recursively their enclosed types.
*
* @param genType JAVA Type
for which is the map created
* @return map of the package names for all the enclosed types and recursively their enclosed types
*/
static Map createChildImports(final GeneratedType genType) {
Map childImports = new LinkedHashMap<>();
for (GeneratedType genTypeChild : genType.getEnclosedTypes()) {
createChildImports(genTypeChild);
childImports.put(genTypeChild.getName(), genTypeChild.getPackageName());
}
return childImports;
}
/**
* Builds the string which contains either the full path to the type (package name with type) or only type name
* if the package is among imports
.
*
* @param parentGenType generated type which contains type
* @param type JAVA Type
for which is the string with type info generated
* @param imports map of necessary imports for parentGenType
* @return string with type name for type
in the full format or in the short format
* @throws IllegalArgumentException
*
* - if the
type
equals null
* - if the name of the
type
equals
* null
* - if the name of the package of the
type
* equals null
* - if the
imports
equals null
*
*/
static String getExplicitType(final GeneratedType parentGenType, final Type type,
final Map imports) {
checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
checkArgument(imports != null, "Imports Map cannot be NULL!");
final JavaTypeName importedType = imports.get(type.getName());
final StringBuilder builder = new StringBuilder();
if (type.getIdentifier().equals(importedType)) {
builder.append(type.getName());
addActualTypeParameters(builder, type, parentGenType, imports);
if (builder.toString().equals("Void")) {
return "void";
}
} else {
if (type.equals(Types.voidType())) {
return "void";
}
builder.append(type.getFullyQualifiedName());
addActualTypeParameters(builder, type, parentGenType, imports);
}
return builder.toString();
}
/**
* Adds actual type parameters from type
to builder
if type
is
* ParametrizedType
.
*
* @param builder string builder which contains type name
* @param type JAVA Type
for which is the string with type info generated
* @param parentGenType generated type which contains type
* @param imports map of necessary imports for parentGenType
* @return if type
is of the type ParametrizedType
* then builder
+ actual type
* parameters else only builder
*/
private static StringBuilder addActualTypeParameters(final StringBuilder builder, final Type type,
final GeneratedType parentGenType, final Map imports) {
if (type instanceof ParameterizedType) {
final ParameterizedType pType = (ParameterizedType) type;
final Type[] pTypes = pType.getActualTypeArguments();
builder.append('<').append(getParameters(parentGenType, pTypes, imports)).append('>');
}
return builder;
}
/**
* Generates the string with all actual type parameters from pTypes
.
*
* @param parentGenType generated type for which is the JAVA code generated
* @param paramTypes array of Type
instances = actual type parameters
* @param availableImports map of imports for parentGenType
* @return string with all actual type parameters from pTypes
*/
private static String getParameters(final GeneratedType parentGenType, final Type[] paramTypes,
final Map availableImports) {
if (paramTypes == null || paramTypes.length == 0) {
return "?";
}
final StringBuilder builder = new StringBuilder();
for (int i = 0; i < paramTypes.length; i++) {
final Type t = paramTypes[i];
String separator = COMMA;
if (i == paramTypes.length - 1) {
separator = "";
}
if (Types.voidType().equals(t)) {
builder.append("java.lang.Void").append(separator);
} else {
if (t instanceof WildcardType) {
builder.append("? extends ");
}
builder.append(getExplicitType(parentGenType, t, availableImports)).append(separator);
}
}
return builder.toString();
}
/**
* Returns the reference to highest (top parent) Generated Transfer Object.
*
* @param childTransportObject is generated transfer object which can be extended by other generated transfer object
* @return in first case that childTransportObject
is not extended then
* childTransportObject
is returned. In second case the method is recursive called until first
* case.
* @throws IllegalArgumentException if childTransportObject
equals null
*/
static GeneratedTransferObject getTopParentTransportObject(final GeneratedTransferObject childTransportObject) {
if (childTransportObject == null) {
throw new IllegalArgumentException("Parameter childTransportObject can't be null.");
}
if (childTransportObject.getSuperType() == null) {
return childTransportObject;
}
return getTopParentTransportObject(childTransportObject.getSuperType());
}
/**
* Selects from input list of properties only those which have read only attribute set to true.
*
* @param properties list of properties of generated transfer object
* @return subset of properties
which have read only attribute set to true
*/
static List resolveReadOnlyPropertiesFromTO(final List properties) {
List readOnlyProperties = new ArrayList<>();
if (properties != null) {
for (final GeneratedProperty property : properties) {
if (property.isReadOnly()) {
readOnlyProperties.add(property);
}
}
}
return readOnlyProperties;
}
/**
* Returns the list of the read only properties of all extending generated transfer object from genTO
* to highest parent generated transfer object.
*
* @param genTO generated transfer object for which is the list of read only properties generated
* @return list of all read only properties from actual to highest parent generated transfer object. In case when
* extension exists the method is recursive called.
*/
static List getPropertiesOfAllParents(final GeneratedTransferObject genTO) {
List propertiesOfAllParents = new ArrayList<>();
if (genTO.getSuperType() != null) {
final List allPropertiesOfTO = genTO.getSuperType().getProperties();
List readOnlyPropertiesOfTO = resolveReadOnlyPropertiesFromTO(allPropertiesOfTO);
propertiesOfAllParents.addAll(readOnlyPropertiesOfTO);
propertiesOfAllParents.addAll(getPropertiesOfAllParents(genTO.getSuperType()));
}
return propertiesOfAllParents;
}
}