This page covers topics of interest to people writing custom reports for GenJ:
A GenJ-report is a Java-type that extends the abstract type genj.report.Report. Writing your own functionality starts by defining your own class in a source-file and placing that file into the reports-subdirectory (its name has to start with Report*).
Let's assume we want to write a test-report named "Test". First create a text file named ReportTest.java in ./reports. Then compile it by running the compile-script in the same directory. This script runs javac - the java compiler. The end-result is a file named ReportTest.class which GenJ can load and execute after it's selected in the Report View.
Let's have a look at the sample ReportTest.java. It contains one public class-definition that conforms to the Report contract (extends Report). Part of that contract is to implement public void start(Object context) - the entry point into the Report. We can use all methods provided by the super-type Report - here we're simply using its println-method - println() dumps text into a widget that the user sees when executing the report.
import genj.gedcom.*; import genj.report.*; import genj.report.Report; public class ReportTest extends Report { /** * the 'main' method */ public void start(Object context) { println("Hello World!"); } }
Now, we want to make sure this report integrates nicely into the Report View with name, some basic information and reference to the author. To achieve this we create a file ReportTest.properties in the same directory as the report and fill it with the required information like so:
# File: ReportTest.properties # These are the text resources for ReportTest name = A Simple Test info = This is a test report to get going with writing reports for GenJ author = John Doe
The report should show up nicely now but we have to do something in start to make this worthwhile for some user of ReportTest. By default reports can be run only on a Gedcom context - that means the argument to start() will be an instance of the Java-type Gedcom. So we assume that context instanceof Gedcom. A simple Use Case here could be to report the number of individuals in the passed in Gedcom object. We do that by changing the start method above:
public void start(Object context) { // we know the context is an instance of Gedcom by default Gedcom gedcom = (Gedcom)context; // let's compute the number of individuals int count = gedcom.getEntities(Gedcom.INDI).size() // ... and report it println("Gedcom "+gedcom.getName()+" contains "+count+" individuals"); }
The API for genj.report.Report and genj.gedcom.Gedcom contains many methods for accessing and presenting the information contained (see the GenJ Javadocs for more).
In order to support internationalization, your report should NOT print out string constants directly. Instead, it should use a properties file to define the strings it prints out, and then get them from that file. That way, the properties file can be translated to other languages. For example, do not do this: println("Birth Date is"); Instead do this:
println(i18n("BirthDateIs"));and put a line like this in your properties file:
BirthDateIs = Birth Date is
Every report has an options pane, which you can easily use, by creating public class variables in your report class and properties in your properties file. For example, lets say your report generates text by default, but can create HTML, and you would like users to specify which they want. Start by creating a new public variable in your report java class:
public boolean generateHTML = true ;and then add text to your properties file:
generateHTML = Do you want HTML output generated?load your report, and take a look at the options pane to see the results.
To create a pull down menu with some choices in it, use these public variables:
public int outputFormat = 0; public static String[] outputFormats = { "Soundex", "Metahphone", "Double Metaphone", "NYSIIS", "Phonex" };and this to your properties file:
outputFormat=Output format outputFormat.de=Ausgabe-FormatThere are some more complex options available. Take a look at ReportEvent.java for more examples.
All the java code in the report directory is compiled by the rcompile script, and all of it is loaded into GenJ. So, if you want to use some other classes in your report, you can just copy the java or class files into report, and they will be loaded and waiting for you to use.
For example, if you want to use a DMSoundex class, just put the DMSoundex.java file in the report directory, and run rcompile. It is now available for you, but does not show up as a report, because it is not a subclass of genj.report.Report.
//Methods available on class Indi (Individuals) //This is not a complete list! Just enough to get you started. String getBirthAsString() // Note: This is a birth DATE (not a whole object) String getDeathAsString() // Note: This is a death DATE (not a whole object) String getName() String getFirstName() String getLastName() Fam getFamc() // Gets family where this person is a child Indi getFather() Indi getMother Indi[] getChildren() //Methods available on class Fam (Families) //This is not a complete list! Just enough to get you started. Indi getChild(int which) Indi[] getChildren() Indi getHusband() Indi getWife()You may also want to look at the PropertyDate class, and the methods which return these objects from other classes.
Most data in the GEDCOM file is available via tags, rather than from methods on objects. For example, to get naturalization information about a person you would not do this: indi.getNaturalization(). Instead you would do this:
PropertyEvent naturalization = (PropertyEvent)indi.getProperty(new TagPath("INDI:NATU")) ;Or you can get more specific pieces of data with calls like this:
PropertyDate prop = (PropertyDate)indi.getProperty(new TagPath("INDI:NATU:DATE")); String dateOfNaturalization = prop.getValue() ;Getting notes data is quite a bit more complex. The following code will return an event's note as a string, if the event has a note. You would call it like this
// Example of use: PropertyEvent immigration = (PropertyEvent)indi.getProperty(new TagPath("INDI:IMMI")) ; String note = getNote(immigration) ; if (note==null) note = i18n("noNote") ; println(note) ; // Here is the back up code: private String getNote(PropertyEvent e) { if (e==null) return null ; MultiLineProperty prop = checkNote(e.getProperty("NOTE")) ; if (prop==null) return null ; return prop.getLinesValue() ; } /** * Checks for a NOTE property and if applicable * return the MultiLineProperty for accessing its value */ MultiLineProperty checkNote(Property prop) { if (prop==null) return null ; // has to be 'NOTE' if (!prop.getTag().equals("NOTE")) return null; // see if prop is a reference to a Note if (prop instanceof PropertyNote) { // .. jump to the referenced target prop = ((PropertyNote)prop).getTarget(); } // see if we have a multiline property containing // the note text now if (prop instanceof MultiLineProperty ) return (MultiLineProperty)prop; // no candidate return null; }
In order to use these TagPath strings, you need to know what four letter codes are part of the GEDCOM
standard.Here are two links with more information to help you:
http://www.math.clemson.edu/~rsimms/genealogy/ll/gedcom55.pdf
http://www.gendex.com/gedcom55/55gcappa.htm
Below is a "cheat sheet" of the most common tags:
Top Level: INDI (individual), FAM (Family)
Events: BIRT (Birth), DEAT (Death), IMMI (Immigration),NATU (Naturalization), BAPT (Baptism)
Others: CHIL (Child), FAMC (Family were child), HUSB (Husband), WIFE (Wife)
Components: ADDR (address), DATE (date), NOTE (Note), PLAC (place)
The "Editor View" shows exactly what tags have data for which entities, and is a great way to understand your GEDCOM data and structure.
You can look at the source code for the Events report to see how these Tags are used. Also, you can look at the GEDCOM file itself to see which tags are subtags of other tags.