Francois, 

voici une nouvelle version du rapport flash. Je ne l'ai teste qu'un tout petit 
peu. J'ai develope les nouvelles fonctionalites dont on parlait. Au niveau du 
design, c'est simple mais ne comprends pas le module super generique de 
gestion des locations dont Andre pouvait parler. C'est uniquement designe 
pour le besoin. J'ai teste avec tous les fichiers que j'avais recu.

Dis-moi ce que tu en penses. Il faut que je revois le texte du .properties. 
N'y touche pas pour l'instant.

Frederic
/**
 * Reports are Freeware Code Snippets
 *
 * This report is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
import genj.fo.Document;
import genj.gedcom.Gedcom;
import genj.gedcom.Indi;
import genj.gedcom.Fam;
import genj.gedcom.Property;
import genj.gedcom.PropertyDate;
import genj.gedcom.PropertyPlace;
import genj.gedcom.TagPath;
import genj.gedcom.time.PointInTime;
import genj.report.Report;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.text.DecimalFormat;

import javax.swing.ImageIcon;
import javax.swing.*;

/** UNCOMMENT FOR GEO ADD-IN **
import genj.geo.GeoLocation;
import genj.geo.GeoService;
import genj.geo.GeoServiceException;
*/

    //
    // Flash lists are structured lists with locations and individual lastnames
    // The following report allows 3 keys to sort the items in the report.
    // As described below, 2 of these keys will be location related, 1 will be the 
    // lastname. 
    // Design of the keys allow 1 of the keys to be made of 2 location
    // elements to provide more possibilities, so as to use 3 location elements in total.
    // The first key will be used for a potential table of content.
    // So typically, one can sort by Country/Department, then city, then lastname.
    // (or any combination of these 3).
    // Rather than choosing Country/Department and City, users can chose any other 
    // jurisdiction in the way they structure places. For instance, they can also choose 
    // "City/District, Region, lastname" as long as city, district and region are
    // jurisdiction levels in the gedcom.
    //
    // - Lastnames will be taken from the only available lastnames
    //   If no lastname exist for a person, individual will not be reported
    // - Choices exist insofar as locations depending on whether there is a PLAC tag
    //   in the header or not in the Gedcom.
    //
    //   + If a PLAC tag is found in the header, user will be prompted to consider 
    //   any of their components and ask which ones to use
    //   For CITY tags, city will be CITY only,STAE,POST,CTRY.
    //   For PLAC tags, key defined by user will be used. 
    //
    //   + If no PLAC tag is found, only 1 location key will be used, and the combined
    //   location key will be empty and considered as the 3rd sorting key. In this case
    //   the options related to pointing to the PLAC tag will be disregarded
    //   and the sorting option will only be used to know if location precedes lastname
    //   or the other way around. 
    //   For CITY tags, city will be CITY only,STAE,POST,CTRY.
    //   For PLAC tags, city will be first available jurisdiction only. 
    //
    // PLAC tags and CITY tags will only be extracted for events only.
    // Tags considered as events in the gedcom norm are:
    // Family - ANUL, CENS, DIV, DIVF, ENGA, MARR, MARB, MARC, MARL, MARS, EVEN
    // Indiv - ADOP, BIRT, BAPM, BARM, BASM, BLES, BURI, CENS, CHR, CHRA, CONF
    // CREM, DEAT, EMIG, FCOM, GRAD, IMMI, NATU, ORDN, RETI, PROB, WILL, EVEN
    //
    //
    // Main logic for the report is to build records like this:
    // | Key1 | Key2 | Key3 | Sosa nb | B | C | M | D | O | Ymin | Ymax |
    //
    // where
    //   Key1, key2, key3 : any combination of 
    //         Loc1/Loc2 :  location levels 1 and 2, e.g. Country/State, Region/City, etc
    //         Name :       lastname of the person
    //         Place :      location level 3, e.g. City or District within a city, etc
    //   Sosa Nb : number of events in the Place where Name is an ancestor of the DeCujus
    //   B :       number of Name born in that lace
    //   C :       number of Name christened in that place
    //   M :       number of Name married in that place
    //   D :       number of Name dead in that place
    //   O :       number of other events for Name in that place
    //   Ymin :    first year of an event of Name in that Place
    //   Ymax :    last year of an event of Name in that Place
    //  
    // Using MapTree (sorted mapping lists, this means 3 levels of pointers
    //   primary -> Key ; secondary
    //                    secondary -> Key ; tertiary
    //                                       tertiary -> Key, Range
    //   and Range holds the volumes and year range
    //  
    //    
    // Options also include to highlight lines that have a lot of events, 
    // or a large year span
    //
    //

/**
 * GenJ - Report
 * @author Nils Meier <[EMAIL PROTECTED]>
 * @author Frederic Lapeyre <[EMAIL PROTECTED]>
 * @version 2.1
 */
public class ReportFlashListDev extends Report {

  private final static TagPath CITY = new TagPath(".:ADDR:CITY");
  private final static TagPath STAE = new TagPath(".:STAE");
  private final static TagPath CTRY = new TagPath(".:CTRY");

  private final static String FORMAT_LNORMAL = "font-weight=normal,text-align=left";
  private final static String FORMAT_CNORMAL = "font-weight=normal,text-align=center";
  private final static String FORMAT_RNORMAL = "font-weight=normal,text-align=right";
  private final static String FORMAT_LSTRONG = "font-weight=bold,text-align=left";
  private final static String FORMAT_CSTRONG = "font-weight=bold,text-align=center";
  private final static String FORMAT_RSTRONG = "font-weight=bold,text-align=right";
  private final static String FORMAT_CBACKGROUND = "background-color=#ffffcc,font-weight=bold,text-align=center";
  private final static String FORMAT_RBACKGROUND = "background-color=#ffffcc,font-weight=bold,text-align=right";

  /** option - Whether to use a TOC or not */
  public boolean weAddaTOC = true;

  /** option - Position of the legend if any */
  public int     displayLegend = LEGEND_BOT;
  
  private final static int
    LEGEND_NO = 0,
    LEGEND_TOP = 1,
    LEGEND_BOT = 2;

