ProductId.java

  1. /*
  2.  * ProductId
  3.  */
  4. package gov.usgs.earthquake.product;


  5. import java.util.Date;
  6. import java.util.Objects;

  7. /**
  8.  * Attributes that uniquely identify a product.
  9.  *
  10.  * <dl>
  11.  * <dt>Source</dt>
  12.  * <dd>
  13.  *   The organization <u>sending</u> the product;
  14.  *   not necessarily the author of the product.
  15.  *
  16.  *   Typically a FDSN network code.
  17.  * </dd>
  18.  *
  19.  * <dt>Type</dt>
  20.  * <dd>
  21.  *   The type of product being sent.
  22.  * </dd>
  23.  *
  24.  * <dt>Code</dt>
  25.  * <dd>
  26.  *   A unique code assigned by the <code>source</code> and <code>type</code>.
  27.  *   Source and Type are effectively a namespace for codes.
  28.  *
  29.  *   If the same <code>code</code> is re-used, it indicates a different
  30.  *   version of the same product.
  31.  * </dd>
  32.  *
  33.  * <dt>Update Time</dt>
  34.  * <dd>
  35.  *   A timestamp representing when a product was created.
  36.  *
  37.  *   Update Time is also used as a <strong>version</strong>.
  38.  *   Products from the same <code>source</code> and <code>type</code> with
  39.  *   the same <code>code</code> are considered different versions of the
  40.  *   same product.
  41.  *
  42.  *   More recent (newer) <code>updateTime</code>s
  43.  *   supersede less recent (older) <code>updateTimes</code>.
  44.  * </dd>
  45.  * </dl>
  46.  */
  47. public class ProductId implements Comparable<ProductId> {

  48.     /** Product source. */
  49.     private String source;

  50.     /** Product type. */
  51.     private String type;

  52.     /** Product code. */
  53.     private String code;

  54.     /** Product update time. */
  55.     private Date updateTime;

  56.     /**
  57.      * Create a new ProductId.
  58.      *
  59.      * Same as new ProductId(type, code, source, new Date()).
  60.      * @param source
  61.      *            the product source.
  62.      * @param type
  63.      *            the product type.
  64.      * @param code
  65.      *            the product code.
  66.      */
  67.     public ProductId(final String source, final String type, final String code) {
  68.         this(source, type, code, new Date());
  69.     }

  70.     /**
  71.      * Create a new ProductId.
  72.      *
  73.      * @param source
  74.      *            the product source.
  75.      * @param type
  76.      *            the product type.
  77.      * @param code
  78.      *            the product code.
  79.      * @param updateTime
  80.      *            when the product was updated.
  81.      */
  82.     public ProductId(final String source, final String type, final String code,
  83.             final Date updateTime) {
  84.         setSource(source);
  85.         setType(type);
  86.         setCode(code);
  87.         setUpdateTime(updateTime);
  88.     }

  89.     /**
  90.      * @return the source
  91.      */
  92.     public String getSource() {
  93.         return source;
  94.     }

  95.     /**
  96.      * @param source
  97.      *            the source to set
  98.      */
  99.     public void setSource(String source) {
  100.         this.source = escapeIdPart(source);
  101.     }

  102.     /**
  103.      * @return the type
  104.      */
  105.     public String getType() {
  106.         return type;
  107.     }

  108.     /**
  109.      * @param type
  110.      *            the type to set
  111.      */
  112.     public void setType(String type) {
  113.         this.type = escapeIdPart(type);
  114.     }

  115.     /**
  116.      * @return the code
  117.      */
  118.     public String getCode() {
  119.         return code;
  120.     }

  121.     /**
  122.      * @param code
  123.      *            the code to set
  124.      */
  125.     public void setCode(String code) {
  126.         this.code = escapeIdPart(code);
  127.     }

  128.     /**
  129.      * @return the updateTime
  130.      */
  131.     public Date getUpdateTime() {
  132.         return updateTime;
  133.     }

  134.     /**
  135.      * @param updateTime
  136.      *            the updateTime to set
  137.      */
  138.     public void setUpdateTime(Date updateTime) {
  139.         this.updateTime = updateTime;
  140.     }

  141.     /**
  142.      * Convert this product id to a string. This string does not include the
  143.      * update time.
  144.      *
  145.      * @return a product id string.
  146.      */
  147.     public String toString() {
  148.         return "urn:usgs-product:" + source + ":" + type + ":" + code + ":"
  149.                 + Long.toString(updateTime.getTime());
  150.     }

  151.     /**
  152.      * Parse a product id string.
  153.      *
  154.      * @param str
  155.      *            a valid product id string.
  156.      * @return a ProductId object.
  157.      */
  158.     public static ProductId parse(final String str) {
  159.         String[] parts = str.split(":");
  160.         try {
  161.             if (!"urn".equals(parts[0]) || !"usgs-product".equals(parts[1])) {
  162.                 throw new Exception("Expected product urn");
  163.             }
  164.             String source = parts[2];
  165.             String type = parts[3];
  166.             String code = parts[4];
  167.             String updateTime = parts[5];
  168.             return new ProductId(source, type, code, new Date(Long
  169.                     .valueOf(updateTime)));
  170.         } catch (Exception e) {
  171.             throw new IllegalArgumentException("Invalid ProductId '" + str + "'");
  172.         }
  173.     }

  174.     /**
  175.      * Override the default Object.equals().
  176.      */
  177.     @Override
  178.     public boolean equals(final Object obj) {
  179.         return obj != null && obj instanceof ProductId
  180.                 && this.compareTo((ProductId) obj) == 0;
  181.     }

  182.     /**
  183.      * Implement the Comparable interface.
  184.      *
  185.      * @param that
  186.      *            product id being compared.
  187.      * @return -1 if this precedes that, 0 if same, and 1 if that precedes this.
  188.      */
  189.     @Override
  190.     public int compareTo(ProductId that) {
  191.         int compare = getUpdateTime().compareTo(that.getUpdateTime());

  192.         // same update time?
  193.         if (compare == 0) {
  194.             // to string includes source, type, code
  195.             compare = toString().compareTo(that.toString());
  196.         }

  197.         return compare;
  198.     }

  199.     /**
  200.      * Override default Object.hashCode().
  201.      */
  202.     @Override
  203.     public int hashCode() {
  204.         return Objects.hash(getSource(), getType(), getCode(), getUpdateTime());
  205.     }

  206.     /**
  207.      * Whether these are the same product, even if they are different versions.
  208.      *
  209.      * It is possible for isSameProduct to return true if equals returns false,
  210.      * but if equals returns true isSameProduct will also return true.
  211.      *
  212.      * @param that
  213.      *            a ProductId to test.
  214.      * @return true if these are the same product (source,type,code), false
  215.      *         otherwise.
  216.      */
  217.     public boolean isSameProduct(ProductId that) {
  218.         if (getSource().equals(that.getSource())
  219.                 && getType().equals(that.getType())
  220.                 && getCode().equals(that.getCode())) {
  221.             return true;
  222.         }
  223.         return false;
  224.     }

  225.     /**
  226.      * Escape id parts so they do not interfere with formatting/parsing.
  227.      *
  228.      * @param part
  229.      *            part to escape.
  230.      * @return escaped part.
  231.      */
  232.     private String escapeIdPart(final String part) {
  233.         if (part == null) {
  234.             return null;
  235.         }

  236.         String escaped = part;
  237.         if (escaped.indexOf(":") != -1) {
  238.             escaped = escaped.replace(":", "_");
  239.         }
  240.         return escaped;
  241.     }
  242. }