summaryrefslogtreecommitdiffstats
path: root/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis
diff options
context:
space:
mode:
Diffstat (limited to 'bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis')
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Analyze.scala28
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala102
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.scala88
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.scala68
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnnotationVisitorTrait.scala39
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AttributeVisitorTrait.scala15
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.scala10
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.scala24
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/PackageTally.scala46
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Packages.scala27
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/SubVisitorTrait.scala19
-rw-r--r--bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/package.scala24
12 files changed, 490 insertions, 0 deletions
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Analyze.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Analyze.scala
new file mode 100644
index 00000000000..ed15c1bcc74
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Analyze.scala
@@ -0,0 +1,28 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import org.objectweb.asm._
+import java.io.{InputStream, File}
+import com.yahoo.container.plugin.util.IO.withFileInputStream
+
+/**
+ * Main entry point for class analysis
+ * @author tonytv
+ */
+object Analyze {
+ def analyzeClass(classFile : File) : ClassFileMetaData = {
+ try {
+ withFileInputStream(classFile) { fileInputStream =>
+ analyzeClass(fileInputStream)
+ }
+ } catch {
+ case e : RuntimeException => throw new RuntimeException("An error occurred when analyzing " + classFile.getPath, e)
+ }
+ }
+
+ def analyzeClass(inputStream : InputStream) : ClassFileMetaData = {
+ val visitor = new AnalyzeClassVisitor()
+ new ClassReader(inputStream).accept(visitor, ClassReader.SKIP_DEBUG)
+ visitor.result
+ }
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala
new file mode 100644
index 00000000000..b57a07d5c30
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala
@@ -0,0 +1,102 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import org.objectweb.asm._
+import com.yahoo.osgi.annotation.{ExportPackage, Version}
+import collection.mutable
+
+/**
+ * Picks up classes used in class files.
+ * @author tonytv
+ */
+private class AnalyzeClassVisitor extends ClassVisitor(Opcodes.ASM5) with AnnotationVisitorTrait with AttributeVisitorTrait {
+ private var name : String = null
+ protected val imports : ImportsSet = mutable.Set()
+ protected var exportPackageAnnotation: Option[ExportPackageAnnotation] = None
+
+
+ override def visitAttribute(attribute: Attribute): Unit = super.visitAttribute(attribute)
+
+ override def visitMethod(access: Int, name: String, desc: String, signature: String,
+ exceptions: Array[String]): MethodVisitor = {
+
+ imports ++= (Type.getReturnType(desc) +: Type.getArgumentTypes(desc)).flatMap(getClassName)
+
+ imports ++= Option(exceptions) getOrElse(Array()) flatMap internalNameToClassName
+
+ AnalyzeSignatureVisitor.analyzeMethod(signature, this)
+ new AnalyzeMethodVisitor(this)
+ }
+
+ override def visitField(access: Int, name: String, desc: String, signature: String, value: AnyRef): FieldVisitor = {
+ imports ++= getClassName(Type.getType(desc)).toList
+
+ AnalyzeSignatureVisitor.analyzeField(signature, this)
+ new FieldVisitor(Opcodes.ASM5) with SubVisitorTrait with AttributeVisitorTrait with AnnotationVisitorTrait {
+ val analyzeClassVisitor = AnalyzeClassVisitor.this
+
+ override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = super.visitAnnotation(desc, visible)
+ override def visitAttribute(attribute: Attribute): Unit = super.visitAttribute(attribute)
+ override def visitEnd(): Unit = super.visitEnd()
+ }
+ }
+
+ override def visit(version: Int, access: Int, name: String, signature: String, superName: String, interfaces: Array[String]) {
+ this.name = internalNameToClassName(name).get
+
+ imports ++= (superName +: interfaces) flatMap internalNameToClassName
+ AnalyzeSignatureVisitor.analyzeClass(signature, this)
+ }
+
+ override def visitInnerClass(name: String, outerName: String, innerName: String, access: Int) {}
+ override def visitOuterClass(owner: String, name: String, desc: String) {}
+ override def visitSource(source: String, debug: String) {}
+ override def visitEnd() {}
+
+ def addImports(imports: TraversableOnce[String]) {
+ this.imports ++= imports
+ }
+
+ override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = {
+ if (Type.getType(desc).getClassName == classOf[ExportPackage].getName) {
+ visitExportPackage()
+ } else {
+ super.visitAnnotation(desc, visible)
+ }
+ }
+
+ def visitExportPackage(): AnnotationVisitor = {
+ def defaultVersionValue[T](name: String) = classOf[Version].getMethod(name).getDefaultValue().asInstanceOf[T]
+
+ new AnnotationVisitor(Opcodes.ASM5) {
+ var major: Int = defaultVersionValue("major")
+ var minor: Int = defaultVersionValue("minor")
+ var micro: Int = defaultVersionValue("micro")
+ var qualifier: String = defaultVersionValue("qualifier")
+
+ override def visit(name: String, value: AnyRef) {
+ def valueAsInt = value.asInstanceOf[Int]
+
+ name match {
+ case "major" => major = valueAsInt
+ case "minor" => minor = valueAsInt
+ case "micro" => micro = valueAsInt
+ case "qualifier" => qualifier = value.asInstanceOf[String]
+ }
+ }
+
+ override def visitEnd() {
+ exportPackageAnnotation = Some(ExportPackageAnnotation(major, minor, micro, qualifier))
+ }
+
+ override def visitEnum(name: String, desc: String, value: String) {}
+ override def visitArray(name: String): AnnotationVisitor = this
+ override def visitAnnotation(name: String, desc: String): AnnotationVisitor = this
+ }
+ }
+
+ def result = {
+ assert(!imports.contains("int"))
+ new ClassFileMetaData(name, imports.toSet, exportPackageAnnotation)
+ }
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.scala
new file mode 100644
index 00000000000..5d65b3972c0
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeMethodVisitor.scala
@@ -0,0 +1,88 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import org.objectweb.asm._
+
+/**
+ * Picks up classes used in method bodies.
+ * @author tonytv
+ */
+private class AnalyzeMethodVisitor(val analyzeClassVisitor : AnalyzeClassVisitor)
+ extends MethodVisitor(Opcodes.ASM5) with AnnotationVisitorTrait with AttributeVisitorTrait with SubVisitorTrait {
+
+
+ override def visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor = super.visitParameterAnnotation(parameter, desc, visible)
+ override def visitAnnotationDefault(): AnnotationVisitor = super.visitAnnotationDefault()
+ override def visitAttribute(attribute: Attribute): Unit = super.visitAttribute(attribute)
+ override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = super.visitAnnotation(desc, visible)
+ override def visitEnd(): Unit = super.visitEnd()
+
+ override def visitMultiANewArrayInsn(desc: String, dims: Int) {
+ imports ++= getClassName(Type.getType(desc)).toList
+ }
+
+
+ override def visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
+ imports ++= internalNameToClassName(owner)
+ imports ++= Type.getArgumentTypes(desc).flatMap(getClassName)
+ imports ++= getClassName(Type.getReturnType(desc))
+ }
+
+ override def visitFieldInsn(opcode: Int, owner: String, name: String, desc: String) {
+ imports ++= internalNameToClassName(owner) ++ getClassName(Type.getType(desc)).toList
+
+ }
+
+ override def visitTypeInsn(opcode: Int, `type` : String) {
+ imports ++= internalNameToClassName(`type`)
+ }
+
+ override def visitTryCatchBlock(start: Label, end: Label, handler: Label, `type` : String) {
+ if (`type` != null) //null means finally block
+ imports ++= internalNameToClassName(`type`)
+ }
+
+ override def visitLocalVariable(name: String, desc: String, signature: String, start: Label, end: Label, index: Int) {
+ imports += Type.getType(desc).getClassName
+ }
+
+ override def visitLdcInsn(constant: AnyRef) {
+ constant match {
+ case typeConstant: Type => imports ++= getClassName(typeConstant)
+ case _ =>
+ }
+ }
+
+ override def visitInvokeDynamicInsn(name: String, desc: String, bootstrapMethod: Handle, bootstrapMethodArgs: AnyRef*) {
+ bootstrapMethodArgs.foreach {
+ case typeConstant: Type =>
+ imports ++= getClassName(typeConstant)
+ case handle: Handle =>
+ imports ++= internalNameToClassName(handle.getOwner)
+ imports ++= Type.getArgumentTypes(desc).flatMap(getClassName)
+ case _ : Number =>
+ case _ : String =>
+ case other => throw new AssertionError(s"Unexpected type ${other.getClass} with value '$other'")
+ }
+ }
+
+ override def visitMaxs(maxStack: Int, maxLocals: Int) {}
+ override def visitLineNumber(line: Int, start: Label) {}
+ //only for debugging
+ override def visitLookupSwitchInsn(dflt: Label, keys: Array[Int], labels: Array[Label]) {}
+
+
+ override def visitTableSwitchInsn(min: Int, max: Int, dflt: Label, labels: Label*): Unit = super.visitTableSwitchInsn(min, max, dflt, labels: _*)
+ override def visitIincInsn(`var` : Int, increment: Int) {}
+ override def visitLabel(label: Label) {}
+ override def visitJumpInsn(opcode: Int, label: Label) {}
+ override def visitVarInsn(opcode: Int, `var` : Int) {}
+ override def visitIntInsn(opcode: Int, operand: Int) {}
+ override def visitInsn(opcode: Int) {}
+ override def visitFrame(`type` : Int, nLocal: Int, local: Array[AnyRef], nStack: Int, stack: Array[AnyRef]) {}
+ override def visitCode() {}
+}
+
+
+
+
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.scala
new file mode 100644
index 00000000000..693e0e17482
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeSignatureVisitor.scala
@@ -0,0 +1,68 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.signature.{SignatureReader, SignatureVisitor}
+
+
+/**
+ * @author tonytv
+ */
+
+private class AnalyzeSignatureVisitor(val analyzeClassVisitor: AnalyzeClassVisitor)
+ extends SignatureVisitor(Opcodes.ASM5)
+ with SubVisitorTrait {
+
+
+ override def visitEnd(): Unit = super.visitEnd()
+
+ override def visitClassType(className: String) {
+ imports ++= internalNameToClassName(className)
+ }
+
+ override def visitFormalTypeParameter(name: String) {}
+
+ override def visitClassBound() = this
+
+ override def visitInterfaceBound() = this
+
+ override def visitSuperclass() = this
+
+ override def visitInterface() = this
+
+ override def visitParameterType() = this
+
+ override def visitReturnType() = this
+
+ override def visitExceptionType() = this
+
+ override def visitBaseType(descriptor: Char) {}
+
+ override def visitTypeVariable(name: String) {}
+
+ override def visitArrayType() = this
+
+ override def visitInnerClassType(name: String) {}
+
+ override def visitTypeArgument() {}
+
+ override def visitTypeArgument(wildcard: Char) = this
+}
+
+
+object AnalyzeSignatureVisitor {
+ def analyzeClass(signature: String, analyzeClassVisitor: AnalyzeClassVisitor) {
+ if (signature != null) {
+ new SignatureReader(signature).accept(new AnalyzeSignatureVisitor(analyzeClassVisitor))
+ }
+ }
+
+ def analyzeMethod(signature: String, analyzeClassVisitor: AnalyzeClassVisitor) {
+ analyzeClass(signature, analyzeClassVisitor)
+ }
+
+ def analyzeField(signature: String, analyzeClassVisitor: AnalyzeClassVisitor) {
+ if (signature != null)
+ new SignatureReader(signature).acceptType(new AnalyzeSignatureVisitor(analyzeClassVisitor))
+ }
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnnotationVisitorTrait.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnnotationVisitorTrait.scala
new file mode 100644
index 00000000000..8beb47c765f
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnnotationVisitorTrait.scala
@@ -0,0 +1,39 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import org.objectweb.asm.{Opcodes, AnnotationVisitor, Type}
+
+/**
+ * Picks up classes used in annotations.
+ * @author tonytv
+ */
+private trait AnnotationVisitorTrait {
+ protected val imports: ImportsSet
+
+ def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = {
+ imports ++= getClassName(Type.getType(desc)).toList
+
+ visitAnnotationDefault()
+ }
+
+ def visitAnnotationDefault(): AnnotationVisitor =
+ new AnnotationVisitor(Opcodes.ASM5) {
+ override def visit(name: String, value: AnyRef) {}
+
+ override def visitEnum(name: String, desc: String, value: String) {
+ imports ++= getClassName(Type.getType(desc)).toList
+ }
+
+ override def visitArray(name: String): AnnotationVisitor = this
+
+ override def visitAnnotation(name: String, desc: String): AnnotationVisitor = {
+ imports ++= getClassName(Type.getType(desc)).toList
+ this
+ }
+
+ override def visitEnd() {}
+ }
+
+ def visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor =
+ visitAnnotation(desc, visible)
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AttributeVisitorTrait.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AttributeVisitorTrait.scala
new file mode 100644
index 00000000000..0603cebf5af
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AttributeVisitorTrait.scala
@@ -0,0 +1,15 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import org.objectweb.asm.{Type, Attribute}
+
+/**
+ * @author tonytv
+ */
+private trait AttributeVisitorTrait {
+ protected val imports: ImportsSet
+
+ def visitAttribute(attribute: Attribute) {
+ imports ++= getClassName(Type.getObjectType(attribute.`type`)).toList
+ }
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.scala
new file mode 100644
index 00000000000..00ed9efb360
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.scala
@@ -0,0 +1,10 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+/**
+ * The result of analyzing a .class file.
+ * @author tonytv
+ */
+sealed case class ClassFileMetaData(name:String,
+ referencedClasses : Set[String],
+ exportPackage : Option[ExportPackageAnnotation])
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.scala
new file mode 100644
index 00000000000..00265b80761
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.scala
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import com.yahoo.container.plugin.util.Strings
+
+/**
+ * @author tonytv
+ */
+case class ExportPackageAnnotation(major: Int, minor: Int, micro: Int, qualifier: String) {
+ requireNonNegative(major, "major")
+ requireNonNegative(minor, "minor")
+ requireNonNegative(micro, "micro")
+ require(qualifier.matches("""(\p{Alpha}|\p{Digit}|_|-)*"""),
+ exportPackageError("qualifier must follow the format (alpha|digit|'_'|'-')* but was '%s'.".format(qualifier)))
+
+
+ private def requireNonNegative(i: Int, fieldName: String) {
+ require(i >= 0, exportPackageError("%s must be non-negative but was %d.".format(fieldName, i)))
+ }
+
+ private def exportPackageError(s: String) = "ExportPackage anntotation: " + s
+
+ def osgiVersion : String = (List(major, minor, micro) ++ Strings.noneIfEmpty(qualifier)).mkString(".")
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/PackageTally.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/PackageTally.scala
new file mode 100644
index 00000000000..ecbf9448dfd
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/PackageTally.scala
@@ -0,0 +1,46 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import com.yahoo.container.plugin.util.Maps
+
+/**
+ *
+ * @author tonytv
+ */
+final class PackageTally (private val definedPackagesMap : Map[String, Option[ExportPackageAnnotation]],
+ referencedPackagesUnfiltered : Set[String]) {
+
+ val referencedPackages = referencedPackagesUnfiltered diff definedPackages
+
+ def definedPackages = definedPackagesMap.keySet
+
+ def exportedPackages = definedPackagesMap collect { case (name, Some(export)) => (name, export) }
+
+ /**
+ * Represents the classes for two package tallies that are deployed as a single unit.
+ *
+ * ExportPackageAnnotations from this has precedence over the other.
+ */
+ def combine(other: PackageTally): PackageTally = {
+ new PackageTally(
+ Maps.combine(definedPackagesMap, other.definedPackagesMap)(_ orElse _),
+ referencedPackages ++ other.referencedPackages)
+ }
+}
+
+
+object PackageTally {
+ def fromAnalyzedClassFiles(analyzedClassFiles : Seq[ClassFileMetaData]) : PackageTally = {
+ combine(
+ for (metaData <- analyzedClassFiles)
+ yield {
+ new PackageTally(
+ Map(Packages.packageName(metaData.name) -> metaData.exportPackage),
+ metaData.referencedClasses.map(Packages.packageName))
+ })
+ }
+
+ def combine(packageTallies : Iterable[PackageTally]) : PackageTally = (empty /: packageTallies)(_.combine(_))
+
+ val empty : PackageTally = new PackageTally(Map(), Set())
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Packages.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Packages.scala
new file mode 100644
index 00000000000..2900d3f1551
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/Packages.scala
@@ -0,0 +1,27 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+/**
+ * Utility methods related to packages.
+ * @author tonytv
+ */
+object Packages {
+ case class PackageMetaData(definedPackages: Set[String], referencedExternalPackages: Set[String])
+
+ def packageName(fullClassName: String) = {
+ def nullIfNotFound(index : Int) = if (index == -1) 0 else index
+
+ fullClassName.substring(0, nullIfNotFound(fullClassName.lastIndexOf(".")))
+ }
+
+
+
+ def analyzePackages(allClasses: Seq[ClassFileMetaData]): PackageMetaData = {
+ val (definedPackages, referencedClasses) =
+ (for (classMetaData <- allClasses)
+ yield (packageName(classMetaData.name), classMetaData.referencedClasses.map(packageName))).
+ unzip
+
+ PackageMetaData(definedPackages.toSet, referencedClasses.flatten.toSet diff definedPackages.toSet)
+ }
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/SubVisitorTrait.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/SubVisitorTrait.scala
new file mode 100644
index 00000000000..d4e7f4fb028
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/SubVisitorTrait.scala
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin.classanalysis
+
+import collection.mutable
+
+/**
+ * A visitor that's run for sub construct of a class
+ * and forwards all its imports the the owning ClassVisitor at the end.
+ * @author tonytv
+ */
+private trait SubVisitorTrait {
+ val analyzeClassVisitor : AnalyzeClassVisitor
+
+ val imports : ImportsSet = mutable.Set()
+
+ def visitEnd() {
+ analyzeClassVisitor.addImports(imports)
+ }
+}
diff --git a/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/package.scala b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/package.scala
new file mode 100644
index 00000000000..a94bc7710d2
--- /dev/null
+++ b/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/package.scala
@@ -0,0 +1,24 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.plugin
+
+import org.objectweb.asm.Type
+import collection.mutable
+
+package object classanalysis {
+ type ImportsSet = mutable.Set[String]
+
+ def internalNameToClassName(internalClassName: String) : Option[String] = {
+ getClassName(Type.getObjectType(internalClassName))
+ }
+
+ def getClassName(aType: Type): Option[String] = {
+ import Type._
+
+ aType.getSort match {
+ case ARRAY => getClassName(aType.getElementType)
+ case OBJECT => Some(aType.getClassName)
+ case _ => None
+ }
+ }
+}
+