/* * [Born.java] * * Summary: Displays birth and death dates and age at time of death, or birth and age for living person. * * Copyright: (c) 2008-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2008-12-14 initial version * 1.1 2009-04-25 add birthday cake, refactor code * 1.2 2009-05-05 add deathdate. Use BigDate.isAnniversary method. */ package com.mindprod.htmlmacros.macro; import com.mindprod.common18.BigDate; import com.mindprod.common18.ST; import com.mindprod.fastcat.FastCat; import static java.lang.System.*; /** * Displays birth and death dates and age at time of death, or birth and age for living person. * * @author Roedy Green, Canadian Mind Products * @version 1.2 2009-05-05 add deathdate. Use BigDate.isAnniversary method. * @see com.mindprod.stores.Book * @since 2008-12-14 */ @SuppressWarnings( { "UnusedDeclaration" } ) public final class Born extends Macro { // TODO: handle dates of form YYYY-MM /** * true if want debugging harness code included */ private static final boolean DEBUGGING = false; /** * warning message */ private static final String IMPROBABLE_AGE = "Warning: improbable age "; /** * generic error message. allows yyyy and yyyy-mm-dd but not yyyy-mm. */ private static final String USAGE = "\nBorn macro needs date birth-yyyy-mm-dd [death-yyyy-mm-dd] [checked]"; /** * Make sure no future dates * * @param date birth or death date to check. * * @throws IllegalArgumentException */ private static void ensureNotFuture( BigDate date ) { if ( date.compareTo( Global.TODAY ) > 0 ) { throw new IllegalArgumentException( "Born date " + date.toString() + " is in the future." ); } } /** * generate HTML to display the age now or at death. * * @param ageInYears how old the person is today or how old they were the day they died. * * @return HTML to display the age now or at death. */ private static String expandAge( final int ageInYears ) { final FastCat sb = new FastCat( 3 ); sb.append( "age:" ); sb.append( ageInYears ); sb.append( "" ); return sb.toString(); } /** * generate HTML to display birthday * * @param birthDate person's birth date whose age you want expanded * @param isBirthYearOnlyNoDay true if birth date should be displayed with year only. * @param isBirthdayToday true if today is this person's birthday, even if they are dead. * * @return HTML to display the birthdate */ private static String expandBirthdate( final BigDate birthDate, final boolean isBirthYearOnlyNoDay, final boolean isBirthdayToday ) { final FastCat sb = new FastCat( 5 ); sb.append( "(" ); if ( isBirthYearOnlyNoDay ) { sb.append( birthDate.toYYYYString() ); } else { sb.append( birthDate.toString() ); } sb.append( "" ); return sb.toString(); } /** * generate HTML to display the date of death. * * @param deathDate person's death date * @param deathYearOnly true if death date should be displayed with year only. * @param isDeathday true if today is this person's birthday, even if they are dead. * * @return HTML to display the date of death. */ private static String expandDied( final BigDate deathDate, final boolean deathYearOnly, final boolean isDeathday ) { final FastCat sb = new FastCat( 5 ); sb.append( "" ); if ( deathYearOnly ) { sb.append( deathDate.toYYYYString() ); } else { sb.append( deathDate.toString() ); } sb.append( "" ); return sb.toString(); } /** * Trim birth and death string * * @param birthAndDeath string from Born macro * * @return Remove () */ public static String trimBorn( String birthAndDeath ) { assert !ST.isEmpty( birthAndDeath ) : "birthAndDeath string empty"; // (1921-01-19 1995-12-20 age:74) // strip out "(" and ")" final int offset1 = birthAndDeath.indexOf( '(' ); if ( offset1 >= 0 ) { birthAndDeath = birthAndDeath.substring( 0, offset1 ) + birthAndDeath.substring( offset1 + 1 ); } final int offset2 = birthAndDeath.lastIndexOf( ')' ); if ( offset2 >= 0 ) { birthAndDeath = birthAndDeath.substring( 0, offset2 ) + birthAndDeath.substring( offset2 + 1 ); } return birthAndDeath; }// /method /** * guts of Born macro expansion, when no deathDate * * @param birthDate person's birth date whose age you want expanded * @param isBirthYearOnlyNoDay true if birthDate date contains birth year only. * @param isBirthdayToday true if today is this person's birthday, even if they are dead. * @param checked true if we are sure the birthdate is correct, and this person has not died. * * @return age in years */ private String expandStillAlive( final BigDate birthDate, final boolean isBirthYearOnlyNoDay, final boolean isBirthdayToday, final boolean checked ) { // compute age as of TimeZone where macros expanded at elapsedTime macros // expanded. final int[] ymd = BigDate.age( birthDate, Global.TODAY ); final int ageInYears = ymd[ 0 ]; // this person/organisation is ostensibly still alive if ( DEBUGGING && !( ( 16 <= ageInYears && ageInYears <= 100 ) || ageInYears > 115 || checked ) ) { err.println( IMPROBABLE_AGE + ageInYears + " : " + birthDate.toString() + " : " + fileBeingProcessed.toString() ); } final FastCat sb = new FastCat( 5 ); sb.append( "" ); sb.append( expandBirthdate( birthDate, isBirthYearOnlyNoDay, isBirthdayToday ) ); sb.append( " " ); sb.append( expandAge( ageInYears ) ); sb.append( ")" ); return sb.toString(); } /** * expand birth when dead, but death unknown * * @param birthDate person's birth date whose age you want expanded * @param isBirthYearOnlyNoDay true if birth date should be displayed with year only. * @param isBirthdayToday true if today is this person's birthday, even if they are dead. * * @return HTML string with birth death and age in years */ private String expandUnknownDead( final BigDate birthDate, final boolean isBirthYearOnlyNoDay, final boolean isBirthdayToday ) { // this person/organisation is dead, but we do not know when final FastCat sb = new FastCat( 3 ); sb.append( "" ); sb.append( expandBirthdate( birthDate, isBirthYearOnlyNoDay, isBirthdayToday ) ); sb.append( " age:?)" ); return sb.toString(); } /** * guts of Born macro expansion * * @param birthDate person's birth date whose age you want expanded * @param isBirthYearOnlyNoDay true if birth date should be displayed with year only. * @param deathDate person's death date * @param deathYearOnly true if death date should be displayed with year only. * @param isBirthdayToday true if today is this person's birthday, even if they are dead. * @param isDeathday true if today is anniversary of this person's death, or today is this person's death. * @param checked true if we are sure the birthdate and deathdate are correct. * * @return HTML string with birth death and age in years */ private String expandKnownDead( final BigDate birthDate, final boolean isBirthYearOnlyNoDay, final boolean isBirthdayToday, final boolean isDeathday, final BigDate deathDate, final boolean deathYearOnly, final boolean checked ) { final int[] ymd = BigDate.age( birthDate, deathDate ); final int ageInYears = ymd[ 0 ]; if ( DEBUGGING && !( ( 16 <= ageInYears && ageInYears <= 105 ) || ageInYears > 115 || checked ) ) { err.println( IMPROBABLE_AGE + ageInYears + " : " + birthDate.toString() + " : " + deathDate.toString() + " : " + fileBeingProcessed.toString() ); } final FastCat sb = new FastCat( 9 ); sb.append( "" ); sb.append( expandBirthdate( birthDate, isBirthYearOnlyNoDay, isBirthdayToday ) ); sb.append( " " ); sb.append( expandDied( deathDate, deathYearOnly, isDeathday ) ); sb.append( " " ); sb.append( expandAge( ageInYears ) ); sb.append( ")" ); return sb.toString(); } /** * Born 1948-02-04 generates (born: 1948-02-04 age 60) * Born 1900-01-01 2000-01-01 generates (born: 1900-01-01 died: 2000-01-01 age: 100) * * @param parms dates as ISO string. * @param quiet true if want output suppressed. * @param verbose @return expanded macro text */ public String expandMacro( final String[] parms, final boolean quiet, final boolean verbose ) { if ( !quiet ) { out.print( "B" ); } int parmsLength = parms.length; if ( !( 1 <= parmsLength && parmsLength <= 3 ) ) { throw new IllegalArgumentException( USAGE ); } try { if ( parms[ 0 ].equals( "unknown" ) ) { return ""; // empty expansion. } boolean checked; if ( parms[ parmsLength - 1 ].equals( "checked" ) ) { checked = true; parmsLength--; } else { checked = false; } final boolean isBirthYearOnlyNoDay = parms[ 0 ].indexOf( '-' ) < 0; BigDate birthDate; final boolean isBirthdayToday; if ( isBirthYearOnlyNoDay ) { // yyyybc birthDate = new BigDate( BigDate.parseYYYY( parms[ 0 ] ), 7, 1 ); // pick half way through year to maximize odds of being correct isBirthdayToday = false; } else { // yyyy-mm-dd birthDate = new BigDate( parms[ 0 ] ); // people with 02-29 birthdays are counted on the 28th in non leap years. isBirthdayToday = BigDate.isAnniversary( birthDate, Global.TODAY ); } if ( parmsLength == 1 ) { // no death date ensureNotFuture( birthDate ); return expandStillAlive( birthDate, isBirthYearOnlyNoDay, isBirthdayToday, checked ); } else { // both birth and death date. final boolean isDeathYearOnlyNoDay = !parms[ 1 ].equals( "unknown" ) && parms[ 1 ].indexOf( '-' ) < 0; BigDate deathDate; final boolean isDeathday; if ( isDeathYearOnlyNoDay ) { // yyyybc deathDate = new BigDate( BigDate.parseYYYY( parms[ 1 ] ), 7, 1 ); // pick half way through year to maximize odds of being correct isDeathday = false; } else { if ( parms[ 1 ].equals( "unknown" ) ) { // person is dead, but we don't know when ensureNotFuture( birthDate ); return expandUnknownDead( birthDate, isBirthYearOnlyNoDay, isBirthdayToday ); } deathDate = new BigDate( parms[ 1 ] ); isDeathday = BigDate.isAnniversary( deathDate, Global.TODAY ); } ensureNotFuture( birthDate ); ensureNotFuture( deathDate ); return expandKnownDead( birthDate, isBirthYearOnlyNoDay, isBirthdayToday, isDeathday, deathDate, isDeathYearOnlyNoDay, checked ); } } catch ( StringIndexOutOfBoundsException e ) { throw new IllegalArgumentException( USAGE ); } catch ( NumberFormatException e ) { throw new IllegalArgumentException( USAGE ); } } }