  public String displayLegends[] = {
      translate("legendNone"),
      translate("legendTop"),
      translate("legendBot")
   };

  /** option - Formatting options for repeating headers and keys */
  public boolean repeatHeader = true; 
  public boolean repeatKeys = false; 
  
  /** option - display zeros as '0' (true) or '-' (false) */
  public boolean displayZeros = false;
  
  /** option - Thresholds to highlight important lines */
  public int nbEvents = 3;          // highlight if more than 3 events per row
  public int yearSpan = 50;         // highlight if events span over 50 years
  public int minSosa = 1;           // highlight if line is a sosa above limit

  /** option - Increments when processing file to let user know how we are going */
  public int counterIncrement = 2;
  public String counterIncrements[] = { translate("DoNotShow"), "10", "100", "1000", "10000" };
  
/** UNCOMMENT FOR GEO ADD-IN **
  // option - produce geographique data
  public boolean produceGeoData = true;
*/
  
  /** option by prompting - Key structure */
  private int posLoc1 = 0; 
  private int posLoc2 = 2;
  private int posLoc3 = 4;
  
  private boolean existPLACTag = true;  // Will be false if no PLAC tag found in header

  private final static int
    LOC32_SURN_LOC1 = 0,
    LOC32_LOC1_SURN = 1,
    SURN_LOC32_LOC1 = 2,
    SURN_LOC1_LOC32 = 3,
    LOC1_LOC32_SURN = 4,
    LOC1_SURN_LOC32 = 5;

  private int recordKey = LOC32_SURN_LOC1;  // Will indicate sorting structure

  private String recordKeyText = "";        // Text for the legend

  /**
  * Overriden image - we're using the provided FO image
  */
  protected ImageIcon getImage() {
    return Report.IMG_FO;
  }

  /**
   * While we generate information on stdout it's not really
   * necessary because we're returning a Document
   * that is handled by the UI anyways
   */
  public boolean usesStandardOut() {
    return false;
  }

  /**
   * One of the report's entry point
   */
  public void start(Gedcom gedcom) {
    start(gedcom, gedcom.getEntities(Gedcom.INDI), null);
  }

  /**
   * One of the report's entry point
   */
  public void start(Indi[] indis) {
    start(indis[0].getGedcom(), Arrays.asList(indis), null);
  }

  /**
   * One of the report's entry point
   */
  public void start(Indi indi) {
    start(indi.getGedcom(), indi.getGedcom().getEntities(Gedcom.INDI), indi);
  }

