1/*
2 * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package test.astro;
24
25import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
26import static test.astro.AstroConstants.DEC_MAX;
27import static test.astro.AstroConstants.DEC_MIN;
28import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE;
29import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE;
30import static test.astro.AstroConstants.RA_MAX;
31import static test.astro.AstroConstants.RA_MIN;
32
33import java.io.File;
34import java.io.IOException;
35
36import javax.xml.parsers.ParserConfigurationException;
37import javax.xml.parsers.SAXParser;
38import javax.xml.parsers.SAXParserFactory;
39import javax.xml.transform.TransformerConfigurationException;
40import javax.xml.transform.sax.SAXResult;
41import javax.xml.transform.sax.TransformerHandler;
42import javax.xml.transform.stream.StreamResult;
43
44import org.xml.sax.ErrorHandler;
45import org.xml.sax.InputSource;
46import org.xml.sax.SAXException;
47import org.xml.sax.SAXParseException;
48import org.xml.sax.XMLReader;
49
50/*
51 * AstroProcessor is to carry out the user's query with filters and produce a table of
52 * star records that match the criterion, and finally output with HTML format.
53 */
54public class AstroProcessor {
55    private String catalogFileName;
56
57    private FilterFactory ffact;
58    private InputSourceFactory isfact;
59
60    private SAXParserFactory spf;
61
62    /*
63     * Constructor for the Main astro class.
64     *
65     * @param fFactClass the class of the FilterFactory implementation
66     *
67     * @param catalogfilename the name of the XML input document (database)
68     *
69     * @param isFactClass the class of the Input Source Factory implementation
70     */
71    public AstroProcessor(Class<FilterFactory> fFactClass, String catalogFileName, Class<InputSourceFactory> isFactClass) throws Exception {
72        // create the Filter Factory instance...
73
74        ffact = fFactClass.newInstance();
75
76        // create the Input Source Instance
77
78        isfact = isFactClass.newInstance();
79
80        spf = SAXParserFactory.newInstance();
81        spf.setNamespaceAware(true);
82        spf.setValidating(true);
83
84        // All XML Readers are required to recognize these two:
85        spf.setFeature("http://xml.org/sax/features/namespaces", true);
86        spf.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
87
88        // Other features...
89        spf.setFeature("http://xml.org/sax/features/validation", true);
90        spf.setFeature("http://apache.org/xml/features/validation/schema", true);
91        spf.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
92
93        this.catalogFileName = catalogFileName;
94    }
95
96    /*
97     * Sets the star stellar type query.
98     *
99     * @param arg stellar type string, can be a substring.
100     */
101    public TransformerHandler getStellarTypeFilter(String arg) throws TransformerConfigurationException, SAXException, ParserConfigurationException,
102            IOException {
103        String stellarType = null;
104        if (arg != null && arg.length() != 0) {
105            stellarType = arg; // set value of query
106        } else {
107            throw new IllegalArgumentException("\n  Stellar type string of length zero found.");
108        }
109
110        return ffact.newStellarTypeFilter(stellarType);
111    }
112
113    /*
114     * Sets the right ascension parameters for a query. Parameters are validated
115     * to be in the range of 0h and 24h inclusive.
116     *
117     * @param min minimum right ascension in hours.
118     *
119     * @param max maximum right ascension in hours.
120     */
121    public TransformerHandler getRAFilter(double min, double max) throws TransformerConfigurationException, SAXException, ParserConfigurationException,
122            IOException {
123        double raMin = RA_MIN; // hours
124        double raMax = RA_MAX; // hours
125        if (min < max) {
126            if ((min >= RA_MIN && min <= RA_MAX) && (max >= RA_MIN && max <= RA_MAX)) {
127                raMin = min; // set value of query
128                raMax = max; // set value of query
129
130            }
131        } else {
132            throw new IllegalArgumentException("min must be less than max.\n" + "min=" + min + ", max=" + max);
133        }
134
135        return ffact.newRAFilter(raMin, raMax);
136    }
137
138    /*
139     * Sets the right ascension and dec parameters for a query. Parameters are
140     * validated to be in the range of ra 0h and 24h and dec -90 to +90
141     * inclusive.
142     *
143     * @param rmin minimum right ascension in hours.
144     *
145     * @param rmax maximum right ascension in hours.
146     *
147     * @param dmin minimum declination in degs.
148     *
149     * @param dmax maximum declination in degs.
150     */
151    public TransformerHandler getRADECFilter(double rmin, double rmax, double dmin, double dmax) throws TransformerConfigurationException, SAXException,
152            ParserConfigurationException, IOException {
153        double raMin = RA_MIN; // hours
154        double raMax = RA_MAX; // hours
155        double decMin = DEC_MIN; // degrees
156        double decMax = DEC_MAX; // degrees
157        if (rmin < rmax && dmin < dmax) {
158            if ((rmin >= RA_MIN && rmin <= RA_MAX) && (rmax >= RA_MIN && rmax <= RA_MAX)) {
159                raMin = rmin; // set value of query
160                raMax = rmax; // set value of query
161            }
162            if ((dmin >= DEC_MIN && dmin <= DEC_MAX) && (dmax >= DEC_MIN && dmax <= DEC_MAX)) {
163                decMin = dmin; // set value of query
164                decMax = dmax; // set value of query
165            }
166
167        } else {
168            throw new IllegalArgumentException("min must be less than max.\n" + "rmin=" + rmin + ", rmax=" + rmax + ", dmin=" + dmin + ", dmax=" + dmax);
169        }
170
171        return ffact.newRADECFilter(raMin, raMax, decMin, decMax);
172    }
173
174    /*
175     * Sets the declination parameters for a query. Parameters are validated to
176     * be in the range of -90 and +90 degrees inclusive.
177     *
178     * @param min minimum declination in degrees.
179     *
180     * @param max maximum declination in degrees.
181     */
182    public TransformerHandler getDecFilter(double min, double max) throws TransformerConfigurationException, SAXException, ParserConfigurationException,
183            IOException {
184        double decMin = DEC_MIN; // degrees
185        double decMax = DEC_MAX; // degrees
186        if (min < max) {
187            if ((min >= DEC_MIN && min <= DEC_MAX) && (max >= DEC_MIN && max <= DEC_MAX)) {
188                decMin = min; // set value of query
189                decMax = max; // set value of query
190            }
191        } else {
192            throw new IllegalArgumentException("min must be less than max.\n" + "min=" + min + ", max=" + max);
193        }
194
195        return ffact.newDECFilter(decMin, decMax);
196    }
197
198    /*
199     * Runs the filter process against the astronomical database.
200     *
201     * @throws Exception
202     */
203    public void process(String outputfile, TransformerHandler... filters) throws Exception {
204        XMLReader catparser = getXMLReader();
205
206        File catalogfile = new File(catalogFileName);
207        InputSource catsrc = isfact.newInputSource(catalogfile.getPath());
208
209        TransformerHandler outfilter = ffact.newHTMLOutput();
210        // create an array from the Vector of filters...
211
212        // hook the filters up to each other, there may be zero filters
213        int nfilters = filters.length;
214        if (nfilters != 0) {
215            TransformerHandler prev = null;
216            for (int i = 0; i < filters.length; i++) {
217                TransformerHandler curr = filters[i];
218                if (prev != null) {
219                    prev.setResult(new SAXResult(curr));
220                }
221                prev = curr;
222            }
223            // hook up the last filter to the output filter
224            prev.setResult(new SAXResult(outfilter));
225            // hook up the catalog parser to the first filter...
226            catparser.setContentHandler(filters[0]);
227        } else {
228            // There are no query filters,
229            // hook up the catalog parser directly to output filter...
230            catparser.setContentHandler(outfilter);
231        }
232        // hook up the output filter to the output file or std out
233        if (outputfile != null) {
234            outfilter.setResult(new StreamResult(outputfile));
235        } else {
236            outfilter.setResult(new StreamResult(System.out));
237        }
238
239        catparser.parse(catsrc);
240    }
241
242    private XMLReader getXMLReader() throws Exception {
243        SAXParser parser = spf.newSAXParser();
244        parser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA_NS_URI);
245        parser.setProperty(JAXP_SCHEMA_SOURCE, "catalog.xsd");
246        XMLReader catparser = parser.getXMLReader();
247        catparser.setErrorHandler(new CatalogErrorHandler());
248        return catparser;
249    }
250
251    /*
252     * Error Handler for the parsing of the XML astronomical catalog.
253     */
254    private static class CatalogErrorHandler implements ErrorHandler {
255        private String getParseExceptionInfo(SAXParseException spe) {
256            String systemId = spe.getSystemId();
257            if (systemId == null) {
258                systemId = "null";
259            }
260            String info = "Catalog URI=" + systemId + " Line=" + spe.getLineNumber() + ": " + spe.getMessage();
261            return info;
262        }
263
264        public void warning(SAXParseException spe) throws SAXException {
265            String message = "Warning: " + getParseExceptionInfo(spe);
266            throw new SAXException(message);
267        }
268
269        public void error(SAXParseException spe) throws SAXException {
270            String message = "Error: " + getParseExceptionInfo(spe);
271            throw new SAXException(message);
272        }
273
274        public void fatalError(SAXParseException spe) throws SAXException {
275            String message = "Fatal Error: " + getParseExceptionInfo(spe);
276            throw new SAXException(message);
277        }
278    }
279}
280