/* This file is subject to the terms and conditions of the GNU General Public License. See the file COPYING in the main directory of this archive for more details. This file is heavily influenced by test/Test.java by Daniel resare of Metamatrix.se. libby.miller@bristol.ac.uk 2003-02-05 */ import se.metamatrix.mimedir.*; import java.io.FileReader; import java.util.*; import java.text.*; import java.math.BigInteger; import java.security.MessageDigest; /** * enhanced test class to export RDF * see http://www.w3.org/2002/12/cal/ for more details */ class ical2rdf implements ContentHandler, ErrorHandler { static int level=0; static String icalns="http://www.w3.org/2002/12/cal/ical#"; //static String xns="http://www.w3.org/2002/12/cal/prod_apple#"; //for proper xns processing static StringBuffer result=new StringBuffer(); String xns=null; static boolean vcalUnclosed=false; public static void main(String args[]) throws Exception { FileReader fr=null; if(args!=null) { fr = new FileReader(args[0]); //fixme - need to remove this stuff@@ /* if(args.length>1 && (!args[1].equals(""))) { xns=args[1]; }else{ xns="http://www.w3.org/2002/12/cal/prod_apple#"; } */ } level=0; ical2rdf t = new ical2rdf(); Parser p = new Parser(t, t); // construct a value vector and feed to the parser Value v[] = new Value[2]; v[0] = new Value("x-text-test", "text"); v[1] = new Value("x-boolean-test", "boolean"); //trying to bypass resare's datetime handling,m as tzs depend on tz of //computer running on. // v[2] = new Value("dtstart", "datetime"); // v[3] = new Value("dtend", "datetime"); // v[4] = new Value("dtstamp", "datetime"); // v[5] = new Value("rrule", "rrule"); p.setValues(v); level++; p.parse(fr); level--; System.out.print(t.getXmlnsString()); System.out.println(result.toString()); System.out.print(t.getRDFClose()); } /** * A simple error handling routine that prints every recieved * error to stdout */ public void error(int level, int code, String details) { /* System.out.println("level: " + level + " detail: " + details); System.out.println("level: " + level); */ } /** * A simple content handler that prints every correctly parsed * line along with its parameters to stdout. hacked by libby to do RDF. */ public void contentLine(String name, Parameter param[], Object value, String group) { name=fixXProperties(name); name=processHyphens(name); //process the strings //fix begining and end tags, i.e. vcalendar, vevent, valarm, vtimezone if( name.equals("begin") ) { level++; processBeginTag(name, value, param); } else if ( name.equals("end") ) { processEndTag(name, value); level--; } else{ //System.out.println("NAME "+name); //System.out.println("VALUE "+value); level++; if(name.equals("dtend") || name.equals("dtstart") || name.equals("dtstamp") || name.equals("lastModified")) { processDateTimeTag(name, value, param); } else if(name.equals("attendee") || name.equals("organizer")) { processPeopleTag(name, value, param); } else if(name.equals("trigger")) { processTriggerTag(name, value, param); } else if(name.equals("attach")) { //should this have a NS? processResourceTag(name, value,""); } else if (name.equals("action") || name.equals("transp") || name.equals("class")) { processResourceTag(name,value.toString().toLowerCase(),icalns); } //rrule! else if (name.equals("rrule")) { processRruleTag(name, value, param); } else if (name.equals("prodid")) { processProdidTag(name, value); } else { processSimpleTag(name, value); } level--; } } /* preliminary processing of the strings */ /** escape commas etc. what else? &? better way? \n */ public String escape(String name) { String s=""; while(name.indexOf(",")!=-1) { int i=name.indexOf(","); s=s+name.substring(0,i)+"\\"+","; name=name.substring(i+1); } s=s+name; name=s; s=""; while(name.indexOf("\n")!=-1) { int i=name.indexOf("\n"); s=s+name.substring(0,i)+"\\n"; name=name.substring(i+1); } s=s+name; name=s; return name; } /** fix ns x properties (problem: could have more than one source of x properties? currently apple) */ public String fixXProperties(String name) { if(name.startsWith("x-")){ name="x:"+name.substring(2); } return name; } /** remove hyphens and convert to camelcase */ public String processHyphens(String name) { while(name.indexOf("-")!=-1) { int i=name.indexOf("-"); String xx= name.substring(i+1,i+2); String s=name.substring(0,i)+xx.toUpperCase()+name.substring(i+2); name=s; } return name; } /** note that vcalendar tag is closed at first vevent because seen as a syntactic not container format. You are allowed more than one vcalendar in a document and more than one vevent in a vcalendar according to RFC 2445. See also http://lists.w3.org/Archives/Public/www-rdf-calendar/2003Jan/0009.html 2003-02-14 updated as per http://ilrt.org/discovery/chatlogs/rdfig/2003-02-12.html#T17-33-35 now a container not sure exactly where component property should go - valarm? - for now valarm is not a component */ public void processBeginTag(String name, Object value, Parameter param[]) { String stringValue=value.toString(); String newVal=stringValue.substring(0,1)+stringValue.substring(1).toLowerCase(); //not sure about this: see http://lists.w3.org/Archives/Public/www-rdf-calendar/2003Jan/0009.html if(newVal.equals("Vcalendar")) { print("<" + newVal + ">"); } else if (newVal.equals("Valarm")) { print("<" + newVal.toLowerCase() + " rdf:parseType='Resource'>"); } else if(newVal.equals("Vevent") || newVal.equals("Vtimezone")) { print(""); print("<" + newVal + ">"); } else if(newVal.equals("Standard") || newVal.equals("Daylight") ) { // print(""); print("<" + newVal.toLowerCase() + " rdf:parseType='Resource'>"); } else { //component? print("<" + newVal + ">"); } } /** not sure exactly where component property should go - valarm? for now valarm is not a component */ public void processEndTag(String name, Object value) { String stringValue=value.toString(); String newVal=stringValue.substring(0,1)+stringValue.substring(1).toLowerCase(); if(newVal.equals("Standard")||newVal.equals("Daylight")) { print(""); // print("");//?? } else if (newVal.equals("Valarm")){ print(""); } else if (!newVal.equals("Vcalendar")) { print(""); print(""); } else{ print(""); } } /* maybe should be for dtstamp too - check DanC? Note: the hack for getting rid of the DATE param where there's no timezone, and for sorting out 00:00:00==a date not a datetime */ public void processDateTimeTag(String name, Object value, Parameter[] param){ print("<" + name + " rdf:parseType='Resource'>"); level++; String dateString=""; if(name.equals("dtstamp") || name.equals("lastModified")){ dateString=processDateTimeZ(value); }else{ dateString=processDateTimeNoTZ(value); } if(dateString.indexOf("T00:00:00")!=-1 || dateString.length()<11) { dateString=dateString.substring(0,10); print(""+dateString+""); } else { print(""+processDateTimeNoTZ(value)+""); } for(int i = 0; i < param.length; i++) { if(!param[i].getValue().equals("DATE")) { print("<" + param[i].getName() + ">"+param[i].getValue()+""); } } level--; print(""); } public void processPeopleTag(String name, Object value, Parameter[] param){ print("<" + name + " rdf:parseType='Resource'>"); level++; //l.c. all of value here or just MAIKLTO: part? print(""); for(int i = 0; i < param.length; i++) { print("<" + param[i].getName() + ">"+param[i].getValue()+""); } level--; print(""); } /** for trigger see also http://lists.w3.org/Archives/Public/www-rdf-calendar/2003Jan/0022.html */ public void processTriggerTag(String name, Object value, Parameter param[]){ level++; print("<" + name + " rdf:parseType='Resource'>"); level++; print("" + value +""); for(int i = 0; i < param.length; i++) { if(!param[i].getValue().equals("DURATION")) { print("<" + param[i].getName() + ">"+param[i].getValue()+""); } } level--; print(""); } /** recur rule rrule tag processing */ public void processRruleTag(String name, Object value,Parameter param[]) { print("<"+name+" rdf:parseType='Resource'>"); rrule(value.toString(),param); print(""); } /** subprocesing for rrule */ public void rrule(String name,Parameter param[]) { Hashtable params=new Hashtable(); for(int i = 0; i < param.length; i++) { String nn=param[i].getName(); String val=param[i].getValue(); if(!params.containsKey(nn)){ params.put(nn, val); }else{ String vv=(String)params.get(nn); vv=vv+","+val; params.put(nn,vv); } // print("<" + param[i].getName() // +">"+param[i].getValue()+""); } /* as per http://ilrt.org/discovery/chatlogs/rdfig/2003-02-12.html#T18-17-33 18:17:33 PROPOSED(2q1): to represent RRULE as ical2rdf.pl,v 1.6 2003/01/22 21:17:12 does, using rrule, freq, byday, and also with interval always specified. If ics interval is omitted, translation must add interval="1" */ boolean interval=false; Enumeration e=params.keys(); while(e.hasMoreElements()){ String key=(String)e.nextElement(); if(key.equals("interval")){ interval=true; } printNoEscComma("<"+key+">", true); printNoEscComma((String)params.get(key), false); printNoEscComma("\n", false); } if(!interval){ printNoEscComma("1\n", true); } } /** resource tags */ public void processResourceTag(String name, Object value, String ns) { print("<"+name+" rdf:resource='"+ns+value+"'/>"); } /** prodid. new 2003-06-24 see http://rdfig.xmlhack.com/2003/02/26/2003-02-26.html#1046279854.884486 http://ilrt.org/discovery/chatlogs/rdfig/2003-02-26.html#T17-21-04 take: prodns - http://www.w3.org/2002/12/cal/prod/ then remove first three chars of e.g. -//Apple Computer\, Inc//iCal 1.0//EN then replace all spaces with _ then replace all // with _ then add first 10 chars then add _ then add first 5 chars of the sha1. */ public void processProdidTag(String name, Object value) { String sha1=sha1Hash(value.toString()); String processed=processSpaces(value.toString().substring(3)); String prodns="http://www.w3.org/2002/12/cal/prod/"; xns=prodns+processed.substring(0,10)+"_"+sha1.substring(0,5); print("<"+name+">"+value+""); } public String processSpaces(String name) { while(name.indexOf(" ")!=-1) { int i=name.indexOf(" "); String s=name.substring(0,i)+"_"+name.substring(i+1); name=s; } while(name.indexOf("//")!=-1) { int j=name.indexOf("//"); String s=name.substring(0,j)+"_"+name.substring(j+2); name=s; } return name; } /** simple tags */ public void processDateTimeTag(String name, Object value) { print("<"+name+">"+processDateTimeZ(value)+""); } public void processSimpleTag(String name, Object value) { print("<"+name+">"+value+""); } /** start RDF part */ public String getXmlnsString(){ String xmlns=""; return xmlns; } /** end rdf part */ public String getRDFClose(){ return ""; } /** datetime processing the way we like it. Note that preprocessing (in DateTimeValueFactory assumes GMT, so we ignore that. */ public String processDateTimeNoTZ(Object d){ String dt=(String)d; //assume a string StringBuffer sb = new StringBuffer(); char [] myCharArray = dt.toCharArray(); for (int counter=0; counter<8; counter++) { if (counter == 4 || counter == 6 ){ sb.append("-"); sb.append(myCharArray[counter]); }else{ sb.append(myCharArray[counter]); } } if(dt.indexOf("T")!=-1){ //it's a datetime for (int counter=8; counter