/*
* Copyright 2012 Esri.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.esri.gpt.control.georss;
import com.esri.gpt.catalog.search.IMapViewer;
import com.esri.gpt.catalog.search.MapViewerFactory;
import com.esri.gpt.catalog.search.ResourceIdentifier;
import com.esri.gpt.catalog.search.ResourceLink;
import com.esri.gpt.catalog.search.ResourceLinkBuilder;
import com.esri.gpt.framework.collection.StringAttribute;
import com.esri.gpt.framework.context.ApplicationContext;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.jsf.MessageBroker;
import com.esri.gpt.framework.util.LogUtil;
import com.esri.gpt.framework.util.Val;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
/**
* Feed link builder.
*/
public class FeedLinkBuilder {
private static final Logger LOGGER = Logger.getLogger(FeedLinkBuilder.class.getCanonicalName());
private static final TreeSet<String> knownContentTypes = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
static {
knownContentTypes.add("unknown");
knownContentTypes.add("liveData");
knownContentTypes.add("downloadableData");
knownContentTypes.add("offlineData");
knownContentTypes.add("staticMapImage");
knownContentTypes.add("document");
knownContentTypes.add("application");
knownContentTypes.add("geographicService");
knownContentTypes.add("clearinghouse");
knownContentTypes.add("mapFiles");
knownContentTypes.add("geographicActivities");
}
private static final TreeMap<String,String> additionalContentTypes = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
static {
additionalContentTypes.put("CSW", "geographicService");
}
private final String metadataPath = "/rest/document";
private final String resourceDetailsPath = "/catalog/search/resource/details.page";
private final String resourcePreviewPath = "/catalog/search/resource/livedata-preview.page";
private final String imagePath = "/catalog/images";
private RequestContext context;
private String baseContextPath;
private MessageBroker messageBroker;
private Map<String, String> labels = new HashMap<String, String>();
private final String mapViewerUrl;
private ResourceIdentifier resourceIdentifier;
/**
* Creates instance of the link builder.
*
* @param context request context
* @param baseContextPath base context path
* @param messageBroker message broker
*/
public FeedLinkBuilder(RequestContext context, String baseContextPath, MessageBroker messageBroker) {
this.context = context;
this.baseContextPath = baseContextPath;
this.messageBroker = messageBroker;
this.mapViewerUrl = context.getCatalogConfiguration().getSearchConfig().getMapViewerUrl();
this.resourceIdentifier = ResourceIdentifier.newIdentifier(context);
}
/**
* Gets map viewer URL.
*
* @return map viewer URL
*/
protected String getMapViewerUrl() {
return mapViewerUrl;
}
/**
* Buids links and resources.
*
* @param record record
*/
public void build(IFeedRecord record) {
// links
buildOpenLink(record);
buildMetadataLink(record);
buildDetailsLink(record);
buildPreviewLink(record);
buildAGSLinks(record);
buildAddToMapLink(record);
// resource links
buildContentTypeResource(record);
buildThumbnailResource(record);
}
/**
* Builds ArcGIS links.
* @param record record
*/
protected void buildAGSLinks(IFeedRecord record) {
String resourceUrl = Val.chkStr(record.getResourceUrl());
String serviceType = Val.chkStr(record.getServiceType()).toLowerCase();
String restUrl = Val.chkStr(resourceIdentifier.guessAgsServiceRestUrl(resourceUrl));
String url;
String resourceKey;
ResourceLink link;
if ((restUrl.length() > 0) && serviceType.equals("ags")) {
// kml
if ((restUrl.toLowerCase().endsWith("/mapserver") || restUrl.toLowerCase().endsWith("/imageserver"))) {
url = restUrl + "/kml/mapImage.kmz";
if (restUrl.toLowerCase().endsWith("/imageserver")) {
url = restUrl + "/kml/image.kmz";
}
resourceKey = "catalog.rest.addToGlobeKml";
link = this.makeLink(url, ResourceLink.TAG_AGSKML, resourceKey);
record.getResourceLinks().add(link);
}
// nmf
if ((restUrl.toLowerCase().endsWith("/mapserver") || restUrl.toLowerCase().endsWith("/imageserver"))) {
url = restUrl + "?f=nmf";
resourceKey = "catalog.rest.addToGlobeNmf";
link = this.makeLink(url, ResourceLink.TAG_AGSNMF, resourceKey);
record.getResourceLinks().add(link);
}
// lyr
if ((restUrl.toLowerCase().endsWith("/mapserver") || restUrl.toLowerCase().endsWith("/imageserver") || restUrl.toLowerCase().endsWith("/globeserver"))) {
url = restUrl + "?f=lyr";
resourceKey = "catalog.rest.addToArcMap";
link = this.makeLink(url, ResourceLink.TAG_AGSLYR, resourceKey);
record.getResourceLinks().add(link);
}
}
}
/**
* Builds 'Add To Map' link.
* @param record record
*/
protected void buildAddToMapLink(IFeedRecord record) {
String resourceUrl = Val.chkStr(record.getResourceUrl());
String serviceType = Val.chkStr(record.getServiceType()).toLowerCase();
String serviceName = Val.chkStr(record.getService());
String viewerUrl = Val.chkStr(this.getMapViewerUrl());
IMapViewer iMapViewer = null;
if ((viewerUrl.length() != 0)) {
// return if a map viewer link cannot be built
if (resourceUrl.length() == 0 || (serviceType.length() == 0)) {
return;
} else {
boolean canHandle = (serviceType.equalsIgnoreCase(ResourceLinkBuilder.ServiceType.AGS.name())
|| serviceType.equalsIgnoreCase(ResourceLinkBuilder.ServiceType.AIMS.name())
|| serviceType.equalsIgnoreCase(ResourceLinkBuilder.ServiceType.WMS.name())
|| serviceType.equalsIgnoreCase(ResourceLinkBuilder.ServiceType.WFS.name()) || serviceType
.equalsIgnoreCase(ResourceLinkBuilder.ServiceType.WCS.name()));
if (!canHandle) {
return;
}
}
} else {
iMapViewer = MapViewerFactory.createMapViewer(resourceUrl, serviceType, record, context);
if (iMapViewer == null) {
return;
}
String addToMapUrl = iMapViewer.readAddToMapUrl();
if (addToMapUrl == null || "".equals(addToMapUrl)) {
return;
}
String resourceKey = "catalog.rest.addToMap";
ResourceLink link = this.makeLink(addToMapUrl, ResourceLink.TAG_ADDTOMAP,
resourceKey);
link.setTarget(iMapViewer.readTarget());
record.getResourceLinks().add(link);
return;
}
// maybe url is already an add to map url? if so no modigications are required
if ((resourceUrl.toLowerCase().startsWith("http:") || resourceUrl
.toLowerCase().startsWith("https:"))
&& resourceUrl.toLowerCase().contains("resources=map")) {
} else {
// set up an ArcGIS soap service for the viewer
if (serviceType.equals("ags")) {
resourceUrl = Val.chkStr(resourceIdentifier.guessAgsMapServerSoapUrl(resourceUrl));
}
// set up an ArcIMS service for the viewer
int nIdx = resourceUrl.toLowerCase().indexOf(
"/servlet/com.esri.esrimap.esrimap?servicename=");
if (nIdx > 0) {
resourceUrl = resourceUrl.substring(0, nIdx) + "/"
+ resourceUrl.substring(nIdx + 46);
}
// set up an OGC service for the viewer
if (serviceType.equalsIgnoreCase("wms")
|| serviceType.equalsIgnoreCase("wfs")
|| serviceType.equalsIgnoreCase("wcs")) {
nIdx = resourceUrl.toLowerCase().indexOf("?");
if (nIdx > 0) {
resourceUrl = resourceUrl.substring(0, nIdx);
}
}
}
// TODO: explanation?
resourceUrl = resourceUrl.replaceAll("/$", "")
+ (serviceName.length() > 0 ? "/" + serviceName : "");
resourceUrl = Val.chkStr(resourceUrl);
if (resourceUrl.length() > 0) {
String url = viewerUrl + "?resources=map:" + serviceType + "@"
+ resourceUrl;
String resourceKey = "catalog.rest.addToMap";
ResourceLink link = this.makeLink(url, ResourceLink.TAG_ADDTOMAP,
resourceKey);
record.getResourceLinks().add(link);
}
String url = "";
try {
RequestContext rContext = context;
ServletRequest sRequest = rContext.getServletRequest();
if (sRequest instanceof HttpServletRequest) {
HttpServletRequest hSRequest = (HttpServletRequest) sRequest;
String path = Val.chkStr(hSRequest.getPathInfo()).toLowerCase();
if (path.contains("catalog/search/search.page")
|| path.contains("catalog/main/home.page")) {
url = "javascript:GptUtils.popUp(\'" + url + "\',"
+ "GptMapViewer.TITLE," + "GptMapViewer.dimensions.WIDTH,"
+ "GptMapViewer.dimensions.HEIGHT);";
}
}
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error creating 'Add to Map' link.", e);
}
}
/**
* Builds 'Metadata' link.
* @param record record
*/
protected void buildMetadataLink(IFeedRecord record) {
String uuid = Val.chkStr(record.getUuid());
String url = baseContextPath + metadataPath + "?id=" + encodeUrlParam(uuid);
String resourceKey = "catalog.rest.viewFullMetadata";
ResourceLink link = makeLink(url, ResourceLink.TAG_METADATA, resourceKey);
record.getResourceLinks().add(link);
}
/**
* Builds 'Details' link.
* @param record record
*/
protected void buildDetailsLink(IFeedRecord record) {
String uuid = Val.chkStr(record.getUuid());
String url = baseContextPath + this.resourceDetailsPath + "?uuid=" + encodeUrlParam(uuid);
String resourceKey = "catalog.rest.viewDetails";
ResourceLink link = makeLink(url, ResourceLink.TAG_DETAILS, resourceKey);
record.getResourceLinks().add(link);
}
/**
* Builds 'Preview' link.
* @param record record
*/
protected void buildPreviewLink(IFeedRecord record) {
String id = Val.chkStr(record.getUuid());
String resourceUrl = Val.chkStr(record.getResourceUrl());
String serviceType = Val.chkStr(record.getServiceType()).toLowerCase();
String tmp = resourceUrl.toLowerCase();
if ((resourceUrl.length() == 0)) {
return;
} else if (tmp.indexOf("?getxml=") != -1) {
return;
}
String sFilter = Val.chkStr(ApplicationContext.getInstance().getConfiguration().getCatalogConfiguration().getParameters().getValue("resourceLinkBuilder.preview.filter"));
if (sFilter.length() > 0) {
try {
Pattern pattern = Pattern.compile(sFilter, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(resourceUrl);
if (matcher.matches()) {
return;
}
} catch (Exception ex) {
}
}
String url = baseContextPath + resourcePreviewPath + "?uuid=" + encodeUrlParam(id) + "&url=" + encodeUrlParam(resourceUrl);
if (serviceType.length() > 0) {
url += "&resourceType=" + serviceType;
}
String infoUrl = baseContextPath + this.metadataPath + "?f=html&showRelativeUrl=true&id=" + encodeUrlParam(id);
url += "&info=" + encodeUrlParam(infoUrl);
String resourceKey = "catalog.rest.preview";
ResourceLink link = this.makeLink(url, ResourceLink.TAG_PREVIEW, resourceKey);
if (serviceType.length() > 0) {
link.getParameters().add(new StringAttribute("resourceType", serviceType));
}
record.getResourceLinks().add(link);
}
/**
* Builds 'Open' link.
* @param record record
*/
protected void buildOpenLink(IFeedRecord record) {
String resourceUrl = Val.chkStr(record.getResourceUrl());
String serviceType = Val.chkStr(record.getServiceType()).toLowerCase();
// return if we cannot open
if ((resourceUrl.length() == 0) || serviceType.equals("aims")) {
return;
}
String resourceKey = "catalog.rest.open";
ResourceLink link = this.makeLink(resourceUrl, ResourceLink.TAG_OPEN,
resourceKey);
if (serviceType.length() > 0) {
link.getParameters().add(new StringAttribute("resourceType", serviceType));
}
record.getResourceLinks().add(link);
}
/**
* Builds 'Content Type' resource link.
* @param record record
*/
protected void buildContentTypeResource(IFeedRecord record) {
String contentType = record.getContentType().isEmpty() ? "unknown" : record.getContentType();
if (!knownContentTypes.contains(contentType)) {
if (additionalContentTypes.containsKey(contentType)) {
contentType = additionalContentTypes.get(contentType);
} else {
contentType = "unknown";
}
}
String url = baseContextPath + imagePath + "/ContentType_" + contentType + ".png";
String resourceKey = "catalog.search.filterContentTypes." + contentType;
ResourceLink link = this.makeLink(url, ResourceLink.TAG_CONTENTTYPE, resourceKey);
record.getResourceLinks().setIcon(link);
record.getResourceLinks().add(link);
}
/**
* Builds 'Thumbnail' resource link.
* @param record record
*/
protected void buildThumbnailResource(IFeedRecord record) {
IFeedAttribute thumbUrl = record.getData(IFeedRecord.STD_COLLECTION_INDEX).get("thumbnail.url");
if (thumbUrl != null && thumbUrl.getValue() instanceof List && !((List) thumbUrl.getValue()).isEmpty()) {
Object oVal = ((List) thumbUrl.getValue()).get(0);
if (oVal instanceof IFeedAttribute && ((IFeedAttribute) oVal).getValue() instanceof String) {
String url = (String) ((IFeedAttribute) oVal).getValue();
String resourceKey = "catalog.rest.thumbNail";
ResourceLink link = this.makeLink(url, ResourceLink.TAG_THUMBNAIL, resourceKey);
record.getResourceLinks().setThumbnail(link);
}
}
}
/**
* Makes a resource link.
* @param url URL
* @param tag tag
* @param resourceKey resource key for the label
* @return link
*/
protected ResourceLink makeLink(String url, String tag, String resourceKey) {
ResourceLink link = new ResourceLink();
link.setUrl(url);
link.setTag(tag);
link.setLabelResourceKey(resourceKey);
link.setLabel(Val.chkStr(makeLabel(resourceKey)));
return link;
}
/**
* Makes label.
* @param resourceKey resource key for the label
* @return label
*/
protected String makeLabel(String resourceKey) {
String label = labels.get(resourceKey);
if (label == null) {
label = messageBroker.retrieveMessage(resourceKey);
labels.put(resourceKey, label);
}
label = Val.chkStr(label);
if (label.length() > 0) {
return label;
} else {
return resourceKey;
}
}
/**
* Encodes URL parameter.
* @param value parameter to encode
* @return encoded parameter
*/
protected String encodeUrlParam(String value) {
value = Val.chkStr(value);
try {
return URLEncoder.encode(value, "UTF-8");
} catch (UnsupportedEncodingException ex) {
LogUtil.getLogger().fine("Unsupported encoding: UTF-8");
return value;
}
}
}