  /**
   * Our main logic
   */
  private void start(Gedcom gedcom, Collection indis, Indi indiDeCujus) {

    //Get location and lastname options based on PLAC tag in gedcom and user input
    // Updates values of recordKey, posLoc1, posLoc2, posLoc3, existPLACTag
    if (!getFlashOptions(gedcom)) return;
    
    // get de-cujus (sosa 1) if entry point is generic
    if (indiDeCujus == null) {
      String msg = translate("AskDeCujus");
      indiDeCujus = (Indi)getEntityFromUser(msg, gedcom, Gedcom.INDI);
      if (indiDeCujus == null) return;
      }

    // prepare our index
    Map primary = new TreeMap();
    int countIndiv = 0;
    counterIncrement = (int)Math.pow(10, counterIncrement);
    println(translate("StartingAnalysis"));      
    for (Iterator it = indis.iterator(); it.hasNext();) {
      analyze(  (Indi) it.next(), primary, indiDeCujus);
      if (counterIncrement > 1) {
         countIndiv++;
         if ((int)Math.floor(countIndiv/counterIncrement) * counterIncrement == countIndiv)
            println(String.valueOf(countIndiv));      
         }
      }
    if (counterIncrement > 1) 
      println(translate("NowWriting")+"...");      

    // write main file out
    Document doc = new Document(getName());
        
    // Display toc
    if (weAddaTOC) {
      doc.addTOC();
      if (primary.size()>10) doc.nextPage();
    }
    
    // Display legend on top
    if (displayLegend == LEGEND_TOP) {
      displayLegend(doc);
    }
    
    // Display one header if will not repeat them
    if (!repeatHeader) {
       displayHeader(doc, true);
       doc.endTable();
       }

    // Define totals which will map Country_State_Place (key) to ranges (vol + geo)
    // as Group By lastname on primary/seconday/tertiary keys, and then adding the 
    // geo coordinates
    Map totals = new TreeMap();

    // loop on 3 main maps to perform a couple of operations:
    // - write the main file
    // - assign geo coordinates to group by lastnames into totals (if code included)       
    for (Iterator ps = primary.keySet().iterator(); ps.hasNext(); ) {
      String p = (String)ps.next();

      // primary key (by default Cntry/State)
      // secod parameter is a meaningful anchor for easier external referencing to this flash list
      doc.startSection(p,p.replaceAll(" ","-").replaceAll("/","+").replaceAll(",","+")); 
      displayHeader(doc, repeatHeader);
      String secondaryKey = "";
      
      Map secondary = (Map)lookup(primary, p, null);
      for (Iterator ss = secondary.keySet().iterator(); ss.hasNext(); ) {
        String s = (String)ss.next();
        Map tertiary = (Map)lookup(secondary, s, null);
        for (Iterator ts = tertiary.keySet().iterator(); ts.hasNext(); ) {
          String t = (String)ts.next();
          Range range = (Range)lookup(tertiary, t, null);
           
          String lformat = FORMAT_LNORMAL;
          String cformat = FORMAT_CNORMAL;
          String rformat = FORMAT_RNORMAL;
          if ((range.getNbEvents()  >= nbEvents) ||
              (range.getYearSpan()  >= yearSpan) ||
              (range.getValueSosa() >= minSosa)) {
             lformat = FORMAT_LSTRONG;
             cformat = FORMAT_CSTRONG;
             rformat = FORMAT_RSTRONG;
             }
          
          doc.nextTableRow(lformat);
          if (repeatKeys == true || secondaryKey != s)
             doc.addText(s);
          else    
             doc.addText(" ");
          if (secondaryKey != s) secondaryKey = s;
          doc.nextTableCell(lformat);
          doc.addText(t);
          doc.nextTableCell(rformat);
          doc.addText((!displayZeros && (range.nbSosa == 0)) ? "-" : range.getNbSosa());
          doc.nextTableCell(rformat);
          doc.addText((!displayZeros && (range.nbBirth == 0)) ? "-" : range.getNbBirth());
          doc.nextTableCell(rformat);
          doc.addText((!displayZeros && (range.nbChris == 0)) ? "-" : range.getNbChris());
          doc.nextTableCell(rformat);
          doc.addText((!displayZeros && (range.nbMarr == 0)) ? "-" : range.getNbMarr());
          doc.nextTableCell(rformat);
          doc.addText((!displayZeros && (range.nbDeath == 0)) ? "-" : range.getNbDeath());
          doc.nextTableCell(rformat);
          doc.addText((!displayZeros && (range.nbOther == 0)) ? "-" : range.getNbOther());
          doc.nextTableCell(cformat);
          doc.addText(range.getFirst());
          doc.nextTableCell(cformat);
          doc.addText(range.getLast());

/** UNCOMMENT FOR GEO ADD-IN **
        if (produceGeoData) {
           // add sum of volumes for that lastname into totals 
           addTotals(p.replaceAll(" ","-").replaceAll("/","+"), s.replaceAll(" ","-").replaceAll("/","+"), t.replaceAll(" ","-").replaceAll("/","+"), range, totals);
           }
*/
        }
      }
      doc.endTable();
      // done
    }

    // Display legend on top
    if (displayLegend == LEGEND_BOT) {
      displayLegend(doc);
    }
    
    // done with main output
    println(translate("Completed"));      
    showDocumentToUser(doc);

/** UNCOMMENT FOR GEO ADD-IN **
    // Print geo output only if geoOption is on
    if (produceGeoData) {
       // Get locations of gedcom - match locations
       Collection locations = GeoLocation.parseEntities(indis);
       getLocations(gedcom, locations);
       // Add geo coordinates to totals 
       addGeoData(totals, locations);
       // Define and produce output for geo data
       Document docGeo = new Document(translate("geoname"));
       produceGeoDoc(totals, docGeo);
       showDocumentToUser(docGeo);
       }
*/
  
  } // end_of_start

  
  /**
  * Get location and sorting structre options from user
  */
  private boolean getFlashOptions(Gedcom gedcom) {
    // Get address format and ask user for sorting structure
    // Updates values of recordKey, posLoc1, posLoc2, posLoc3, existPLACTag
    if (gedcom.getPlaceFormat() == "") {
      // no tag PLAC found
      String[] choices = { translate("SurnLoc"), translate("LocSurn") };
      recordKeyText  = (String)getValueFromUser(translate("recordKey"), choices, choices[0]);
      existPLACTag = false;
      if (recordKeyText  == null) { return false; } 
      if (recordKeyText.compareTo(translate("LocSurn")) == 0) { 
          recordKey = LOC1_SURN_LOC32; 
          } 
      else { 
          recordKey = SURN_LOC1_LOC32; 
          }
      } 
      // tag PLAC found
      else {
      List tag = Arrays.asList(gedcom.getPlaceFormat().split("\\,")); // original tag
      ArrayList choices = new ArrayList((Collection)Arrays.asList(gedcom.getPlaceFormat().split("\\,"))); // list used for selection only
      
      String selection1 = (String)getValueFromUser(translate("recordKey1"), (Object[])choices.toArray(), choices.get(0));
      if (selection1 == null) { return false; } 
         else { choices.remove(choices.indexOf(selection1)); }
      
      String selection2 = (String)getValueFromUser(translate("recordKey2"), (Object[])choices.toArray(), choices.get(0));
      if (selection2 == null) { return false; } 
         else { choices.remove(choices.indexOf(selection2)); }
      
      String selection3 = (String)getValueFromUser(translate("recordKey3"), (Object[])choices.toArray(), choices.get(0));
      if (selection3 == null) { return false; } 
         else { choices.remove(choices.indexOf(selection3)); }

      int[] list = {tag.indexOf(selection1), tag.indexOf(selection2), tag.indexOf(selection3)};
      Arrays.sort(list);
      posLoc1 = list[0];
      posLoc2 = list[1];
      posLoc3 = list[2];
      String recordKeys[] = {
         tag.get(posLoc3)+"/"+tag.get(posLoc2)+" > "+translate("surname")+" > " +tag.get(posLoc1),
         tag.get(posLoc3)+"/"+tag.get(posLoc2)+" > "+tag.get(posLoc1)+" > "+translate("surname"),
         translate("surname")+" > "+tag.get(posLoc3)+"/"+tag.get(posLoc2)+" > "+tag.get(posLoc1),
         translate("surname")+" > "+tag.get(posLoc1)+" > "+tag.get(posLoc3)+"/"+tag.get(posLoc2),
         tag.get(posLoc1)+" -> "+tag.get(posLoc3)+"/"+tag.get(posLoc2)+" > "+translate("surname"),
         tag.get(posLoc1)+" -> "+translate("surname")+" > "+tag.get(posLoc3)+"/"+tag.get(posLoc2)
         };
      recordKeyText = (String)getValueFromUser(translate("recordKey"), recordKeys, recordKeys[0]);
      if (recordKeyText == null) { return false; } 
         else {
           List table = Arrays.asList(recordKeys); 
           recordKey = table.indexOf(recordKeyText);
         }
      existPLACTag = true;
      }
    return true;  
    // done
  }

  
  /**
  * Header function
  */
  private void displayLegend(Document doc) {
    // Display legend 
    // Display sorting key and location fields used
    doc.addText(" ");     doc.nextParagraph();
    doc.addText(" ");     doc.nextParagraph();
    doc.addText(" ");     doc.nextParagraph();
    doc.addText(" ");     doc.nextParagraph();
    doc.startSection(translate("legendText"), "legend");
    doc.addText(" ");     doc.nextParagraph();
    doc.addText(translate("recordKey")+" "+recordKeyText);  doc.nextParagraph();
    doc.addText(" ");     doc.nextParagraph();
    doc.addText(translate("legendS"));     doc.nextParagraph();
    doc.addText(translate("legendB"));     doc.nextParagraph();
    doc.addText(translate("legendC"));     doc.nextParagraph();
    doc.addText(translate("legendM"));     doc.nextParagraph();
    doc.addText(translate("legendD"));     doc.nextParagraph();
    doc.addText(translate("legendO"));     doc.nextParagraph();
    doc.addText(translate("legendMin"));   doc.nextParagraph();
    doc.addText(translate("legendMax")); 
    doc.addText(" ");     doc.nextParagraph();
    doc.addText(" ");     doc.nextParagraph();
    doc.addText(" ");     doc.nextParagraph();
    doc.addText(" ");     doc.nextParagraph();
    // done
  }

