Clone wiki

semdiff / Developing_your_own_Detector

Developing your own Detector

SemDiff allows its users to extend its services by plugging in their own detectors. New detectors can specify dependencies on older detectors, which allow them to reuse and build on the results computed by earlier detectors.

Getting Started

A detector takes as input a transaction (bundle of source file pairs) and returns as output a number of facts about the transaction. A fact represents a nugget of information that describes something that your detector discovered about those file pairs. For example, SemDiff's 'CallDiff' outputs one fact per detected insertion or deletion of a method invocation within a method, so that each returned CallDiff fact represents something like: 'a call to foo( ) was inserted in method bar'. SemDiff then displays and decorates each fact detected by your detector in its Transactions View. For example, each CallDiff fact is decorated with a small image to represent a deletion or insertion of a method call.

To develop your own detector, first create your own Eclipse plug-in project. You can use this plug-in project to create and declare multiple detectors. For each detector you wish to create, you must first declare it to SemDiff so that it can be loaded upon startup of the Eclipse workbench. To declare your detector

  • Go to the MANIFEST.MF file in the META-INF folder
  • Navigate to the 'Extensions' tab
  • Under the 'All Extensions' heading, declare two extensions: one for one for the 'ca.mcgill.cs.swevo.semdiff.core.detector' extension point and one for the 'ca.mcgill.cs.swevo.semdiff.core.hibernateClass' extension point

The detector extension point declares the main working class of your detector, i.e., the class that computes facts each time SemDiff invokes it to analyze a transaction. The hibernateClass provides information about the facts computed by your detector so that SemDiff can persist them for users.

The detector Extension Point

This extension point allows you to specify the following properties of your detector:

  • id (unique id)
  • label (textual descriptor that SemDiff shows to users in its UI)
  • class (the entry point to your detector)
  • labelProvider (a class for decorating the facts/results of your detector as they show up in the Transactions View, if necessary)
  • icon (icon that SemDiff shows to users in its UI)
  • resultDescription
  • visible (set this to true if the results of your detector should be displayed to users in the Transactions View)
  • needsPPA (set this to true if your detector requires type bindings inferred using PPA)

The class satisfying the 'class' property must implement SemDiff's 'Detector' interface. The Detector interface specifies a single method, which is called by SemDiff for each transaction to be scanned by your detector. The method's signature is

public DetectorResults detect(Repository repository, Transaction transaction, DetectorInput input); 

Your detector can call methods on the DetectorInput instance to retrieve the source file pairs for that transaction. It can also use the DetectorInput instance to retrieve the results of earlier detectors required for your analysis. It must return its computed facts/results by bundling them in a DetectorResults instance. The Repository instance provides additional information about the current repository being analyzed and the Transaction instance provides additional information about the current transaction being analyzed.

The class satisfying the 'labelProvider' property must extend SemDiff's 'AbstractLabelProvider' class. You can override the 'getImage' and 'getLabel' methods of that class to provide image icons and textual descriptions for the individual facts/results computed by your detector.

You may also specify dependencies for your detector, i.e., results of other detectors required by your detector. To do this, right click on your detector entry, then click 'New -> dependency' for each intermediate detector required by your own detector. To retrieve the results of a given detector in the code for your detector, use a snippet similar to

public DetectorResults detect(Repository repository, Transaction transaction, DetectorInput input) {
 List<Result> results = input.getRequiredResults("ca.mcgill.cs.swevo.semdiff.detector.structdiff");

in your implemented detector, where 'transaction' is the input Transaction instance provided to your detector by SemDiff. In the above code snippet, we are retrieving required results of the StructDiff detector by using StructDiff's id, for example.

To help you get started with your detector, you can refer to the source code of SemDiff's StructDiff, CallDiff, and FieldDiff.

The hibernateClass Extension Point

Your detector's facts/results will be persisted to a DB by SemDiff. To allow SemDiff to do this, you must create and declare a fact/result class using the hibernateClass extension point. For your result class, don't forget to

  • include the @java.persistence.Entity and @javax.persistence.DiscriminatorValue annotations at the top of your class
  • use the @Administrator annotation to tag getters giving access to temporary data structures. If you want those data structures to be persisted, use @Embeddable.

You can refer to SemDiff's StructDiffResult, CallResult, and FieldResult for examples of result classes and the points listed above.

NOTE: Don't forget to edit your MANIFEST.MF file to include the following line:

  • 'Eclipse-RegisterBuddy: ca.mcgill.cs.swevo.semdiff.core, org.hibernate.semdiff, org.apache.log4j' This line is required for your detector to work properly.

NOTE: When working with your hibernateClass extension, don't forget that your class is persisted to a DB. Whenever you update the interface of your class, you will need to update the DB as well. To update a given DB, you can use SemDiff's Update Database option in the main SemDiff menu.