/*
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("" + newVal.toLowerCase() + ">");
// print("");//??
}
else if (newVal.equals("Valarm")){
print("" + newVal.toLowerCase() + ">");
}
else if (!newVal.equals("Vcalendar"))
{
print("" + newVal + ">");
print("");
}
else{
print("" + newVal + ">");
}
}
/*
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()+""+param[i].getName()+">");
}
}
level--;
print("" + name + ">");
}
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()+""+param[i].getName()+">");
}
level--;
print("" + name + ">");
}
/**
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()+""+param[i].getName()+">");
}
}
level--;
print("" + name + ">");
}
/** recur rule rrule tag processing */
public void processRruleTag(String name, Object value,Parameter
param[])
{
print("<"+name+" rdf:parseType='Resource'>");
rrule(value.toString(),param);
print(""+name+">");
}
/** 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()+""+param[i].getName()+">");
}
/*
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(""+key+">\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+""+name+">");
}
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)+""+name+">");
}
public void processSimpleTag(String name, Object value)
{
print("<"+name+">"+value+""+name+">");
}
/** 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