   /**
   * Header function
   */
  private void displayHeader(Document doc, boolean dHeader) {
    // Start table
      doc.startTable("genj:csv=true,width=100%");
      doc.addTableColumn("column-width=30%");     // secondary key (default is Names)
      doc.addTableColumn("column-width=23%");     // tertiary key (default is place)
      doc.addTableColumn("column-width=5%");      // Sosa nb
      doc.addTableColumn("column-width=5%");      // Birth nb
      doc.addTableColumn("column-width=5%");      // Christ. nb
      doc.addTableColumn("column-width=5%");      // Marriage nb
      doc.addTableColumn("column-width=5%");      // Death nb
      doc.addTableColumn("column-width=5%");      // Other nb
      doc.addTableColumn("column-width=10%");     // first year
      doc.addTableColumn("column-width=7%");     // last year

      // Display header row on top of the columns
      if (dHeader) {
         doc.nextTableRow();
         doc.addText(" ");
         doc.nextTableCell();
         doc.addText(" ");
         doc.nextTableCell(FORMAT_RBACKGROUND);
         doc.addText(translate("colS"));
         doc.nextTableCell(FORMAT_RBACKGROUND);
         doc.addText(translate("colB"));
         doc.nextTableCell(FORMAT_RBACKGROUND);
         doc.addText(translate("colC"));
         doc.nextTableCell(FORMAT_RBACKGROUND);
         doc.addText(translate("colM"));
         doc.nextTableCell(FORMAT_RBACKGROUND);
         doc.addText(translate("colD"));
         doc.nextTableCell(FORMAT_RBACKGROUND);
         doc.addText(translate("colO"));
         doc.nextTableCell(FORMAT_CBACKGROUND);
         doc.addText(translate("colMin"));
         doc.nextTableCell(FORMAT_CBACKGROUND);
         doc.addText(translate("colMax"));
      }
    // done
  }
  
  /**
   * Analyze an individual
   */
  private void analyze(Indi indi, Map primary, Indi indiDeCujus) {

    // consider non-empty last names only
    String name = indi.getLastName();
    if (name.length()==0)
      return;
    name = name.trim();

    // determine is individual is an ancestor of De-Cujus
    boolean isSosa = indi.isAncestorOf(indiDeCujus);
    if (indi == indiDeCujus) isSosa = true;
     
    // loop over all dates in indi
    for (Iterator dates = indi.getProperties(PropertyDate.class).iterator(); dates.hasNext(); ) {
      // consider valid dates only
      PropertyDate date = (PropertyDate)dates.next();
      if (!date.isValid()) continue;
      // compute first and last year
      int start = date.getStart().getYear();
      int end = date.isRange() ? date.getEnd().getYear() : start;
      if (start>end) continue;
      // find all places for it
      analyzePlaces(name, start, end, date.getParent(), primary, isSosa);
      // find all cities for it
      analyzeCities(name, start, end, date.getParent(), primary, isSosa);

      // next date
    }

    // loop over all dates in family of indi
    for (Iterator families = Arrays.asList(indi.getFamiliesWhereSpouse()).iterator();
    families.hasNext(); ) {
      Fam family = (Fam)families.next();
      for (Iterator dates = family.getProperties(PropertyDate.class).iterator(); dates.hasNext(); ) {
        // consider valid dates only
        PropertyDate date = (PropertyDate)dates.next();
        if (!date.isValid()) continue;
        // compute first and last year
        int start = date.getStart().getYear();
        int end = date.isRange() ? date.getEnd().getYear() : start;
        if (start>end) continue;
        // find all places for it
        analyzePlaces(name, start, end, date.getParent(), primary, isSosa);
        // find all cities for it
        analyzeCities(name, start, end, date.getParent(), primary, isSosa);
      } // next date
    } // next family

    
    // done
  }

  /**
   * Analyze all cities for given indi, start, end & property
   */
  private void analyzeCities(String name, int start, int end, Property prop, Map primary, boolean isSosa) {
    // Consider places of events only
    if (!isEvent(prop)) return;
      
    Property[] cities = prop.getProperties(CITY);
    for (int c = 0; c < cities.length; c++) {
      // consider non-empty cities only
      String loc1 = cities[c].getDisplayValue().trim();
      if (loc1.length()==0) continue;
      
      // if PLAC tag in the gedcom, use "-/-" for loc3/loc2 (we cannot guess the mapping
      // between jurisdictions and CITY,STAE,CTRY for sure!)
      // otherwise use "" for loc3/loc2
      String loc32 = "";
      if (existPLACTag) { loc32 = "-/-"; }
      // keep it
      keep(name, start, end, loc1, primary, prop, isSosa, loc32);
      // next city
    }
    // done
  }

