Event.java
- /*
- * Event
- */
- package gov.usgs.earthquake.indexer;
- import gov.usgs.earthquake.product.ProductId;
- import java.math.BigDecimal;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.TreeSet;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- /**
- * An event is a group of products that are nearby in space and time.
- *
- * Which products appear in an event depend primarily on the
- * ProductIndexQuery.ResultType that is used when retrieving an event from the
- * index. Unless CURRENT is used, you may not get what you expect.
- */
- public class Event implements Comparable<Event> {
- /** Origin product type */
- public static final String ORIGIN_PRODUCT_TYPE = "origin";
- /** Associate product type */
- public static final String ASSOCIATE_PRODUCT_TYPE = "associate";
- /** Disassociate product type */
- public static final String DISASSOCIATE_PRODUCT_TYPE = "disassociate";
- /** Property for othereventsource */
- public static final String OTHEREVENTSOURCE_PROPERTY = "othereventsource";
- /** Property for othereventsourcecode */
- public static final String OTHEREVENTSOURCECODE_PROPERTY = "othereventsourcecode";
- /** An ID used by the ProductIndex. */
- private Long indexId = null;
- /** Products nearby in space and time. Keyed by type. */
- private Map<String, List<ProductSummary>> products = new HashMap<String, List<ProductSummary>>();
- /** Cached summary. */
- private EventSummary eventSummary = null;
- /**
- * Default constructor.
- *
- * All fields are set to null, and the list of products is empty.
- */
- public Event() {
- }
- /**
- * Construct an event with only an indexId. The products map will be empty.
- *
- * @param indexId
- * the indexId to set.
- */
- public Event(final Long indexId) {
- this.setIndexId(indexId);
- }
- /**
- * Construct and event with an indexId and a list of products.
- *
- * @param indexId
- * the product index id.
- * @param products
- * the list of products.
- */
- public Event(final Long indexId,
- final Map<String, List<ProductSummary>> products) {
- this.setIndexId(indexId);
- this.setProducts(products);
- }
- /**
- * Copy constructor for event.
- *
- * The products associated with this event are not cloned, but the list of
- * products is.
- *
- * @param copy
- * the event to clone.
- */
- public Event(final Event copy) {
- this(copy.getIndexId(), copy.getAllProducts());
- }
- /**
- * Get the index id.
- *
- * @return the indexId or null if one hasn't been assigned.
- */
- public Long getIndexId() {
- return indexId;
- }
- /**
- * Set the index id.
- *
- * @param indexId
- * the indexId to set.
- */
- public void setIndexId(Long indexId) {
- this.indexId = indexId;
- }
- /**
- * Get all products associated with event, even if they are deleted.
- *
- * @return all products associated with event.
- */
- public Map<String, List<ProductSummary>> getAllProducts() {
- return products;
- }
- /**
- * Get the event products.
- *
- * Only returns products that have not been deleted or superseded. This
- * method returns a copy of the underlying product map that has been
- * filtered to remove deleted products.
- *
- * @return a map of event products.
- * @see #getAllProducts()
- */
- public Map<String, List<ProductSummary>> getProducts() {
- Map<String, List<ProductSummary>> notDeleted = new HashMap<String, List<ProductSummary>>();
- Iterator<String> types = products.keySet().iterator();
- while (types.hasNext()) {
- String type = types.next();
- List<ProductSummary> notDeletedProducts = getProducts(type);
- if (notDeletedProducts.size() > 0) {
- notDeleted.put(type, notDeletedProducts);
- }
- }
- return notDeleted;
- }
- /**
- * Set products.
- *
- * ProductSummaries are not cloned, but lists are.
- *
- * @param newProducts
- * the products to set.
- */
- public void setProducts(final Map<String, List<ProductSummary>> newProducts) {
- this.products.clear();
- Iterator<String> iter = new TreeSet<String>(newProducts.keySet())
- .iterator();
- while (iter.hasNext()) {
- String type = iter.next();
- this.products.put(type,
- new ArrayList<ProductSummary>(newProducts.get(type)));
- }
- eventSummary = null;
- }
- /**
- * A convenience method for adding a product summary to an event object.
- *
- * Note: this method does not update any associated product index.
- *
- * @param summary
- * the summary to add to this event.
- */
- public void addProduct(final ProductSummary summary) {
- String type = summary.getId().getType();
- List<ProductSummary> list = products.get(type);
- if (list == null) {
- list = new ArrayList<ProductSummary>();
- products.put(type, list);
- }
- if (!list.contains(summary)) {
- list.add(summary);
- }
- eventSummary = null;
- }
- /**
- * A convenience method for removing a product summary from an event object.
- *
- * Note: this method does not update any associated product index.
- *
- * @param summary
- * the summary to remove from this event.
- */
- public void removeProduct(final ProductSummary summary) {
- String type = summary.getId().getType();
- // find the list of products of this type
- List<ProductSummary> list = products.get(type);
- if (list != null) {
- // remove the product from the list
- list.remove(summary);
- if (list.size() == 0) {
- // if the list is now empty, remove the list
- products.remove(type);
- }
- }
- eventSummary = null;
- }
- /**
- * Convenience method to get products of a given type.
- *
- * This method always returns a copy of the internal list, and may be empty.
- * Only returns products that have not been deleted or superseded.
- *
- * @param type
- * the product type.
- * @return a list of products of that type, which may be empty.
- */
- public List<ProductSummary> getProducts(final String type) {
- ArrayList<ProductSummary> typeProducts = new ArrayList<ProductSummary>();
- if (products.containsKey(type)) {
- // only return products that haven't been deleted
- typeProducts.addAll(getWithoutDeleted(getWithoutSuperseded(products
- .get(type))));
- }
- return typeProducts;
- }
- /**
- * Get all event products (including those that are deleted or superseded).
- *
- * @return a list of event products.
- */
- public List<ProductSummary> getAllProductList() {
- List<ProductSummary> allProductList = new ArrayList<ProductSummary>();
- Map<String, List<ProductSummary>> allProducts = getAllProducts();
- Iterator<String> iter = allProducts.keySet().iterator();
- while (iter.hasNext()) {
- allProductList.addAll(allProducts.get(iter.next()));
- }
- return allProductList;
- }
- /**
- * Get all event products that have not been deleted or superseded as a
- * list.
- *
- * @return a list of event products.
- */
- public List<ProductSummary> getProductList() {
- List<ProductSummary> productList = new ArrayList<ProductSummary>();
- Map<String, List<ProductSummary>> notDeletedProducts = getProducts();
- Iterator<String> iter = notDeletedProducts.keySet().iterator();
- while (iter.hasNext()) {
- productList.addAll(notDeletedProducts.get(iter.next()));
- }
- return productList;
- }
- /**
- * Get preferred products of all types.
- *
- * This map will contain one product of each type, chosen by preferred
- * weight.
- *
- * @return a map from product type to the preferred product of that type.
- */
- public Map<String, ProductSummary> getPreferredProducts() {
- Map<String, ProductSummary> preferredProducts = new HashMap<String, ProductSummary>();
- Map<String, List<ProductSummary>> notDeletedProducts = getProducts();
- Iterator<String> types = notDeletedProducts.keySet().iterator();
- while (types.hasNext()) {
- String type = types.next();
- preferredProducts.put(type,
- getPreferredProduct(notDeletedProducts.get(type)));
- }
- return preferredProducts;
- }
- /**
- * Get the preferred product of a specific type.
- *
- * @param type
- * type of product to get.
- * @return most preferred product of that type, or null if no product of
- * that type is associated.
- */
- public ProductSummary getPreferredProduct(final String type) {
- return getPreferredProduct(getProducts(type));
- }
- /**
- * Get a map of all event ids associated with this event.
- *
- * Same as Event.getEventCodes(this.getAllProductList());
- *
- * @deprecated use {@link #getAllEventCodes(boolean)} instead.
- * @return map of all event ids associated with this event.
- */
- public Map<String, String> getEventCodes() {
- return getEventCodes(this.getAllProductList());
- }
- /**
- * Get a map of all event ids associated with this event.
- *
- * Map key is eventSource, Map value is eventSourceCode.
- *
- * @deprecated use {@link #getAllEventCodes(boolean)} instead.
- * @param summaries
- * the summaries list to extract event codes from.
- * @return map of all event ids associated with this event.
- */
- public static Map<String, String> getEventCodes(
- final List<ProductSummary> summaries) {
- Map<String, String> eventIds = new HashMap<String, String>();
- // order most preferred last,
- // to minimize impact of multiple codes from same source
- List<ProductSummary> sorted = getSortedMostPreferredFirst(
- getWithoutSuperseded(summaries));
- Collections.reverse(sorted);
- // done ordering
- Iterator<ProductSummary> iter = sorted.iterator();
- while (iter.hasNext()) {
- ProductSummary product = iter.next();
- String source = product.getEventSource();
- String code = product.getEventSourceCode();
- if (source != null && code != null) {
- eventIds.put(source.toLowerCase(), code.toLowerCase());
- }
- }
- return eventIds;
- }
- /**
- * Get a map of all event ids associated with this event, recognizing that
- * one source may have multiple codes (they broke the rules, but it
- * happens).
- *
- * @param includeDeleted
- * whether to include ids for sub events whose products have all
- * been deleted.
- * @return Map from source to a list of codes from that source.
- */
- public Map<String, List<String>> getAllEventCodes(
- final boolean includeDeleted) {
- Map<String, List<String>> allEventCodes = new HashMap<String, List<String>>();
- Map<String, Event> subEvents = getSubEvents();
- Iterator<String> iter = subEvents.keySet().iterator();
- while (iter.hasNext()) {
- Event subEvent = subEvents.get(iter.next());
- if (!includeDeleted && subEvent.isDeleted()) {
- // check for non-deleted products that should
- // keep the event code alive
- List<ProductSummary> nonDeletedProducts = getWithoutDeleted(
- getWithoutSuperseded(subEvent.getAllProductList()));
- if (nonDeletedProducts.size() == 0) {
- // filter deleted events
- continue;
- }
- // otherwise, event has active products;
- // prevent same source associations
- }
- // add code to list for source
- String source = subEvent.getSource();
- String sourceCode = subEvent.getSourceCode();
- List<String> sourceEventCodes = allEventCodes.get(source);
- if (sourceEventCodes == null) {
- // create list for source
- sourceEventCodes = new ArrayList<String>();
- allEventCodes.put(source, sourceEventCodes);
- }
- // keep list distinct
- if (!sourceEventCodes.contains(sourceCode)) {
- sourceEventCodes.add(sourceCode);
- }
- }
- return allEventCodes;
- }
- /**
- * Get a list of all the preferred products sorted based on their
- * authoritative weights
- *
- * @return sorted list of ProductSummary objects
- */
- public List<ProductSummary> getPreferredProductsSorted() {
- Map<String, ProductSummary> preferred = getPreferredProducts();
- // Transform the preferred HashMap into a List so we can sort based on
- // preferred weight
- List<ProductSummary> productList = new ArrayList<ProductSummary>(preferred.values());
- // Sort the list, then iterate through it until we find the specified
- // property
- Collections.sort(productList, new MostPreferredFirstComparator());
- return productList;
- }
- /**
- * Get the event id.
- *
- * The event id is the combination of event source and event source code.
- *
- * @return the event id, or null if either event source or event source code
- * is null.
- * @see #getSource()
- * @see #getSourceCode()
- */
- public String getEventId() {
- ProductSummary product = getEventIdProduct();
- if (product != null) {
- return product.getEventId();
- }
- return null;
- }
- /**
- * Get the preferred source for this event. If an origin product exists,
- * it's value is used.
- *
- * @return Source from preferred product or null
- */
- public String getSource() {
- ProductSummary product = getEventIdProduct();
- if (product != null) {
- return product.getEventSource();
- }
- return null;
- }
- /**
- * Get the preferred source code for this event. If an origin product
- * exists, it's value is used.
- *
- * @return Source code from preferred product or null
- */
- public String getSourceCode() {
- ProductSummary product = getEventIdProduct();
- if (product != null) {
- return product.getEventSourceCode();
- }
- return null;
- }
- /**
- * Get the product used for eventsource and eventsourcecode.
- *
- * Event ID comes from the preferred origin product.
- *
- * @return The most preferred product summary. This summary is used to
- * determine the eventsouce and eventsourcecode.
- * @see #getPreferredOriginProduct()
- */
- protected ProductSummary getEventIdProduct() {
- ProductSummary product = getPreferredOriginProduct();
- if (product == null) {
- product = getProductWithOriginProperties();
- }
- return product;
- }
- /**
- * Get the most recent product with origin properties (id, lat, lon, time).
- *
- * <strong>NOTE</strong>: this product may have been superseded by a delete.
- * When an event has not been deleted, this method should be consistent with
- * {@link #getPreferredOriginProduct()}.
- *
- * Products are checked in the following order, sorted most preferred first
- * within each group. The first matching product is returned:
- * <ol>
- * <li>"origin" products not superseded or deleted,
- * that have origin properties</li>
- * <li>"origin" products superseded by a delete,
- * that have origin properties</li>
- * <li>products not superseded or deleted,
- * that have origin properties</li>
- * <li>products superseded by a delete,
- * that have origin properties</li>
- * </ol>
- *
- * @return the most recent product with origin properties.
- * @see #productHasOriginProperties(ProductSummary)
- */
- public ProductSummary getProductWithOriginProperties() {
- Map<String, List<ProductSummary>> allProducts = getAllProducts();
- List<ProductSummary> productsList = null;
- ProductSummary preferredProduct = null;
- Iterator<ProductSummary> iter = null;
- productsList = allProducts.get(ORIGIN_PRODUCT_TYPE);
- if (productsList != null) {
- // "origin" products not superseded or deleted
- productsList = getSortedMostPreferredFirst(getWithoutDeleted(
- getWithoutSuperseded(allProducts.get(ORIGIN_PRODUCT_TYPE))));
- iter = productsList.iterator();
- while (iter.hasNext()) {
- preferredProduct = iter.next();
- if (productHasOriginProperties(preferredProduct)) {
- return preferredProduct;
- }
- }
- // "origin" products superseded by a delete
- productsList = getSortedMostPreferredFirst(getWithoutSuperseded(
- getWithoutDeleted(allProducts.get(ORIGIN_PRODUCT_TYPE))));
- iter = productsList.iterator();
- while (iter.hasNext()) {
- preferredProduct = iter.next();
- if (productHasOriginProperties(preferredProduct)) {
- return preferredProduct;
- }
- }
- }
- // products not superseded or deleted
- productsList = getSortedMostPreferredFirst(getWithoutDeleted(
- getWithoutSuperseded(productTypeMapToList(allProducts))));
- iter = productsList.iterator();
- while (iter.hasNext()) {
- preferredProduct = iter.next();
- if (productHasOriginProperties(preferredProduct)) {
- return preferredProduct;
- }
- }
- // products superseded by a delete
- productsList = getSortedMostPreferredFirst(getWithoutSuperseded(
- getWithoutDeleted(productTypeMapToList(allProducts))));
- iter = productsList.iterator();
- while (iter.hasNext()) {
- preferredProduct = iter.next();
- if (productHasOriginProperties(preferredProduct)) {
- return preferredProduct;
- }
- }
- return null;
- }
- /**
- * Get the most preferred origin-like product for this event.
- *
- * The event is considered deleted if the returned product is null, deleted,
- * or does not have origin properties. Information about the event
- * may still be available using {@link #getProductWithOriginProperties()}.
- *
- * Products are checked in the following order, sorted most preferred first
- * within each group. The first matching product is returned:
- * <ul>
- * <li>If any "origin" products exist:
- * <ol>
- * <li>"origin" products not superseded or deleted,
- * that have origin properties.</li>
- * <li>"origin" products not superseded,
- * that have an event id.</li>
- * </ol>
- * </li>
- * <li>If no "origin" products exist:
- * <ol>
- * <li>products not superseded or deleted,
- * that have origin properties.</li>
- * <li>products not superseded,
- * that have an event id.</li>
- * </ol>
- * </li>
- * </ul>
- *
- * @return the most recent product with origin properties.
- * @see #productHasOriginProperties(ProductSummary)
- */
- public ProductSummary getPreferredOriginProduct() {
- Map<String, List<ProductSummary>> allProducts = getAllProducts();
- List<ProductSummary> productsList = null;
- ProductSummary preferredProduct = null;
- Iterator<ProductSummary> iter = null;
- productsList = allProducts.get(ORIGIN_PRODUCT_TYPE);
- if (productsList != null) {
- // "origin" products not superseded or deleted,
- // that have origin properties
- productsList = getSortedMostPreferredFirst(getWithoutDeleted(
- getWithoutSuperseded(allProducts.get(ORIGIN_PRODUCT_TYPE))));
- iter = productsList.iterator();
- while (iter.hasNext()) {
- preferredProduct = iter.next();
- if (productHasOriginProperties(preferredProduct)) {
- return preferredProduct;
- }
- }
- // "origin" products not superseded,
- // that have event id
- productsList = getSortedMostPreferredFirst(getWithoutSuperseded(
- allProducts.get(ORIGIN_PRODUCT_TYPE)));
- iter = productsList.iterator();
- while (iter.hasNext()) {
- preferredProduct = iter.next();
- if (preferredProduct.getEventSource() != null
- && preferredProduct.getEventSourceCode() != null) {
- return preferredProduct;
- }
- }
- return null;
- }
- // products not superseded or deleted,
- // that have origin properties
- productsList = getSortedMostPreferredFirst(getWithoutDeleted(
- getWithoutSuperseded(productTypeMapToList(allProducts))));
- iter = productsList.iterator();
- while (iter.hasNext()) {
- preferredProduct = iter.next();
- if (productHasOriginProperties(preferredProduct)) {
- return preferredProduct;
- }
- }
- // products not superseded,
- // that have event id
- productsList = getSortedMostPreferredFirst(getWithoutSuperseded(
- productTypeMapToList(allProducts)));
- iter = productsList.iterator();
- while (iter.hasNext()) {
- preferredProduct = iter.next();
- if (preferredProduct.getEventSource() != null
- && preferredProduct.getEventSourceCode() != null) {
- return preferredProduct;
- }
- }
- return null;
- }
- /**
- * Check if a product can define an event (id, lat, lon, time).
- *
- * @param product
- * product to check.
- * @return true if product has id, lat, lon, and time properties.
- */
- public static boolean productHasOriginProperties(
- final ProductSummary product) {
- return (product.getEventSource() != null
- && product.getEventSourceCode() != null
- && product.getEventLatitude() != null
- && product.getEventLongitude() != null && product
- .getEventTime() != null);
- }
- /**
- * Get the most preferred magnitude product for event.
- *
- * Currently calls {@link #getPreferredOriginProduct()}.
- *
- * @return the most preferred magnitude product for event.
- */
- public ProductSummary getPreferredMagnitudeProduct() {
- return getPreferredOriginProduct();
- }
- /**
- * Get the preferred time for this event. If an origin product exists, it's
- * value is used.
- *
- * @return Time from preferred product or null
- */
- public Date getTime() {
- ProductSummary preferred = getProductWithOriginProperties();
- if (preferred != null) {
- return preferred.getEventTime();
- }
- return null;
- }
- /**
- * Get the preferred latitude for this event. If an origin product exists,
- * it's value is used.
- *
- * @return Latitude from preferred product or null
- */
- public BigDecimal getLatitude() {
- ProductSummary preferred = getProductWithOriginProperties();
- if (preferred != null) {
- return preferred.getEventLatitude();
- }
- return null;
- }
- /**
- * Get the preferred longitude for this event. If an origin product exists,
- * it's value is used.
- *
- * @return Longitude from preferred product or null
- */
- public BigDecimal getLongitude() {
- ProductSummary preferred = getProductWithOriginProperties();
- if (preferred != null) {
- return preferred.getEventLongitude();
- }
- return null;
- }
- /**
- * Event update time is most recent product update time.
- *
- * @return the most recent product update time.
- */
- public Date getUpdateTime() {
- Date updateTime = null;
- Date time = null;
- Iterator<ProductSummary> iter = getAllProductList().iterator();
- while (iter.hasNext()) {
- time = iter.next().getId().getUpdateTime();
- if (updateTime == null || time.after(updateTime)) {
- time = updateTime;
- }
- }
- return updateTime;
- }
- /**
- * Get the preferred depth for this event. If an origin product exists, it's
- * value is used.
- *
- * @return Depth from preferred product or null
- */
- public BigDecimal getDepth() {
- ProductSummary preferred = getProductWithOriginProperties();
- if (preferred != null) {
- return preferred.getEventDepth();
- }
- return null;
- }
- /**
- * Get the preferred magntitude for this event. If an origin product exists, it's
- * value is used.
- *
- * @return magnitude from preferred product or null
- */
- public BigDecimal getMagnitude() {
- ProductSummary preferred = getPreferredMagnitudeProduct();
- if (preferred != null) {
- return preferred.getEventMagnitude();
- }
- return null;
- }
- /**
- * @return boolean if the preferred event is deleted
- */
- public boolean isDeleted() {
- ProductSummary preferred = getPreferredOriginProduct();
- if (preferred != null && !preferred.isDeleted() &&
- Event.productHasOriginProperties(preferred)) {
- // have "origin" type product, that isn't deleted,
- // and has origin properties
- return false;
- }
- // otherwise, deleted
- return true;
- }
- /**
- * Get the most preferred product from a list of products.
- *
- * @param all
- * a list of products containing only one type of product.
- * @return the product with the highest preferred weight, and if tied the
- * most recent update time wins.
- */
- public static ProductSummary getPreferredProduct(
- final List<ProductSummary> all) {
- ProductSummary preferred = null;
- Iterator<ProductSummary> iter = all.iterator();
- while (iter.hasNext()) {
- ProductSummary summary = iter.next();
- if (preferred == null) {
- preferred = summary;
- } else {
- long summaryWeight = summary.getPreferredWeight();
- long preferredWeight = preferred.getPreferredWeight();
- if (summaryWeight > preferredWeight
- || (summaryWeight == preferredWeight && summary.getId()
- .getUpdateTime()
- .after(preferred.getId().getUpdateTime()))) {
- preferred = summary;
- }
- }
- }
- return preferred;
- }
- /**
- * Summarize this event into preferred values.
- *
- * NOTE: the event summary may include information from an origin product,
- * even when the preferred origin for the event has been deleted. Use
- * getPreferredOriginProduct() to check the preferred origin of the event.
- *
- * @return an event summary.
- */
- public EventSummary getEventSummary() {
- if (eventSummary != null) {
- return eventSummary;
- }
- EventSummary summary = new EventSummary();
- summary.setIndexId(this.getIndexId());
- summary.setDeleted(this.isDeleted());
- ProductSummary eventIdProduct = this.getEventIdProduct();
- if (eventIdProduct != null) {
- summary.setSource(eventIdProduct.getEventSource());
- summary.setSourceCode(eventIdProduct.getEventSourceCode());
- }
- ProductSummary originProduct = this.getProductWithOriginProperties();
- if (originProduct != null) {
- summary.setLatitude(originProduct.getEventLatitude());
- summary.setLongitude(originProduct.getEventLongitude());
- summary.setTime(originProduct.getEventTime());
- summary.setDepth(originProduct.getEventDepth());
- }
- ProductSummary magnitudeProduct = this.getPreferredMagnitudeProduct();
- if (magnitudeProduct != null) {
- summary.setMagnitude(magnitudeProduct.getEventMagnitude());
- }
- // we may be able to avoid implementing this here, since the mapping
- // interface will be driven by the PHP product index.
- summary.getEventCodes().putAll(this.getEventCodes());
- // cache summary
- eventSummary = summary;
- return summary;
- }
- /**
- * Comparison class that compares two ProductSummary objects based on their
- * preferred weight and update time.
- *
- */
- static class MostPreferredFirstComparator implements
- Comparator<ProductSummary> {
- @Override
- public int compare(ProductSummary p1, ProductSummary p2) {
- if (p1.getPreferredWeight() > p2.getPreferredWeight()) {
- return -1;
- } else if (p1.getPreferredWeight() < p2.getPreferredWeight()) {
- return 1;
- } else {
- Date p1Update = p1.getUpdateTime();
- Date p2Update = p2.getUpdateTime();
- if (p1Update.after(p2Update)) {
- return -1;
- } else if (p2Update.after(p1Update)) {
- return 1;
- } else {
- return 0;
- }
- }
- }
- }
- @Override
- public int compareTo(Event that) {
- int r;
- List<ProductSummary> thisProducts = this.getProductList();
- List<ProductSummary> thatProducts = that.getProductList();
- if ((r = (thatProducts.size() - thisProducts.size())) != 0) {
- return r;
- }
- Iterator<ProductSummary> thisIter = thisProducts.iterator();
- Iterator<ProductSummary> thatIter = thatProducts.iterator();
- while (thisIter.hasNext() && thatIter.hasNext()) {
- // just compare product ids for now
- r = thisIter.next().getId().compareTo(thatIter.next().getId());
- if (r != 0) {
- return r;
- }
- }
- return 0;
- }
- /**
- * Find the most preferred product.
- *
- * If preferredType is not null, products of this type are favored over
- * those not of this type.
- *
- * If preferredNotNullProperty is not null, products that have this property
- * set are favored over those without this property set.
- *
- * @param products
- * the list of products to search.
- * @param preferredType
- * the preferred product type, if available.
- * @param preferredNotNullProperty
- * the preferred property name, if available.
- * @return The most preferred product summary of the given type.
- */
- public static ProductSummary getMostPreferred(
- final List<ProductSummary> products, final String preferredType,
- final String preferredNotNullProperty) {
- ProductSummary mostPreferred = null;
- Iterator<ProductSummary> iter = products.iterator();
- while (iter.hasNext()) {
- ProductSummary next = iter.next();
- // ignore products that don't have the preferredNotNullProperty
- if (preferredNotNullProperty != null
- && next.getProperties().get(preferredNotNullProperty) == null) {
- continue;
- }
- if (mostPreferred == null) {
- // first product is most preferred so far
- mostPreferred = next;
- continue;
- }
- if (preferredType != null) {
- if (next.getType().equals(preferredType)) {
- if (!mostPreferred.getType().equals(preferredType)) {
- // prefer products of this type
- mostPreferred = next;
- }
- } else if (mostPreferred.getType().equals(preferredType)) {
- // already have preferred product of preferred type
- continue;
- }
- }
- if (next.getPreferredWeight() > mostPreferred.getPreferredWeight()) {
- // higher preferred weight
- mostPreferred = next;
- } else if (next.getPreferredWeight() == mostPreferred
- .getPreferredWeight()
- && next.getUpdateTime()
- .after(mostPreferred.getUpdateTime())) {
- // same preferred weight, newer update
- mostPreferred = next;
- }
- }
- return mostPreferred;
- }
- /**
- * Remove deleted products from the list.
- *
- * @param products
- * list of products to filter.
- * @return copy of the products list with deleted products removed.
- */
- public static List<ProductSummary> getWithoutDeleted(
- final List<ProductSummary> products) {
- List<ProductSummary> withoutDeleted = new ArrayList<ProductSummary>();
- Iterator<ProductSummary> iter = products.iterator();
- while (iter.hasNext()) {
- ProductSummary next = iter.next();
- if (!next.isDeleted()) {
- withoutDeleted.add(next);
- }
- }
- return withoutDeleted;
- }
- /**
- * Remove deleted products from the list.
- *
- * @param products
- * list of products to filter.
- * @return copy of the products list with deleted products removed.
- */
- public static List<ProductSummary> getWithEventId(
- final List<ProductSummary> products) {
- List<ProductSummary> withEventId = new ArrayList<ProductSummary>();
- Iterator<ProductSummary> iter = products.iterator();
- while (iter.hasNext()) {
- ProductSummary next = iter.next();
- if (next.getEventId() != null) {
- withEventId.add(next);
- }
- }
- return withEventId;
- }
- /**
- * Remove old versions of products from the list.
- *
- * @param products
- * list of products to filter.
- * @return a copy of the products list with products of the same
- * source+type+code but with older updateTimes (superseded) removed.
- */
- public static List<ProductSummary> getWithoutSuperseded(
- final List<ProductSummary> products) {
- // place product into latest, keyed by source+type+code,
- // keeping only most recent update for each key
- Map<String, ProductSummary> latest = new HashMap<String, ProductSummary>();
- Iterator<ProductSummary> iter = products.iterator();
- while (iter.hasNext()) {
- ProductSummary summary = iter.next();
- ProductId id = summary.getId();
- // key is combination of source, type, and code
- // since none of these may contain ":", it is used as a delimiter to
- // prevent collisions.
- String key = new StringBuffer(id.getSource()).append(":").append(
- id.getType()).append(":").append(id.getCode()).toString();
- if (!latest.containsKey(key)) {
- // first product
- latest.put(key, summary);
- } else {
- // keep latest product
- ProductSummary other = latest.get(key);
- if (other.getId().getUpdateTime().before(id.getUpdateTime())) {
- latest.put(key, summary);
- }
- }
- }
- // those that are in the latest map have not been superseded
- return new ArrayList<ProductSummary>(latest.values());
- }
- /**
- * Sort a list of products, most preferred first.
- *
- * @param products
- * the list of products to sort.
- * @return a copy of the list sorted with most preferred first.
- */
- public static List<ProductSummary> getSortedMostPreferredFirst(
- final List<ProductSummary> products) {
- List<ProductSummary> mostPreferredFirst = new ArrayList<ProductSummary>(
- products);
- Collections
- .sort(mostPreferredFirst, new MostPreferredFirstComparator());
- return mostPreferredFirst;
- }
- static List<ProductSummary> productTypeMapToList(
- final Map<String, List<ProductSummary>> products) {
- List<ProductSummary> list = new ArrayList<ProductSummary>();
- Iterator<String> iter = products.keySet().iterator();
- while (iter.hasNext()) {
- list.addAll(products.get(iter.next()));
- }
- return list;
- }
- static Map<String, List<ProductSummary>> productListToTypeMap(
- final List<ProductSummary> products) {
- Map<String, List<ProductSummary>> typeMap = new HashMap<String, List<ProductSummary>>();
- Iterator<ProductSummary> iter = products.iterator();
- while (iter.hasNext()) {
- ProductSummary product = iter.next();
- List<ProductSummary> typeProducts = typeMap.get(product.getType());
- if (typeProducts == null) {
- typeProducts = new ArrayList<ProductSummary>();
- typeMap.put(product.getType(), typeProducts);
- }
- typeProducts.add(product);
- }
- return typeMap;
- }
- /**
- * Return a list of sub-events that make up this event.
- *
- * Event lines are drawn by eventid. Products that have no eventid are
- * included with the sub event whose id is considered preferred.
- *
- * @return map from eventid to event object with products for that eventid.
- */
- public Map<String, Event> getSubEvents() {
- // Map of sub-events keyed by product "eventId"
- Map<String, Event> subEvents = new HashMap<String, Event>();
- // Map of events by source_type_code
- Map<String, Event> productEvents = new HashMap<String, Event>();
- // this is the event that will have products without event id...
- String preferredEventId = this.getEventId();
- Event preferredSubEvent = new Event();
- // put a placeholder with no products into the map for this purpose.
- subEvents.put(preferredEventId, preferredSubEvent);
- // List of all products associated to the current event
- List<ProductSummary> allProducts = this.getAllProductList();
- // handle products with a current version
- HashSet<ProductSummary> withoutSuperseded = new HashSet<ProductSummary>(getWithoutSuperseded(allProducts));
- Iterator<ProductSummary> products = withoutSuperseded.iterator();
- while (products.hasNext()) {
- ProductSummary product = products.next();
- Event subEvent = null;
- String subEventId = product.getEventId();
- if (subEventId == null) {
- // maybe try to find another version of product with id?
- subEvent = preferredSubEvent;
- } else {
- subEvent = subEvents.get(subEventId);
- if (subEvent == null) {
- // first product for this sub event
- subEvent = new Event();
- subEvents.put(subEventId, subEvent);
- }
- }
- subEvent.addProduct(product);
- ProductId id = product.getId();
- String key = id.getSource() + "_" + id.getType() + "_" + id.getCode();
- productEvents.put(key, subEvent);
- }
- // handle superseded products
- HashSet<ProductSummary> superseded = new HashSet<ProductSummary>(allProducts);
- superseded.removeAll(withoutSuperseded);
- products = superseded.iterator();
- while (products.hasNext()) {
- ProductSummary next = products.next();
- ProductId id = next.getId();
- String key = id.getSource() + "_" + id.getType() + "_" + id.getCode();
- Event subEvent = productEvents.get(key);
- subEvent.addProduct(next);
- }
- return subEvents;
- }
- /**
- * Check if this event has an associate product for another given Event.
- *
- * @param otherEvent
- * the other event.
- * @return true if there is an associate product, false otherwise.
- */
- public boolean hasAssociateProduct(final Event otherEvent) {
- if (otherEvent == null) {
- // cannot have an association to a null event...
- return false;
- }
- String otherEventSource = otherEvent.getSource();
- String otherEventSourceCode = otherEvent.getSourceCode();
- if (otherEventSource == null || otherEventSourceCode == null) {
- // same without source+code
- return false;
- }
- // search associate products
- Iterator<ProductSummary> iter = getProducts(ASSOCIATE_PRODUCT_TYPE)
- .iterator();
- while (iter.hasNext()) {
- ProductSummary associate = iter.next();
- if (otherEventSource.equalsIgnoreCase(associate.getProperties()
- .get(OTHEREVENTSOURCE_PROPERTY))
- && otherEventSourceCode
- .equalsIgnoreCase(associate.getProperties().get(
- OTHEREVENTSOURCECODE_PROPERTY))) {
- // associated
- return true;
- }
- }
- return false;
- }
- /**
- * Check if this event has an disassociate product for another given Event.
- *
- * @param otherEvent
- * the other event.
- * @return true if there is an disassociate product, false otherwise.
- */
- public boolean hasDisassociateProduct(final Event otherEvent) {
- if (otherEvent == null) {
- // cannot have an disassociation to a null event...
- return false;
- }
- String otherEventSource = otherEvent.getSource();
- String otherEventSourceCode = otherEvent.getSourceCode();
- if (otherEventSource == null || otherEventSourceCode == null) {
- // same without source+code
- return false;
- }
- // search disassociate products
- Iterator<ProductSummary> iter = getProducts(DISASSOCIATE_PRODUCT_TYPE)
- .iterator();
- while (iter.hasNext()) {
- ProductSummary associate = iter.next();
- if (otherEventSource.equalsIgnoreCase(associate.getProperties()
- .get(OTHEREVENTSOURCE_PROPERTY))
- && otherEventSourceCode
- .equalsIgnoreCase(associate.getProperties().get(
- OTHEREVENTSOURCECODE_PROPERTY))) {
- // disassociated
- return true;
- }
- }
- return false;
- }
- /**
- * Same as isAssociated(that, new DefaultAssociator());
- * @param that an event to test
- * @return boolean true if associated, false otherwise
- */
- public boolean isAssociated(final Event that) {
- return this.isAssociated(that, new DefaultAssociator());
- }
- /**
- * Check if an event is associated to this event.
- *
- * Reasons events may be considered disassociated:
- * <ol>
- * <li>Share a common EVENTSOURCE with different EVENTSOURCECODE.</li>
- * <li>Either has a disassociate product for the other.</li>
- * <li>Preferred location in space and time is NOT nearby, and no other
- * reason to associate.</li>
- * </ol>
- *
- * Reasons events may be considered associated:
- * <ol>
- * <li>Share a common EVENTID</li>
- * <li>Either has an associate product for the other.</li>
- * <li>Their preferred location in space and time is nearby.</li>
- * </ol>
- *
- * @param that
- * candidate event to test.
- * @param associator
- * An associator to compare two events
- * @return true if associated, false otherwise.
- */
- public boolean isAssociated(final Event that, final Associator associator) {
- return associator.eventsAssociated(this, that);
- }
- /**
- * Depending on logger level, takes in summary data and appends to buffer
- * @param logger logger object
- */
- public void log(final Logger logger) {
- if (logger.isLoggable(Level.FINE)) {
- EventSummary summary = this.getEventSummary();
- logger.fine(new StringBuffer("Event")
- .append("indexid=").append(summary.getIndexId())
- .append(", eventid=").append(summary.getId())
- .append(", latitude=").append(summary.getLatitude())
- .append(", longitude=").append(summary.getLongitude())
- .append(", time=").append(summary.getTime())
- .append(", deleted=").append(summary.isDeleted()).toString());
- if (logger.isLoggable(Level.FINER)) {
- StringBuffer buf = new StringBuffer("Products in event");
- List<ProductSummary> products = this.getAllProductList();
- Iterator<ProductSummary> iter = products.iterator();
- while (iter.hasNext()) {
- ProductSummary next = iter.next();
- buf.append("\n\tstatus=").append(next.getStatus())
- .append(", id=").append(next.getId().toString())
- .append(", eventid=").append(next.getEventId())
- .append(", latitude=").append(next.getEventLatitude())
- .append(", longitude=").append(next.getEventLongitude())
- .append(", time=").append(next.getEventTime());
- }
- logger.finer(buf.toString());
- }
- }
- }
- }