/** * Copyright Intellectual Reserve, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gedcomx.date; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; /** * The duration between two simple dates * @author John Clark. */ public class GedcomxDateDuration extends GedcomxDate { private Integer years = null; private Integer months = null; private Integer days = null; private Integer hours = null; private Integer minutes = null; private Integer seconds = null; /** * Create a new duration from the formal string * @param str The formal duration string */ public GedcomxDateDuration(String str) { // Durations must start with P if(str == null || str.length() < 1 || str.charAt(0) != 'P') { throw new GedcomxDateException("Invalid Duration: Must start with P"); } String duration = str.substring(1); if(duration.length() < 1) { throw new GedcomxDateException("Invalid Duration: You must have a duration value"); } // 5.3.2 allows for NON normalized durations // We assume that if there is a space, it is non-normalized if(duration.contains(" ")) { // When we implement non normalized durations we can call parseNonNormalizedDuration(duration) throw new GedcomxDateException("Invalid Duration: Non normalized durations are not implemented yet"); } else { parseNormalizedDuration(duration); } } /** * Parse the normalized duration * @param duration the formal duration string */ private void parseNormalizedDuration(String duration) { String currentNum = ""; boolean inTime = false; HashSet<String> seen = new HashSet<String>(); List valid = Arrays.asList("Y", "Mo", "D", "T", "H", "Mi", "S"); for(char character : duration.toCharArray()) { if(Character.isDigit(character)) { currentNum += character; continue; } switch(character) { case 'Y': if(currentNum.length() < 1) { throw new GedcomxDateException("Invalid Duration: Invalid years"); } if(seen.contains("Y")) { throw new GedcomxDateException("Invalid Duration: Duplicate years"); } if(!valid.contains("Y")) { throw new GedcomxDateException("Invalid Duration: Years out of order"); } this.years = Integer.valueOf(currentNum); seen.add("Y"); valid = valid.subList(valid.indexOf("Y")+1,valid.size()); currentNum = ""; break; case 'M': if(inTime) { if(currentNum.length() < 1) { throw new GedcomxDateException("Invalid Duration: Invalid minutes"); } if(seen.contains("Mi")) { throw new GedcomxDateException("Invalid Duration: Duplicate minutes"); } if(!valid.contains("Mi")) { throw new GedcomxDateException("Invalid Duration: Minutes out of order"); } this.minutes = Integer.valueOf(currentNum); seen.add("Mi"); valid = valid.subList(valid.indexOf("Mi")+1,valid.size()); currentNum = ""; } else { if(currentNum.length() < 1) { throw new GedcomxDateException("Invalid Duration: Invalid months"); } if(seen.contains("Mo")) { throw new GedcomxDateException("Invalid Duration: Duplicate months"); } if(!valid.contains("Mo")) { throw new GedcomxDateException("Invalid Duration: Months out of order"); } this.months = Integer.valueOf(currentNum); seen.add("Mo"); valid = valid.subList(valid.indexOf("Mo")+1,valid.size()); currentNum = ""; } break; case 'D': if(currentNum.length() < 1) { throw new GedcomxDateException("Invalid Duration: Invalid days"); } if(seen.contains("D")) { throw new GedcomxDateException("Invalid Duration: Duplicate days"); } if(!valid.contains("D")) { throw new GedcomxDateException("Invalid Duration: Days out of order"); } this.days = Integer.valueOf(currentNum); seen.add("D"); valid = valid.subList(valid.indexOf("D")+1,valid.size()); currentNum = ""; break; case 'H': if(!inTime) { throw new GedcomxDateException("Invalid Duration: Missing T before hours"); } if(currentNum.length() < 1) { throw new GedcomxDateException("Invalid Duration: Invalid hours"); } if(seen.contains("H")) { throw new GedcomxDateException("Invalid Duration: Duplicate hours"); } if(!valid.contains("H")) { throw new GedcomxDateException("Invalid Duration: Hours out of order"); } this.hours = Integer.valueOf(currentNum); seen.add("H"); valid = valid.subList(valid.indexOf("H")+1,valid.size()); currentNum = ""; break; case 'S': if(!inTime) { throw new GedcomxDateException("Invalid Duration: Missing T before seconds"); } if(currentNum.length() < 1) { throw new GedcomxDateException("Invalid Duration: Invalid seconds"); } if(seen.contains("S")) { throw new GedcomxDateException("Invalid Duration: Duplicate seconds"); } // You cannot have seconds out of order because it's last this.seconds = Integer.valueOf(currentNum); seen.add("S"); valid = new ArrayList<String>(); currentNum = ""; break; case 'T': if(seen.contains("T")) { throw new GedcomxDateException("Invalid Duration: Duplicate T"); } inTime = true; seen.add("T"); valid = valid.subList(valid.indexOf("T")+1,valid.size()); break; default: throw new GedcomxDateException("Invalid Duration: Unknown letter "+character); } } // If there is any leftover we have an invalid duration if(!currentNum.equals("")) { throw new GedcomxDateException("Invalid Duration: No letter after "+currentNum); } } /** * The type of this date * @return The date type */ @Override public GedcomxDateType getType() { return GedcomxDateType.DURATION; } /** * A Duration is NEVER Approximate * @return True if the duration is approximate (It never is) */ @Override public boolean isApproximate() { return false; } /** * The formal string representation of the duration * @return The formal string */ @Override public String toFormalString() { StringBuilder duration = new StringBuilder("P"); if(years != null) { duration.append(years).append('Y'); } if(months != null) { duration.append(months).append('M'); } if(days != null) { duration.append(days).append('D'); } if(hours != null || minutes != null || seconds != null) { duration.append('T'); if (hours != null) { duration.append(hours).append('H'); } if (minutes != null) { duration.append(minutes).append('M'); } if (seconds != null) { duration.append(seconds).append('S'); } } return duration.toString(); } /** * Get the years * @return The Years */ public Integer getYears() { return years; } /** * Get the months * @return The Months */ public Integer getMonths() { return months; } /** * Get the days * @return The Days */ public Integer getDays() { return days; } /** * Get the hours * @return The Hours */ public Integer getHours() { return hours; } /** * Get the minutes * @return The Minutes */ public Integer getMinutes() { return minutes; } /** * Get the seconds * @return The Seconds */ public Integer getSeconds() { return seconds; } }