/*
* #%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 org.wisdom.source.ast.visitor;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.visitor.GenericVisitorAdapter;
import org.wisdom.api.Controller;
import org.wisdom.api.DefaultController;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Visit @{ClassOrInterfaceDeclaration} and to know if the visited class is a Wisdom Controller.
* <p>
* <p>
* A class is considered a controller if at least one of this conditions is meet:
* - it is annotated by the {@link #CONTROL_ANNO_NAME} annotation,
* - it extends the wisdom {@link DefaultController},
* - it implements the wisdom {@link Controller} interface.
* </p>
*
* @author barjo
*/
public class ClassSourceVisitor extends GenericVisitorAdapter<Boolean, Object> {
/**
* The simple name of the Controller annotation.
*/
private static final String CONTROL_ANNO_NAME = org.wisdom.api.annotations.Controller.class.getSimpleName();
/**
* The simple name of the Controller and DefaultController class.
*/
private static final Set<String> CONTROL_CLASSNAMES = new HashSet<>(2);
static {
CONTROL_CLASSNAMES.add(Controller.class.getSimpleName());
CONTROL_CLASSNAMES.add(DefaultController.class.getSimpleName());
}
/**
* We need to override this method to manage the case where the visitor returns {@code null}. {@code null} is
* considered as {@code false}.
*
* @param n the unit
* @param arg meaningless
* @return whether or not the compilation unit is a controller.
*/
public Boolean visit(final CompilationUnit n, final Object arg) {
final Boolean result = super.visit(n, arg);
return result != null && result;
}
/**
* Visit the Class declaration and return true if it corresponds to a wisdom controller.
*
* @param declaration The class declaration created by the JavaParser.
* @param extra Extra out value argument, not used here.
* @return <code>true</code> if the declaration correspond to a wisdom controller, <code>false</code> otherwise.
*/
public Boolean visit(ClassOrInterfaceDeclaration declaration, Object extra) {
if (declaration.getAnnotations() != null
&& containsAnnotation(declaration.getAnnotations(), CONTROL_ANNO_NAME)) {
return true;
}
//Get the list of extended and implemented class
List<ClassOrInterfaceType> hierarchy = new ArrayList<>();
if (declaration.getExtends() != null) {
hierarchy.addAll(declaration.getExtends());
}
if (declaration.getImplements() != null) {
hierarchy.addAll(declaration.getImplements());
}
return containsClassName(hierarchy, CONTROL_CLASSNAMES);
}
/**
* Check if the list of annotation contains the annotation of given name.
*
* @param annos, the annotation list
* @param annotationName, the annotation name
* @return <code>true</code> if the annotation list contains the given annotation.
*/
private boolean containsAnnotation(List<AnnotationExpr> annos, String annotationName) {
for (AnnotationExpr anno : annos) {
if (anno.getName().getName().equals(annotationName)) {
return true;
}
}
return false;
}
/**
* Check if the list of class or interface contains a class which name is given in the <code>simpleNames</code> set.
*
* @param klassList The list of class or interface
* @param simpleNames a set of class simple name.
* @return <code>true</code> if the list contains a class or interface which name is present in the
* <code>simpleNames</code> set.
*/
private boolean containsClassName(List<ClassOrInterfaceType> klassList, Set<String> simpleNames) {
for (ClassOrInterfaceType ctype : klassList) {
if (simpleNames.contains(ctype.getName())) {
return true;
}
}
return false;
}
}