SearchResponseParser.java

  1. package gov.usgs.earthquake.indexer;

  2. import java.math.BigDecimal;
  3. import java.net.URI;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import java.util.Map;

  7. import gov.usgs.earthquake.distribution.FileProductStorage;
  8. import gov.usgs.earthquake.product.Product;
  9. import gov.usgs.earthquake.product.ProductId;
  10. import gov.usgs.earthquake.product.io.XmlProductHandler;
  11. import gov.usgs.util.XmlUtils;

  12. import org.xml.sax.Attributes;
  13. import org.xml.sax.SAXException;
  14. import org.xml.sax.helpers.DefaultHandler;

  15. /**
  16.  * Parser for SearchXML response.
  17.  */
  18. public class SearchResponseParser extends DefaultHandler {

  19.   private SearchResponse response = null;

  20.   private SearchQuery query = null;

  21.   private ProductSummary pSummary = null;
  22.   private EventSummary eSummary = null;

  23.   private Event event = null;

  24.   private boolean inQueryElement = false;
  25.   private boolean inErrorElement = false;

  26.   private FileProductStorage storage;
  27.   private SearchResponseXmlProductSource productHandler = null;

  28.   /**
  29.    * Constructor
  30.    *
  31.    * @param storage a FileProductStorage
  32.    */
  33.   public SearchResponseParser(final FileProductStorage storage) {
  34.     this.storage = storage;
  35.   }

  36.   /** @return SearchResponse */
  37.   public SearchResponse getSearchResponse() {
  38.     return response;
  39.   }

  40.   @Override
  41.   public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
  42.     if (productHandler != null) {
  43.       productHandler.startElement(uri, localName, qName, attributes);
  44.     } else if (SearchXML.INDEXER_XMLNS.equals(uri)) {
  45.       if (SearchXML.RESPONSE_ELEMENT.equals(localName)) {
  46.         response = new SearchResponse();
  47.       } else if (SearchXML.RESULT_ELEMENT.equals(localName)) {
  48.         if (response == null)
  49.           throw new SAXException("Unexpected result element without response element parent.");
  50.         SearchMethod method = SearchMethod
  51.             .fromXmlMethodName(XmlUtils.getAttribute(attributes, uri, SearchXML.METHOD_ATTRIBUTE));
  52.         query = SearchQuery.getSearchQuery(method, new ProductIndexQuery());
  53.         // create results container now
  54.         if (query instanceof EventDetailQuery) {
  55.           ((EventDetailQuery) query).setResult(new ArrayList<Event>());
  56.         } else if (query instanceof EventsSummaryQuery) {
  57.           ((EventsSummaryQuery) query).setResult(new ArrayList<EventSummary>());
  58.         } else if (query instanceof ProductDetailQuery) {
  59.           ((ProductDetailQuery) query).setResult(new ArrayList<Product>());
  60.         } else if (query instanceof ProductsSummaryQuery) {
  61.           ((ProductsSummaryQuery) query).setResult(new ArrayList<ProductSummary>());
  62.         }
  63.       } else if (SearchXML.QUERY_ELEMENT.equals(localName)) {
  64.         if (query == null)
  65.           throw new SAXException("Unexpected query element without result element parent.");
  66.         inQueryElement = true;
  67.         ProductIndexQuery piQuery = query.getProductIndexQuery();

  68.         // Update the ProductIndexQuery with each given attribute
  69.         // Event Source Attribute
  70.         String eventSource = XmlUtils.getAttribute(attributes, uri, SearchXML.EVENT_SOURCE_ATTRIBUTE);
  71.         if (eventSource != null) {
  72.           piQuery.setEventSource(eventSource);
  73.         }
  74.         // Event Source Code Attribute
  75.         String eventSourceCode = XmlUtils.getAttribute(attributes, uri, SearchXML.EVENT_SOURCE_CODE_ATTRIBUTE);
  76.         if (eventSourceCode != null) {
  77.           piQuery.setEventSourceCode(eventSourceCode);
  78.         }
  79.         // Max Event Depth Attribute
  80.         String maxEventDepth = XmlUtils.getAttribute(attributes, uri, SearchXML.MAX_EVENT_DEPTH_ATTRIBUTE);
  81.         if (maxEventDepth != null) {
  82.           piQuery.setMaxEventDepth(new BigDecimal(maxEventDepth));
  83.         }
  84.         // Max Event Latitude Attribute
  85.         String maxEventLatitude = XmlUtils.getAttribute(attributes, uri, SearchXML.MAX_EVENT_LATITUDE_ATTRIBUTE);
  86.         if (maxEventLatitude != null) {
  87.           piQuery.setMaxEventLatitude(new BigDecimal(maxEventLatitude));
  88.         }
  89.         // Max Event Longitude Attribute
  90.         String maxEventLongitude = XmlUtils.getAttribute(attributes, uri, SearchXML.MAX_EVENT_LONGITUDE_ATTRIBUTE);
  91.         if (maxEventLongitude != null) {
  92.           piQuery.setMaxEventLongitude(new BigDecimal(maxEventLongitude));
  93.         }
  94.         // Max Event Magnitude Attribute
  95.         String maxEventMagnitude = XmlUtils.getAttribute(attributes, uri, SearchXML.MAX_EVENT_MAGNITUDE_ATTRIBUTE);
  96.         if (maxEventMagnitude != null) {
  97.           piQuery.setMaxEventMagnitude(new BigDecimal(maxEventMagnitude));
  98.         }
  99.         // Max Event Time Attribute
  100.         String maxEventTime = XmlUtils.getAttribute(attributes, uri, SearchXML.MAX_EVENT_TIME_ATTRIBUTE);
  101.         if (maxEventTime != null) {
  102.           piQuery.setMaxEventTime(XmlUtils.getDate(maxEventTime));
  103.         }
  104.         // Max Product Update Time Attribute
  105.         String maxProductUpdateTime = XmlUtils.getAttribute(attributes, uri,
  106.             SearchXML.MAX_PRODUCT_UPDATE_TIME_ATTRIBUTE);
  107.         if (maxProductUpdateTime != null) {
  108.           piQuery.setMaxProductUpdateTime(XmlUtils.getDate(maxProductUpdateTime));
  109.         }
  110.         // Min Event Depth Attribute
  111.         String minEventDepth = XmlUtils.getAttribute(attributes, uri, SearchXML.MIN_EVENT_DEPTH_ATTRIBUTE);
  112.         if (minEventDepth != null) {
  113.           piQuery.setMinEventDepth(new BigDecimal(minEventDepth));
  114.         }
  115.         // Min Event Latitude Attribute
  116.         String minEventLatitude = XmlUtils.getAttribute(attributes, uri, SearchXML.MIN_EVENT_LATITUDE_ATTRIBUTE);
  117.         if (minEventLatitude != null) {
  118.           piQuery.setMinEventLatitude(new BigDecimal(minEventLatitude));
  119.         }
  120.         // Min Event Longitude Attribute
  121.         String minEventLongitude = XmlUtils.getAttribute(attributes, uri, SearchXML.MIN_EVENT_LONGITUDE_ATTRIBUTE);
  122.         if (minEventLongitude != null) {
  123.           piQuery.setMinEventLongitude(new BigDecimal(minEventLongitude));
  124.         }
  125.         // Min Event Magnitude Attribute
  126.         String minEventMagnitude = XmlUtils.getAttribute(attributes, uri, SearchXML.MIN_EVENT_MAGNITUDE_ATTRIBUTE);
  127.         if (minEventMagnitude != null) {
  128.           piQuery.setMinEventMagnitude(new BigDecimal(minEventMagnitude));
  129.         }
  130.         // Min Event Time Attribute
  131.         String minEventTime = XmlUtils.getAttribute(attributes, uri, SearchXML.MIN_EVENT_TIME_ATTRIBUTE);
  132.         if (minEventTime != null) {
  133.           piQuery.setMinEventTime(XmlUtils.getDate(minEventTime));
  134.         }
  135.         // Min Product Update Time Attribute
  136.         String minProductUpdateTime = XmlUtils.getAttribute(attributes, uri,
  137.             SearchXML.MIN_PRODUCT_UPDATE_TIME_ATTRIBUTE);
  138.         if (minProductUpdateTime != null) {
  139.           piQuery.setMinProductUpdateTime(XmlUtils.getDate(minProductUpdateTime));
  140.         }
  141.         // Product Code Attribute
  142.         String productCode = XmlUtils.getAttribute(attributes, uri, SearchXML.PRODUCT_CODE_ATTRIBUTE);
  143.         if (productCode != null) {
  144.           piQuery.setProductCode(productCode);
  145.         }
  146.         // Product Source Attribute
  147.         String productSource = XmlUtils.getAttribute(attributes, uri, SearchXML.PRODUCT_SOURCE_ATTRIBUTE);
  148.         if (productSource != null) {
  149.           piQuery.setProductSource(productSource);
  150.         }
  151.         // Product Status Attribute
  152.         String productStatus = XmlUtils.getAttribute(attributes, uri, SearchXML.PRODUCT_STATUS_ATTRIBUTE);
  153.         if (productStatus != null) {
  154.           piQuery.setProductStatus(productStatus);
  155.         }
  156.         // Product Type Attribute
  157.         String productType = XmlUtils.getAttribute(attributes, uri, SearchXML.PRODUCT_TYPE_ATTRIBUTE);
  158.         if (productType != null) {
  159.           piQuery.setProductType(productType);
  160.         }
  161.         // Product Version Attribute
  162.         String productVersion = XmlUtils.getAttribute(attributes, uri, SearchXML.PRODUCT_VERSION_ATTRIBUTE);
  163.         if (productVersion != null) {
  164.           piQuery.setProductVersion(productVersion);
  165.         }

  166.         // Set result type. At the moment we ony support the "current"
  167.         // type.
  168.         piQuery.setResultType(ProductIndexQuery.RESULT_TYPE_CURRENT);
  169.       } else if (SearchXML.PRODUCT_SUMMARY_ELEMENT.equals(localName)) {
  170.         if (inQueryElement) {
  171.           // This product summary is being used to pass ID information
  172.           // for the query
  173.           ProductIndexQuery piQuery = query.getProductIndexQuery();
  174.           piQuery.getProductIds().add(ProductId.parse(XmlUtils.getAttribute(attributes, uri, SearchXML.ID_ATTRIBUTE)));
  175.         } else {
  176.           // This is a more complete returned product summary
  177.           pSummary = new ProductSummary();

  178.           // Set the attributes of the ProductSummary
  179.           // Depth attribute
  180.           String depth = XmlUtils.getAttribute(attributes, uri, SearchXML.DEPTH_ATTRIBUTE);
  181.           if (depth != null) {
  182.             pSummary.setEventDepth(new BigDecimal(depth));
  183.           }
  184.           // Latitude attribute
  185.           String latitude = XmlUtils.getAttribute(attributes, uri, SearchXML.LATITUDE_ATTRIBUTE);
  186.           if (latitude != null) {
  187.             pSummary.setEventLatitude(new BigDecimal(latitude));
  188.           }
  189.           // Longitude attribute
  190.           String longitude = XmlUtils.getAttribute(attributes, uri, SearchXML.LONGITUDE_ATTRIBUTE);
  191.           if (longitude != null) {
  192.             pSummary.setEventLongitude(new BigDecimal(longitude));
  193.           }
  194.           // Magnitude attribute
  195.           String magnitude = XmlUtils.getAttribute(attributes, uri, SearchXML.MAGNITUDE_ATTRIBUTE);
  196.           if (magnitude != null) {
  197.             pSummary.setEventMagnitude(new BigDecimal(magnitude));
  198.           }
  199.           // Event Source attribute
  200.           String eventSource = XmlUtils.getAttribute(attributes, uri, SearchXML.EVENT_SOURCE_ATTRIBUTE);
  201.           if (eventSource != null) {
  202.             pSummary.setEventSource(eventSource);
  203.           }
  204.           // Event Source Code attribute
  205.           String eventSourceCode = XmlUtils.getAttribute(attributes, uri, SearchXML.EVENT_SOURCE_CODE_ATTRIBUTE);
  206.           if (eventSourceCode != null) {
  207.             pSummary.setEventSourceCode(eventSourceCode);
  208.           }
  209.           // Time attribute
  210.           String time = XmlUtils.getAttribute(attributes, uri, SearchXML.TIME_ATTRIBUTE);
  211.           if (time != null) {
  212.             pSummary.setEventTime(XmlUtils.getDate(time));
  213.           }
  214.           // ID attribute
  215.           String id = XmlUtils.getAttribute(attributes, uri, SearchXML.ID_ATTRIBUTE);
  216.           if (id != null) {
  217.             pSummary.setId(ProductId.parse(id));
  218.           }
  219.           // Preferred Weight attribute
  220.           String preferredWeight = XmlUtils.getAttribute(attributes, uri, SearchXML.PREFERRED_WEIGHT_ATTRIBUTE);
  221.           if (preferredWeight != null) {
  222.             pSummary.setPreferredWeight(Integer.parseInt(preferredWeight));
  223.           }
  224.           // Status attribute
  225.           String status = XmlUtils.getAttribute(attributes, uri, SearchXML.STATUS_ATTRIBUTE);
  226.           if (status != null) {
  227.             pSummary.setStatus(status);
  228.           }
  229.           // Version attribute
  230.           String version = XmlUtils.getAttribute(attributes, uri, SearchXML.VERSION_ATTRIBUTE);
  231.           if (version != null) {
  232.             pSummary.setVersion(version);
  233.           }
  234.         }
  235.       } else if (SearchXML.EVENT_ELEMENT.equals(localName)) {
  236.         event = new Event();
  237.         // Nothing further needs to be done here as properties are set
  238.         // by ProductSummaries for the event
  239.       } else if (SearchXML.EVENT_SUMMARY_ELEMENT.equals(localName)) {
  240.         eSummary = new EventSummary();

  241.         // Configure EventSummary attributes
  242.         // Depth attribute
  243.         String depth = XmlUtils.getAttribute(attributes, uri, SearchXML.DEPTH_ATTRIBUTE);
  244.         if (depth != null) {
  245.           eSummary.setDepth(new BigDecimal(depth));
  246.         }
  247.         // Latitude attribute
  248.         String latitude = XmlUtils.getAttribute(attributes, uri, SearchXML.LATITUDE_ATTRIBUTE);
  249.         if (latitude != null) {
  250.           eSummary.setLatitude(new BigDecimal(latitude));
  251.         }
  252.         // Longitude attribute
  253.         String longitude = XmlUtils.getAttribute(attributes, uri, SearchXML.LONGITUDE_ATTRIBUTE);
  254.         if (longitude != null) {
  255.           eSummary.setLongitude(new BigDecimal(longitude));
  256.         }
  257.         // Magnitude attribute
  258.         String magnitude = XmlUtils.getAttribute(attributes, uri, SearchXML.MAGNITUDE_ATTRIBUTE);
  259.         if (magnitude != null) {
  260.           eSummary.setMagnitude(new BigDecimal(magnitude));
  261.         }
  262.         // Source attribute
  263.         String source = XmlUtils.getAttribute(attributes, uri, SearchXML.SOURCE_ATTRIBUTE);
  264.         if (source != null) {
  265.           eSummary.setSource(source);
  266.         }
  267.         // Source code attribute
  268.         String sourceCode = XmlUtils.getAttribute(attributes, uri, SearchXML.SOURCE_CODE_ATTRIBUTE);
  269.         if (sourceCode != null) {
  270.           eSummary.setSourceCode(sourceCode);
  271.         }
  272.         // Time attribute
  273.         String time = XmlUtils.getAttribute(attributes, uri, SearchXML.TIME_ATTRIBUTE);
  274.         if (time != null) {
  275.           eSummary.setTime(XmlUtils.getDate(time));
  276.         }
  277.       } else if (SearchXML.ERROR_ELEMENT.equals(localName)) {
  278.         inErrorElement = true;
  279.       }
  280.     } else if (XmlProductHandler.PRODUCT_XML_NAMESPACE.equals(uri)) {
  281.       if (XmlProductHandler.PROPERTY_ELEMENT.equals(localName)) {
  282.         if (pSummary != null) {
  283.           // Currently working on a Product Summary
  284.           pSummary.getProperties().put(
  285.               XmlUtils.getAttribute(attributes, uri, XmlProductHandler.PROPERTY_ATTRIBUTE_NAME),
  286.               XmlUtils.getAttribute(attributes, uri, XmlProductHandler.PROPERTY_ATTRIBUTE_VALUE));
  287.         } else if (eSummary != null) {
  288.           // Currently working on an Event Summary
  289.           eSummary.getProperties().put(
  290.               XmlUtils.getAttribute(attributes, uri, XmlProductHandler.PROPERTY_ATTRIBUTE_NAME),
  291.               XmlUtils.getAttribute(attributes, uri, XmlProductHandler.PROPERTY_ATTRIBUTE_VALUE));
  292.         } else if (inQueryElement) {
  293.           // Currently working on a query
  294.           // This is defined in the schema, but not in the class
  295.           // for either query
  296.         } else {
  297.           throw new SAXException("Property element without appropriate parent encountered.");
  298.         }
  299.       } else if (XmlProductHandler.LINK_ELEMENT.equals(localName)) {
  300.         if (pSummary != null) {
  301.           // This link is occurring as part of a product summary
  302.           String relation = XmlUtils.getAttribute(attributes, uri, XmlProductHandler.LINK_ATTRIBUTE_RELATION);
  303.           Map<String, List<URI>> links = pSummary.getLinks();
  304.           if (links.containsKey(relation))
  305.             links.get(relation)
  306.                 .add(URI.create(XmlUtils.getAttribute(attributes, uri, XmlProductHandler.LINK_ATTRIBUTE_HREF)));
  307.           else {
  308.             List<URI> newList = new ArrayList<URI>();
  309.             newList.add(URI.create(XmlUtils.getAttribute(attributes, uri, XmlProductHandler.LINK_ATTRIBUTE_HREF)));
  310.             links.put(relation, newList);
  311.           }
  312.         } else {
  313.           throw new SAXException("Link element without appropriate parent encountered.");
  314.         }
  315.       } else if (XmlProductHandler.PRODUCT_ELEMENT.equals(localName)) {
  316.         // We are starting to process a product and need to pass data
  317.         // through
  318.         // until that is complete.
  319.         productHandler = new SearchResponseXmlProductSource(storage);
  320.         productHandler.startElement(uri, localName, qName, attributes);
  321.       }
  322.     }
  323.   }

  324.   @Override
  325.   public void endElement(String uri, String localName, String qName) throws SAXException {
  326.     if (productHandler != null) {
  327.       productHandler.endElement(uri, localName, qName);
  328.       if (XmlProductHandler.PRODUCT_ELEMENT.equals(localName)) {
  329.         ProductDetailQuery pdQuery = (ProductDetailQuery) query;
  330.         pdQuery.getResult().add(productHandler.getProduct());
  331.         productHandler = null;
  332.       }
  333.     } else if (SearchXML.INDEXER_XMLNS.equals(uri)) {
  334.       if (SearchXML.RESPONSE_ELEMENT.equals(localName)) {
  335.         // Do nothing, this is the end element of the document.
  336.       } else if (SearchXML.RESULT_ELEMENT.equals(localName)) {
  337.         // One of the results has completed
  338.         if (response == null)
  339.           throw new SAXException("result element found without response parent");
  340.         else {
  341.           response.addResult(query);
  342.           query = null;
  343.         }
  344.       } else if (SearchXML.QUERY_ELEMENT.equals(localName)) {
  345.         // The query element of a result has completed
  346.         // Because the ProductIndexQuery is part of the
  347.         // query object controlled by the result element
  348.         // nothing particular needs to be done other than
  349.         // set the flag for being in the query element to false
  350.         inQueryElement = false;
  351.       } else if (SearchXML.PRODUCT_SUMMARY_ELEMENT.equals(localName)) {
  352.         if (inQueryElement) {
  353.           // Nothing needs to be done because this was just used
  354.           // to get the product ID into the list of IDs for the
  355.           // query.
  356.         } else {
  357.           if (event != null) {
  358.             // We're adding product summaries to events.
  359.             Map<String, List<ProductSummary>> eventProducts = event.getAllProducts();
  360.             String productType = pSummary.getType();
  361.             if (eventProducts.containsKey(productType)) {
  362.               // Key exists, so just add the product summary to it
  363.               eventProducts.get(productType).add(pSummary);
  364.             } else {
  365.               List<ProductSummary> newList = new ArrayList<ProductSummary>();
  366.               newList.add(pSummary);
  367.               eventProducts.put(productType, newList);
  368.             }
  369.           } else if (query != null && query.getType() == SearchMethod.PRODUCTS_SUMMARY) {
  370.             // This was a product summary query and these are its
  371.             // results
  372.             ProductsSummaryQuery psQuery = (ProductsSummaryQuery) query;
  373.             psQuery.getResult().add(pSummary);
  374.           } else {
  375.             throw new SAXException("productSummary element encountered without recognized parent");
  376.           }
  377.           pSummary = null;
  378.         }
  379.       } else if (SearchXML.EVENT_ELEMENT.equals(localName)) {
  380.         if (query != null && query.getType() == SearchMethod.EVENT_DETAIL) {
  381.           // This was an event detail query and has opened properly
  382.           EventDetailQuery edQuery = (EventDetailQuery) query;
  383.           edQuery.getResult().add(event);
  384.           event = null;
  385.         } else {
  386.           throw new SAXException("event element encountered without recognized parent");
  387.         }
  388.       } else if (SearchXML.EVENT_SUMMARY_ELEMENT.equals(localName)) {
  389.         if (query != null && query.getType() == SearchMethod.EVENTS_SUMMARY) {
  390.           // This was an event summary query and has opened properly
  391.           EventsSummaryQuery esQuery = (EventsSummaryQuery) query;
  392.           esQuery.getResult().add(eSummary);
  393.           esQuery = null;
  394.         } else {
  395.           throw new SAXException("eventSummary element encountered without recognized parent");
  396.         }
  397.       } else if (SearchXML.ERROR_ELEMENT.equals(localName)) {
  398.         inErrorElement = false;
  399.       }
  400.     } else if (XmlProductHandler.PRODUCT_XML_NAMESPACE.equals(uri)) {
  401.       if (XmlProductHandler.PROPERTY_ELEMENT.equals(localName)) {
  402.         // Simple tag is handled when the start element is encountered
  403.       } else if (XmlProductHandler.LINK_ELEMENT.equals(localName)) {
  404.         // Simple tag is handled when the start element is encountered
  405.       }
  406.     }
  407.   }

  408.   @Override
  409.   public void characters(char[] ch, int start, int length) throws SAXException {
  410.     if (productHandler != null) {
  411.       // Pass through if product is being generated
  412.       productHandler.characters(ch, start, length);
  413.     } else if (inErrorElement) {
  414.       query.setError(new String(ch));
  415.     }
  416.   }

  417. }