  /**
   * Analyze all places for given indi, start, end & property
   */
  private void analyzePlaces(String name, int start, int end, Property prop, Map primary, boolean isSosa) {
    // Consider places of events only
    if (!isEvent(prop)) return;
      
    // loop over places
    for (Iterator places = prop.getProperties(PropertyPlace.class).iterator(); places.hasNext(); ) {
      // Get place
      PropertyPlace place = (PropertyPlace)places.next();
      
      // if PLAC tag in the gedcom, use locations as per specified by user
      String loc1 = "";
      String loc2 = "";
      String loc3 = "";
      String loc32 = "";
      if (existPLACTag) {
         loc1 = place.getJurisdiction(posLoc1);
         loc2 = place.getJurisdiction(posLoc2);
         loc3 = place.getJurisdiction(posLoc3);
         if (loc1 != null) loc1 = loc1.trim();
         if (loc2 != null) loc2 = loc2.trim();
         if (loc3 != null) loc3 = loc3.trim();
         if ((loc1 == null) || (loc1.length() == 0)) loc1 = "-";
         if ((loc2 == null) || (loc2.length() == 0)) loc2 = "-";
         if ((loc3 == null) || (loc3.length() == 0)) loc3 = "-";
         // consider non-empty places only
         if ((loc1 == "-") && (loc2 == "-") && (loc3 == "-")) continue;
         loc32 = loc3+"/"+loc2;
         }
      else {
         // if no PLAC tag in the gedcom, use only first available jurisdiction
         loc1 = place.getFirstAvailableJurisdiction().trim();
         // consider non-empty places only
         if (loc1.length()==0) continue;
         }
      // keep it
      keep(name, start, end, loc1, primary, prop, isSosa, loc32);
      // next place
    }
    // done
  }

  /**
   * Analyze all places for given indi, start, end & property
   */
  private boolean isEvent(Property prop) {
    // returns true if property is in list of events, false otherwise
    List listOfEvents = Arrays.asList( "ADOP", "ANUL", "BIRT", "BAPM", "BARM", "BASM", "BLES", "BURI", "CENS", "CHR", "CHRA", "CONF", "CREM", "DEAT", "DIV", "DIVF", "EMIG", "ENGA", "EVEN", "FCOM", "GRAD", "IMMI", "MARR", "MARB", "MARC", "MARL", "MARS", "NATU", "ORDN", "RETI", "PROB", "WILL" );
    return listOfEvents.contains(prop.getTag());
    // done
  }

  private void keep(String name, int start, int end, String loc1, Map primary, Property prop, boolean isSosa, String loc32) {

    // calculate primary and secondary key
    String ps, ss, ts;
    switch (recordKey) {
      case LOC32_SURN_LOC1:
        ps = loc32;
        ss = name;
        ts = loc1;
        break;
      case LOC32_LOC1_SURN:
        ps = loc32;
        ss = loc1;
        ts = name;
        break;
      case SURN_LOC32_LOC1:
        ps = name;
        ss = loc32;
        ts = loc1;
        break;
      case SURN_LOC1_LOC32:
        ps = name;
        ss = loc1;
        ts = loc32;
        break;
      case LOC1_LOC32_SURN:
        ps = loc1;
        ss = loc32;
        ts = name;
        break;
      case LOC1_SURN_LOC32:
        ps = loc1;
        ss = name;
        ts = loc32;
        break;
      default:
        throw new IllegalArgumentException("no such report type");
    }
    
    // remember
    Map secondary = (Map)lookup(primary, ps, TreeMap.class);
    Map tertiary  = (Map)lookup(secondary, ss, TreeMap.class);
    Range range = (Range)lookup(tertiary, ts, Range.class);
    range.add(start, end, isSosa, prop.getTag());
    // done
  }

  /**
   * Lookup an object in a map with a default class
   */
  private Object lookup(Map index, String key, Class fallback) {
    // look up and create lazily if necessary
    Object result = index.get(key);
    if (result==null) {
      try {
        result = fallback.newInstance();
      } catch (Throwable t) {
        t.printStackTrace();
        throw new IllegalArgumentException("can't instantiate fallback "+fallback);
      }
      index.put(key, result);
    }
    // done
    return result;
  }

  /**
   * our ranges
   */
  static class Range {
    int firstYear = Integer.MAX_VALUE, 
        lastYear = -Integer.MAX_VALUE,
        nbSosa   = 0,
        nbBirth  = 0,
        nbChris  = 0,
        nbMarr   = 0,
        nbDeath  = 0,
        nbOther  = 0;
    double 
        geoLat   = 0,
        geoLon   = 0;

    void add(int start, int end, boolean isSosa, String tag) {
      // check for valid year - this might still be UNKNOWN even though a date was valid
      if (start!=PointInTime.UNKNOWN)
        firstYear = Math.min(firstYear, start);
      if (end!=PointInTime.UNKNOWN)
        lastYear = Math.max(lastYear, end);
      if (isSosa) 
        nbSosa++;
      if (tag == "BIRT") nbBirth++;
         else 
      if (tag == "CHR") nbChris++;
         else 
      if (tag == "MARR") nbMarr++;
         else 
      if (tag == "DEAT") nbDeath++;
         else nbOther++;
    }
    
    void add(Range rangeElt) {
      firstYear = Math.min(firstYear, rangeElt.getValueFirst());
      lastYear  = Math.max(lastYear, rangeElt.getValueLast());
      nbSosa += rangeElt.getValueSosa();
      nbBirth += rangeElt.getValueBirth();
      nbChris += rangeElt.getValueChris();
      nbMarr += rangeElt.getValueMarr();
      nbDeath += rangeElt.getValueDeath();
      nbOther += rangeElt.getValueOther();
    }
    
    void setGeo(double lat, double lon) {
      geoLat = lat;
      geoLon = lon;
    }
    
    String getFirst() {
      // check for valid year - this might still be UNKNOWN even though a date was valid
      if (firstYear==Integer.MAX_VALUE|| lastYear==Integer.MAX_VALUE)
        return "";
      return Integer.toString(firstYear);
    }

    String getLast() {
      return Integer.toString(lastYear);
    }
  
    String getNbSosa() {
      return Integer.toString(nbSosa);
    }
  
    String getNbBirth() {
      return Integer.toString(nbBirth);
    }
  
    String getNbChris() {
      return Integer.toString(nbChris);
    }
  
    String getNbMarr() {
      return Integer.toString(nbMarr);
    }
  
    String getNbDeath() {
      return Integer.toString(nbDeath);
    }
    
    String getNbOther() {
      return Integer.toString(nbOther);
    }
    
    int getNbEvents() {
      return (nbBirth+nbChris+nbMarr+nbDeath+nbOther);
    }
  
