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  */
  23 package test.astro;
  24 
  25 import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
  26 import static test.astro.AstroConstants.DEC_MAX;
  27 import static test.astro.AstroConstants.DEC_MIN;
  28 import static test.astro.AstroConstants.JAXP_SCHEMA_LANGUAGE;
  29 import static test.astro.AstroConstants.JAXP_SCHEMA_SOURCE;
  30 import static test.astro.AstroConstants.RA_MAX;
  31 import static test.astro.AstroConstants.RA_MIN;
  32 
  33 import java.io.File;
  34 import java.io.IOException;
  35 
  36 import javax.xml.parsers.ParserConfigurationException;
  37 import javax.xml.parsers.SAXParser;
  38 import javax.xml.parsers.SAXParserFactory;
  39 import javax.xml.transform.TransformerConfigurationException;
  40 import javax.xml.transform.sax.SAXResult;
  41 import javax.xml.transform.sax.TransformerHandler;
  42 import javax.xml.transform.stream.StreamResult;
  43 
  44 import org.xml.sax.ErrorHandler;
  45 import org.xml.sax.InputSource;
  46 import org.xml.sax.SAXException;
  47 import org.xml.sax.SAXParseException;
  48 import 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  */
  54 public 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 }