FAQ for Developers
This document provides pointers in the code to Qualyzer developers. Because the code changes more quickly and more often than this document, it should not be seen as the tablets of law.
- FAQ for Developers
- Where is ...
- How is it done?
- How to ...
- Tricky Modules
Where is ...
The RTF Parser
The RTF parser is implemented in the
qualyzer.editors.RTFDocumentProvider2 class. The parsing (open) starts in method
setDocumentContent(). The conversion from the editor to RTF (save) starts in method
All classes representing the Qualyzer model elements (e.g., Transcript, Memo, Participant) are in the package
All model classes have a fPersistenceID long field that contains the primary key from the database. Changes in the model are reported to listeners. It is currently possible to listen to changes in instances of: Project, Code, Investigator, Participant, Transcript, and Memo. Each of these classes have a corresponding listener (e.g.,
Listener management is performed by
qualyzer.model.ListenerManager. The single instance of the ListenerManager is held by the
Model objects are created, saved, and deleted through the
Creation and modification of model objects are validated with the classes in
qualyzer.model.validation. For example, a modification of an investigator is validated by the
The persistence layer (DB)
We use Hibernate to persist model objects to a relational DB. We embed the H2 database with Qualyzer. This is a relational database management system written in Java and running in the same virtual machine as Qualyzer.
The database data file is saved in the directory
Four classes are responsible for the persistence:
qualyzer.model.HibernateDBManager- This class establishes a connection to the database and enables the creation of an Hibernate session.
qualyzer.model.PersistenceManager- This class provides top-level services on the DB such as retrieving the project object for a project. This class is a singleton and can be accessed from anywhere.
qualyzer.util.HibernateUtil- This class provides common operations on the DB such as saving and refreshing an object without throwing any exception.
qualyzer.model.Facade- This class provides all the operations to create, save, and delete model objects.
The version number
The version number is declared by the constant
QualyzerActivator.CURRENT_VERSION. The version of each project is saved in the file
Properties in the
.project file such as the version number are read and written using
Most of the menus and shortcuts are defined in the
The editors such as the Transcript Editor and their actions such as ItalicAction are located in the
The dialogs and the wizards are in the
qualyzer.wizards package respectively.
The "action" corresponding to a menu selection or a keyboard shortcut are mostly located in the
The project explorer, i.e., the navigation tree located on the left of Qualyzer, is located in the
How is it done?
See the Logging page for an in-depth presentation of our logging infrastructure.
Update of the DB schema between two versions
When Qualyzer is started (
QualyzerActivator.start()), we compare the CURRENT_VERSION constant with the PROJECT_VERSION property of each project. If the two values differ, we update the schema of the project's database (
Facade.updateProject). Hibernate then automatically update the schema of the database to add, remove, and modify tables and columns.
Since we added the automatic update of the schema, we only modified the model once (addition of one field). There is only so much that can be automatically done when updating a schema so if the model changes significantly between two versions, extensive testing will be required. By significant changes, we mean the addition of non-optional fields, changes in the relations between two model classes, etc.
We follow the following process:
- Each String value that needs to be translated contains a key that usually looks like
class.msgKey. For example, the string that tells whether a database upgrade went well contains the value "QualyzerActivator.upgradeWell".
- Each line that contains a String literal that should not be translated must end with "$NON-NLS-1$"
- Each package that contains String objects that needs to be translated must contain a file named "messages.properties".
- Each package that contains String objects that needs to be translated must contain a final class named "Messages".
- To retrieve the translated value of a String (e.g., to display on the GUI), call Messages.getString(key).
Bold/Underline/Italic in the transcript editor
The content of the Transcript and Memo editors is displayed using two information:
- The textual content obtained from a RTF file.
org.eclipse.jface.text.source.AnnotationModelthat contains a set of
org.eclipse.jface.text.source.Annotationobjects associated with a
org.eclipse.jface.text.Position. An Annotation is just an object containing a style (e.g., BOLD).
qualyzer.editors.RTFDecorationSupport is responsible for mapping an annotation (e.g., "BOLD") to a visual style (e.g., SWT.BOLD).
qualyzer.editors.RTFDocumentProvider2 is responsible for parsing a RTF file to extract the textual content and associate appropriate annotations with parts of the text. For example, the RTF control command \b will be translated to an Annotation of style "BOLD", which will ultimately be converted to the visual style SWT.BOLD.
Highlighting of codes in the transcript editor
We refer to the part of the text that is associated with a code as a "fragment". For each fragment in a document, we generate a
qualyzer.editors.FragmentAnnotation associated with the position of the fragment.
The color and the style of the highlighted fragments are declared in
Import and Export of projects
The GUI uses the Import and Export wizard extensions of Eclipse. See the classes
The Import wizard delegates most of the work to Eclipse: the user selects a zip file and Eclipse imports all the projects contained in the zip file to the workspace. Then, in the
performFinish() method, Qualyzer loops through all projects and refreshes the project explorer while deleting the non-Qualyzer projects (e.g., it is possible to package a Java project with a Qualyzer project in the same zip file. The Java project will be deleted from the workspace).
The Export wizard also delegates most of the work to Eclipse: the
ProjectExportWizardPage just tells to Eclipse to automatically include all files in the project. This is the only custom code written for this feature.
Synchronization between views
We use the
Listener/Observer pattern to synchronize the various views in Qualyzer. For example, if a fragment is coded in a transcript, the code editor is refreshed to update the code count. See the section
Where is The Model for more information about the model and its listeners.
Each view or editor that needs to be updated when the model changes needs to implement the appropriate listener class and needs to register to the
ListenerManager. Each view/editor registered as a listener must unregister itself when it is disposed (usually in the
A view or editor never explicitly notifies other listeners: when the model is updated through the
Facade notifies the appropriate listeners.
For an example of view synchronization, see the
Split into Plug-ins, Features, and Products
Qualyzer is a RCP application and the code is split into many modules called plug-ins and features:
- ca.mcgill.cs.swevo.qualyzer: This plug-in contains all the code of Qualyzer, the splash screen and the intro files (displayed for first-time users).
- ca.mcgill.cs.swevo.qualyzer.dependencies: This feature indicates which plug-ins outside of the Eclipse ecosystem are required by Qualyzer. For example, the org.hibernate plug-in is referenced here. Each dependency is packaged in its own plug-in, and is in the source repository.
- ca.mcgill.cs.swevo.qualyzer.feature: This feature refers to the qualyzer plug-in. It also contains a product definition file, qualyzer.product, that describes the main features composing Qualyzer.
- ca.mcgill.cs.swevo.qualyzer.logging: This feature packages three logging libraries (log4j, slf4j, and Apache commons logging) and contains the configuration for these logging frameworks in the
src folder. slf4j is used by Hibernate (*Bart: I think*).
- ca.mcgill.cs.swevo.qualyzer.rcp_feature: This feature refers to all the Eclipse plug-ins that are needed to run Qualyzer. It also indicates that the org.eclipse.rcp feature should be included. When a new release is built, this feature ensures that all the necessary Eclipse plug-ins are added in the release.
- com.h2database: This plug-in packages the database library we use to persist the model.
- net.javazoom.BasicPLayer: This plug-in packages the music player we use to play mp3 and wave files (audio recording of an interview).
- net.sf.colorer: This plug-in packages the colorer editor. The colorer editor is a mix of Java files and native files. This is the text editor backend we use because it provides automatic word wrap at the end of each line, a feature currently not supported by Eclipse.
- org.hibernate: This plug-in packages the hibernate library we use to automatically map our model to a relational database.
- qualyzer.bitbucket.org: This project found in our source repository contains the source of the qualyzer.org website. This project is not distributed with Qualyzer.
How to ...
Test a dialog, a wizard, a view, or an editor
One the main problem of testing dialogs and wizards is that they are modal, which means that once they request the user input, it is very difficult to programmatically provide this input. We thus created our own Testing Framework.
The main idea is that for each command (e.g., creating a new investigator), the handler associated with the command implements the
ITestableHandler and uses a few low-level patterns described in the testing framework wiki page. For example, after opening a dialog, we always pass it to a
Add a new model class
To add a new model class that needs to be persisted (e.g., a Reviewer), follow these steps:
- Create a class in the package
- The class must be annotated with
- This class must have a field named
private Long fPersistenceId.
- Each field that needs to be persisted needs a corresponding getter and setter (even the fPersistenceId). Look at existing model classes for examples.
- Consult the mapping section of the Hibernate documentation for more information on how to annotated the getters. Most getters don't need to be annotated. Referring to other model elements require careful thoughts. Look at the existing model classes for examples on how to do it.
- Add a reference to the new class in
- Start Qualyzer with an existing project. Test that the automated schema conversion does not corrupt the project.
Create a new release/build
We have a detailed procedure to build a new release. We currently support Windows, MacOS and Linux. Support for Mac is difficult because Apple no longer ships Java by default (it must be downloaded by the user) and the graphic libraries vary depending on the version of the OS.
We follow three steps to build a new release:
- Using the Eclipse Product / Overview / Export wizard, we build a set of releases for all the platforms. This can be launched from any OS as long as you have installed the Delta pack (described in our detailed procedure).
- We further package the windows build into an installation file (e.g., qualyzer.msi). This is donw using WarSetup.
- Finally, we upload all the files to SourceForge.
Making a release is costly because we try to manually test all the packages before uploading them (we do a quick manual testing to see that we can at least open Qualyzer and create a transcript). In the past, the main problem we encountered was that the colorer editor (used by the transcript and memo editors) could not be loaded.
This section describes modules that should be very carefully modified because we consider them brittle or relying on some black magic. Eventually, the list of tricky modules should be shortened by relying on extensive testing, but some modules are inherently tricky and no amount of testing will make it easy to modify them.
The Code Editor
This editor is tricky from a usability perspective. Any change to this view must be carefully considered to avoid feature creep. Most researchers have their own way of looking at the data so it could be very easy to add 100 columns to the tables and the trees in this view.
The only tricky implementation detail is how the hierarchy of codes is saved and loaded: take a look at
qualyzer.providers.TreeModel to see how this is done.
The RTF Parser
The RTF parser is tricky because the specification is surprisingly complex and text editors implement it in a very different way. With time, we hope to build a collection of RTF documents that we can automatically test against to make sure that the parser is not broken after a change.
The performance of the RTF parser is also important because it is not uncommon to open large documents (several megs). For example, the use of string literals concatenation is strickly forbidden ("abc" + "def") and StringBuilder should always be used.
The transcript/memo editor
These editors are tricky because they rely on the colorer editor to provide automated word wrapping. The directory structure of the
net.sf.colorer plug-in is very sensitive and should not be changed.
Because the way annotations work in Eclipse, it is extremely difficult to make overlapping annotations or to leave an annotation "open" (e.g., all the text that the user will enter will be in bold). These are features requested by some users and as of now, we are not sure how to implement them.
Relations in the Persistence Layer
The Hibernate framework makes it easy to automatically map an object model to a relational database, but relations between model classes can be particularly difficult to configure. It seems there is always a parameter that needs to be added somewhere. Carefully testing on both ends of the relation is required when adding a relation. And plan for a few hours, just in case.