package act.util;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* 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%
*/
import act.app.App;
import act.app.event.AppEventId;
import act.sys.Env;
import org.osgl.$;
import org.osgl.Osgl;
import org.osgl.logging.Logger;
import org.osgl.util.S;
public class ClassFinderData {
private static final Logger logger = App.logger;
public enum By {
ANNOTATION() {
@Override
void tryFind(
ClassNode startNode,
boolean publicOnly,
boolean noAbstract,
$.Visitor<ClassNode> visitor
) {
startNode.visitAnnotatedClasses(visitor, publicOnly, noAbstract);
}
},
SUPER_TYPE() {
@Override
void tryFind(
ClassNode startNode,
boolean publicOnly,
boolean noAbstract,
$.Visitor<ClassNode> visitor
) {
startNode.visitSubTree(visitor, publicOnly, noAbstract);
}
};
public void find(final App app, final ClassFinderData data) {
ClassInfoRepository repo = app.classLoader().classInfoRepository();
final ClassNode theNode = repo.node(data.what);
if (null == theNode) {
logger.error("Cannot locate the \"what\" class[%s] in class info repository", data.what);
return;
}
$.Visitor<ClassNode> visitor = new $.Visitor<ClassNode>() {
@Override
public void visit(ClassNode classNode) throws Osgl.Break {
ClassLoader cl = app.classLoader();
if (data.className.startsWith("act.")) {
cl = cl.getParent();
}
Class<?> targetClass = $.classForName(classNode.name(), app.classLoader());
if (!Env.matches(targetClass)) {
logger.debug("ignore target class[%s]: environment spec not matching", targetClass);
return;
}
if (data.isStatic) {
Class<?> host = $.classForName(data.className, cl);
$.invokeStatic(host, data.methodName, targetClass);
} else {
Object host = app.getInstance(data.className);
$.invokeVirtual(host, data.methodName, targetClass);
}
}
};
tryFind(theNode, data.publicOnly, data.noAbstract, visitor);
}
abstract void tryFind(
ClassNode startNode,
boolean publicOnly,
boolean noAbstract,
$.Visitor<ClassNode> visitor);
}
/**
* The name of the class used to find the target classes
*/
private String what;
/**
* Define when to invoke the found logic
*/
private AppEventId when = AppEventId.DEPENDENCY_INJECTOR_PROVISIONED;
/**
* Specify how to find the target classes by matching
* the `what` class
*/
private By how;
/**
* Only scan public class?
*/
private boolean publicOnly = true;
/**
* Do not scan abstract class?
*/
private boolean noAbstract = true;
/**
* The name of the class that host the found callback logic
*/
private String className;
/**
* The name of the method that defines the found callback logic
*/
private String methodName;
/**
* Is the found callback logic defined in static or instance method
*/
private boolean isStatic;
public ClassFinderData what(String targetClassName) {
this.what = targetClassName;
return this;
}
public ClassFinderData publicOnly(boolean b) {
this.publicOnly = b;
return this;
}
public ClassFinderData noAbstract(boolean b) {
this.noAbstract = b;
return this;
}
public boolean whatSpecified() {
return S.notBlank(this.what) && S.neq(SubClassFinder.DEF_VALUE, this.what);
}
public ClassFinderData when(String loadOn) {
this.when = AppEventId.valueOf(loadOn);
return this;
}
public ClassFinderData how(By by) {
this.how = by;
return this;
}
public ClassFinderData callback(String className, String methodName, boolean isStatic) {
this.className = className;
this.methodName = methodName;
this.isStatic = isStatic;
return this;
}
public boolean isValid() {
return S.noBlank(className, methodName);
}
public void scheduleFind() {
final App app = App.instance();
app.jobManager().on(when, jobId(), new Runnable() {
@Override
public void run() {
how.find(app, ClassFinderData.this);
}
});
}
private String jobId() {
return S.fmt("ClassFinder[%s@%s.%s]", what, className, methodName);
}
}