package org.autoplot.servlet; import java.util.Arrays; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Extract a clean Java code for parsing ISO8601 strings. * @author jbf */ public class TimeRangeParser { /** * get an integer, allowing a letter at the end. * @param val * @param deft * @return */ private static int getInt( String val, int deft ) { if ( val==null ) { if ( deft!=-99 ) return deft; else throw new IllegalArgumentException("bad digit"); } int n= val.length()-1; if ( Character.isLetter( val.charAt(n) ) ) { return Integer.parseInt(val.substring(0,n)); } else { return Integer.parseInt(val); } } /** * get the double, allowing a letter at the end. * @param val * @param deft * @return */ private static double getDouble( String val, double deft ) { if ( val==null ) { if ( deft!=-99 ) return deft; else throw new IllegalArgumentException("bad digit"); } int n= val.length()-1; if ( Character.isLetter( val.charAt(n) ) ) { return Double.parseDouble(val.substring(0,n)); } else { return Double.parseDouble(val); } } private static final String simpleFloat= "\\d?\\.?\\d+"; public static final String iso8601duration= "P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?("+simpleFloat+"S)?)?"; public static final Pattern iso8601DurationPattern= Pattern.compile(iso8601duration); /** * returns a 7 element array with [year,mon,day,hour,min,sec,nanos] or [-9999]. * @param stringIn * @return [year,mon,day,hour,min,sec,nanos] */ public static int[] parseISO8601Duration( String stringIn ) { Matcher m= iso8601DurationPattern.matcher(stringIn); if ( m.matches() ) { double dsec=getDouble( m.group(7),0 ); int sec= (int)dsec; int nanosec= (int)( ( dsec - sec ) * 1e9 ); return new int[] { getInt( m.group(1), 0 ), getInt( m.group(2), 0 ), getInt( m.group(3), 0 ), getInt( m.group(5), 0 ), getInt( m.group(6), 0 ), sec, nanosec }; } else { throw new IllegalArgumentException("unable to parse: "+stringIn); } } /** * ISO8601 datum parser. This does not support 2-digit years, which * were removed in ISO 8601:2004. * * @param str * @param context * @return */ public static int parseISO8601Datum( String str, int[] result, int lsd ) { StringTokenizer st= new StringTokenizer( str, "-T:.Z", true ); Object dir= null; final Object DIR_FORWARD = "f"; final Object DIR_REVERSE = "r"; int want= 0; boolean haveDelim= false; while ( st.hasMoreTokens() ) { char delim= ' '; if ( haveDelim ) { delim= st.nextToken().charAt(0); if ( st.hasMoreElements()==false ) { // "Z" break; } } else { haveDelim= true; } String tok= st.nextToken(); if ( dir==null ) { if ( tok.length()==4 ) { // typical route int iyear= Integer.parseInt( tok ); result[0]= iyear; want= 1; dir=DIR_FORWARD; } else if ( tok.length()==6 ) { want= lsd; if ( want!=6 ) throw new IllegalArgumentException("lsd must be 6"); result[want]= Integer.parseInt( tok.substring(0,2) ); want--; result[want]= Integer.parseInt( tok.substring(2,4) ); want--; result[want]= Integer.parseInt( tok.substring(4,6) ); want--; dir=DIR_REVERSE; } else if ( tok.length()==7 ) { result[0]= Integer.parseInt( tok.substring(0,4) ); result[1]= 1; result[2]= Integer.parseInt( tok.substring(4,7) ); want= 3; dir=DIR_FORWARD; } else if ( tok.length()==8 ) { result[0]= Integer.parseInt( tok.substring(0,4) ); result[1]= Integer.parseInt( tok.substring(4,6) ); result[2]= Integer.parseInt( tok.substring(6,8) ); want= 3; dir=DIR_FORWARD; } else { dir= DIR_REVERSE; want= lsd; // we are going to have to reverse these when we're done. int i= Integer.parseInt( tok ); result[want]= i; want--; } } else if ( dir==DIR_FORWARD) { if ( want==1 && tok.length()==3 ) { // $j result[1]= 1; result[2]= Integer.parseInt( tok ); want= 3; } else if ( want==3 && tok.length()==6 ) { result[want]= Integer.parseInt( tok.substring(0,2) ); want++; result[want]= Integer.parseInt( tok.substring(2,4) ); want++; result[want]= Integer.parseInt( tok.substring(4,6) ); want++; } else if ( want==3 && tok.length()==4 ) { result[want]= Integer.parseInt( tok.substring(0,2) ); want++; result[want]= Integer.parseInt( tok.substring(2,4) ); want++; } else { int i= Integer.parseInt( tok ); if ( delim=='.' && want==6 ) { int n= 9-tok.length(); result[want]= i * ((int)Math.pow(10,n)); } else { result[want]= i; } want++; } } else if ( dir==DIR_REVERSE ) { // what about 1200 in reverse? int i= Integer.parseInt( tok ); if ( delim=='.' ) { int n= 9-tok.length(); result[want]= i * ((int)Math.pow(10,n)); } else { result[want]= i; } want--; } } if ( dir==DIR_REVERSE ) { int iu= want+1; int id= lsd; while( iu