From 8dbb55b57ed0352db9298edbb4f35eadc15f384e Mon Sep 17 00:00:00 2001 From: Nicolas Delsaux Date: Wed, 16 Nov 2016 11:52:19 +0100 Subject: [PATCH 1/2] Creating the test exposing #564 in the cleanest way --- .../wisdom/source/ast/util/ExtractUtil.java | 11 ++++-- .../ControllerReferencingConstant.java | 37 +++++++++++++++++++ .../visitor/ControllerSourceVisitorTest.java | 18 +++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 extensions/wisdom-raml/wisdom-source-watcher/src/test/java/controller/ControllerReferencingConstant.java diff --git a/extensions/wisdom-raml/wisdom-source-watcher/src/main/java/org/wisdom/source/ast/util/ExtractUtil.java b/extensions/wisdom-raml/wisdom-source-watcher/src/main/java/org/wisdom/source/ast/util/ExtractUtil.java index a6414993b..a9d21c8c4 100644 --- a/extensions/wisdom-raml/wisdom-source-watcher/src/main/java/org/wisdom/source/ast/util/ExtractUtil.java +++ b/extensions/wisdom-raml/wisdom-source-watcher/src/main/java/org/wisdom/source/ast/util/ExtractUtil.java @@ -19,15 +19,16 @@ */ package org.wisdom.source.ast.util; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.comments.JavadocComment; -import com.github.javaparser.ast.expr.MemberValuePair; +import static java.util.Collections.singleton; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import static java.util.Collections.singleton; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.comments.JavadocComment; +import com.github.javaparser.ast.expr.MemberValuePair; +import com.github.javaparser.ast.visitor.GenericVisitorAdapter; /** * A set of function that helps to extract various entity from the javaparser AST. @@ -50,6 +51,8 @@ private ExtractUtil(){ * @return string version of the node value. */ public static String asString(Node node){ +// return node.accept(new GenericVisitorAdapter() { +// }, ""); String string = node.toString(); if("\"\"".equals(string)){ diff --git a/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/controller/ControllerReferencingConstant.java b/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/controller/ControllerReferencingConstant.java new file mode 100644 index 000000000..58672c68b --- /dev/null +++ b/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/controller/ControllerReferencingConstant.java @@ -0,0 +1,37 @@ +/* + * #%L + * Wisdom-Framework + * %% + * Copyright (C) 2013 - 2015 Wisdom Framework + * %% + * Licensed 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. + * #L% + */ +package controller; + +import org.wisdom.api.DefaultController; +import org.wisdom.api.annotations.Route; +import org.wisdom.api.http.HttpMethod; +import org.wisdom.api.http.Result; + +/** + * A controller referencing uri through a constant + */ +public class ControllerReferencingConstant extends DefaultController { + public static final String URI = "/constant"; + + @Route(method= HttpMethod.GET, uri=URI) + public Result get() { + return ok(); + } +} diff --git a/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/org/wisdom/source/ast/visitor/ControllerSourceVisitorTest.java b/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/org/wisdom/source/ast/visitor/ControllerSourceVisitorTest.java index ce57e2549..46499edb0 100644 --- a/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/org/wisdom/source/ast/visitor/ControllerSourceVisitorTest.java +++ b/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/org/wisdom/source/ast/visitor/ControllerSourceVisitorTest.java @@ -240,6 +240,24 @@ public void testControllerUsingPath() throws IOException, ParseException { } + @Test + public void testReferencingConstant() throws IOException, ParseException { + File file = new File("src/test/java/controller/ControllerReferencingConstant.java"); + final CompilationUnit declaration = JavaParser.parse(file); + ControllerModel model = new ControllerModel(); + visitor.visit(declaration, model); + + final Collection routes = (Collection) model.getRoutes().get("/constant"); + assertThat(routes).isNotNull(); + assertThat(routes).hasSize(1); + + for (ControllerRouteModel route : routes) { + assertThat(route.getHttpMethod().name()).isEqualToIgnoringCase(route.getMethodName()); + assertThat(route.getParams()).isEmpty(); + assertThat(route.getPath()).isEqualToIgnoringCase("/constant"); + } + } + private ControllerRouteModel getModelByPath(ControllerModel model, String name) { for (ControllerRouteModel route : (Collection) model.getRoutesAsMultiMap().values()) { if (route.getPath().equals(name)) { From b7a36db608e0cbf33dd96fc5574b40fb5cc2fa80 Mon Sep 17 00:00:00 2001 From: Nicolas Delsaux Date: Wed, 16 Nov 2016 15:48:50 +0100 Subject: [PATCH 2/2] Fixes #564 by allowing elements from inner classes to be referenced in annotation. The following are supported : - direct field access - access to field declared in an inner class or interface The following are not supported - access to field declared in another class, even if in the same package - access to a class defined from an expression (which can't happen, but is nevertheless considered as valid by javaparser) --- .../wisdom/source/ast/util/ExtractUtil.java | 104 ++++++++++++++++-- .../ControllerReferencingConstant.java | 16 +++ .../visitor/ControllerSourceVisitorTest.java | 2 +- 3 files changed, 111 insertions(+), 11 deletions(-) diff --git a/extensions/wisdom-raml/wisdom-source-watcher/src/main/java/org/wisdom/source/ast/util/ExtractUtil.java b/extensions/wisdom-raml/wisdom-source-watcher/src/main/java/org/wisdom/source/ast/util/ExtractUtil.java index a9d21c8c4..932ae098e 100644 --- a/extensions/wisdom-raml/wisdom-source-watcher/src/main/java/org/wisdom/source/ast/util/ExtractUtil.java +++ b/extensions/wisdom-raml/wisdom-source-watcher/src/main/java/org/wisdom/source/ast/util/ExtractUtil.java @@ -25,9 +25,19 @@ import java.util.List; import java.util.Set; +import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.BodyDeclaration; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.TypeDeclaration; +import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.comments.JavadocComment; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.FieldAccessExpr; import com.github.javaparser.ast.expr.MemberValuePair; +import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.expr.StringLiteralExpr; import com.github.javaparser.ast.visitor.GenericVisitorAdapter; /** @@ -37,7 +47,89 @@ */ public class ExtractUtil implements NameConstant { - /** + private static final class StringExtractor extends GenericVisitorAdapter { + @Override + public String visit(StringLiteralExpr n, String arg) { + String string = n.toString(); + + if("\"\"".equals(string)){ + return ""; + } + + return string.substring(1,string.length()-1); + } + + @Override + public String visit(FieldAccessExpr n, String arg) { + return visit(n.getFieldExpr(), findClassNamed(n.getScope(), getClassDeclarationOf(n))); + } + + private ClassOrInterfaceDeclaration findClassNamed(Expression scope, ClassOrInterfaceDeclaration classDeclarationOf) { + for(BodyDeclaration b : classDeclarationOf.getMembers()) { + if (b instanceof ClassOrInterfaceDeclaration) { + ClassOrInterfaceDeclaration classDeclaration = (ClassOrInterfaceDeclaration) b; + if(classDeclaration.getName().equals(scope.toString())) { + return classDeclaration; + } + } + } + // Not found ? Damn, maybe it is declared in a damned local class + if(classDeclarationOf.getParentNode() instanceof ClassOrInterfaceDeclaration) { + return findClassNamed(scope, (ClassOrInterfaceDeclaration) classDeclarationOf.getParentNode()); + } else { + Node node = classDeclarationOf; + while(node.getParentNode()!=null) { + node = node.getParentNode(); + } + if (node instanceof CompilationUnit) { + CompilationUnit cu = (CompilationUnit) node; + for(TypeDeclaration d : cu.getTypes()) { + if (d instanceof ClassOrInterfaceDeclaration) { + ClassOrInterfaceDeclaration classDeclaration = (ClassOrInterfaceDeclaration) d; + if(classDeclaration.getName().equals(scope.toString())) { + return classDeclaration; + } + } + } + } + } + throw new UnsupportedOperationException( + String.format("Can't find declaration of %s in %s. String extraction from another file doesn't work. Sorry.", + scope.toString(), classDeclarationOf.getName())); + } + + @Override + public String visit(NameExpr n, String arg) { + return visit(n, getClassDeclarationOf(n)); + } + + private String visit(NameExpr n, ClassOrInterfaceDeclaration classDeclarationOf) { + return evaluateFieldNamed(n.getName(), classDeclarationOf); + } + + private String evaluateFieldNamed(String name, ClassOrInterfaceDeclaration classDeclarationOf) { + for(BodyDeclaration b : classDeclarationOf.getMembers()) { + if (b instanceof FieldDeclaration) { + FieldDeclaration fieldDeclaration = (FieldDeclaration) b; + for(VariableDeclarator variable : fieldDeclaration.getVariables()) { + if(variable.getId().getName().equals(name)) { + return variable.getInit().accept(this, ""); + } + } + } + } + return ""; + } + + private ClassOrInterfaceDeclaration getClassDeclarationOf(Node n) { + while(!(n instanceof ClassOrInterfaceDeclaration)) { + n = n.getParentNode(); + } + return (ClassOrInterfaceDeclaration) n; + } + } + + /** * Hide implicit public constructor. */ private ExtractUtil(){ @@ -51,15 +143,7 @@ private ExtractUtil(){ * @return string version of the node value. */ public static String asString(Node node){ -// return node.accept(new GenericVisitorAdapter() { -// }, ""); - String string = node.toString(); - - if("\"\"".equals(string)){ - return ""; - } - - return string.substring(1,string.length()-1); + return node.accept(new StringExtractor(), ""); } /** diff --git a/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/controller/ControllerReferencingConstant.java b/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/controller/ControllerReferencingConstant.java index 58672c68b..17b43c9c9 100644 --- a/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/controller/ControllerReferencingConstant.java +++ b/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/controller/ControllerReferencingConstant.java @@ -27,11 +27,27 @@ /** * A controller referencing uri through a constant */ +class Constants_For_ControllerReferencingConstant { + public static final String URI = "/constant"; +} public class ControllerReferencingConstant extends DefaultController { + public static interface Constants { + public static final String URI = "/constant"; + } public static final String URI = "/constant"; @Route(method= HttpMethod.GET, uri=URI) public Result get() { return ok(); } + + @Route(method= HttpMethod.POST, uri=Constants.URI) + public Result post() { + return ok(); + } + + @Route(method= HttpMethod.PUT, uri=Constants_For_ControllerReferencingConstant.URI) + public Result put() { + return ok(); + } } diff --git a/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/org/wisdom/source/ast/visitor/ControllerSourceVisitorTest.java b/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/org/wisdom/source/ast/visitor/ControllerSourceVisitorTest.java index 46499edb0..83b25b653 100644 --- a/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/org/wisdom/source/ast/visitor/ControllerSourceVisitorTest.java +++ b/extensions/wisdom-raml/wisdom-source-watcher/src/test/java/org/wisdom/source/ast/visitor/ControllerSourceVisitorTest.java @@ -249,7 +249,7 @@ public void testReferencingConstant() throws IOException, ParseException { final Collection routes = (Collection) model.getRoutes().get("/constant"); assertThat(routes).isNotNull(); - assertThat(routes).hasSize(1); + assertThat(routes).hasSize(3); for (ControllerRouteModel route : routes) { assertThat(route.getHttpMethod().name()).isEqualToIgnoringCase(route.getMethodName());