package io.vertx.codegen;
/*
* Copyright 2014 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
import io.vertx.codegen.doc.Doc;
import io.vertx.codegen.doc.Text;
import io.vertx.codegen.type.ClassKind;
import io.vertx.codegen.type.ClassTypeInfo;
import io.vertx.codegen.type.ParameterizedTypeInfo;
import io.vertx.codegen.type.TypeInfo;
import io.vertx.codegen.type.TypeVariableInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* @author <a href="http://tfox.org">Tim Fox</a>
*/
public class MethodInfo implements Comparable<MethodInfo> {
final String name;
final MethodKind kind;
final TypeInfo returnType;
final Text returnDescription;
final boolean fluent;
final boolean cacheReturn;
final String comment;
final Doc doc;
final boolean staticMethod;
final boolean defaultMethod;
List<TypeParamInfo.Method> typeParams;
LinkedHashSet<ClassTypeInfo> ownerTypes;
List<ParamInfo> params;
public MethodInfo(Set<ClassTypeInfo> ownerTypes, String name, MethodKind kind,
TypeInfo returnType, Text returnDescription, boolean fluent, boolean cacheReturn,
List<ParamInfo> params, String comment, Doc doc, boolean staticMethod, boolean defaultMethod,
List<TypeParamInfo.Method> typeParams) {
this.comment = comment;
this.kind = kind;
this.name = name;
this.returnType = returnType;
this.returnDescription = returnDescription;
this.fluent = fluent;
this.cacheReturn = cacheReturn;
this.doc = doc;
this.staticMethod = staticMethod;
this.defaultMethod = defaultMethod;
this.params = params;
this.typeParams = typeParams;
this.ownerTypes = new LinkedHashSet<>(ownerTypes);
}
public String getName() {
return name;
}
public String getName(Case _case) {
return _case.format(Case.CAMEL.parse(name));
}
public MethodKind getKind() {
return kind;
}
public TypeInfo getReturnType() {
return returnType;
}
/**
* Resolve the method parameter that is of kind {@link ClassKind#CLASS_TYPE} and that matches the specified type variable e.g:<br/>
* <br/>
* {@code <U> Map.Entry<String, U> getEntry(String s, Class<U> type);}
* <br/>
* <br/>
* returns for {@code <U>} the second method parameter.
*
* @param typeVar the type variable to check
* @return the matching method parameter or null
*/
public ParamInfo resolveClassTypeParam(TypeVariableInfo typeVar) {
TypeArgExpression res = resolveTypeArg(typeVar);
if (res != null && res.isClassType()) {
return res.getParam();
}
return null;
}
public TypeArgExpression resolveTypeArg(TypeVariableInfo typeVar) {
for (TypeParamInfo.Method typeParam : typeParams) {
if (typeParam.getName().equals(typeVar.getName())) {
for (ParamInfo param : params) {
if (param.getType().getKind() == ClassKind.CLASS_TYPE &&
param.getType().isParameterized()) {
TypeInfo arg_ = ((ParameterizedTypeInfo) param.getType()).getArg(0);
if (arg_.isVariable()) {
TypeVariableInfo ttt = (TypeVariableInfo) arg_;
if (ttt.getParam().equals(typeParam)) {
return new TypeArgExpression(TypeArgExpression.CLASS_TYPE_ARG, ttt, param, 0);
}
}
} else if (param.getType().getKind() == ClassKind.API && param.getType().isParameterized()) {
ParameterizedTypeInfo type = (ParameterizedTypeInfo) param.getType();
int index = 0;
for (TypeInfo i : type.getArgs()) {
if (i instanceof TypeVariableInfo) {
TypeVariableInfo tt = (TypeVariableInfo) i;
if (tt.getParam().equals(typeParam)) {
return new TypeArgExpression(TypeArgExpression.API_ARG, tt, param, index);
}
}
index++;
}
}
}
return null;
}
}
return null;
}
public Text getReturnDescription() {
return returnDescription;
}
public Set<ClassTypeInfo> getOwnerTypes() {
return ownerTypes;
}
/**
* Returns the method signature, the returned object is freely modifiable.
*
* @return the method signature
*/
public Signature getSignature() {
return new Signature(name, new ArrayList<>(params));
}
/**
* Return true if the provided type is the sole owner of this method, i.e this method
* is only declared by the provided type.
*
* @param owner the tested type
* @return true when this method is owned by the <code>owner</code> argument
*/
public boolean isOwnedBy(ClassTypeInfo owner) {
return ownerTypes.contains(owner) && ownerTypes.size() == 1;
}
public boolean isFluent() {
return fluent;
}
public boolean isCacheReturn() {
return cacheReturn;
}
/**
* @return true if the method has a nullable return
*/
public boolean isNullableReturn() {
return returnType instanceof TypeVariableInfo || returnType.isNullable();
}
public List<ParamInfo> getParams() {
return params;
}
public ParamInfo getParam(int index) {
return params.get(index);
}
public String getComment() {
return comment;
}
public Doc getDoc() {
return doc;
}
public boolean isStaticMethod() {
return staticMethod;
}
public boolean isDefaultMethod() {
return defaultMethod;
}
public List<TypeParamInfo.Method> getTypeParams() {
return typeParams;
}
public void mergeTypeParams(List<TypeParamInfo.Method> mergedTypeParams) throws IllegalArgumentException {
int l = Math.min(typeParams.size(), mergedTypeParams.size());
if (typeParams.subList(0, l).equals(mergedTypeParams.subList(0, l))) {
if (mergedTypeParams.size() > typeParams.size()) {
typeParams.addAll(mergedTypeParams.subList(typeParams.size(), mergedTypeParams.size()));
}
} else {
throw new IllegalArgumentException("Merged type params " + mergedTypeParams + " don't match the existing ones " + typeParams);
}
}
public void collectImports(Collection<ClassTypeInfo> imports) {
params.stream().map(ParamInfo::getType).forEach(a -> a.collectImports(imports));
}
@Override
public int compareTo(MethodInfo o) {
int cmp = name.compareTo(o.name);
if (cmp != 0) {
return cmp;
}
Iterator<ParamInfo> i1 = params.iterator();
Iterator<ParamInfo> i2 = o.params.iterator();
while (i1.hasNext() && i2.hasNext()) {
ParamInfo p1 = i1.next();
ParamInfo p2 = i2.next();
cmp = p1.getType().getRaw().getName().compareTo(p2.getType().getRaw().getName());
if (cmp != 0) {
return cmp;
}
}
if (i1.hasNext()) {
if (!i2.hasNext()) {
return 1;
}
} else if (!i2.hasNext()) {
return -1;
}
return 0;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (typeParams.size() > 0) {
for (int i = 0;i < typeParams.size();i++) {
sb.append(i > 0 ? ", " : "<");
sb.append(typeParams.get(i).getName());
}
sb.append("> ");
}
sb.append(returnType.getName());
sb.append(' ');
sb.append(getSignature().toString());
return sb.toString();
}
}