001: package org.garret.rdf.xml;
002:
003: import org.xml.sax.*;
004: import org.xml.sax.helpers.DefaultHandler;
005: import javax.xml.parsers.*;
006: import java.util.*;
007: import java.text.*;
008: import java.io.*;
009: import org.garret.rdf.*;
010:
011: /**
012: * Parse XML requests and store/fetch data in storage
013: */
014: public class XmlServer {
015: VersionedStorage store;
016: PrintStream writer;
017:
018: public static String[] dateFormats = {
019: "EEE, d MMM yyyy kk:mm:ss z", "EEE, d MMM yyyy kk:mm:ss",
020: "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd" };
021: public static DateFormat[] dateFormatters;
022:
023: static {
024: dateFormatters = new DateFormat[dateFormats.length];
025: for (int i = 0; i < dateFormatters.length; i++) {
026: dateFormatters[i] = new SimpleDateFormat(dateFormats[i],
027: Locale.ENGLISH);
028: }
029: }
030:
031: public XmlServer(VersionedStorage store, PrintStream writer) {
032: this .store = store;
033: this .writer = writer;
034: }
035:
036: static class RdfResource {
037: RdfResource outer;
038: ArrayList props;
039: Object value;
040: String uri;
041:
042: RdfResource(RdfResource outer) {
043: this .outer = outer;
044: props = new ArrayList();
045: }
046: }
047:
048: static class RdfPattern {
049: RdfPattern outer;
050: ArrayList props;
051: Object value;
052: String uri;
053: int depth;
054: SearchKind kind;
055: Date timestamp;
056:
057: RdfPattern(RdfPattern outer) {
058: this .outer = outer;
059: props = new ArrayList();
060: kind = SearchKind.LatestVersion;
061: }
062: }
063:
064: DefaultHandler getHandler() {
065: return new RdfHandler();
066: }
067:
068: class RdfHandler extends DefaultHandler {
069: DefaultHandler handler;
070:
071: RdfHandler() {
072: handler = new RdfDocumentParser();
073: }
074:
075: public void startElement(String uri, String localName,
076: String qName, Attributes attributes)
077: throws SAXException {
078: handler.startElement(uri, localName, qName, attributes);
079: }
080:
081: public void endElement(String uri, String localName,
082: String qName) throws SAXException {
083: handler.endElement(uri, localName, qName);
084: }
085:
086: public void characters(char ch[], int start, int length)
087: throws SAXException {
088: handler.characters(ch, start, length);
089: }
090:
091: class RdfDocumentParser extends DefaultHandler {
092: public void startElement(String uri, String localName,
093: String qName, Attributes attributes)
094: throws SAXException {
095: String name = uri + localName;
096: if (XmlSymbols.Store.equals(name)) {
097: handler = new RdfStoreHandler(this );
098: } else if (XmlSymbols.Find.equals(name)) {
099: handler = new RdfFindHandler(this );
100: } else {
101: throw new SAXException("Unknown operation '" + name
102: + "'");
103: }
104: }
105: }
106:
107: class RdfStoreHandler extends DefaultHandler {
108: RdfResource curr;
109: DefaultHandler prevHandler;
110:
111: RdfStoreHandler(DefaultHandler prevHandler) {
112: curr = new RdfResource(null);
113: this .prevHandler = prevHandler;
114: store.beginTransaction();
115: }
116:
117: public void startElement(String uri, String localName,
118: String qName, Attributes attributes)
119: throws SAXException {
120: RdfResource res = new RdfResource(curr);
121: for (int i = 0, n = attributes.getLength(); i < n; i++) {
122: String name = attributes.getURI(i)
123: + attributes.getLocalName(i);
124: String value = attributes.getValue(i);
125: if (XmlSymbols.Uri.equals(name)) {
126: res.uri = value;
127: } else if (XmlSymbols.Ref.equals(name)) {
128: if ((res.value = store.getObject(value)) == null) {
129: throw new SAXException("Object with URI '"
130: + value + "' is not found");
131: }
132: } else {
133: res.props.add(createProperty(name, value));
134: }
135: }
136: if (res.uri == null) {
137: String parentURI = curr.uri == null ? XmlSymbols.NS
138: + new java.rmi.server.UID() : curr.uri;
139: res.uri = parentURI + '/' + localName;
140: }
141: curr = res;
142: }
143:
144: public void characters(char ch[], int start, int length) {
145: if (curr != null && length != 0) {
146: curr.value = convertValue(new String(ch, start,
147: length));
148: }
149: }
150:
151: public void endElement(String uri, String localName,
152: String qName) {
153: if (curr.outer == null) {
154: store.commit();
155: handler = prevHandler;
156: } else {
157: String name = uri + localName;
158: Object value;
159: if (curr.props.size() != 0) {
160: Thing thing = store.createObject(curr.uri,
161: name, (NameVal[]) curr.props
162: .toArray(new NameVal[curr.props
163: .size()]));
164: value = thing.vh;
165: } else {
166: value = curr.value;
167: }
168: curr = curr.outer;
169: curr.props.add(new NameVal(name, value));
170: }
171: }
172:
173: private NameVal createProperty(String name, String value)
174: throws SAXException {
175: if (XmlSymbols.Subtype.equals(name)) {
176: VersionHistory vh = store.getObject(value);
177: if (vh == null) {
178: throw new SAXException("Object with URI '"
179: + value + "' is not found");
180: }
181: return new NameVal(name, vh);
182: } else {
183: return new NameVal(name, convertValue(value));
184: }
185: }
186: }
187:
188: class RdfFindHandler extends DefaultHandler {
189: RdfPattern curr;
190: DefaultHandler prevHandler;
191:
192: RdfFindHandler(DefaultHandler prevHandler) {
193: this .prevHandler = prevHandler;
194: writer.println("<?xml version='1.0'?>");
195: writer
196: .println("<vr:result xmlns:vr=\"http://www.perst.org#\" xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">");
197: }
198:
199: public void startElement(String uri, String localName,
200: String qName, Attributes attributes)
201: throws SAXException {
202: RdfPattern pattern = new RdfPattern(curr);
203: for (int i = 0, n = attributes.getLength(); i < n; i++) {
204: String name = attributes.getURI(i)
205: + attributes.getLocalName(i);
206: String value = attributes.getValue(i);
207: if (XmlSymbols.Uri.equals(name)) {
208: pattern.uri = value;
209: } else if (XmlSymbols.Before.equals(name)) {
210: if ((pattern.timestamp = parseDate(value)) == null) {
211: throw new SAXException(
212: "Invalid date format " + value);
213: }
214: pattern.kind = SearchKind.LatestBefore;
215: } else if (XmlSymbols.After.equals(name)) {
216: if ((pattern.timestamp = parseDate(value)) == null) {
217: throw new SAXException(
218: "Invalid date format " + value);
219: }
220: pattern.kind = SearchKind.OldestAfter;
221: } else if (XmlSymbols.All.equals(name)) {
222: pattern.kind = Boolean.valueOf(value)
223: .booleanValue() ? SearchKind.AllVersions
224: : SearchKind.LatestVersion;
225: } else if (XmlSymbols.Depth.equals(name)) {
226: pattern.depth = Integer.parseInt(value, 10);
227: } else {
228: pattern.props.add(new NameVal(name,
229: convertPattern(value)));
230: }
231: }
232: curr = pattern;
233: }
234:
235: public void characters(char ch[], int start, int length) {
236: if (curr != null && length != 0) {
237: curr.value = convertPattern(new String(ch, start,
238: length));
239: }
240: }
241:
242: public void endElement(String uri, String localName,
243: String qName) {
244: if (curr == null) {
245: writer.println("</vr:result>");
246: handler = prevHandler;
247: } else {
248: String name = uri + localName;
249: if (curr.outer == null) {
250: String type = XmlSymbols.Thing.equals(name) ? null
251: : name;
252: Iterator iterator = store.search(type,
253: curr.uri, (NameVal[]) curr.props
254: .toArray(new NameVal[curr.props
255: .size()]), curr.kind,
256: curr.timestamp);
257: while (iterator.hasNext()) {
258: Thing thing = (Thing) iterator.next();
259: dumpObject(thing, writer, 1, curr.kind,
260: curr.timestamp, curr.depth);
261: }
262: } else {
263: Object value;
264: if (curr.props.size() != 0) {
265: value = curr.props
266: .toArray(new NameVal[curr.props
267: .size()]);
268: } else {
269: value = curr.value;
270: }
271: curr.outer.props.add(new NameVal(name, value));
272: }
273: curr = curr.outer;
274: }
275: }
276: }
277: }
278:
279: private static Object convertValue(String str) {
280: if (str.length() > 0) {
281: char ch = str.charAt(0);
282: if (ch == '+' || ch == '-' || (ch >= '0' && ch <= '9')) {
283: try {
284: return new Double(Double.parseDouble(str));
285: } catch (NumberFormatException x) {
286: }
287: Date date = parseDate(str);
288: if (date != null) {
289: return date;
290: }
291: }
292: }
293: return str;
294: }
295:
296: private static Object convertPattern(String str) {
297: int comma;
298: int len = str.length();
299: if (len > 3
300: && (str.charAt(0) == '[' || str.charAt(0) == '(')
301: && (str.charAt(len - 1) == ']' || str.charAt(len - 1) == ')')
302: && (comma = str.indexOf(',', 1)) > 0) {
303: String from = str.substring(1, comma).trim();
304: Object fromVal = convertValue(from);
305: boolean fromInclusive = str.charAt(0) == '[';
306: String till = str.substring(comma + 1, len - 1).trim();
307: Object tillVal = convertValue(till);
308: boolean tillInclusive = str.charAt(len - 1) == ']';
309: if (!fromVal.getClass().equals(tillVal.getClass())) {
310: fromVal = from;
311: tillVal = till;
312: }
313: return new Range(fromVal, fromInclusive, tillVal,
314: tillInclusive);
315: }
316: return convertValue(str);
317: }
318:
319: static Date parseDate(String str) {
320: for (int i = 0; i < dateFormatters.length; i++) {
321: ParsePosition pos = new ParsePosition(0);
322: Date date = dateFormatters[i].parse(str, pos);
323: if (date != null && pos.getIndex() == str.length()) {
324: return date;
325: }
326: }
327: return null;
328: }
329:
330: static String getQualifiedName(String uri, PrintStream writer) {
331: int col = uri.lastIndexOf(':');
332: int hash = uri.lastIndexOf('#');
333: int path = uri.lastIndexOf('/');
334: int namespaceLen = Math.max(Math.max(col, hash), path);
335:
336: writer.print('<');
337: if (namespaceLen > 0) {
338: String prefix = uri.substring(0, namespaceLen + 1);
339: String name = uri.substring(namespaceLen + 1);
340: if (prefix.equals(Symbols.RDF)) {
341: name = "rdf:" + name;
342: } else if (prefix.equals(Symbols.RDFS)) {
343: name = "rdfs:" + name;
344: } else if (prefix.equals(Symbols.NS)) {
345: name = "vr:" + name;
346: } else {
347: writer.print(name + " xmls=\"" + prefix + "\"");
348: return name;
349: }
350: uri = name;
351: }
352: writer.print(uri);
353: return uri;
354: }
355:
356: static void dumpObject(Thing thing, PrintStream writer, int indent,
357: SearchKind kind, Date timestamp, int depth) {
358: writeTab(writer, indent);
359: String typeName = getQualifiedName(thing.type.vh.uri, writer);
360: writer.println(" rdf:about=\"" + thing.vh.uri
361: + "\" vr:timestamp=\"" + thing.timestamp + "\">");
362: for (int i = 0; i < thing.props.length; i++) {
363: PropVal pv = thing.props[i];
364: Object val = pv.val;
365: if (val instanceof VersionHistory) {
366: VersionHistory ptr = (VersionHistory) val;
367: if (kind != SearchKind.AllVersions) {
368: if (depth > 0 || ptr.uri.startsWith(thing.vh.uri)) {
369: Thing t = ptr.getVersion(kind, timestamp);
370: if (t != null) {
371: dumpObject(t, writer, indent + 1, kind,
372: timestamp, depth - 1);
373: continue;
374: }
375: }
376: }
377: writeTab(writer, indent + 1);
378: getQualifiedName(pv.def.name, writer);
379: writer.println(" rdf:resource=\"" + ptr.uri + "\"/>");
380: } else {
381: writeTab(writer, indent + 1);
382: String propName = getQualifiedName(pv.def.name, writer);
383: writer.println(">" + val + "</" + propName + ">");
384: }
385: }
386: writeTab(writer, indent);
387: writer.println("</" + typeName + ">");
388: }
389:
390: static void writeTab(PrintStream writer, int ident) {
391: while (--ident >= 0)
392: writer.print('\t');
393: }
394:
395: public static void main(String[] args) throws Exception {
396: if (args.length < 2) {
397: System.err
398: .println("Usage: ImportXML database-file xml-file {xml-file}");
399: return;
400: }
401: VersionedStorage store = new VersionedStorage();
402: store.open(args[0]);
403: XmlServer server = new XmlServer(store, System.out);
404: SAXParserFactory factory = SAXParserFactory.newInstance();
405: factory.setNamespaceAware(true);
406: factory.setValidating(false);
407: for (int i = 1; i < args.length; i++) {
408: SAXParser parser = factory.newSAXParser();
409: parser.parse(new File(args[i]), server.getHandler());
410: }
411: store.close();
412: System.out.println("Press any key to exit...");
413: System.in.read();
414: }
415: }
|