001/*
002 * Copyright 2014 Global Biodiversity Information Facility (GBIF)
003 * Licensed under the Apache License, Version 2.0 (the "License");
004 * you may not use this file except in compliance with the License.
005 * You may obtain a copy of the License at
006 * http://www.apache.org/licenses/LICENSE-2.0
007 * Unless required by applicable law or agreed to in writing, software
008 * distributed under the License is distributed on an "AS IS" BASIS,
009 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010 * See the License for the specific language governing permissions and
011 * limitations under the License.
012 */
013package org.gbif.api.model.occurrence;
014
015import org.gbif.api.jackson.ExtensionKeyDeserializer;
016import org.gbif.api.jackson.ExtensionSerializer;
017import org.gbif.api.jackson.TermMapListDeserializer;
018import org.gbif.api.jackson.TermMapListSerializer;
019import org.gbif.api.vocabulary.Country;
020import org.gbif.api.vocabulary.EndpointType;
021import org.gbif.api.vocabulary.Extension;
022import org.gbif.dwc.terms.Term;
023import org.gbif.dwc.terms.TermFactory;
024
025import java.util.Date;
026import java.util.List;
027import java.util.Map;
028import java.util.UUID;
029
030import javax.annotation.Nullable;
031import javax.validation.constraints.NotNull;
032
033import com.google.common.base.Objects;
034import com.google.common.base.Strings;
035import com.google.common.collect.Maps;
036import org.codehaus.jackson.annotate.JsonAnyGetter;
037import org.codehaus.jackson.annotate.JsonAnySetter;
038import org.codehaus.jackson.annotate.JsonIgnore;
039import org.codehaus.jackson.map.annotate.JsonDeserialize;
040import org.codehaus.jackson.map.annotate.JsonSerialize;
041import static com.google.common.base.Preconditions.checkNotNull;
042
043/**
044 * An extended map holding all core terms of an occurrence record.
045 * Major extensions that we index are also supported, i.e. media, identifiers and measurements or facts.
046 */
047public class VerbatimOccurrence {
048
049  private Integer key;
050  private UUID datasetKey;
051  private UUID publishingOrgKey;
052  private Country publishingCountry;
053  private EndpointType protocol;
054  private Date lastCrawled;
055  private Date lastParsed;
056  private Integer crawlId;
057
058  // the verbatim fields for the occurrence
059  private Map<Term, String> verbatimFields = Maps.newHashMap();
060  // verbatim extension data
061  private Map<Extension, List<Map<Term, String>>> extensions = Maps.newHashMap();
062
063  /**
064   * Get the value of a specific field (Term).
065   */
066  @Nullable
067  public String getVerbatimField(Term term) {
068    checkNotNull(term, "term can't be null");
069    return verbatimFields.get(term);
070  }
071
072  /**
073   * @return true if a verbatim field exists and is not null or an empty string
074   */
075  public boolean hasVerbatimField(Term term) {
076    checkNotNull(term, "term can't be null");
077    return !Strings.isNullOrEmpty(verbatimFields.get(term));
078  }
079
080  /**
081   * For setting a specific field without having to replace the entire verbatimFields Map.
082   *
083   * @param term the field to set
084   * @param fieldValue the field's value
085   */
086  public void setVerbatimField(Term term, @Nullable String fieldValue) {
087    checkNotNull(term, "term can't be null");
088    verbatimFields.put(term, fieldValue);
089  }
090
091  /**
092   * The GBIF assigned, persistent key to the occurrence record.
093   * OccurrenceID itself is kept in the verbatim verbatimFields map.
094   */
095  @NotNull
096  public Integer getKey() {
097    return key;
098  }
099
100  public void setKey(Integer key) {
101    this.key = key;
102  }
103
104  @NotNull
105  public UUID getDatasetKey() {
106    return datasetKey;
107  }
108
109  public void setDatasetKey(UUID datasetKey) {
110    this.datasetKey = datasetKey;
111  }
112
113  @NotNull
114  public UUID getPublishingOrgKey() {
115    return publishingOrgKey;
116  }
117
118  public void setPublishingOrgKey(UUID publishingOrgKey) {
119    this.publishingOrgKey = publishingOrgKey;
120  }
121
122  /**
123   * The country of the organization that publishes the dataset to which the occurrence belongs.
124   */
125  @Nullable
126  public Country getPublishingCountry() {
127    return publishingCountry;
128  }
129
130  public void setPublishingCountry(Country publishingCountry) {
131    this.publishingCountry = publishingCountry;
132  }
133
134  @NotNull
135  public EndpointType getProtocol() {
136    return protocol;
137  }
138
139  public void setProtocol(EndpointType protocol) {
140    this.protocol = protocol;
141  }
142
143  @Nullable
144  /**
145   * The date this record was last crawled/harvested from the endpoint.
146   */
147  public Date getLastCrawled() {
148    return lastCrawled == null ? null : new Date(lastCrawled.getTime());
149  }
150
151  public void setLastCrawled(@Nullable Date lastCrawled) {
152    this.lastCrawled = lastCrawled == null ? null : new Date(lastCrawled.getTime());
153  }
154
155  /**
156   * The date this record was last parsed from raw xml/json into verbatim verbatimFields.
157   */
158  @Nullable
159  public Date getLastParsed() {
160    return lastParsed;
161  }
162
163  public void setLastParsed(@Nullable Date lastParsed) {
164    this.lastParsed = lastParsed == null ? null : new Date(lastParsed.getTime());
165  }
166
167  /**
168   * Crawling attempt id.
169   */
170  @Nullable
171  public Integer getCrawlId() {
172    return crawlId;
173  }
174
175  public void setCrawlId(Integer crawlId) {
176    this.crawlId = crawlId;
177  }
178
179  /**
180   * A map holding all verbatim core terms.
181   */
182  @NotNull
183  @JsonIgnore
184  public Map<Term, String> getVerbatimFields() {
185    return verbatimFields;
186  }
187
188  public void setVerbatimFields(Map<Term, String> verbatimFields) {
189    this.verbatimFields = verbatimFields;
190  }
191
192  /**
193   * A map holding all verbatim extension terms.
194   */
195  @NotNull
196  @JsonSerialize(keyUsing = ExtensionSerializer.class, contentUsing = TermMapListSerializer.class)
197  @JsonDeserialize(keyUsing = ExtensionKeyDeserializer.class, contentUsing = TermMapListDeserializer.class)
198  public Map<Extension, List<Map<Term, String>>> getExtensions() {
199    return extensions;
200  }
201
202  public void setExtensions(Map<Extension, List<Map<Term, String>>> extensions) {
203    this.extensions = extensions;
204  }
205
206  @Override
207  public String toString() {
208    return Objects.toStringHelper(this).add("lastParsed", lastParsed).add("key", key).add("datasetKey", datasetKey)
209      .add("publishingOrgKey", publishingOrgKey).add("publishingCountry", publishingCountry).add("protocol", protocol)
210      .add("crawlId",crawlId)
211      .add("lastCrawled", lastCrawled)
212      .add("extensions", extensions)
213      .toString();
214  }
215
216  @Override
217  public int hashCode() {
218    return Objects
219      .hashCode(key, datasetKey, publishingOrgKey, publishingCountry, protocol, lastCrawled, lastParsed, crawlId,
220                verbatimFields, extensions);
221  }
222
223  @Override
224  public boolean equals(Object obj) {
225    if (this == obj) {
226      return true;
227    }
228    if (obj == null || getClass() != obj.getClass()) {
229      return false;
230    }
231    final VerbatimOccurrence other = (VerbatimOccurrence) obj;
232    return Objects.equal(this.key, other.key)
233      && Objects.equal(this.datasetKey, other.datasetKey)
234      && Objects.equal(this.publishingOrgKey, other.publishingOrgKey)
235      && Objects.equal(this.publishingCountry, other.publishingCountry)
236      && Objects.equal(this.protocol, other.protocol)
237      && Objects.equal(this.lastCrawled, other.lastCrawled)
238      && Objects.equal(this.lastParsed, other.lastParsed)
239      && Objects.equal(this.verbatimFields, other.verbatimFields)
240      && Objects.equal(this.extensions, other.extensions)
241      && Objects.equal(this.crawlId, other.crawlId);
242  }
243
244  /**
245   * This private method is only for deserialization via jackson and not exposed anywhere else!
246   */
247  @JsonAnySetter
248  private void addJsonVerbatimField(String key, String value) {
249    if(!Strings.isNullOrEmpty(value)) {
250      Term t = TermFactory.instance().findTerm(key);
251      verbatimFields.put(t, value);
252    }
253  }
254
255  /**
256   * This private method is only for serialization via jackson and not exposed anywhere else!
257   * It maps the verbatimField terms into properties with their full qualified name.
258   */
259  @JsonAnyGetter
260  private Map<String, String> jsonVerbatimFields() { // note: for 1.6.0 MUST use non-getter name; otherwise doesn't matter
261    Map<String, String> extendedProps = Maps.newHashMap();
262    for (Map.Entry<Term, String> prop : verbatimFields.entrySet()) {
263      extendedProps.put(prop.getKey().qualifiedName(), prop.getValue());
264    }
265    return extendedProps;
266  }
267}