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.asm.AnnotationVisitor;
import act.asm.ClassWriter;
import act.asm.Type;
import act.plugin.Extends;
import org.osgl.$;
import org.osgl.exception.NotAppliedException;
import org.osgl.logging.L;
import org.osgl.logging.Logger;
import org.osgl.util.C;
import org.osgl.util.E;
import org.osgl.util.S;
import java.lang.annotation.Annotation;
public abstract class ClassDetector extends ByteCodeVisitor {
protected static final Logger logger = L.get(ClassDetector.class);
private String className;
public abstract boolean found();
public String className() {
return className;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
className = Type.getObjectType(name).getClassName();
super.visit(version, access, name, signature, superName, interfaces);
}
private static class FilteredClassDetector extends ClassDetector {
private final ClassFilter filter;
private boolean found = false;
private boolean skip = false;
FilteredClassDetector(ClassFilter filter) {
E.NPE(filter);
this.filter = filter;
}
@Override
public int hashCode() {
return $.hc(filter, FilteredClassDetector.class);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof FilteredClassDetector) {
FilteredClassDetector that = (FilteredClassDetector)obj;
return $.eq(that.filter, this.filter);
}
return false;
}
@Override
public boolean found() {
return found;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
if (filter.noAbstract() && ((access & ACC_ABSTRACT) != 0 || (access & ACC_INTERFACE) != 0)) {
skip = true;
return;
}
if (filter.publicOnly() && (access & ACC_PUBLIC) != 1) {
skip = true;
return;
}
Class<?> superType = filter.superType();
if (null == superType) {
return; // we will check annotation type anyway
}
final String expected = superType.getName();
if (checkName(expected, superName)) {
found = true;
return;
}
int len = interfaces.length;
for (int i = 0; i < len; ++i) {
String s = interfaces[i];
if (checkName(expected, s)) {
found = true;
return;
}
}
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationVisitor av = super.visitAnnotation(desc, visible);
if (found || skip) {
return av;
}
if (isExtendsAnnotation(desc)) {
Class<?> superType = filter.superType();
if (null != superType) {
return new ExtendedAnnotationVisitor(av, superType.getName());
} else {
return av;
}
}
Class<? extends Annotation> annoType = filter.annotationType();
if (null == annoType) {
return av;
}
if (isAnnotation(annoType, desc)) {
found = true;
}
return av;
}
private boolean checkName(String expected, String found) {
Type type = Type.getObjectType(found);
return type.getClassName().equals(expected);
}
private final class ExtendedAnnotationVisitor extends AnnotationVisitor {
private String expected;
public ExtendedAnnotationVisitor(AnnotationVisitor av, String expected) {
super(ASM5, av);
this.expected = expected;
}
@Override
public void visit(String name, Object value) {
super.visit(name, value);
found = found || "value".equals(name) && (value instanceof Type) && ((Type) value).getClassName().equals(expected);
}
}
}
private static boolean isExtendsAnnotation(String desc) {
return isAnnotation(Extends.class, desc);
}
private static boolean isAnnotation(Class<? extends Annotation> annoType, String desc) {
return S.eq(annoType.getName(), Type.getType(desc).getClassName());
}
public static ClassDetector chain(ClassWriter cw, ClassFilter... filters) {
return (ClassDetector) chain(cw, of(filters));
}
public static ClassDetector of(final ClassFilter... filters) {
E.illegalArgumentIf(filters.length == 0);
if (filters.length == 1) {
return new FilteredClassDetector(filters[0]);
}
return new ClassDetector() {
C.List<ClassDetector> detectors = C.listOf(filters).map(new $.F1<ClassFilter, ClassDetector>() {
@Override
public ClassDetector apply(ClassFilter classFilter) throws NotAppliedException, $.Break {
return new FilteredClassDetector(classFilter);
}
});
private C.List<ClassDetector> matches = C.newList();
@Override
public int hashCode() {
return $.hc(detectors);
}
@Override
public boolean equals(Object obj) {
return obj == this;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
for (ClassDetector detector : detectors) {
detector.visit(version, access, name, signature, superName, interfaces);
if (detector.found()) {
matches.add(detector);
}
}
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
AnnotationVisitor av = super.visitAnnotation(desc, visible);
if (matches.size() == detectors.size() || !isExtendsAnnotation(desc)) {
return av;
}
final C.List<ClassDetector> unmatched = detectors.without(matches);
return new AnnotationVisitor(ASM5, av) {
@Override
public void visit(String name, Object value) {
super.visit(name, value);
for (ClassDetector detector : unmatched) {
AnnotationVisitor av0 = detector.visitAnnotation(desc, visible);
av0.visit(name, value);
if (detector.found()) {
matches.add(detector);
}
}
}
};
}
@Override
public boolean found() {
return !matches.isEmpty();
}
};
}
}