source: FCSSimpleEndpoint/trunk/src/main/java/eu/clarin/sru/server/fcs/XMLStreamWriterHelper.java @ 5546

Last change on this file since 5546 was 5546, checked in by Oliver Schonefeld, 10 years ago
  • support new FCS specification (with some backwards compatibility for old spec)

HEADS UP: not yet ready for release; needs more testing

  • Property svn:eol-style set to native
File size: 18.2 KB
Line 
1package eu.clarin.sru.server.fcs;
2
3import javax.xml.stream.XMLStreamException;
4import javax.xml.stream.XMLStreamWriter;
5
6
7/**
8 * This class provides several helper methods for writing records in the
9 * CLARIN-FCS record schema. These methods <em>do not</em> cover the full
10 * spectrum of all variations of records that are permitted by the CLARIN-FCS
11 * specification.
12 *
13 * @see <a
14 *      href="https://trac.clarin.eu/wiki/FCS/Specification">
15 *      CLARIN FCS specification, section "Operation searchRetrieve"</a>
16 */
17public final class XMLStreamWriterHelper {
18    private static final String FCS_NS          = "http://clarin.eu/fcs/1.0";
19    private static final String FCS_PREFIX      = "fcs";
20    private static final String FCS_KWIC_NS     =
21            "http://clarin.eu/fcs/1.0/kwic";
22    private static final String FCS_KWIC_PREFIX = "kwic";
23    private static final String FCS_KWIC_MIMETYPE =
24            "application/x-clarin-fcs-kwic+xml";
25    private static final String FCS_HITS_NS =
26            "http://clarin.eu/fcs/dataview/hits";
27    private static final String FCS_HITS_PREFIX = "hits";
28    private static final String FCS_HITS_MIMETYPE =
29            "application/x-clarin-fcs-hits+xml";
30
31
32    /**
33     * Write the start of a resource (i.e. the <code>&lt;Resource&gt;</code>
34     * element). Calls to this method need to be balanced with calls to the
35     * {@link #writeEndResource(XMLStreamWriter)} method.
36     *
37     *
38     * @param writer
39     *            the {@link XMLStreamWriter} to be used
40     * @param pid
41     *            the persistent identifier of this resource or
42     *            <code>null</code>, if not applicable
43     * @param ref
44     *            the reference of this resource or <code>null</code>, if not
45     *            applicable
46     * @throws XMLStreamException
47     *             if an error occurred
48     */
49    public static void writeStartResource(XMLStreamWriter writer, String pid,
50            String ref) throws XMLStreamException {
51        if (writer == null) {
52            throw new NullPointerException("writer == null");
53        }
54
55        writer.setPrefix(FCS_PREFIX, FCS_NS);
56        writer.writeStartElement(FCS_NS, "Resource");
57        writer.writeNamespace(FCS_PREFIX, FCS_NS);
58        if ((pid != null) && !pid.isEmpty()) {
59            writer.writeAttribute("pid", pid);
60        }
61        if ((ref != null) && !ref.isEmpty()) {
62            writer.writeAttribute("ref", ref);
63        }
64    }
65
66
67    /**
68     * Write the end of a resource (i.e. the <code>&lt;/Resource&gt;</code>
69     * element). Calls to this method need to be balanced with calls to the
70     * {@link #writeStartResource(XMLStreamWriter, String, String)} method.
71     *
72     * @param writer
73     *            the {@link XMLStreamWriter} to be used
74     * @throws XMLStreamException
75     *             if an error occurred
76     */
77    public static void writeEndResource(XMLStreamWriter writer)
78            throws XMLStreamException {
79        if (writer == null) {
80            throw new NullPointerException("writer == null");
81        }
82
83        writer.writeEndElement(); // "Resource" element
84    }
85
86
87    /**
88     * Write the start of a resource fragment (i.e. the
89     * <code>&lt;ResourceFragment&gt;</code> element). Calls to this method need
90     * to be balanced with calls to the
91     * {@link #writeEndResourceFragment(XMLStreamWriter)} method.
92     *
93     *
94     * @param writer
95     *            the {@link XMLStreamWriter} to be used
96     * @param pid
97     *            the persistent identifier of this resource or
98     *            <code>null</code>, if not applicable
99     * @param ref
100     *            the reference of this resource or <code>null</code>, if not
101     *            applicable
102     * @throws XMLStreamException
103     *             if an error occurred
104     */
105    public static void writeStartResourceFragment(XMLStreamWriter writer,
106            String pid, String ref) throws XMLStreamException {
107        if (writer == null) {
108            throw new NullPointerException("writer == null");
109        }
110
111        writer.writeStartElement(FCS_NS, "ResourceFragment");
112        if ((pid != null) && !pid.isEmpty()) {
113            writer.writeAttribute("pid", pid);
114        }
115        if ((ref != null) && !ref.isEmpty()) {
116            writer.writeAttribute("ref", ref);
117        }
118    }
119
120
121    /**
122     * Write the end of a resource fragment (i.e. the
123     * <code>&lt;/ResourceFragment&gt;</code> element). Calls to this method
124     * need to be balanced with calls to the
125     * {@link #writeStartResourceFragment(XMLStreamWriter, String, String)}
126     * method.
127     *
128     * @param writer
129     *            the {@link XMLStreamWriter} to be used
130     * @throws XMLStreamException
131     *             if an error occurred
132     */
133    public static void writeEndResourceFragment(XMLStreamWriter writer)
134            throws XMLStreamException {
135        if (writer == null) {
136            throw new NullPointerException("writer == null");
137        }
138
139        writer.writeEndElement(); // "ResourceFragment" element
140    }
141
142
143    /**
144     * Write the start of a data view (i.e. the
145     * <code>&lt;DataView&gt;</code> element). Calls to this method need
146     * to be balanced with calls to the
147     * {@link #writeEndResource(XMLStreamWriter)} method.
148     *
149     *
150     * @param writer
151     *            the {@link XMLStreamWriter} to be used
152     * @param mimetype
153     *            the MIME type of this data view
154     *            applicable
155     * @throws XMLStreamException
156     *             if an error occurred
157     */
158    public static void writeStartDataView(XMLStreamWriter writer,
159            String mimetype) throws XMLStreamException {
160        if (writer == null) {
161            throw new NullPointerException("writer == null");
162        }
163        if (mimetype == null) {
164            throw new NullPointerException("mimetype == null");
165        }
166        if (mimetype.isEmpty()) {
167            throw new IllegalArgumentException("mimetype is empty");
168        }
169
170        writer.writeStartElement(FCS_NS, "DataView");
171        writer.writeAttribute("type", mimetype);
172    }
173
174
175    /**
176     * Write the end of a data view (i.e. the <code>&lt;/DataView&gt;</code>
177     * element). Calls to this method need to be balanced with calls to the
178     * {@link #writeStartDataView(XMLStreamWriter, String)} method.
179     *
180     * @param writer
181     *            the {@link XMLStreamWriter} to be used
182     * @throws XMLStreamException
183     *             if an error occurred
184     */
185    public static void writeEndDataView(XMLStreamWriter writer)
186            throws XMLStreamException {
187        if (writer == null) {
188            throw new NullPointerException("writer == null");
189        }
190
191        writer.writeEndElement(); // "DataView" element
192    }
193
194
195    /**
196     * Convince method to write a KWIC data view. It automatically performs the
197     * calls to {@link #writeStartDataView(XMLStreamWriter, String)} and
198     * {@link #writeEndDataView(XMLStreamWriter)}.
199     *
200     * @param writer
201     *            the {@link XMLStreamWriter} to be used
202     * @param left
203     *            the left context of the KWIC or <code>null</code> if not
204     *            applicable
205     * @param keyword
206     *            the keyword of the KWIC
207     * @param right
208     *            the right context of the KWIC or <code>null</code> if not
209     *            applicable
210     * @throws XMLStreamException
211     *             if an error occurred
212     * @deprecated The the HITS data view instead.
213     */
214    @Deprecated
215    public static void writeKWICDataView(XMLStreamWriter writer, String left,
216            String keyword, String right) throws XMLStreamException {
217        if (writer == null) {
218            throw new NullPointerException("writer == null");
219        }
220        if (keyword == null) {
221            throw new NullPointerException("keyword == null");
222        }
223
224        writeStartDataView(writer, FCS_KWIC_MIMETYPE);
225
226        // actual "kwic" data view
227        writer.setPrefix(FCS_KWIC_PREFIX, FCS_KWIC_NS);
228        writer.writeStartElement(FCS_KWIC_NS, "kwic");
229        writer.writeNamespace(FCS_KWIC_PREFIX, FCS_KWIC_NS);
230
231        writer.writeStartElement(FCS_KWIC_NS, "c");
232        writer.writeAttribute("type", "left");
233        if (left != null) {
234            writer.writeCharacters(left);
235        }
236        writer.writeEndElement(); // "c" element
237
238        writer.writeStartElement(FCS_KWIC_NS, "kw");
239        writer.writeCharacters(keyword);
240        writer.writeEndElement(); // "kw" element
241
242        writer.writeStartElement(FCS_KWIC_NS, "c");
243        writer.writeAttribute("type", "right");
244        if (right != null) {
245            writer.writeCharacters(right);
246        }
247        writer.writeEndElement(); // "c" element
248
249        writer.writeEndElement(); // "kwic" element
250
251        writeEndDataView(writer);
252    }
253
254
255    /**
256     * Convince method for writing a record with a KWIC data view. The following
257     * code (arguments omitted) would accomplish the same result:
258     *
259     * <pre>
260     * ...
261     * writeStartResource(...);
262     * writeKWICDataView(...);
263     * writeEndResource(...);
264     * ...
265     * </pre>
266     *
267     * @param writer
268     *            the {@link XMLStreamWriter} to be used
269     * @param pid
270     *            the persistent identifier of this resource or
271     *            <code>null</code>, if not applicable
272     * @param ref
273     *            the reference of this resource or <code>null</code>, if not
274     *            applicable
275     * @param left
276     *            the left context of the KWIC or <code>null</code> if not
277     *            applicable
278     * @param keyword
279     *            the keyword of the KWIC
280     * @param right
281     *            the right context of the KWIC or <code>null</code> if not
282     *            applicable
283     * @throws XMLStreamException
284     *             if an error occurred
285     * @deprecated The the HITS data view instead.
286     */
287    @Deprecated
288    public static void writeResourceWithKWICDataView(XMLStreamWriter writer,
289            String pid, String ref, String left, String keyword, String right)
290            throws XMLStreamException {
291        if (writer == null) {
292            throw new NullPointerException("writer == null");
293        }
294
295        writeStartResource(writer, pid, ref);
296        writeKWICDataView(writer, left, keyword, right);
297        writeEndResource(writer);
298    }
299
300
301
302    /**
303     * Convince method to write a simple HITS data view. It automatically
304     * performs the calls to
305     * {@link #writeStartDataView(XMLStreamWriter, String)} and
306     * {@link #writeEndDataView(XMLStreamWriter)}.
307     *
308     * @param writer
309     *            the {@link XMLStreamWriter} to be used
310     * @param left
311     *            the left context of the hit or <code>null</code> if not
312     *            applicable
313     * @param hit
314     *            the actual hit, that will be highlighted
315     * @param right
316     *            the right context of the hit or <code>null</code> if not
317     *            applicable
318     * @throws XMLStreamException
319     *             if an error occurred
320     */
321    public static void writeHitsDataView(XMLStreamWriter writer, String left,
322            String hit, String right) throws XMLStreamException {
323        if (writer == null) {
324            throw new NullPointerException("writer == null");
325        }
326        if (hit == null) {
327            throw new NullPointerException("hit == null");
328        }
329
330        writeStartDataView(writer, FCS_HITS_MIMETYPE);
331
332        // actual "hits" data view
333        writer.setPrefix(FCS_HITS_PREFIX, FCS_HITS_NS);
334        writer.writeStartElement(FCS_HITS_NS, "Result");
335        writer.writeNamespace(FCS_HITS_PREFIX, FCS_HITS_NS);
336
337        if ((left != null) && !left.isEmpty()) {
338            writer.writeCharacters(left);
339        }
340
341        writer.writeStartElement(FCS_HITS_NS, "Hit");
342        writer.writeCharacters(hit);
343        writer.writeEndElement(); // "Hit" element
344
345        if ((right != null) && !right.isEmpty()) {
346            writer.writeCharacters(right);
347        }
348
349        writer.writeEndElement(); // "Result" element
350
351        writeEndDataView(writer);
352    }
353
354
355    /**
356     * Convince method for writing a record with a KWIC data view. The following
357     * code (arguments omitted) would accomplish the same result:
358     *
359     * <pre>
360     * ...
361     * writeStartResource(...);
362     * writeHitsDataView(...);
363     * writeEndResource(...);
364     * ...
365     * </pre>
366     *
367     * @param writer
368     *            the {@link XMLStreamWriter} to be used
369     * @param pid
370     *            the persistent identifier of this resource or
371     *            <code>null</code>, if not applicable
372     * @param ref
373     *            the reference of this resource or <code>null</code>, if not
374     *            applicable
375     * @param left
376     *            the left context of the hit or <code>null</code> if not
377     *            applicable
378     * @param hit
379     *            the actual hit, that will be highlighted
380     * @param right
381     *            the right context of the hit or <code>null</code> if not
382     *            applicable
383     * @throws XMLStreamException
384     *             if an error occurred
385     */
386    public static void writeResourceWithHitsDataView(XMLStreamWriter writer,
387            String pid, String ref, String left, String hit, String right)
388            throws XMLStreamException {
389        if (writer == null) {
390            throw new NullPointerException("writer == null");
391        }
392
393        writeStartResource(writer, pid, ref);
394        writeHitsDataView(writer, left, hit, right);
395        writeEndResource(writer);
396    }
397
398
399    /**
400     * Convince method to write a simple HITS data view. It automatically
401     * performs the calls to
402     * {@link #writeStartDataView(XMLStreamWriter, String)} and
403     * {@link #writeEndDataView(XMLStreamWriter)}.
404     *
405     * @param writer
406     *            the {@link XMLStreamWriter} to be used
407     * @param text
408     *            the text content of the hit
409     * @param hits
410     *            an even-element array containing tuples for the hit markers in
411     *            the text content
412     * @param secondIsLength
413     *            if <code>true</code> the second element of each tuple in this
414     *            <code>hits</code> array is interpreted as an length; if
415     *            <code>false</code> it is interpreted as an end-offset
416     * @throws XMLStreamException
417     *             if an error occurred
418     */
419    public static void writeHitsDataView(XMLStreamWriter writer, String text,
420            int[] hits, boolean secondIsLength) throws XMLStreamException {
421        if (writer == null) {
422            throw new NullPointerException("writer == null");
423        }
424        if (text == null) {
425            throw new NullPointerException("text == null");
426        }
427        if (hits == null) {
428            throw new NullPointerException("text == null");
429        }
430        if ((hits.length == 0) || ((hits.length % 2) != 0)) {
431            throw new NullPointerException("length of hits array must " +
432                    "contain an even number of elements");
433        }
434
435        writeStartDataView(writer, FCS_HITS_MIMETYPE);
436
437        // actual "hits" data view
438        writer.setPrefix(FCS_HITS_PREFIX, FCS_HITS_NS);
439        writer.writeStartElement(FCS_HITS_NS, "Result");
440        writer.writeNamespace(FCS_HITS_PREFIX, FCS_HITS_NS);
441
442        int pos = 0;
443        for (int i = 0; i < hits.length; i += 2) {
444            int start  = hits[i];
445            int end    = hits[i + 1];
446
447            if ((start < 0) && (start > text.length())) {
448                throw new IllegalArgumentException("start index out of " +
449                        "bounds: start=" + start);
450            }
451            if (secondIsLength) {
452                if (end < 1) {
453                    throw new IllegalArgumentException(
454                            "length must be larger than 0: length = " + end);
455                }
456                end += start;
457            }
458            if (start >= end) {
459                throw new IllegalArgumentException("end offset must be " +
460                        "larger then start offset: start=" + start +
461                        ", end="+ end);
462            }
463
464            if (start > pos) {
465                String s = text.substring(pos, start);
466                writer.writeCharacters(s);
467            }
468            writer.writeStartElement(FCS_HITS_NS, "Hit");
469            writer.writeCharacters(text.substring(start, end));
470            writer.writeEndElement(); // "Hits" element
471            pos = end;
472        }
473        if (pos < text.length() - 1) {
474            writer.writeCharacters(text.substring(pos));
475        }
476
477        writer.writeEndElement(); // "Result" element
478
479        writeEndDataView(writer);
480    }
481
482
483    /**
484     * Convince method to write a simple HITS data view. It automatically
485     * performs the calls to
486     * {@link #writeStartDataView(XMLStreamWriter, String)} and
487     * {@link #writeEndDataView(XMLStreamWriter)}.
488     *
489     * <pre>
490     * ...
491     * writeStartResource(...);
492     * writeHitsDataView(...);
493     * writeEndResource(...);
494     * ...
495     * </pre>
496     *
497     * @param writer
498     *            the {@link XMLStreamWriter} to be used
499     * @param pid
500     *            the persistent identifier of this resource or
501     *            <code>null</code>, if not applicable
502     * @param ref
503     *            the reference of this resource or <code>null</code>, if not
504     *            applicable
505     * @param text
506     *            the text content of the hit
507     * @param hits
508     *            an even-element array containing tuples for the hit markers in
509     *            the text content
510     * @param secondIsLength
511     *            if <code>true</code> the second element of each tuple in this
512     *            <code>hits</code> array is interpreted as an length; if
513     *            <code>false</code> it is interpreted as an end-offset
514     * @throws XMLStreamException
515     *             if an error occurred
516     */
517    public static void writeResourceWithHitsDataView(XMLStreamWriter writer,
518            String pid, String ref, String text,
519            int[] hits, boolean secondIsLength)
520            throws XMLStreamException {
521        if (writer == null) {
522            throw new NullPointerException("writer == null");
523        }
524
525        writeStartResource(writer, pid, ref);
526        writeHitsDataView(writer, text, hits, secondIsLength);
527        writeEndResource(writer);
528    }
529
530} // class XMLStreamWriterHelper
Note: See TracBrowser for help on using the repository browser.