View Javadoc

1   /*
2    * Copyright 2008 Google Inc.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.vectomatic.dom.svg.impl;
17  
18  import org.vectomatic.dom.svg.OMSVGSVGElement;
19  import org.vectomatic.dom.svg.ui.ExternalSVGResource;
20  import org.vectomatic.dom.svg.ui.SVGResource;
21  import org.vectomatic.dom.svg.utils.OMSVGParser;
22  
23  import com.google.gwt.core.client.JavaScriptObject;
24  import com.google.gwt.http.client.Request;
25  import com.google.gwt.http.client.RequestBuilder;
26  import com.google.gwt.http.client.RequestCallback;
27  import com.google.gwt.http.client.RequestException;
28  import com.google.gwt.http.client.Response;
29  import com.google.gwt.resources.client.ResourceCallback;
30  import com.google.gwt.resources.client.ResourceException;
31  
32  /**
33   * Implementation of ExternalSVGResource derived from Google's original
34   * ExternalTextResourcePrototype implementation
35   * @author laaglu
36   */
37  public class ExternalSVGResourcePrototype implements ExternalSVGResource {
38  
39  	/**
40  	 * Maps the HTTP callback onto the ResourceCallback.
41  	 */
42  	private class ESRCallback implements RequestCallback {
43  		final ResourceCallback<SVGResource> callback;
44  
45  		public ESRCallback(ResourceCallback<SVGResource> callback) {
46  			this.callback = callback;
47  		}
48  
49  		public void onError(Request request, Throwable exception) {
50  			callback.onError(new ResourceException(
51  					ExternalSVGResourcePrototype.this,
52  					"Unable to retrieve external resource", exception));
53  		}
54  
55  		public void onResponseReceived(Request request, final Response response) {
56  			// Get the contents of the JSON bundle
57  			String responseText = response.getText();
58  
59  			// Call eval() on the object.
60  			JavaScriptObject jso = evalObject(responseText);
61  			if (jso == null) {
62  				callback.onError(new ResourceException(
63  						ExternalSVGResourcePrototype.this,
64  						"eval() returned null"));
65  				return;
66  			}
67  
68  			// Populate the TextResponse cache array
69  			for (int i = 0; i < cache.length; i++) {
70  				final String resourceText = extractString(jso, i);
71  				cache[i] = new SVGResource() {
72  
73  					public String getName() {
74  						return name;
75  					}
76  
77  					public OMSVGSVGElement getSvg() {
78  						return OMSVGParser.parse(resourceText);
79  					}
80  
81  				};
82  			}
83  
84  			// Finish by invoking the callback
85  			callback.onSuccess(cache[index]);
86  		}
87  	}
88  
89  	/**
90  	 * Evaluate the JSON payload. The regular expression to validate the safety
91  	 * of the payload is taken from RFC 4627 (D. Crockford).
92  	 * 
93  	 * @param data
94  	 *            the raw JSON-encapsulated string bundle
95  	 * @return the evaluated JSON object, or <code>null</code> if there is an
96  	 *         error.
97  	 */
98  	private static native JavaScriptObject evalObject(String data) /*-{
99  	    var safe = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
100 	      data.replace(/"(\\.|[^"\\])*"/g, '')));
101 
102 	    if (!safe) {
103 	      return null;
104 	    }
105 
106 	    return eval('(' + data + ')') || null;
107 	  }-*/;
108 
109 	/**
110 	 * Extract the specified String from a JavaScriptObject that is array-like.
111 	 * @param jso
112 	 * the JavaScriptObject returned from {@link #evalObject(String)}
113 	 * @param index
114 	 * the index of the string to extract
115 	 * @return the requested string, or <code>null</code> if it does not exist.
116 	 */
117 	private static native String extractString(JavaScriptObject jso, int index) /*-{
118 	    return (jso.length > index) && jso[index] || null;
119 	  }-*/;
120 
121 	/**
122 	 * This is a reference to an array nominally created in the IRB that
123 	 * contains the ExternalSVGResource. It is intended to be shared between
124 	 * all instances of the ETR that have a common parent IRB.
125 	 */
126 	private final SVGResource[] cache;
127 	private final int index;
128 	private final String name;
129 	private final String url;
130 
131 	/**
132 	 * Constructor
133 	 * @param name The resource name
134 	 * @param url The resource URL
135 	 * @param cache A cache of loaded resource
136 	 * @param index The index for this resource in the cache
137 	 */
138 	public ExternalSVGResourcePrototype(String name, String url,
139 			SVGResource[] cache, int index) {
140 		this.name = name;
141 		this.url = url;
142 		this.cache = cache;
143 		this.index = index;
144 	}
145 
146 	/**
147 	 * Returns the SVG resource name
148 	 * @return the SVG resource name
149 	 */
150 	public String getName() {
151 		return name;
152 	}
153 
154 	/**
155 	 * Possibly fire off an HTTPRequest for the SVG resource.
156 	 * @param callback The request callback
157 	 */
158 	public void getSvg(ResourceCallback<SVGResource> callback)
159 			throws ResourceException {
160 
161 		// If we've already parsed the JSON bundle, short-circuit.
162 		if (cache[index] != null) {
163 			callback.onSuccess(cache[index]);
164 			return;
165 		}
166 
167 		// Otherwise, fire an HTTP request.
168 		RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
169 		try {
170 			rb.sendRequest("", new ESRCallback(callback));
171 		} catch (RequestException e) {
172 			throw new ResourceException(this,
173 					"Unable to initiate request for external resource", e);
174 		}
175 	}
176 }