Skip to content

Commit

Permalink
improved method resolution on inherited types; refactored type creation;
Browse files Browse the repository at this point in the history
  • Loading branch information
sdaschner committed Aug 13, 2015
1 parent 187eb26 commit f2b7f52
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.sebastian_daschner.jaxrs_analyzer.LogProvider;
import com.sebastian_daschner.jaxrs_analyzer.analysis.project.AnnotationInterpreter;
import com.sebastian_daschner.jaxrs_analyzer.analysis.project.methods.MethodAnalyzer;
import com.sebastian_daschner.jaxrs_analyzer.analysis.utils.JavaUtils;
import com.sebastian_daschner.jaxrs_analyzer.model.results.ClassResult;
import com.sebastian_daschner.jaxrs_analyzer.model.types.Type;
import javassist.CtClass;
Expand Down Expand Up @@ -144,7 +145,10 @@ private boolean isRelevant() {
private void analyzeFields(final ClassResult classResult) {
for (CtField ctField : ctClass.getDeclaredFields()) {
try {
final Type fieldType = new Type(ctField.getType());
final Type fieldType = JavaUtils.getFieldType(ctField);
if (fieldType == null)
continue;

Stream.of(ctField.getAnnotations()).forEach(a -> AnnotationInterpreter.interpretFieldAnnotation(a, fieldType, classResult.getMethods()));

} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.SignatureAttribute;

import javax.json.JsonObject;
import javax.ws.rs.*;
Expand Down Expand Up @@ -140,10 +139,9 @@ private boolean isRelevant() {
* Analyzes the current method.
*
* @return The method result
* @throws NotFoundException If some method could not be accessed
* @throws BadBytecode If the return type could not be determined
* @throws BadBytecode If the method could not be analyzed
*/
private MethodResult analyzeInternal() throws NotFoundException, BadBytecode {
private MethodResult analyzeInternal() throws BadBytecode {
final MethodResult methodResult = new MethodResult();

analyzeMethodInformation(method, methodResult);
Expand All @@ -159,8 +157,7 @@ private MethodResult analyzeInternal() throws NotFoundException, BadBytecode {
analyzeMethodContent(methodResult);
else {
// build empty response with return type
final String sig = method.getGenericSignature() != null ? method.getGenericSignature() : method.getSignature();
final Type returnType = new Type(SignatureAttribute.toMethodSignature(sig).getReturnType());
final Type returnType = JavaUtils.getReturnType(method);
if (!Types.RESPONSE.equals(returnType) && !Types.PRIMITIVE_VOID.equals(returnType)) {
final HttpResponse emptyResponse = new HttpResponse();
emptyResponse.getEntityTypes().add(returnType);
Expand All @@ -176,9 +173,9 @@ private MethodResult analyzeInternal() throws NotFoundException, BadBytecode {
*
* @param ctMethod The method to analyze
* @param methodResult The method result
* @throws NotFoundException If some method could not be accessed
* @throws BadBytecode If the method could not be analyzed
*/
private void analyzeMethodInformation(final CtMethod ctMethod, final MethodResult methodResult) throws NotFoundException {
private void analyzeMethodInformation(final CtMethod ctMethod, final MethodResult methodResult) throws BadBytecode {

if (!hasJaxRsAnnotations(ctMethod) && annotatedSuperMethod != null) {
// handle inherited annotations from superclass
Expand All @@ -198,15 +195,15 @@ private void analyzeMethodInformation(final CtMethod ctMethod, final MethodResul
*
* @param ctMethod The method to analyze
* @param methodResult The method result
* @throws NotFoundException If some method could not be accessed
* @throws BadBytecode If the method signature could not be analyzed
*/
private void analyzeMethodParameters(final CtMethod ctMethod, final MethodResult methodResult) throws NotFoundException {
private void analyzeMethodParameters(final CtMethod ctMethod, final MethodResult methodResult) throws BadBytecode {
// method parameters and parameter annotations
final CtClass[] parameterTypes = ctMethod.getParameterTypes();
final List<Type> parameterTypes = JavaUtils.getParameterTypes(ctMethod);

for (int index = 0; index < parameterTypes.length; index++) {
final Type parameterType = new Type(parameterTypes[index]);
final Object[][] parameterAnnotations = ctMethod.getAvailableParameterAnnotations();
final Object[][] parameterAnnotations = ctMethod.getAvailableParameterAnnotations();
for (int index = 0; index < parameterTypes.size(); index++) {
final Type parameterType = parameterTypes.get(index);

if (isEntityParameter(ctMethod, index)) {
methodResult.setRequestBodyType(parameterType);
Expand All @@ -216,7 +213,6 @@ private void analyzeMethodParameters(final CtMethod ctMethod, final MethodResult
for (final Object annotation : parameterAnnotations[index]) {
AnnotationInterpreter.interpretMethodParameterAnnotation(annotation, parameterType, methodResult);
}

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.sebastian_daschner.jaxrs_analyzer.LogProvider;
import com.sebastian_daschner.jaxrs_analyzer.analysis.bytecode.simulation.MethodPool;
import com.sebastian_daschner.jaxrs_analyzer.analysis.bytecode.simulation.MethodSimulator;
import com.sebastian_daschner.jaxrs_analyzer.analysis.utils.JavaUtils;
import com.sebastian_daschner.jaxrs_analyzer.model.elements.Element;
import com.sebastian_daschner.jaxrs_analyzer.model.elements.HttpResponse;
import com.sebastian_daschner.jaxrs_analyzer.model.elements.JsonValue;
Expand All @@ -28,8 +29,6 @@
import com.sebastian_daschner.jaxrs_analyzer.model.types.Type;
import com.sebastian_daschner.jaxrs_analyzer.model.types.Types;
import javassist.CtMethod;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.SignatureAttribute;

import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -67,16 +66,10 @@ void analyze(final CtMethod method, final MethodResult result) {
projectMethods.stream().forEach(MethodPool.getInstance()::addProjectMethod);

final Element returnedElement = methodSimulator.simulate(visitedInstructions);
final String signature = method.getGenericSignature() != null ? method.getGenericSignature() : method.getSignature();
final Type returnType;
try {
returnType = new Type(SignatureAttribute.toMethodSignature(signature).getReturnType());
} catch (BadBytecode e) {
throw new RuntimeException(e);
}
final Type returnType = JavaUtils.getReturnType(method);

// void resource methods are interpreted later
if (Types.PRIMITIVE_VOID.equals(returnType)) {
// void resource methods are interpreted later; stop analyzing on error
if (returnType == null || Types.PRIMITIVE_VOID.equals(returnType)) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.bytecode.SignatureAttribute;

import javax.json.*;
import javax.ws.rs.core.MediaType;
Expand Down Expand Up @@ -209,27 +208,19 @@ private static boolean isGetter(final CtMethod method) {
}

private static Pair<String, Type> mapField(final CtField field) {
try {
final String sig = field.getGenericSignature() != null ? field.getGenericSignature() : field.getSignature();
final Type fieldType = new Type(SignatureAttribute.toTypeSignature(sig));
return Pair.of(field.getName(), fieldType);
} catch (Exception e) {
LogProvider.error("Could not analyze field: " + field);
LogProvider.debug(e);
final Type type = JavaUtils.getFieldType(field);
if (type == null)
return null;
}

return Pair.of(field.getName(), type);
}

private static Pair<String, Type> mapGetter(final CtMethod method) {
try {
final String sig = method.getGenericSignature() != null ? method.getGenericSignature() : method.getSignature();
final Type returnType = new Type(SignatureAttribute.toMethodSignature(sig).getReturnType());
return Pair.of(normalizeGetter(method.getName()), returnType);
} catch (Exception e) {
LogProvider.error("Could not analyze method: " + method);
LogProvider.debug(e);
final Type returnType = JavaUtils.getReturnType(method);
if (returnType == null)
return null;
}

return Pair.of(normalizeGetter(method.getName()), returnType);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@

package com.sebastian_daschner.jaxrs_analyzer.analysis.utils;

import com.sebastian_daschner.jaxrs_analyzer.LogProvider;
import com.sebastian_daschner.jaxrs_analyzer.model.methods.MethodIdentifier;
import com.sebastian_daschner.jaxrs_analyzer.model.types.Type;
import com.sebastian_daschner.jaxrs_analyzer.model.types.Types;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMember;
import javassist.NotFoundException;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.SignatureAttribute;

import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -145,23 +148,78 @@ private static Type determineMostSpecific(final Type firstType, final Type secon
public static CtBehavior getMethod(final MethodIdentifier identifier) {
final CtClass ctClass = identifier.getContainingClass().getCtClass();

// raw parameter types are taken because of type erasure in bytecode method call

if (JavaUtils.isInitializerName(identifier.getMethodName())) {
return Stream.of(ctClass.getDeclaredConstructors()).filter(b -> identifier.getParameters().equals(getParameterTypes(b))).findAny().orElse(null);
return Stream.of(ctClass.getDeclaredConstructors()).filter(b -> identifier.getParameters().equals(getRawParameterTypes(b))).findAny().orElse(null);
}
return Stream.of(ctClass.getDeclaredMethods())

return Stream.concat(Stream.of(ctClass.getDeclaredMethods()), Stream.of(ctClass.getMethods()))
.filter(b -> identifier.getMethodName().equals(b.getName()))
.filter(b -> identifier.getParameters().equals(getParameterTypes(b))).findAny().orElse(null);
.filter(b -> identifier.getParameters().equals(getRawParameterTypes(b))).findAny().orElse(null);
}

private static List<Type> getRawParameterTypes(final CtBehavior behavior) {
try {
return Stream.of(SignatureAttribute.toMethodSignature(behavior.getSignature()).getParameterTypes()).map(Type::new).collect(Collectors.toList());
} catch (BadBytecode e) {
// ignore
return Collections.emptyList();
}
}

private static List<Type> getParameterTypes(final CtBehavior behavior) {
/**
* Returns the parameter types of the given method.
*
* @param behavior The method
* @return The parameter types
*/
public static List<Type> getParameterTypes(final CtBehavior behavior) {
try {
return Stream.of(behavior.getParameterTypes()).map(Type::new).collect(Collectors.toList());
} catch (NotFoundException e) {
final String sig = behavior.getGenericSignature() == null ? behavior.getSignature() : behavior.getGenericSignature();
return Stream.of(SignatureAttribute.toMethodSignature(sig).getParameterTypes()).map(Type::new).collect(Collectors.toList());
} catch (BadBytecode e) {
// ignore
return Collections.emptyList();
}
}

/**
* Returns the type of the given field.
*
* @param field The field
* @return The field type or {@code null} if the field could not be analyzed
*/
public static Type getFieldType(final CtField field) {
try {
final String sig = field.getGenericSignature() == null ? field.getSignature() : field.getGenericSignature();
return new Type(SignatureAttribute.toTypeSignature(sig));
} catch (BadBytecode e) {
// ignore
LogProvider.error("Could not analyze field: " + field);
LogProvider.debug(e);
return null;
}
}

/**
* Returns the return type of the given method
*
* @param behavior The method
* @return The return type or {@code null} if the method could not be analzed
*/
public static Type getReturnType(final CtBehavior behavior) {
try {
final String sig = behavior.getGenericSignature() == null ? behavior.getSignature() : behavior.getGenericSignature();
return new Type(SignatureAttribute.toMethodSignature(sig).getReturnType());
} catch (BadBytecode e) {
// ignore
LogProvider.error("Could not analyze method: " + behavior);
LogProvider.debug(e);
return null;
}
}

/**
* Checks if the given member has the Synthetic attribute (JVMS 4.7.8).
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ public Type(final String type) {
}
}

public Type(final CtClass ctClass) {
private Type(final CtClass ctClass) {
try {
// TODO ...
this.ctClass = TypeExtractor.toErasuredClass(ctClass.getName());
typeParameters = Collections.emptyList();
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.sebastian_daschner.jaxrs_analyzer.analysis.project.classes.testclasses;

import com.sebastian_daschner.jaxrs_analyzer.builder.ClassResultBuilder;
import com.sebastian_daschner.jaxrs_analyzer.builder.HttpResponseBuilder;
import com.sebastian_daschner.jaxrs_analyzer.builder.MethodResultBuilder;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.HttpMethod;
import com.sebastian_daschner.jaxrs_analyzer.model.results.ClassResult;
import com.sebastian_daschner.jaxrs_analyzer.model.results.MethodResult;
import com.sebastian_daschner.jaxrs_analyzer.model.types.Types;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;

@Path("test")
public class TestClass10 extends ATestClass10 {

@Override
public Response getInfo(final String info) {
final Map<String, Integer> map = new HashMap<>();
map.put(info, 100);
return createResponse(map);
}

public static ClassResult getResult() {
final MethodResult method = MethodResultBuilder.withResponses(HttpResponseBuilder.withStatues(200).andEntityTypes(Types.STRING).andHeaders("X-Test").build())
.andPath("{info}").andMethod(HttpMethod.POST).andRequestBodyType(Types.STRING).build();
return ClassResultBuilder.withResourcePath("test").andMethods(method).build();
}

}

abstract class ATestClass10 {

@POST
@Path("{info}")
public abstract Response getInfo(final String info);

protected final Response createResponse(final Map<String, Integer> map) {
return Response.ok("hello").header("X-Test", "world").build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.sebastian_daschner.jaxrs_analyzer.analysis.project.classes.testclasses;

import com.sebastian_daschner.jaxrs_analyzer.builder.ClassResultBuilder;
import com.sebastian_daschner.jaxrs_analyzer.builder.HttpResponseBuilder;
import com.sebastian_daschner.jaxrs_analyzer.builder.MethodResultBuilder;
import com.sebastian_daschner.jaxrs_analyzer.model.rest.HttpMethod;
import com.sebastian_daschner.jaxrs_analyzer.model.results.ClassResult;
import com.sebastian_daschner.jaxrs_analyzer.model.results.MethodResult;
import com.sebastian_daschner.jaxrs_analyzer.model.types.Type;
import com.sebastian_daschner.jaxrs_analyzer.model.types.Types;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.Response;
import java.util.Collections;
import java.util.List;

@Path("test")
public class TestClass11 extends ATestClass11 {

@Override
public Response getInfo(final String info) {
return Response.ok(getContent(info)).build();
}

public static ClassResult getResult() {
final MethodResult method = MethodResultBuilder.withResponses(HttpResponseBuilder.withStatues(200).andEntityTypes(new Type("javax.ws.rs.core.GenericEntity"),
new Type("javax.ws.rs.core.GenericEntity<java.util.List<java.lang.String>>")).build())
.andPath("{info}").andMethod(HttpMethod.POST).andRequestBodyType(Types.STRING).build();
return ClassResultBuilder.withResourcePath("test").andMethods(method).build();
}

}

abstract class ATestClass11 {

@POST
@Path("{info}")
public abstract Response getInfo(final String info);

protected final GenericEntity<List<String>> getContent(final String content) {
// TODO change to direct return, without variable declaration
final GenericEntity<List<String>> entity = new GenericEntity<>(Collections.singletonList(content), List.class);
return entity;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ public Response getInfo(final String info) {
}

public static ClassResult getResult() {
// TODO un-comment
final MethodResult method = MethodResultBuilder.newBuilder()
// .withResponses(HttpResponseBuilder.withStatues(200).andEntityTypes(Types.STRING).andHeaders("X-Test").build())
final MethodResult method = MethodResultBuilder.withResponses(HttpResponseBuilder.withStatues(200).andEntityTypes(Types.STRING).andHeaders("X-Test").build())
.andPath("{info}").andMethod(HttpMethod.POST).andRequestBodyType(Types.STRING).build();
return ClassResultBuilder.withResourcePath("test").andMethods(method).build();
}
Expand Down
Loading

0 comments on commit f2b7f52

Please sign in to comment.