All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opalj.br.analyses.ProjectIndex.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package br
package analyses

import scala.collection.Map
import scala.collection.mutable

/**
 * An index that enables the efficient lookup of source elements (methods and fields)
 * given the method's/field's name and the descriptor/field type. The index contains fields
 * with public, protected, `` and private visibility.
 *
 * Basically an index of the source elements (methods and fields) of a project.
 *
 * This index can be used, e.g., to resolve method calls based on the method's names and/or
 * descriptors.
 *
 * To get an instance of a project index call [[Project.get]] and pass in
 * the [[ProjectIndexKey]] object.
 *
 * @see [[FieldAccessInformation]] to get the information where a field is accessed.
 *
 * @author Michael Eichberg
 */
class ProjectIndex private (
        val fields:  Map[String, Map[FieldType, List[Field]]],
        val methods: Map[String, Map[MethodDescriptor, List[Method]]]
) {

    def findFields(name: String, fieldType: FieldType): List[Field] = {
        fields.get(name).flatMap(_.get(fieldType)).getOrElse(Nil)
    }

    def findFields(name: String): Iterable[Field] = {
        fields.get(name).map(_.values.flatten).getOrElse(Nil)
    }

    def findMethods(name: String, descriptor: MethodDescriptor): List[Method] = {
        methods.get(name).flatMap(_.get(descriptor)).getOrElse(Nil)
    }

    def findMethods(name: String): Iterable[Method] = {
        methods.get(name).map(_.values.flatten).getOrElse(Nil)
    }

    /**
     * Returns a map of some basic statistical information, such as the most often used
     * field/method name.
     */
    def statistics(): Map[String, Any] = {

        def getMostOftenUsed(
            elementsWithSharedName: Iterable[(String, Map[_, Iterable[ClassMember]])]
        ) = {
            elementsWithSharedName.foldLeft((0, mutable.Set.empty[String])) { (c, n) =>
                val nName = n._1
                val nSize = n._2.size
                if (c._1 < nSize)
                    (nSize, mutable.Set(nName))
                else if (c._1 == nSize)
                    (nSize, c._2.addOne(n._1))
                else
                    c
            }
        }

        val fieldsWithSharedName = fields.view.filter(_._2.size > 1)
        val mostOftenUsedFieldName = getMostOftenUsed(fieldsWithSharedName)

        val methodsWithSharedName =
            methods.view.filter(kv => kv._1 != "" && kv._1 != "" && kv._2.size > 1)
        val mostOftenUsedMethodName = getMostOftenUsed(methodsWithSharedName)

        Map(
            "number of field names that are used more than once" ->
                fieldsWithSharedName.size,
            "number of fields that share the same name and type" ->
                fieldsWithSharedName.count(_._2.size > 2),
            "number of usages of the most often used field name" ->
                mostOftenUsedFieldName._1,
            "the most often used field name" ->
                mostOftenUsedFieldName._2.mkString(", "),
            "number of method names that are used more than once (initializers are filtered)" ->
                methodsWithSharedName.size,
            "number of methods that share the same signature (initializers are filtered)" ->
                methodsWithSharedName.count(_._2.size > 2),
            "number of usages of the most often used method name (initializers are filtered)" ->
                mostOftenUsedMethodName._1,
            "the most often used method name (initializers are filtered)" ->
                mostOftenUsedMethodName._2.mkString(", ")
        )
    }
}

/**
 * Factory for [[ProjectIndex]] objects.
 *
 * @author Michael Eichberg
 */
object ProjectIndex {

    def apply(project: SomeProject): ProjectIndex = {

        import scala.collection.mutable.AnyRefMap

        import scala.concurrent.{Future, Await, ExecutionContext}
        import scala.concurrent.duration.Duration
        import ExecutionContext.Implicits.global

        val fieldsFuture: Future[AnyRefMap[String, AnyRefMap[FieldType, List[Field]]]] = Future {
            val estimatedFieldsCount = project.fieldsCount
            val fields = new AnyRefMap[String, AnyRefMap[FieldType, List[Field]]](estimatedFieldsCount)
            for (field <- project.allFields) {
                val fieldName = field.name
                val fieldType = field.fieldType
                fields.get(fieldName) match {
                    case None =>
                        val fieldTypeToField = new AnyRefMap[FieldType, List[Field]](4)
                        fieldTypeToField.update(fieldType, List(field))
                        fields.update(fieldName, fieldTypeToField)
                    case Some(fieldTypeToField) =>
                        fieldTypeToField.get(fieldType) match {
                            case None =>
                                fieldTypeToField.put(fieldType, List(field))
                            case Some(theFields) =>
                                fieldTypeToField.put(fieldType, field :: theFields)
                        }
                }
            }
            fields.foreachValue(_.repack())
            fields.repack()
            fields
        }

        val methods: AnyRefMap[String, AnyRefMap[MethodDescriptor, List[Method]]] = {
            val estimatedMethodsCount = project.methodsCount
            val methods = new AnyRefMap[String, AnyRefMap[MethodDescriptor, List[Method]]](estimatedMethodsCount)
            for (method <- project.allMethods) {
                val methodName = method.name
                val methodDescriptor = method.descriptor
                methods.get(methodName) match {
                    case None =>
                        val descriptorToField = new AnyRefMap[MethodDescriptor, List[Method]](4)
                        descriptorToField.update(methodDescriptor, List(method))
                        methods.update(methodName, descriptorToField)
                    case Some(descriptorToField) =>
                        descriptorToField.get(methodDescriptor) match {
                            case None =>
                                descriptorToField.put(methodDescriptor, List(method))
                            case Some(theMethods) =>
                                descriptorToField.put(methodDescriptor, method :: theMethods)
                        }
                }
            }
            methods.foreachValue(_.repack())
            methods.repack()
            methods
        }

        new ProjectIndex(Await.result(fieldsFuture, Duration.Inf), methods)
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy