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

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