    int getYearSpan() {
      return (lastYear-firstYear);
    }
  
    int getValueFirst() {
      return (firstYear);
    }
  
    int getValueLast() {
      return (lastYear);
    }
  
    int getValueSosa() {
      return (nbSosa);
    }
  
    int getValueBirth() {
      return (nbBirth);
    }
  
    int getValueChris() {
      return (nbChris);
    }
  
    int getValueMarr() {
      return (nbMarr);
    }
  
    int getValueDeath() {
      return (nbDeath);
    }
  
    int getValueOther() {
      return (nbOther);
    }
  
    double getValueLat() {
      return (geoLat);
    }
  
    double getValueLon() {
      return (geoLon);
    }
  
  }
  
/** UNCOMMENT FOR GEO ADD-IN **
  private void getLocations(Gedcom gedcom, Collection locations) {
    //
    try {
      GeoService.getInstance().match(gedcom, locations, true);
    } catch (GeoServiceException e) {
      super.println(e.getMessage());
    }
    if (locations.isEmpty()) {
      getOptionFromUser(translate("none_mapable"), OPTION_OK);
      return;
    }
    // done
  }

  // Summation (used to sum up collections on lastname to get totals by place)
  private void addTotals(String p, String s, String t, Range range, Map totals) {
    // calculate Country+State+Place
    String loc32, loc1, key;
    switch (recordKey) {
      case LOC32_SURN_LOC1:
        loc32 = p;
        loc1 = t;
        break;
      case LOC32_LOC1_SURN:
        loc32 = p;
        loc1 = s;
        break;
      case SURN_LOC32_LOC1:
        loc32 = s;
        loc1 = t;
        break;
      case SURN_LOC1_LOC32:
        loc32 = t;
        loc1 = s;
        break;
      case LOC1_LOC32_SURN:
        loc32 = s;
        loc1 = p;
        break;
      case LOC1_SURN_LOC32:
        loc32 = t;
        loc1 = p;
        break;
      default:
        throw new IllegalArgumentException("no such report type");
    }
    
    // remember place with key as "loc3+loc2+loc1"
    key = loc32+"+"+loc1;
    Range rangeTotal = (Range)lookup(totals, key, Range.class);
    rangeTotal.add(range);
    // done
  }
  
  private void addGeoData(Map totals, Collection locations) {
    int posLoc2Loc = posLoc2 - 1;
    // Loop over locations, get key and assign it the coordinates
    for (Iterator it=locations.iterator(); it.hasNext(); ) {
      // valid location?
      GeoLocation location = (GeoLocation)it.next();
      if (!location.isValid()) continue;
      // Find location in totals
      String country = "--";
      if (location.getCountry() != null) country = location.getCountry().getDisplayName();
      if ((country == null) || (country.length() == 0)) country = "--";
      String state   = "--";
      if (location.getJurisdictions().size() > posLoc2Loc) state = (String)location.getJurisdictions().get(posLoc2Loc); 
      String city    = location.getCity(); 
      if (city == null) city = "--"; // should never be null anyway
      String key= country.replaceAll(" ","-")+"+"+state.replaceAll(" ","-")+"+"+city.replaceAll(" ","-");
      // Assign total entry with the identified geo coordinates
      Range range = (Range)totals.get(key);
      if (range != null) {
         range.setGeo(location.getY(), location.getX());
         }
      // next
    }
  }
  
  private void produceGeoDoc(Map totals, Document docGeo) {
    // Formatting of geo coordinates
    DecimalFormat format = new DecimalFormat("0.00");
    // Table start
    docGeo.startTable("genj:csv=true,width=100%");
    docGeo.addTableColumn("column-width=16%");     // Country
    docGeo.addTableColumn("column-width=20%");     // State
    docGeo.addTableColumn("column-width=20%");     // Place
    docGeo.addTableColumn("column-width=4%");      // latitute
    docGeo.addTableColumn("column-width=4%");      // longitude
    docGeo.addTableColumn("column-width=4%");      // total nb events
    docGeo.addTableColumn("column-width=4%");      // first year
    docGeo.addTableColumn("column-width=4%");      // last year
    docGeo.addTableColumn("column-width=4%");      // Sosa nb
    docGeo.addTableColumn("column-width=4%");      // Birth nb
    docGeo.addTableColumn("column-width=4%");      // Christ. nb
    docGeo.addTableColumn("column-width=4%");      // Marriage nb
    docGeo.addTableColumn("column-width=4%");      // Death nb
    docGeo.addTableColumn("column-width=4%");      // Other nb
    // Table lines
    for (Iterator key = totals.keySet().iterator(); key.hasNext(); ) {
      String ctryStatePlace = (String)key.next();
      Range range = (Range)lookup(totals, ctryStatePlace, null);
      String[] tmpStrings = ctryStatePlace.split("\\+");
      String country = tmpStrings[0];
      String state   = tmpStrings[1];
      String place   = tmpStrings[2];
      docGeo.nextTableRow();
      docGeo.addText(country);
      docGeo.nextTableCell();
      docGeo.addText(state);
      docGeo.nextTableCell();
      docGeo.addText(place);
      docGeo.nextTableCell();
      docGeo.addText(format.format(range.getValueLat()));
      docGeo.nextTableCell();
      docGeo.addText(format.format(range.getValueLon()));
      docGeo.nextTableCell();
      docGeo.addText(String.valueOf(range.getNbEvents()));
      docGeo.nextTableCell();
      docGeo.addText(range.getFirst());
      docGeo.nextTableCell();
      docGeo.addText(range.getLast());
      docGeo.nextTableCell();
      docGeo.addText(range.getNbSosa());
      docGeo.nextTableCell();
      docGeo.addText(range.getNbBirth());
      docGeo.nextTableCell();
      docGeo.addText(range.getNbChris());
      docGeo.nextTableCell();
      docGeo.addText(range.getNbMarr());
      docGeo.nextTableCell();
      docGeo.addText(range.getNbDeath());
      docGeo.nextTableCell();
      docGeo.addText(range.getNbOther());
    }     
  docGeo.endTable();
  }
  
*/

} // End_of_Report
    
