package cgeo.geocaching.models; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.trackable.TrackableBrand; import cgeo.geocaching.connector.trackable.TrackableConnector; import cgeo.geocaching.log.LogEntry; import cgeo.geocaching.log.LogTypeTrackable; import cgeo.geocaching.utils.HtmlUtils; import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.TextUtils; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; public class Trackable implements ILogable { private static final int SPOTTED_UNSET = 0; public static final int SPOTTED_CACHE = 1; public static final int SPOTTED_USER = 2; public static final int SPOTTED_UNKNOWN = 3; public static final int SPOTTED_OWNER = 4; public static final int SPOTTED_ARCHIVED = 5; private String guid = ""; private String geocode = ""; private String iconUrl = ""; private String name = ""; private String type = null; @Nullable private Date released = null; private float distance = -1; private String origin = null; private String owner = null; private String ownerGuid = null; private String spottedName = null; private int spottedType = SPOTTED_UNSET; private String spottedGuid = null; private String goal = null; private String details = null; private String image = null; private List<LogEntry> logs = new ArrayList<>(); private String trackingcode = null; private TrackableBrand brand = null; private TrackableConnector trackableConnector = null; private Boolean missing = null; private boolean locked = false; /** * Merge data from another Trackable. * Squeeze existing data from the new one. * * @param newTrackable from which to pull informations */ public void mergeTrackable(final Trackable newTrackable) { guid = StringUtils.defaultIfEmpty(newTrackable.guid, guid); geocode = StringUtils.defaultIfEmpty(newTrackable.geocode, geocode); iconUrl = StringUtils.defaultIfEmpty(newTrackable.iconUrl, iconUrl); name = StringUtils.defaultIfEmpty(newTrackable.name, name); type = ObjectUtils.defaultIfNull(newTrackable.type, type); released = ObjectUtils.defaultIfNull(newTrackable.released, released); distance = newTrackable.distance == -1 ? distance : newTrackable.distance; origin = ObjectUtils.defaultIfNull(newTrackable.origin, origin); owner = ObjectUtils.defaultIfNull(newTrackable.owner, owner); ownerGuid = ObjectUtils.defaultIfNull(newTrackable.ownerGuid, ownerGuid); spottedName = ObjectUtils.defaultIfNull(newTrackable.spottedName, spottedName); spottedType = newTrackable.spottedType == SPOTTED_UNSET ? spottedType : newTrackable.spottedType; spottedGuid = ObjectUtils.defaultIfNull(newTrackable.spottedGuid, spottedGuid); goal = ObjectUtils.defaultIfNull(newTrackable.goal, goal); details = ObjectUtils.defaultIfNull(newTrackable.details, details); image = ObjectUtils.defaultIfNull(newTrackable.image, image); mergeLogEntry(newTrackable.logs); trackingcode = ObjectUtils.defaultIfNull(newTrackable.trackingcode, trackingcode); brand = ObjectUtils.defaultIfNull(newTrackable.brand, brand); trackableConnector = ObjectUtils.defaultIfNull(newTrackable.trackableConnector, trackableConnector); missing = ObjectUtils.defaultIfNull(newTrackable.missing, missing); } /** * Merge another logEntry list into current logs list. * No duplicates. * LogEntry are then sorted by date. * * @param newLogs to merge */ public void mergeLogEntry(final List<LogEntry> newLogs) { for (final LogEntry newLog : newLogs) { if (!logs.contains(newLog)) { logs.add(newLog); } } Collections.sort(logs, LogEntry.DESCENDING_DATE_COMPARATOR); } /** * Check whether this trackable has a corresponding URL. */ public boolean hasUrl() { return getConnector().hasTrackableUrls(); } @NonNull public String getUrl() { return getConnector().getUrl(this); } @NonNull private TrackableConnector getConnector() { if (trackableConnector == null) { trackableConnector = ConnectorFactory.getConnector(this); } return trackableConnector; } public String getGuid() { return guid; } public void setGuid(final String guid) { this.guid = guid; } @Override public String getGeocode() { return geocode; } public String getUniqueID() { if (StringUtils.isNotEmpty(guid)) { return guid; } if (StringUtils.isNotEmpty(geocode)) { return geocode; } throw new IllegalStateException("Trackable must have at least one of geocode or guid"); } public void setGeocode(final String geocode) { this.geocode = StringUtils.upperCase(geocode); } public String getIconUrl() { return iconUrl; } @DrawableRes public int getIconBrand() { return getBrand().getIconResource(); } public void forceSetBrand(final TrackableBrand trackableBrand) { brand = trackableBrand; } public TrackableBrand getBrand() { if (brand == null) { // Only TravelBug have a guid if (StringUtils.isNotEmpty(guid)) { brand = TrackableBrand.TRAVELBUG; return brand; } // Consult all other Trackable connectors if (StringUtils.isNotEmpty(geocode)) { final TrackableConnector connector = ConnectorFactory.getTrackableConnector(geocode); brand = connector.getBrand(); return brand; } // Fallback to Unknown brand = TrackableBrand.UNKNOWN; } return brand; } public void setIconUrl(final String iconUrl) { this.iconUrl = iconUrl; } @Override public String getName() { return name; } public void setName(final String name) { this.name = name; } public String getType() { return type; } public void setType(final String type) { this.type = type; } @Nullable public Date getReleased() { if (released != null) { return new Date(released.getTime()); } return null; } public void setReleased(@Nullable final Date released) { this.released = released == null ? null : new Date(released.getTime()); // avoid storing external reference in this object } public float getDistance() { return distance; } public void setDistance(final float distance) { this.distance = distance; } public String getOrigin() { return origin; } public void setOrigin(final String origin) { this.origin = origin; } public String getOwner() { return owner; } public void setOwner(final String owner) { this.owner = owner; } public String getOwnerGuid() { return ownerGuid; } public void setOwnerGuid(final String ownerGuid) { this.ownerGuid = ownerGuid; } public String getSpottedName() { return spottedName; } public void setSpottedName(final String spottedName) { this.spottedName = spottedName; } public int getSpottedType() { return spottedType; } public void setSpottedType(final int spottedType) { this.spottedType = spottedType; } public String getSpottedGuid() { return spottedGuid; } public void setSpottedGuid(final String spottedGuid) { this.spottedGuid = spottedGuid; } public String getGoal() { return goal; } public void setGoal(final String goal) { this.goal = HtmlUtils.removeExtraTags(goal); } public String getDetails() { return details; } public void setDetails(final String details) { this.details = HtmlUtils.removeExtraTags(details); } public String getImage() { return image; } public void setImage(final String image) { this.image = image; } /** * Get the trackable missing status. * False if missing status is unknown. * * @return true if missing from cache */ public boolean isMissing() { return missing != null && missing; } /** * Set the trackable missing status * * @param missing * the new missing status */ public void setMissing(final Boolean missing) { this.missing = missing; } @NonNull public List<LogEntry> getLogs() { return logs; } public void setLogs(final List<LogEntry> logs) { this.logs = logs != null ? logs : new ArrayList<LogEntry>(); } @Override public String toString() { if (name != null) { return TextUtils.stripHtml(name); } if (guid != null) { return guid; } return "???"; } public boolean isLoggable() { return getConnector().isLoggable() && !locked; } public void setIsLocked() { locked = true; } public String getTrackingcode() { return trackingcode; } public void setTrackingcode(final String trackingcode) { this.trackingcode = trackingcode; } @NonNull public Collection<Image> getImages() { final List<Image> images = new LinkedList<>(); if (StringUtils.isNotBlank(image)) { images.add(new Image.Builder().setUrl(image).setTitle(StringUtils.defaultIfBlank(name, geocode)).build()); } ImageUtils.addImagesFromHtml(images, geocode, getDetails()); for (final LogEntry log : getLogs()) { images.addAll(log.getLogImages()); } return images; } @NonNull public static List<LogTypeTrackable> getPossibleLogTypes() { final List<LogTypeTrackable> logTypes = new ArrayList<>(); logTypes.add(LogTypeTrackable.RETRIEVED_IT); logTypes.add(LogTypeTrackable.GRABBED_IT); logTypes.add(LogTypeTrackable.NOTE); logTypes.add(LogTypeTrackable.DISCOVERED_IT); return logTypes; } }