/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.felix.scr.impl;
import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;
import junit.framework.TestCase;
/**
* Ensure that the helper / manager / metadata packages can actually be used
* without any other packages.
*/
public class PackageIsolationTest extends TestCase {
public void testIsolation() throws Exception {
final List<String> packages = Arrays.asList(
"org/apache/felix/scr/component",
"org/apache/felix/scr/impl/helper",
"org/apache/felix/scr/impl/inject",
"org/apache/felix/scr/impl/inject/field",
"org/apache/felix/scr/impl/inject/methods",
"org/apache/felix/scr/impl/manager",
"org/apache/felix/scr/impl/metadata");
StringBuilder regexp = new StringBuilder();
regexp.append("target/classes/(");
boolean first = true;
for (String pkg : packages) {
if (!first) {
regexp.append("|");
}
regexp.append(pkg);
first = false;
}
regexp.append(")/[A-Za-z]+\\.class");
Pattern pattern = Pattern.compile(regexp.toString());
DependencyVisitor v = new DependencyVisitor();
check(new File("target/classes"), pattern, v);
for (String pkg : v.getPackages()) {
if (pkg.startsWith("org/apache/felix/scr")
&& !packages.contains(pkg)) {
fail("Unexpected dependency on package " + pkg + " found");
}
}
}
static void check(File file, Pattern regexp, ClassVisitor v) throws Exception {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
if (regexp.matcher(child.toString()).matches()) {
new ClassReader(new FileInputStream(child)).accept(v, 0);
}
check(child, regexp, v);
}
}
}
public static class DependencyVisitor extends ClassVisitor {
Set<String> packages = new HashSet<String>();
Map<String, Map<String, Integer>> groups = new HashMap<String, Map<String, Integer>>();
Map<String, Integer> current;
public Map<String, Map<String, Integer>> getGlobals() {
return groups;
}
public Set<String> getPackages() {
return packages;
}
public DependencyVisitor() {
super(Opcodes.ASM5);
}
// ClassVisitor
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
String p = getGroupKey(name);
current = groups.get(p);
if (current == null) {
current = new HashMap<String, Integer>();
groups.put(p, current);
}
if (signature == null) {
if (superName != null) {
addInternalName(superName);
}
addInternalNames(interfaces);
} else {
addSignature(signature);
}
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
@Override
public AnnotationVisitor visitTypeAnnotation(final int typeRef,
final TypePath typePath, final String desc, final boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
@Override
public FieldVisitor visitField(final int access, final String name,
final String desc, final String signature, final Object value) {
if (signature == null) {
addDesc(desc);
} else {
addTypeSignature(signature);
}
if (value instanceof Type) {
addType((Type) value);
}
return new FieldDependencyVisitor();
}
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
if (signature == null) {
addMethodDesc(desc);
} else {
addSignature(signature);
}
addInternalNames(exceptions);
return new MethodDependencyVisitor();
}
class AnnotationDependencyVisitor extends AnnotationVisitor {
public AnnotationDependencyVisitor() {
super(Opcodes.ASM5);
}
@Override
public void visit(final String name, final Object value) {
if (value instanceof Type) {
addType((Type) value);
}
}
@Override
public void visitEnum(final String name, final String desc,
final String value) {
addDesc(desc);
}
@Override
public AnnotationVisitor visitAnnotation(final String name,
final String desc) {
addDesc(desc);
return this;
}
@Override
public AnnotationVisitor visitArray(final String name) {
return this;
}
}
class FieldDependencyVisitor extends FieldVisitor {
public FieldDependencyVisitor() {
super(Opcodes.ASM5);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
@Override
public AnnotationVisitor visitTypeAnnotation(final int typeRef,
final TypePath typePath, final String desc,
final boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
}
class MethodDependencyVisitor extends MethodVisitor {
public MethodDependencyVisitor() {
super(Opcodes.ASM5);
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
return new AnnotationDependencyVisitor();
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
@Override
public AnnotationVisitor visitTypeAnnotation(final int typeRef,
final TypePath typePath, final String desc,
final boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
@Override
public AnnotationVisitor visitParameterAnnotation(final int parameter,
final String desc, final boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
@Override
public void visitTypeInsn(final int opcode, final String type) {
addType(Type.getObjectType(type));
}
@Override
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
addInternalName(owner);
addDesc(desc);
}
@Override
public void visitMethodInsn(final int opcode, final String owner,
final String name, final String desc, final boolean itf) {
addInternalName(owner);
addMethodDesc(desc);
}
@Override
public void visitInvokeDynamicInsn(String name, String desc,
Handle bsm, Object... bsmArgs) {
addMethodDesc(desc);
addConstant(bsm);
for (int i = 0; i < bsmArgs.length; i++) {
addConstant(bsmArgs[i]);
}
}
@Override
public void visitLdcInsn(final Object cst) {
addConstant(cst);
}
@Override
public void visitMultiANewArrayInsn(final String desc, final int dims) {
addDesc(desc);
}
@Override
public AnnotationVisitor visitInsnAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
@Override
public void visitLocalVariable(final String name, final String desc,
final String signature, final Label start, final Label end,
final int index) {
addTypeSignature(signature);
}
@Override
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
TypePath typePath, Label[] start, Label[] end, int[] index,
String desc, boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
@Override
public void visitTryCatchBlock(final Label start, final Label end,
final Label handler, final String type) {
if (type != null) {
addInternalName(type);
}
}
@Override
public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
addDesc(desc);
return new AnnotationDependencyVisitor();
}
}
class SignatureDependencyVisitor extends SignatureVisitor {
String signatureClassName;
public SignatureDependencyVisitor() {
super(Opcodes.ASM5);
}
@Override
public void visitClassType(final String name) {
signatureClassName = name;
addInternalName(name);
}
@Override
public void visitInnerClassType(final String name) {
signatureClassName = signatureClassName + "$" + name;
addInternalName(signatureClassName);
}
}
// ---------------------------------------------
private String getGroupKey(String name) {
int n = name.lastIndexOf('/');
if (n > -1) {
name = name.substring(0, n);
}
packages.add(name);
return name;
}
private void addName(final String name) {
if (name == null) {
return;
}
String p = getGroupKey(name);
if (current.containsKey(p)) {
current.put(p, current.get(p) + 1);
} else {
current.put(p, 1);
}
}
void addInternalName(final String name) {
addType(Type.getObjectType(name));
}
private void addInternalNames(final String[] names) {
for (int i = 0; names != null && i < names.length; i++) {
addInternalName(names[i]);
}
}
void addDesc(final String desc) {
addType(Type.getType(desc));
}
void addMethodDesc(final String desc) {
addType(Type.getReturnType(desc));
Type[] types = Type.getArgumentTypes(desc);
for (int i = 0; i < types.length; i++) {
addType(types[i]);
}
}
void addType(final Type t) {
switch (t.getSort()) {
case Type.ARRAY:
addType(t.getElementType());
break;
case Type.OBJECT:
addName(t.getInternalName());
break;
case Type.METHOD:
addMethodDesc(t.getDescriptor());
break;
}
}
private void addSignature(final String signature) {
if (signature != null) {
new SignatureReader(signature)
.accept(new SignatureDependencyVisitor());
}
}
void addTypeSignature(final String signature) {
if (signature != null) {
new SignatureReader(signature)
.acceptType(new SignatureDependencyVisitor());
}
}
void addConstant(final Object cst) {
if (cst instanceof Type) {
addType((Type) cst);
} else if (cst instanceof Handle) {
Handle h = (Handle) cst;
addInternalName(h.getOwner());
addMethodDesc(h.getDesc());
}
}
}
}