# GenJ - GenealogyJ
#
# Message strings for the Flash List report
#
# Languages supported :
#     - english (default)
#     - german
#     - french
#
# Last updated: 06/12/2006  

author               = Nils Meier, Frederic Lapeyre <[EMAIL PROTECTED]>
version              = 2.1
category             = Analysis

name                 = Flash List *FL-DEV*
name.fr              = Liste Eclair (Version de Développement)

geoname              = Geographic data
geoname.fr           = Données géographiques

info                 = This report displays a flash-list of the gedcom file. 
This is a summary of the events sorted by place and lastname. A reader of such 
a report can easily and quickly identify any potential interest he/she may have 
to look the gedcom file in more details. 
                     +   
                     + The report displays for each combination of place and 
lastname the number of events, and the year span of these events. It also shows 
the number of events of ancestors of a pre-selected person.
                     +   
                     + The report can be started from a whole gedcom file or 
from a person in that file. If started from the whole file, you will be 
prompted to select the de-cujus person (sosa=1).
                     +   
                     + The report provides numerous display options including 
the possibility to highlight some of the lines depending on a criteria. For 
instance, you can decide to have bold lines for all place/name combinations 
that have events over more than 50 years, or where there has been more than 3 
events or that include more than 1 ancestor.
                     +   
                     + Explanation of the header row: 
                     + -----------------------------   
                     + |Country/State|Name|Place| S | B | C | M | D | O | First 
| Last |
                     +   
                     + where the 3 following fields could be in a different 
order:
                     + - Country/State : main location of event, "--" if 
unknown.
                     + - Name          : lastname of the person
                     + - Place         : place of the events
                     +   
                     + and the following fields with the following meaning:   
                     + - S : number of events in the Place where Name is an 
ancestor of the de-cujus (person selected)
                     + - B : number of births with that Name in that Place
                     + - C : number of christenings with that Name in that Place
                     + - M : number of marriages with that Name in that Place
                     + - D : number of deaths with that Name in that Place
                     + - O : number of other events with that Name in that Place
                     + - First : year of the first event with that Name in that 
Place
                     + - Last : year of the last event with that Name in that 
Place
                     +   
                     +   
                     + Report last updated in May 2006.
                     +   

info.de              = Dieser Report zeigt eine Liste von Orten, Namen und 
Daten zum schnellen Nachschlagen.
                     + Die Liste wird entweder nach Name oder nach Ort sortiert.
info.fr              = <h1><center>Liste Eclair</center></h1>
                     & <h1><center>(Version de Développement)</center></h1>
                     & &nbsp;<br>
                     & <div style="margin-left: 2px; margin-right: 2px;">
                     & Ce rapport crée ce qu'on appelle des listes 
                     & Eclair, c'est-à-dire qu'il permet à quelqu'un de 
                     & rapidement savoir si le fichier gedcom contient 
                     & potentiellement des personnes qui l'intéressent.<br>
                     & Ce résumé est classé de deux maniÚres différentes 
:<br>
                     & <div style="margin-left: 60px; text-align:left;
                     & margin-right: 10px; font-style: normal; color: #000000">
                     & - soit par lieu (ville, etc...),<br>
                     & - soit par nom patronymique.
                     & </div><br>
                     & Ce rapport affiche pour chaque agencement du nom 
                     & patronymique et du lieu, le nombre d'évÚnements, 
                     & mais aussi la période couvertes par ces événements 
                     & avec l'année la plus ancienne et celle la plus 
                     & récente. Il donne également le nombre de 
                     & personnes sosa par rapport à un individu 
                     & sélectionné.<br>
                     & <br>
                     & Ce rapport offre plusieurs options de formattage 
                     & afin de mettre en valeur ou non les lignes en 
                     & fonction de certains critÚres. Par exemple, vous 
                     & pouvez décider d'accentuer les lignes pour toutes 
                     & les combinaisons de noms patronymiques/lieux qui 
                     & ont des évÚnements sur plus de 50 ans, ou qui 
                     & ont vu survenir au moins 3 évÚnements, ou qui 
                     & ont concerné plus d'un ancêtre. Comme vous le 
                     & voyez, les possibilités sont multiples.<br>
                     & Il peut être lancé soit sur le fichier gedcom 
                     & en entier, soit sur une personne déterminée. Si 
                     & il est lancé sur le gedcom, il vous sera demandé 
                     & de choisir le de-cujus (sosa=1).<br>
                     & <br>
                     & <div style="background-color: #ffffcc;">
                     & <u><b>Voici quelques explications sur les 
entêtes</b></u>
                     & <div style="margin-left: 10px; margin-right: 2px;">
                     & <b>|</b> <font color="red">Pays/Départ.|Nom|Lieu</font> 
<b>|</b> 
                     & <font color="red">S</font> <b>|</b> <font 
color="red">N</font> 
                     & <b>|</b> <font color="red">B</font <b>|</b> 
                     & <font color="red">U</font> <b>|</b> <font 
color="red">D</font> 
                     & <b>|</b> <font color="red">A</font> <b>|</b> 
                     & <font color="red">Année Min.</font> <b>|</b> 
                     & <font color="red">Année Max.</font> <b>|</b><br>
                     & <br>
                     & Où les 3 champs suivants, peuvent être classés 
                     & suivant un ordre différent :<br>
                     & <div style="margin-left: 20px;">
                     & - Pays/Département : Lieu principal d'un évÚnement, 
                     & si il n'est pas connu, il sera affiché "--".<br>
                     & - Nom Patronymique : Nom d'une personne,<br>
                     & - Lieu : Endroit où à eu lieu un évÚnement.
                     & </div><br>
                     & <u>Les champs ci-aprés ont les significations 
suivantes</u> :
                     & <div style="margin-left: 40px;">
                     & - <b>S</b> : nombre d'évÚnements à un endroit où le 
Nom 
                     & patronymique est un ancêtre du de-cujus (personne 
                     & sélectionnée).<br>
                     & - <b>N</b> : nombre de naissance avec ce Nom 
Patronymique 
                     & à cet Endroit.<br>
                     & - <b>B</b> : nombre de baptêmes avec ce Nom 
