/* See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * Esri Inc. licenses this file to You 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.livedata; import com.esri.gpt.control.livedata.selector.HttpRequestDefinition; import com.esri.gpt.control.livedata.selector.IHttpResponseListener; import com.esri.gpt.control.livedata.selector.IRegistry; import com.esri.gpt.control.livedata.selector.ISetter; import com.esri.gpt.framework.geometry.Envelope; import com.esri.gpt.framework.http.ResponseInfo; import com.esri.gpt.framework.util.Val; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; import org.w3c.dom.Document; /** * ArcGISRendererFactory */ /*packge*/ class ArcGISRendererFactory extends MapBasedRendererFactory { /** * Creates instance of the factory. * @param properties properties */ public ArcGISRendererFactory(ILiveDataProperties properties) { super(properties); } @Override public boolean isDefinitive() { return true; } @Override public void register(IRegistry reg, final ISetter setter, final String url) { try { URL urlObj = new URL(url); // get just protocola name, host name and port number String fullHost = urlObj.getProtocol() + "://" + urlObj.getHost() + (urlObj.getPort() >= 0 && urlObj.getPort() != urlObj.getDefaultPort() ? ":" + urlObj.getPort() : ""); // get path and find deployment context String path = urlObj.getPath(); String ctx = ""; Pattern ctxPattern = Pattern.compile("^/\\p{Alnum}+/"); Matcher ctxMatcher = ctxPattern.matcher(path); if (ctxMatcher.find()) { ctx = path.substring(ctxMatcher.start(), ctxMatcher.end() - 1); path = path.substring(ctxMatcher.end() - 1); } String restServices = ""; if (path.startsWith("/rest/services/")) { path = path.substring("/rest/services/".length() - 1); restServices = "/rest/services"; } else if (path.startsWith("/services/")) { path = path.substring("/services/".length() - 1); restServices = "/services"; } // create rest URL final String restUrl = fullHost + ctx + restServices + path + "?f=json"; reg.register(new HttpRequestDefinition(restUrl), new IHttpResponseListener() { public void onResponse(ResponseInfo info, String strContent, Document docContent) { // extract singleFusedMapCache flag String singleFusedMapCache = readTopParam(strContent, "singleFusedMapCache"); // extract service type; for image service it will be something beginning with "esriImageService" String serviceType = readTopParam(strContent, "serviceDataType"); // it has to be at least either singleFusedMapCache or serviceType... if (singleFusedMapCache.length() != 0 || serviceType.length() != 0) { // extract envelope; depending on the service it ight be named "initialExtent" or just "extent" Envelope extent = readExtent(strContent, "initialExtent"); if (extent == null) { extent = readExtent(strContent, "extent"); } // prepare final values to create renderer final Envelope finalExtent = extent; final boolean isImageService = serviceType.startsWith("esriImageService"); setter.set(new ArcGISRenderer() { @Override protected Envelope getExtent() { return finalExtent; } @Override protected String getUrl() { return restUrl; } @Override protected boolean isImageService() { return isImageService; } @Override protected int getMapHeightAdjustment() { return getProperties().getMapHeightAdjustment(); } }); } } }); } catch (MalformedURLException ex) { Logger.getLogger(ArcGISRendererFactory.class.getName()).log(Level.SEVERE, null, ex); } } /** * Reads top-level parameter. * @param responseStream JSON response stream * @param topParamName top-level parameter name * @return value of the top-level parameter name or empty string if no top level parameter found */ private String readTopParam(String responseStream, String topParamName) { JSONTokener tokener = new JSONTokener(responseStream); try { if (tokener.more()) { Object obj = tokener.nextValue(); if (obj instanceof JSONObject) { JSONObject jObj = (JSONObject) obj; return Val.chkStr(jObj.getString(topParamName)); } } } catch (JSONException ex) { // invalid JSON response return ""; } return ""; } /** * Reads extent from the JSON response. * @param responseStream response stream * @param extentName extent name * @return envelope or <code>null</code> if requested envelope not found * @throws org.json.JSONException if parsing JSON response failed */ private Envelope readExtent(String responseStream, String extentName) { try { JSONObject jExt = findObject(responseStream, extentName); if (jExt != null) { double xmin = jExt.getDouble("xmin"); double ymin = jExt.getDouble("ymin"); double xmax = jExt.getDouble("xmax"); double ymax = jExt.getDouble("ymax"); JSONObject jSpatialRef = jExt.getJSONObject("spatialReference"); int wkid = jSpatialRef.getInt("wkid"); Envelope envelope = new Envelope(xmin, ymin, xmax, ymax); envelope.setWkid(Integer.toString(wkid)); return envelope; } } catch (JSONException ex) { // invalid JSON response } return null; } /** * Finds named JSON object in the JSON response. * @param responseStream response stream * @param objectName object name * @return envelope or <code>null</code> if requested object not found * @throws org.json.JSONException if parsing JSON response failed */ private JSONObject findObject(String responseStream, String objectName) throws JSONException { InputStreamReader reader = null; try { JSONTokener tokener = new JSONTokener(responseStream); while (tokener.more()) { Object obj = tokener.nextValue(); if (obj instanceof JSONObject) { JSONObject jObj = (JSONObject) obj; try { JSONObject jNamedObject = jObj.getJSONObject(objectName); if (jNamedObject != null) return jNamedObject; } catch (JSONException ex) { // no extent in the current node; advance to the next node } } } } finally { if (reader != null) { try { reader.close(); } catch (IOException ex) { } } } return null; } }