package cgeo.geocaching.connector.su; import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByGeocode; import cgeo.geocaching.connector.capability.ISearchByViewPort; import cgeo.geocaching.connector.gc.MapTokens; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.GeopointFormatter; import cgeo.geocaching.location.Viewport; import cgeo.geocaching.models.Geocache; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.DisposableHandler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.io.InputStream; import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.lang3.StringUtils; public class GeocachingSuConnector extends AbstractConnector implements ISearchByCenter, ISearchByGeocode, ISearchByViewPort { static final CharSequence PREFIX_MULTISTEP_VIRTUAL = "MV"; static final CharSequence PREFIX_TRADITIONAL = "TR"; static final CharSequence PREFIX_VIRTUAL = "VI"; static final CharSequence PREFIX_MULTISTEP = "MS"; static final CharSequence PREFIX_EVENT = "EV"; static final CharSequence PREFIX_CONTEST = "CT"; /** * base URL for all API operations */ private static final String API_URL = "http://www.geocaching.su/site/api.php?"; /** * kind of request to the server */ private static final String PARAMETER_REQUEST_TYPE = "rtype"; private static final String REQUEST_TYPE_BOUNDING_BOX = "0"; private static final String REQUEST_TYPE_CENTER = "1"; private static final String REQUEST_TYPE_CACHE = "2"; /** * level of detail for the result to be returned */ private static final String PARAMETER_RESULT_FIELDS = "istr"; private static final String RESULT_FIELDS_SEARCH = "ms"; private static final String RESULT_FIELDS_DETAILED = "abcdefghiklmnops"; private GeocachingSuConnector() { // singleton } /** * initialization on demand holder pattern */ private static class Holder { @NonNull private static final GeocachingSuConnector INSTANCE = new GeocachingSuConnector(); } public static GeocachingSuConnector getInstance() { return Holder.INSTANCE; } @Override @NonNull public String getName() { return "Geocaching.su"; } @Override @NonNull public String getNameAbbreviated() { return "GCSU"; } @Override @NonNull public String getCacheUrl(@NonNull final Geocache cache) { return getCacheUrlPrefix() + "&cid=" + getCacheId(cache.getGeocode()); } private static String getCacheId(final String geocode) { if (Character.isDigit(geocode.charAt(0))) { return geocode; } return StringUtils.substring(geocode, 2); } @Override public boolean getHttps() { return false; } @Override @NonNull public String getHost() { return "www.geocaching.su"; } @Override public boolean isOwner(@NonNull final Geocache cache) { return false; } @Override public boolean canHandle(@NonNull final String geocode) { return StringUtils.startsWithAny(StringUtils.upperCase(geocode), PREFIX_TRADITIONAL, PREFIX_MULTISTEP_VIRTUAL, PREFIX_VIRTUAL, PREFIX_MULTISTEP, PREFIX_EVENT, PREFIX_CONTEST) && isNumericId(geocode.substring(2)); } @Override @NonNull protected String getCacheUrlPrefix() { return getHostUrl() + "/?pn=101"; } @Override public boolean isActive() { return Settings.isSUConnectorActive(); } @Override public SearchResult searchByCenter(@NonNull final Geopoint center) { return searchCaches("cache", new Parameters(PARAMETER_REQUEST_TYPE, REQUEST_TYPE_CENTER, "radius", "40", "clng", GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center), "clat", GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center), PARAMETER_RESULT_FIELDS, RESULT_FIELDS_SEARCH)); } @Override public SearchResult searchByGeocode(@Nullable final String geocode, @Nullable final String guid, final DisposableHandler handler) { final String id = StringUtils.substring(geocode, 2); return searchCaches("data", new Parameters(PARAMETER_REQUEST_TYPE, REQUEST_TYPE_CACHE, "cid", id, PARAMETER_RESULT_FIELDS, RESULT_FIELDS_DETAILED)); } @Override @NonNull public SearchResult searchByViewport(@NonNull final Viewport viewport, @Nullable final MapTokens tokens) { final Geopoint min = viewport.bottomLeft; final Geopoint max = viewport.topRight; return searchCaches("cache", new Parameters(PARAMETER_REQUEST_TYPE, REQUEST_TYPE_BOUNDING_BOX, "lngmax", GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, max), "lngmin", GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, min), "latmax", GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, max), "latmin", GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, min), PARAMETER_RESULT_FIELDS, RESULT_FIELDS_SEARCH)); } private static SearchResult searchCaches(@NonNull final String endTag, @NonNull final Parameters parameters) { final InputStream responseStream = Network.getResponseStream(Network.getRequest(API_URL, parameters)); try { return parseCaches(endTag, responseStream); } finally { IOUtils.closeQuietly(responseStream); } } private static SearchResult parseCaches(@NonNull final String endTag, final InputStream inputStream) { return GeocachingSuParser.parseCaches(endTag, inputStream); } }