Patronymique 
                     & à cet Endroit.<br>
                     & - <b>U</b> : nombre de mariages avec ce Nom Patronymique 
                     & à cet Endroit.<br>
                     & - <b>D</b> : nombre de décÚs avec ce Nom Patronymique 
                     & à cet Endroit.<br>
                     & - <b>A</b> : nombre d'autres évÚnements avec ce Nom 
Patronymique 
                     & à cet Endroit.<br>
                     & - <b>Min.</b> : année la plus ancienne où cet 
évÚnement a 
                     & eu lieu pour la premiÚre fois avec ce Nom Patronymique 
                     & à cet endroit.<br>
                     & - <b>Max.</b> : année la plus récente où cet 
évÚnement a 
                     & eu lieu pour la derniÚre fois avec ce Nom Patronymique 
                     & à cet endroit.<br>
                     & </div>
                     & &nbsp;<br>
                     & </div>
                     & </div>
                     & &nbsp;<br>
                     & <i>(Rapport mis à jour pour la derniÚre fois le 12 Mai 
2006)</i>
                     & </div>
                     & &nbsp;<br>

isIndexByLastName    = Index by last name then by place
isIndexByLastName.de = Index nach Nachnamen, dann nach Ort
isIndexByLastName.fr = Indexer par le Nom de Famille puis par le Lieu

recordKey            = Sorting structure used for the flash report :
recordKey.fr         = Structure appliquée pour le tri :

LocSurn              = Location > Family Name
LocSurn.fr           = Lieu > Nom de famille

SurnLoc              = Family Name > Location 
SurnLoc.fr           = Nom de famille > Lieu

surname              = Family name
surname.fr           = Nom de famille

recordKey1           = 1/3. Choose a location key: 
recordKey1.fr        = 1/3. Choix d'un type de lieu: 
recordKey2           = 2/3. Choose one more location key:
recordKey2.fr        = 2/3. Choix d'un autre type de lieu:
recordKey3           = 3/3. Choose one last location key:
recordKey3.fr        = 3/3. Choix d'un dernier type de lieu:

repeatHeader         = Repeat header at each block
repeatHeader.fr      = Répétition des entêtes de colonne à chaque bloc

repeatKeys           = Repeat display of sorting fields
repeatKeys.fr        = Répétition de l'affichage des clés de tri

displayLegend        = Display legend
displayLegend.fr     = Affiche une légende

legendNone           = No legend
legendNone.fr        = Pas de légende
legendTop            = Top
legendTop.fr         = En haut
legendBot            = Bottom
legendBot.fr         = En bas

weAddaTOC            = Table Of Contents
weAddaTOC.de         = Inhaltsverzeichnis
weAddaTOC.fr         = Table des MatiÚres

displayZeros         = Display zeros as "0" (tick) or '-' (no tick)
displayZeros.fr      = Si cette option est cochée les "0" seront affichés, 
sinon ils seront remplacés par des '-'

AskDeCujus           = Please select first Sosa individual
AskDeCujus.fr        = Sélectionnez la personne choisie comme de-Cujus

colS                 = S
colS.fr              = S

colB                 = B
colB.fr              = N

colC                 = C
colC.fr              = B

colM                 = M
colM.fr              = U

colD                 = D
colD.fr              = D

colO                 = O
colO.fr              = A

colMin               = First
colMin.fr            = Min.

colMax               = Last
colMax.fr            = Max.

legendText           = *Legend*
legendS              = S : number of events in the Place where Name is an 
ancestor
legendB              = B : number of births with that Name in that Place
legendC              = C : number of christenings with that Name in that Place
legendM              = M : number of marriages with that Name in that Place
legendD              = D : number of deaths with that Name in that Place
legendO              = O : number of other events with that Name in that Place
legendMin            = First : year of the first event with that Name in that 
Place
legendMax            = Last : year of the last event with that Name in that 
Place

legendText.fr        = *Légende*
legendS.fr           = S : nombre d'évÚnements àun endroit où le Nom 
patronymique est un ancêtre.
legendB.fr           = N : nombre de naissance avec ce Nom Patronymique àcet 
Endroit.
legendC.fr           = B : nombre de baptêmes avec ce Nom Patronymiqueàcet 
Endroit.
legendM.fr           = U : nombre de mariages avec ce Nom Patronymique à cet 
Endroit.
legendD.fr           = D : nombre de décÚs avec ce Nom Patronymique à cet 
Endroit.
legendO.fr           = A : nombre d'autres évÚnements avec ce Nom 
Patronymique à cet Endroit.
legendMin.fr         = Min : année la plus ancienne où cet évÚnement a eu 
lieu pour la premiÚre fois avec ce Nom Patronymique àcet endroit.
legendMax.fr         = Max : année la plus récente où cet évÚnement a eu 
lieu pour la derniÚre fois avec ce Nom Patronymique à cet endroit.

nbEvents             = Events limit above which to highlith rows
nbEvents.fr          = Nombre de fois qu'un événement doit arriver pour être 
mis en valeur

yearSpan             = Yearspan over which to highlight rows years
yearSpan.fr          = Laps de temps en nombre d'années qui entrainera sa mise 
en valeur

minSosa              = Sosa limit above which to highlith rows
minSosa.fr           = Seuil sosa pour mise en valeur

counterIncrement     = Counter increment when processing file
counterIncrement.fr  = Incrémentation dans l'affichage du traitement des 
individus lors de la création du fichier

DoNotShow            = Do not show 
DoNotShow.fr         = Ne pas afficher

StartingAnalysis     = Starting Analysis on individuals
StartingAnalysis.fr  = Commencement de l'analyse des individus

NowWriting           = Now writing document
NowWriting.fr        = Maintenant écriture du rapport

Completed            = Completed.
Completed.fr         = Terminé.

# End of File



---------------------------------------------------------------------
<*> Pour vous désincrire de cette liste, envoyez un mail à :
              [EMAIL PROTECTED]
<*> Pour obtenir de l'aide sur les commandes de la liste :
              [EMAIL PROTECTED]

Répondre à