/*
* Licensed to the author 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 net.java.quickcheck.srcgenerator;
import static java.lang.String.*;
import static javax.lang.model.element.ElementKind.*;
import static net.java.quickcheck.srcgenerator.Assertion.*;
import static net.java.quickcheck.srcgenerator.Parameter.Cardinality.*;
import static net.java.quickcheck.srcgenerator.Traversal.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Messager;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.NoType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.tools.Diagnostic.Kind;
import net.java.quickcheck.srcgenerator.Parameter.Cardinality;
class Method{
final String name;
final Type returnType;
final List<Parameter> parameters = new ArrayList<Parameter>();
final List<TypeParameter> typeParameters = new ArrayList<TypeParameter>();
private final Messager messager;
static final String GENERATOR_TYPE_NAME = "net.java.quickcheck.Generator";
Method(CharSequence name, CharSequence returnType, Parameter... parameters){
this(name, returnType, Collections.<TypeParameter> emptyList(), Arrays.asList(parameters), false);
}
Method(CharSequence name, CharSequence returnType, List<TypeParameter> typeParameters, List<Parameter> parameters,
boolean generic) {
assertNotNull(name, "name");
assertNotNull(returnType, "returnType");
assertNotNull(typeParameters, "typeParameters");
assertNotNull(parameters, "parameters");
assertNotNull(generic, "generic");
this.messager = null;
this.name = name.toString();
this.returnType = new Type(returnType, generic);
this.typeParameters.addAll(typeParameters);
this.parameters.addAll(parameters);
}
Method(Messager messager, ExecutableElement method) {
assertNotNull(messager, "messager");
this.messager = messager;
this.messager.printMessage(Kind.NOTE, format("Start method %s",method));
parameters.addAll(parameters(method));
this.name = method.getSimpleName().toString();
this.returnType = returnTypeName(method);
for(TypeParameterElement t : method.getTypeParameters()) this.typeParameters.add(new TypeParameter(t));
}
private List<Parameter> parameters(ExecutableElement method) {
List<Parameter> ps = new ArrayList<Parameter>();
for(int i=0;i<method.getParameters().size();i++) {
final VariableElement v = method.getParameters().get(i);
final Cardinality cardinality = method.getParameters().size() - 1 == i ? VARARRAY : ARRAY;
ps.add(new Parameter(v, cardinality));
}
return ps;
}
private Type returnTypeName(ExecutableElement method) {
verifyReturnType(method);
List<? extends TypeMirror> typeArguments = toDeclaredType(method.getReturnType()).getTypeArguments();
assertTrue(typeArguments.size() > 0, "type argument expected");
//TODO would have to look for the type parameter otherwise
TypeMirror next = typeArguments.get(typeArguments.size() - 1);
return new Type(next.toString(), next instanceof TypeVariable);
}
private void verifyReturnType(ExecutableElement method) {
TypeMirror type = method.getReturnType();
assertTrue(isSubtypeOfGenerator(type), "The return type [%s] is not an implementation of %s.",
method, GENERATOR_TYPE_NAME);
assertTrue(isInterface(type), "The return type (%s) has to be an interface.", type);
}
private boolean isInterface(TypeMirror typeMirror) {
return toTypeElement(typeMirror).getKind() == INTERFACE;
}
private boolean isSubtypeOfGenerator(TypeMirror type) {
if(type instanceof NoType) return false;
if(toTypeElement(type).getQualifiedName().contentEquals(GENERATOR_TYPE_NAME)) return true;
for(TypeMirror t : toTypeElement(type).getInterfaces()){
if(isSubtypeOfGenerator(t)) return true;
}
return false;
}
@Override
public boolean equals(Object other) {
if(!(other instanceof Method)) return false;
Method method = (Method) other;
return method.name.equals(name) && method.returnType.equals(returnType) && method.parameters.equals(parameters)
&& method.typeParameters.equals(typeParameters);
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return format("Method[name=%s, returnType=%s, typeParameter=%s,parameters=%s]", name, returnType,
typeParameters, parameters);
}
}