source: SRUServer/trunk/src/main/java/eu/clarin/sru/server/SRUServerConfig.java @ 1890

Last change on this file since 1890 was 1890, checked in by oschonef, 12 years ago
  • fix parsing of /indexInfo/index/map settings
  • sanity check for existing set
  • Property svn:eol-style set to native
File size: 32.5 KB
Line 
1/**
2 * This software is copyright (c) 2011 by
3 *  - 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 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;
18
19import java.io.IOException;
20import java.net.URL;
21import java.util.ArrayList;
22import java.util.Collections;
23import java.util.Iterator;
24import java.util.List;
25import java.util.Map;
26import java.util.StringTokenizer;
27
28import javax.xml.XMLConstants;
29import javax.xml.namespace.NamespaceContext;
30import javax.xml.parsers.DocumentBuilder;
31import javax.xml.parsers.DocumentBuilderFactory;
32import javax.xml.parsers.ParserConfigurationException;
33import javax.xml.transform.Source;
34import javax.xml.transform.dom.DOMSource;
35import javax.xml.validation.Schema;
36import javax.xml.validation.SchemaFactory;
37import javax.xml.validation.Validator;
38import javax.xml.xpath.XPath;
39import javax.xml.xpath.XPathConstants;
40import javax.xml.xpath.XPathException;
41import javax.xml.xpath.XPathExpression;
42import javax.xml.xpath.XPathExpressionException;
43import javax.xml.xpath.XPathFactory;
44
45import org.w3c.dom.Attr;
46import org.w3c.dom.Document;
47import org.w3c.dom.Element;
48import org.w3c.dom.NodeList;
49import org.xml.sax.InputSource;
50import org.xml.sax.SAXException;
51import org.xml.sax.SAXParseException;
52import org.xml.sax.helpers.DefaultHandler;
53
54
55/**
56 * SRU server configuration.
57 *
58 * <p>
59 * Example:
60 * </p>
61 * <pre>
62 * URL url = MySRUServlet.class.getClassLoader()
63 *               .getResource("META-INF/sru-server-config.xml");
64 * if (url == null) {
65 *     throw new ServletException(&quot;not found, url == null&quot;);
66 * }
67 *
68 * // other runtime configuration, usually obtained from servlet context
69 * HashMap&lt;String, String&gt; params = new HashMap&lt;String, String&gt;();
70 * params.put(SRUServerConfig.SRU_TRANSPORT, "http");
71 * params.put(SRUServerConfig.SRU_HOST, "127.0.0.1");
72 * params.put(SRUServerConfig.SRU_PORT, "80");
73 * params.put(SRUServerConfig.SRU_DATABASE, "sru-server");
74 *
75 * SRUServerConfig config = SRUServerConfig.parse(params, url);
76 * </pre>
77 *
78 * <p>
79 * The XML configuration file must validate against the "sru-server-config.xsd"
80 * W3C schema bundled with the package and need to have the
81 * <code>http://www.clarin.eu/sru-server/1.0/</code> XML namespace.
82 * </p>
83 */
84public final class SRUServerConfig {
85    public static final String SRU_TRANSPORT     = "sru.transport";
86    public static final String SRU_HOST          = "sru.host";
87    public static final String SRU_PORT          = "sru.port";
88    public static final String SRU_DATABASE      = "sru.database";
89    public static final String SRU_ECHO_REQUESTS = "sru.echoRequests";
90    private static final String CONFIG_FILE_NAMESPACE_URI =
91            "http://www.clarin.eu/sru-server/1.0/";
92    private static final String CONFIG_FILE_SCHEMA_URL =
93            "META-INF/sru-server-config.xsd";
94
95    public static final class LocalizedString {
96        private final boolean primary;
97        private final String lang;
98        private final String value;
99
100        private LocalizedString(String value, String lang, boolean primary) {
101            this.value   = value;
102            this.lang    = lang;
103            this.primary = primary;
104        }
105
106        private LocalizedString(String value, String lang) {
107            this(value, lang, false);
108        }
109
110        public boolean isPrimary() {
111            return primary;
112        }
113
114        public String getLang() {
115            return lang;
116        }
117
118        public String getValue() {
119            return value;
120        }
121    } // class LocalizedString
122
123    public static final class DatabaseInfo {
124        private final List<LocalizedString> title;
125        private final List<LocalizedString> description;
126        private final List<LocalizedString> author;
127        private final List<LocalizedString> extent;
128        private final List<LocalizedString> history;
129        private final List<LocalizedString> langUsage;
130        private final List<LocalizedString> restrictions;
131        private final List<LocalizedString> subjects;
132        private final List<LocalizedString> links;
133        private final List<LocalizedString> implementation;
134
135        private DatabaseInfo(List<LocalizedString> title,
136                List<LocalizedString> description,
137                List<LocalizedString> author, List<LocalizedString> extent,
138                List<LocalizedString> history, List<LocalizedString> langUsage,
139                List<LocalizedString> restrictions,
140                List<LocalizedString> subjects, List<LocalizedString> links,
141                List<LocalizedString> implementation) {
142            if ((title != null) && !title.isEmpty()) {
143                this.title = Collections.unmodifiableList(title);
144            } else {
145                this.title = null;
146            }
147            if ((description != null) && !description.isEmpty()) {
148                this.description = Collections.unmodifiableList(description);
149            } else {
150                this.description = null;
151            }
152            if ((author != null) && !author.isEmpty()) {
153                this.author = Collections.unmodifiableList(author);
154            } else {
155                this.author = null;
156            }
157            if ((extent != null) && !extent.isEmpty()) {
158                this.extent = Collections.unmodifiableList(extent);
159            } else {
160                this.extent = null;
161            }
162            if ((history != null) && !history.isEmpty()) {
163                this.history = Collections.unmodifiableList(history);
164            } else {
165                this.history = null;
166            }
167            if ((langUsage != null) && !langUsage.isEmpty()) {
168                this.langUsage = Collections.unmodifiableList(langUsage);
169            } else {
170                this.langUsage = null;
171            }
172            if ((restrictions != null) && !restrictions.isEmpty()) {
173                this.restrictions = Collections.unmodifiableList(restrictions);
174            } else {
175                this.restrictions = null;
176            }
177            if ((subjects != null) && !subjects.isEmpty()) {
178                this.subjects = Collections.unmodifiableList(subjects);
179            } else {
180                this.subjects = null;
181            }
182            if ((links != null) && !links.isEmpty()) {
183                this.links = Collections.unmodifiableList(links);
184            } else {
185                this.links = null;
186            }
187            if ((implementation != null) && !implementation.isEmpty()) {
188                this.implementation =
189                        Collections.unmodifiableList(implementation);
190            } else {
191                this.implementation = null;
192            }
193        }
194
195        public List<LocalizedString> getTitle() {
196            return title;
197        }
198
199        public List<LocalizedString> getDescription() {
200            return description;
201        }
202
203        public List<LocalizedString> getAuthor() {
204            return author;
205        }
206
207        public List<LocalizedString> getExtend() {
208            return extent;
209        }
210
211        public List<LocalizedString> getHistory() {
212            return history;
213        }
214
215        public List<LocalizedString> getLangUsage() {
216            return langUsage;
217        }
218
219        public List<LocalizedString> getRestrictions() {
220            return restrictions;
221        }
222
223        public List<LocalizedString> getSubjects() {
224            return subjects;
225        }
226
227        public List<LocalizedString> getLinks() {
228            return links;
229        }
230
231        public List<LocalizedString> getImplementation() {
232            return implementation;
233        }
234    } // class DatabaseInfo
235
236    public static class SchemaInfo {
237        private final String identifier;
238        private final String name;
239        private final String location;
240        private final boolean sort;
241        private final boolean retrieve;
242        private final List<LocalizedString> title;
243
244        private SchemaInfo(String identifier, String name, String location,
245                boolean sort, boolean retieve, List<LocalizedString> title) {
246            this.identifier = identifier;
247            this.name       = name;
248            this.location   = location;
249            this.sort       = sort;
250            this.retrieve   = retieve;
251            if ((title != null) && !title.isEmpty()) {
252                this.title = Collections.unmodifiableList(title);
253            } else {
254                this.title = null;
255            }
256        }
257
258
259        public String getIdentifier() {
260            return identifier;
261        }
262
263
264        public String getName() {
265            return name;
266        }
267
268
269        public String getLocation() {
270            return location;
271        }
272
273
274        public boolean getSort() {
275            return sort;
276        }
277
278
279        public boolean getRetrieve() {
280            return retrieve;
281        }
282
283
284        public List<LocalizedString> getTitle() {
285            return title;
286        }
287    } // class SchemaInfo
288
289
290    public static class IndexInfo {
291        public static class Set {
292            private final String identifier;
293            private final String name;
294            private final List<LocalizedString> title;
295
296            private Set(String identifier, String name, List<LocalizedString> title) {
297                this.identifier = identifier;
298                this.name       = name;
299                if ((title != null) && !title.isEmpty()) {
300                    this.title = Collections.unmodifiableList(title);
301                } else {
302                    this.title = null;
303                }
304            }
305
306            public String getIdentifier() {
307                return identifier;
308            }
309
310            public String getName() {
311                return name;
312            }
313
314            public List<LocalizedString> getTitle() {
315                return title;
316            }
317        } // class IndexInfo.Set
318
319        public static class Index {
320            public static class Map {
321                private final boolean primary;
322                private final String set;
323                private final String name;
324
325                private Map(boolean primary, String set, String name) {
326                    this.primary = primary;
327                    this.set     = set;
328                    this.name    = name;
329                }
330
331                public boolean isPrimary() {
332                    return primary;
333                }
334
335                public String getSet() {
336                    return set;
337                }
338
339                public String getName() {
340                    return name;
341                }
342            } // class IndexInfo.Index.Map
343            private final List<LocalizedString> title;
344            private final boolean can_search;
345            private final boolean can_scan;
346            private final boolean can_sort;
347            private final List<Index.Map> maps;
348
349
350            public Index(List<LocalizedString> title, boolean can_search,
351                    boolean can_scan, boolean can_sort, List<Map> maps) {
352                if ((title != null) && !title.isEmpty()) {
353                    this.title = Collections.unmodifiableList(title);
354                } else {
355                    this.title = null;
356                }
357                this.can_search = can_search;
358                this.can_scan   = can_scan;
359                this.can_sort   = can_sort;
360                this.maps       = maps;
361            }
362
363
364            public List<LocalizedString> getTitle() {
365                return title;
366            }
367
368
369            public boolean canSearch() {
370                return can_search;
371            }
372
373
374            public boolean canScan() {
375                return can_scan;
376            }
377
378
379            public boolean canSort() {
380                return can_sort;
381            }
382
383
384            public List<Index.Map> getMaps() {
385                return maps;
386            }
387
388        } // class Index
389
390        private final List<IndexInfo.Set> sets;
391        private final List<IndexInfo.Index> indexes;
392
393        private IndexInfo(List<IndexInfo.Set> sets, List<IndexInfo.Index> indexes) {
394            if ((sets != null) && !sets.isEmpty()) {
395                this.sets = Collections.unmodifiableList(sets);
396            } else {
397                this.sets = null;
398            }
399            if ((indexes != null) && !indexes.isEmpty()) {
400                this.indexes = Collections.unmodifiableList(indexes);
401            } else {
402                this.indexes = null;
403            }
404        }
405
406
407        public List<IndexInfo.Set> getSets() {
408            return sets;
409        }
410
411
412        public List<IndexInfo.Index> getIndexes() {
413            return indexes;
414        }
415    } // IndexInfo
416
417    private final String transport;
418    private final String host;
419    private final String port;
420    private final String database;
421    private final boolean echoRequests;
422    private final String baseUrl;
423    private final DatabaseInfo databaseInfo;
424    private final IndexInfo indexInfo;
425    private final List<SchemaInfo> schemaInfo;
426
427
428    private SRUServerConfig(String transport, String host, String port,
429            String database, boolean echoRequests, DatabaseInfo databaseinfo,
430            IndexInfo indexInfo, List<SchemaInfo> schemaInfo) {
431        this.transport    = transport;
432        this.host         = host;
433        this.port         = port;
434        this.database     = database;
435        this.databaseInfo = databaseinfo;
436        this.indexInfo    = indexInfo;
437        if ((schemaInfo != null) && !schemaInfo.isEmpty()) {
438            this.schemaInfo = Collections.unmodifiableList(schemaInfo);
439        } else {
440            this.schemaInfo = null;
441        }
442        this.echoRequests = echoRequests;
443
444        // build baseUrl
445        StringBuilder sb = new StringBuilder();
446        sb.append(host);
447        if (!"80".equals(port)) {
448            sb.append(":").append(port);
449        }
450        sb.append("/").append(database);
451        this.baseUrl = sb.toString();
452    }
453
454
455    public SRUVersion getDefaultVersion() {
456        return SRUVersion.VERSION_1_2;
457    }
458
459
460    public SRURecordPacking getDeaultRecordPacking() {
461        return SRURecordPacking.XML;
462    }
463
464
465    public boolean getEchoRequests() {
466        return echoRequests;
467    }
468
469
470    public String getTransports() {
471        return transport;
472    }
473
474
475    public String getHost() {
476        return host;
477    }
478
479
480    public String getPort() {
481        return port;
482    }
483
484
485    public String getDatabase() {
486        return database;
487    }
488
489
490    public String getBaseUrl() {
491        return baseUrl;
492    }
493
494
495    public DatabaseInfo getDatabaseInfo() {
496        return databaseInfo;
497    }
498
499
500    public IndexInfo getIndexInfo() {
501        return indexInfo;
502    }
503
504
505    public List<SchemaInfo> getSchemaInfo() {
506        return schemaInfo;
507    }
508
509
510    public String getRecordSchemaIdentifier(String recordSchemaName) {
511        if (recordSchemaName != null) {
512            if ((schemaInfo != null) && !schemaInfo.isEmpty()) {
513                for (SchemaInfo schema : schemaInfo) {
514                    if (recordSchemaName.equals(schema.getName())) {
515                        return schema.getIdentifier();
516                    }
517                }
518            }
519        }
520        return null;
521    }
522
523
524    public String getRecordSchemaName(String schemaIdentifier) {
525        if (schemaIdentifier != null) {
526           if ((schemaInfo != null) && !schemaInfo.isEmpty()) {
527               for (SchemaInfo schema : schemaInfo) {
528                   if (schemaIdentifier.equals(schema.getIdentifier())) {
529                       return schema.getName();
530                   }
531               }
532           }
533        }
534        return null;
535    }
536
537
538    /**
539     * Parse a SRU server XML configuration file and create an configuration
540     * object from it.
541     *
542     * @param params
543     *            additional settings
544     * @param configFile
545     *            an {@link URL} pointing to the XML configuration file
546     * @return a initialized <code>SRUEndpointConfig</code> instance
547     * @throws NullPointerException
548     *             if <em>params</em> or <em>configFile</em> is
549     *             <code>null</code>
550     * @throws SRUConfigException
551     *             if an error occurred
552     */
553    public static SRUServerConfig parse(Map<String, String> params,
554            URL configFile) throws SRUConfigException {
555        if (params == null) {
556            throw new NullPointerException("params == null");
557        }
558        if (configFile == null) {
559            throw new NullPointerException("in == null");
560        }
561        try {
562            URL url = SRUServerConfig.class.getClassLoader()
563                    .getResource(CONFIG_FILE_SCHEMA_URL);
564            if (url == null) {
565                throw new SRUConfigException("cannot open \"" +
566                        CONFIG_FILE_SCHEMA_URL + "\"");
567            }
568            SchemaFactory sfactory =
569                SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
570            Schema schema = sfactory.newSchema(url);
571            DocumentBuilderFactory factory =
572                DocumentBuilderFactory.newInstance();
573            factory.setNamespaceAware(true);
574            factory.setSchema(schema);
575            factory.setIgnoringElementContentWhitespace(true);
576            factory.setIgnoringComments(true);
577            factory.setValidating(false);
578
579            // parse input
580            DocumentBuilder builder = factory.newDocumentBuilder();
581            InputSource input = new InputSource(configFile.openStream());
582            input.setPublicId(CONFIG_FILE_NAMESPACE_URI);
583            input.setSystemId(CONFIG_FILE_NAMESPACE_URI);
584            Document doc = builder.parse(input);
585
586            // validate
587            Source source = new DOMSource(doc);
588            Validator validator = schema.newValidator();
589            validator.setErrorHandler(new DefaultHandler() {
590                @Override
591                public void error(SAXParseException e) throws SAXException {
592                    fatalError(e);
593                }
594
595                @Override
596                public void fatalError(SAXParseException e) throws SAXException {
597                    throw new SAXException(
598                            "error parsing endpoint configuration file", e);
599                }
600            });
601            validator.validate(source);
602
603
604            XPathFactory xfactory = XPathFactory.newInstance();
605            XPath xpath = xfactory.newXPath();
606            xpath.setNamespaceContext(new NamespaceContext() {
607                @Override
608                public Iterator<?> getPrefixes(String namespaceURI) {
609                    throw new UnsupportedOperationException();
610                }
611
612                @Override
613                public String getPrefix(String namespaceURI) {
614                    throw new UnsupportedOperationException();
615                }
616
617                @Override
618                public String getNamespaceURI(String prefix) {
619                    if (prefix == null) {
620                        throw new NullPointerException("prefix == null");
621                    }
622                    if (prefix.equals("sru")) {
623                        return CONFIG_FILE_NAMESPACE_URI;
624                    } else if (prefix.equals(XMLConstants.XML_NS_PREFIX)) {
625                        return XMLConstants.XML_NS_URI;
626                    } else {
627                        return XMLConstants.NULL_NS_URI;
628                    }
629                }
630            });
631
632            DatabaseInfo databaseInfo = buildDatabaseInfo(xpath, doc);
633
634            IndexInfo indexInfo = buildIndexInfo(xpath, doc);
635
636            List<SchemaInfo> schemaInfo = buildSchemaInfo(xpath, doc);
637
638            String transport = params.get(SRU_TRANSPORT);
639            if ((transport == null) || transport.isEmpty()) {
640                throw new SRUConfigException("parameter \"" + SRU_TRANSPORT +
641                        "\" is mandatory");
642            } else {
643                StringBuilder sb = new StringBuilder();
644                StringTokenizer st = new StringTokenizer(transport);
645                while (st.hasMoreTokens()) {
646                    String s = st.nextToken().trim().toLowerCase();
647                    if (!("http".equals(s) || "https".equals(s))) {
648                        throw new SRUConfigException(
649                                "unsupported transport \"" + s + "\"");
650                    }
651                    if (sb.length() > 0) {
652                        sb.append(" ");
653                    }
654                    sb.append(s);
655                } // while
656                transport = sb.toString();
657            }
658
659            String host = params.get(SRU_HOST);
660            if ((host == null) || host.isEmpty()) {
661                throw new SRUConfigException("parameter \"" + SRU_HOST +
662                        "\" is mandatory");
663            }
664
665            String port = params.get(SRU_PORT);
666            if ((port == null) || port.isEmpty()) {
667                throw new SRUConfigException("parameter \"" + SRU_PORT +
668                        "\" is mandatory");
669            }
670            // sanity check
671            try {
672                int num = Integer.parseInt(port);
673                if ((num < 1) && (num > 65535)) {
674                    throw new SRUConfigException("parameter \"" + SRU_PORT +
675                            "\" must be between 1 and 65535");
676                }
677            } catch (NumberFormatException e) {
678                throw new SRUConfigException("parameter \"" + SRU_PORT +
679                        "\" must be nummerical");
680            }
681
682            String database = params.get(SRU_DATABASE);
683            if ((database == null) || database.isEmpty()) {
684                throw new SRUConfigException("parameter \"" + SRU_DATABASE +
685                        "\" is mandatory");
686            }
687
688            // cleanup: remove leading slashed
689            while (database.startsWith("/")) {
690                database = database.substring(1);
691            }
692
693            String s;
694            boolean echoRequests = false;
695            if ((s = params.get(SRU_ECHO_REQUESTS)) != null) {
696                echoRequests = Boolean.valueOf(s).booleanValue();
697            }
698
699            return new SRUServerConfig(transport, host, port, database,
700                    echoRequests, databaseInfo, indexInfo,
701                    schemaInfo);
702        } catch (IOException e) {
703            throw new SRUConfigException("error reading configuration file", e);
704        } catch (XPathException e) {
705            throw new SRUConfigException("error parsing configuration file", e);
706        } catch (ParserConfigurationException e) {
707            throw new SRUConfigException("error parsing configuration file", e);
708        } catch (SAXException e) {
709            throw new SRUConfigException("error parsing configuration file", e);
710        } catch (SRUConfigException e) {
711            throw e;
712        }
713    }
714
715
716    private static DatabaseInfo buildDatabaseInfo(XPath xpath, Document doc)
717            throws SRUConfigException, XPathExpressionException {
718        List<LocalizedString> title = buildList(xpath, doc,
719                "//sru:databaseInfo/sru:title");
720        List<LocalizedString> description = buildList(xpath, doc,
721                "//sru:databaseInfo/sru:description");
722        List<LocalizedString> author = buildList(xpath, doc,
723                "//sru:databaseInfo/sru:author");
724        List<LocalizedString> extent = buildList(xpath, doc,
725                "//sru:databaseInfo/sru:extent");
726        List<LocalizedString> history = buildList(xpath, doc,
727                "//sru:databaseInfo/sru:history");
728        List<LocalizedString> langUsage = buildList(xpath, doc,
729                "//sru:databaseInfo/sru:langUsage");
730        List<LocalizedString> restrictions = buildList(xpath, doc,
731                "//sru:databaseInfo/sru:restrictions");
732        List<LocalizedString> subjects = buildList(xpath, doc,
733                "//sru:databaseInfo/sru:subjects");
734        List<LocalizedString> links = buildList(xpath, doc,
735                "//sru:databaseInfo/sru:links");
736        List<LocalizedString> implementation = buildList(xpath, doc,
737                "//sru:databaseInfo/sru:implementation");
738        return new DatabaseInfo(title, description, author, extent, history,
739                langUsage, restrictions, subjects, links, implementation);
740    }
741
742
743    private static IndexInfo buildIndexInfo(XPath xpath, Document doc)
744            throws SRUConfigException, XPathExpressionException {
745        List<IndexInfo.Set> sets      = null;
746        List<IndexInfo.Index> indexes = null;
747
748        XPathExpression expr = xpath.compile("//sru:indexInfo/sru:set");
749        NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
750        if (result.getLength() > 0) {
751            sets = new ArrayList<IndexInfo.Set>(result.getLength());
752            for (int i = 0; i < result.getLength(); i++) {
753                Element e = (Element) result.item(i);
754                String identifier = e.getAttribute("identifier");
755                String name       = e.getAttribute("name");
756                if (identifier.isEmpty()) {
757                    throw new SRUConfigException("attribute 'identifier' may "+
758                            "on element '/indexInfo/set' may not be empty");
759                }
760                if (name.isEmpty()) {
761                    throw new SRUConfigException("attribute 'name' may on " +
762                            "element '/indexInfo/set' may not be empty");
763                }
764                List<LocalizedString> title =
765                        fromNodeList(e.getElementsByTagName("title"));
766                sets.add(new IndexInfo.Set(identifier, name, title));
767            }
768        } // sets
769
770        expr = xpath.compile("//sru:indexInfo/sru:index");
771        result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
772        if (result.getLength() > 0) {
773            indexes = new ArrayList<IndexInfo.Index>(result.getLength());
774            for (int i = 0; i < result.getLength(); i++) {
775                Element e = (Element) result.item(i);
776                List<LocalizedString> title =
777                        fromNodeList(e.getElementsByTagName("title"));
778                boolean can_search = getBooleanAttr(e, "search", false);
779                boolean can_scan   = getBooleanAttr(e, "scan", false);
780                boolean can_sort   = getBooleanAttr(e, "sort", false);
781                List<IndexInfo.Index.Map> maps = null;
782                NodeList result2 = e.getElementsByTagName("map");
783                if ((result2 != null) && (result2.getLength() > 0)) {
784                    maps = new ArrayList<IndexInfo.Index.Map>(
785                            result2.getLength());
786                    boolean foundPrimary = false;
787                    for (int j = 0; j < result2.getLength(); j++) {
788                        Element e2 = (Element) result2.item(j);
789                        boolean primary = getBooleanAttr(e2, "primary", false);
790                        if (primary) {
791                            if (foundPrimary) {
792                                throw new SRUConfigException("only one map " +
793                                        "may be 'primary' in index");
794                            }
795                            foundPrimary = true;
796                        }
797                        String set  = null;
798                        String name = null;
799                        NodeList result3 = e2.getElementsByTagName("name");
800                        if ((result3 != null) && (result3.getLength() > 0)) {
801                            Element e3 = (Element) result3.item(0);
802                            set  = e3.getAttribute("set");
803                            name = e3.getTextContent();
804                            if (set.isEmpty()) {
805                                throw new SRUConfigException("attribute 'set'" +
806                                        " on element '/indexInfo/index/map/" + 
807                                        "name' may not be empty");
808                            }
809                            if ((name == null) || name.isEmpty()) {
810                                throw new SRUConfigException("element " +
811                                        "'/indexInfo/index/map/name' may not " +
812                                        "be empty");
813                            }
814                        }
815                        maps.add(new IndexInfo.Index.Map(primary, set, name));
816                    }
817                }
818                indexes.add(new IndexInfo.Index(title, can_search, can_scan,
819                        can_sort, maps));
820            } // for
821           
822            // sanity check (/index/map/name/@set exists in any set/@name)
823            if (sets != null) {
824                for (IndexInfo.Index index : indexes) {
825                    if (index.getMaps() != null) {
826                        for (IndexInfo.Index.Map maps : index.getMaps()) {
827                            if (findSetByName(sets, maps.getSet()) == null) {
828                                throw new SRUConfigException("/index/map/" +
829                                        "name refers to nonexitsing set (" +
830                                        maps.getSet() + ")");
831                            }
832                        }
833                    }
834                }
835            }
836        } // if
837        return new IndexInfo(sets, indexes);
838    }
839
840
841    private static List<SchemaInfo> buildSchemaInfo(XPath xpath, Document doc)
842            throws SRUConfigException, XPathExpressionException {
843        List<SchemaInfo> schemaInfos = null;
844        XPathExpression expr = xpath.compile("//sru:schemaInfo/sru:schema");
845        NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
846        if (result.getLength() > 0) {
847            schemaInfos = new ArrayList<SchemaInfo>(result.getLength());
848            for (int i = 0; i < result.getLength(); i++) {
849                Element e = (Element) result.item(i);
850                String identifier = e.getAttribute("identifier");
851                String name       = e.getAttribute("name");
852                String location   = e.getAttribute("location");
853                if ((location != null) && location.isEmpty()) {
854                    location = null;
855                }
856                boolean sort     = getBooleanAttr(e, "sort", false);
857                boolean retrieve = getBooleanAttr(e, "retrieve", true);
858                List<LocalizedString> title =
859                        fromNodeList(e.getElementsByTagName("title"));
860                schemaInfos.add(new SchemaInfo(identifier, name, location,
861                        sort, retrieve, title));
862            }
863
864        }
865        return schemaInfos;
866    }
867
868
869    private static IndexInfo.Set findSetByName(List<IndexInfo.Set> sets,
870            String name) {
871        for (IndexInfo.Set set : sets) {
872            if (set.getName().equals(name)) {
873                return set;
874            }
875        }
876        return null;
877    }
878
879   
880    private static List<LocalizedString> buildList(XPath xpath, Document doc,
881            String expression) throws SRUConfigException,
882            XPathExpressionException {
883        XPathExpression expr = xpath.compile(expression);
884        NodeList result = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
885        return fromNodeList(result);
886    }
887
888
889    private static List<LocalizedString> fromNodeList(NodeList nodes)
890            throws SRUConfigException {
891        List<LocalizedString> list = null;
892        if (nodes.getLength() > 0) {
893            list = new ArrayList<LocalizedString>(nodes.getLength());
894            boolean foundPrimary = false;
895            for (int i = 0; i < nodes.getLength(); i++) {
896                Element e = (Element) nodes.item(i);
897                boolean primary = getBooleanAttr(e, "primary", false);
898                if (primary) {
899                    if (foundPrimary) {
900                        throw new SRUConfigException("list may only contain "
901                                + "one element as primary");
902                    }
903                    foundPrimary = true;
904                }
905                list.add(new LocalizedString(e.getTextContent(),
906                        e.getAttributeNS(XMLConstants.XML_NS_URI, "lang"),
907                        primary));
908            }
909        }
910        return list;
911    }
912
913
914    private static boolean getBooleanAttr(Element e, String localName,
915            boolean defaultValue) {
916        boolean result = defaultValue;
917        Attr attr = e.getAttributeNode(localName);
918        if ((attr != null) && attr.getSpecified()) {
919            result = Boolean.valueOf(attr.getValue()).booleanValue();
920        }
921        return result;
922    }
923
924} // class SRUEndpointConfig
Note: See TracBrowser for help on using the repository browser.