CAIS - Comparative Anatomy Information System for the Foundational Model of Anatomy (FMA) and the Mouse Anatomy Ontology (MOA)
Developer information: programmer's guide
This is a brief overview of how the application is structured. It is intended to help the future developers of the application,
so that they are able to quickly understand what is going on with the code and so that less time is spent on learning how the current
code works.
See the javadoc generated from the code.
Important notes
It is probably a good idea to skim through the "User's guide" page first, so that you can become familiar with the terminology
we use e.g. what we mean when we say classes map to each other, etc.
Abstract and Concrete classes in Protégé vs. abstraction in programming languages
It is important to note that when we use the term "abstract" and "concrete" classes, we are not referring to Java abstract classes.
The knowledgebase is implemented in Protégé as a subclass hierarchy, and there are some Protégé classes that do not represent
actual anatomical structures of a species but are needed in order to generally maintain the hierarchy. For example, the class
"Organ" is an abstract class, and subclasses of it such as "Prostate (human)" are concrete classes, instances of which actually
exist in the real world. So when we use "abstract" and "concrete" classes in our explanations, think of them from a biological
perspective rather than a programming languages perspective.
The classes
The application currently consists of the following classes:
MainView
This is the main class of the project that contains the main method.
It has methods for fetching the knowledgebase and displaying the GUI of the application.
DBcontents
This is a class for accessing the knowledgebase and displaying its contents. There are 2 different constructors:
1. The constructor for getting the "mapping directions" panel - the knowledgebase is accessed to find the different species that it
contains and then the GUI for the panel is created.
2. The constructor for the "species trees" panels - All the classes are fetched from the knowledgebase, and the ones that are from
the appropriate species for the panel are displayed in a tree.
DBquery
A class that represents a query to be made to the knowledgebase. The query has a subject (the class selected from the "From" tree),
and object (the class selected from the "To" tree) and a query type (selected from the radio-button list). The class contains methods
to answer all of the query types, and some "utility methods" that answer non-query specific, generic questions and thus can be used
when implementing future query types.
RadioGroup
This class creates radio buttons to display query type choices. The panel that contains the radio buttons also has a "recursion level"
box, which lets the user specify how many levels down the part hierarchy he/she would like to take the query. By default, the level
is 0 so that the query is only performed on the selected structures and not on any of their parts. The maximum number of levels is 4,
but the programmer may wish to change this in the future.
Results
This is a class for creating the results panel and the "previous query" table.
It contains methods for updating the table and for displaying results in textual and hierarchical (tree) form in the tabbed results pane.
Shared
This is a very simple object with two variables of type Cls. It is supposed to represent a "shared" structure between two species i.e.
if structure A for species A maps to structure B from species B, then species A and species B "share" structure A and structure B.
So you keep the two structures that map to each other ((structure A and structure B) in a Shared object. I am not sure this is
very good implementation. For a better explanation of the "shared" query, see the "User's Guide" page.
Global
A global class for displaying the query in the query information box next to the "Execute Query" button.
Applet vs. Application
The program runs as a Java application because when it ran as an applet with Java Web Start, there were problems with the display.
The MainView class is an applet with a main method, and the applet is instantiated and its methods are called in main.
The query methods
All the methods for making the queries to the knowledgebase are in the DBquery class.
When the user selects a structure from the "from" and "to" trees (we refer to the selected structures as the subject and the
object of the query) and a query type and pressed the "Execute query" button, an instance of the DBquery class is created and
the makeQuery() method is called.
Depending on what the subject and object are, and what the query type is, different methods are called to deal with the query in
the makeQuery() method. Here is the basic outline of the method:
1. First check for and deal with unknowns (i.e. when either the subject or the object or both are "unknown"), because their results are
shown no matter which query type is selected.
2. If neither the subject nor the object is "unknown", then check and save the query type.
3. Make sure both the classes being compared are concrete classes, because abstract classes cannot be used in comparisons.
Actually, the user is not allowed to select abstract classes anymore, so it is not possible that either the subject or the object
are abstract, but I am leaving the check in there for now.
4.
a. For the "similar to" and "differs from" queries:
Only compare slots and slot values if the subject and object map to each other. If they don't, then just say what each maps to and quit.
There is one exception to this, and that is that you can compare entities even if they don't map to each other if one of them is a member of a set,
and that set maps to the other entity. That is why the Boolean variable isSetQuery is included as a parameter to the makeQuery() method; it is true
if either the subject or the object or both are sets, and the makeQuery() method is called with their members. If isSetQuery is true, then you can
perform the comparisons checks described in the next paragraph that you would normally only do if the structures map to each other.
Check if either the subject or the object or both are sets. If so, compare the members of the set to the other entity, or if both are sets,
the members of each set to each other. If neither the subject nor the object are sets, then compare them to each other.
Comparing two structures to each other (in the makeComparison() method) entails comparing their slots and slot values (see the
User's Guide
for a description of how the comparison is made). Getting the slots and slot values of the classes is done through the Protégé API, you can find
a link to the Protégé Developer Documentation on the Related Links page.
b. For the "shared" and "not shared" queries:
The compareParts() method is called to get the parts from both classes that either map to or do not map to each other.
c. For the "union" queries:
Since in the current implementation of the union query all we do is show what the subject maps to and what the object maps to,
all you do in this case is call the showMapsTo() method twice, once with the subject as a parameter and once with the object as a
parameter.
d. Recursively making the query down the part hierarchy
The integer variable "depth" is set to the number of levels of the part hierarchy that the query should be made on.
If the depth is not yet 0 (so there is still levels to search), then the searchParts() method is called. The searchParts()
method gets the parts of the current subject and object and calls the makeQuery() method with parts from the current subject
and object as the new subject and object. Every time the searchParts() method is called in the makeQuery() method, the depth
is decremented, so that searchParts() is called as many times as the depth specified.
e. The Boolean queries "is different?" and "is homologous?"
These two Boolean queries are complements of each other. If a structure maps to another structure, the two structures are
homologous and they are not different (so "is different?" would return false and "is homologous?" would return true).
Conversely, if two structures don't map to each other, then they are not homologous and are different. Therefore, when
implementing these queries, all we do is call the checkMapping() method which returns true if its two parameters map to each other,
and false otherwise.
The "Previous results" table
The above description is basically a run-through of the code as it gets executed when a user selects two structures and presses
the "Execute Query" button. Exactly the same thing happens when a user selects a row in the "Previous query" table when he/she
wants to see the results from an old query.
The table contains DBquery objects, and when a row is selected, the object in that row is fetched, a new instance of the DBquery
class is made and the makeQuery() method is called on that new instance.
Future work note: You probably do not need to create a new instance of DBQuery, you could probably just call makeQuery() on the
DBquery object you fetched, but then you need a method to clear the tree results from the initial results, and we have not implemented
this yet.
|