View Javadoc

1   /**********************************************
2    * Copyright (C) 2009 Lukas Laag
3    * This file is part of lib-gwt-svg-samples.
4    * 
5    * libgwtsvg-samples is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    * 
10   * libgwtsvg-samples is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   * 
15   * You should have received a copy of the GNU General Public License
16   * along with libgwtsvg-samples.  If not, see http://www.gnu.org/licenses/
17   **********************************************/
18  package org.vectomatic.svg.samples.client.xpath;
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.vectomatic.dom.svg.impl.Attr;
27  import org.vectomatic.dom.svg.impl.NamedNodeMap;
28  import org.vectomatic.dom.svg.ui.SVGImage;
29  import org.vectomatic.dom.svg.ui.SVGResource;
30  import org.vectomatic.dom.svg.ui.SVGResource.Validated;
31  import org.vectomatic.dom.svg.utils.DOMHelper;
32  import org.vectomatic.dom.svg.utils.SVGConstants;
33  import org.vectomatic.dom.svg.utils.SVGPrefixResolver;
34  import org.vectomatic.svg.samples.client.Main;
35  import org.vectomatic.svg.samples.client.Main.MainBundle;
36  import org.vectomatic.svg.samples.client.SampleBase;
37  
38  import com.google.gwt.core.client.GWT;
39  import com.google.gwt.dom.client.Document;
40  import com.google.gwt.dom.client.Element;
41  import com.google.gwt.dom.client.Node;
42  import com.google.gwt.dom.client.NodeList;
43  import com.google.gwt.dom.client.PreElement;
44  import com.google.gwt.dom.client.SpanElement;
45  import com.google.gwt.dom.client.Text;
46  import com.google.gwt.event.dom.client.ClickEvent;
47  import com.google.gwt.event.dom.client.KeyUpEvent;
48  import com.google.gwt.resources.client.ClientBundle;
49  import com.google.gwt.resources.client.CssResource;
50  import com.google.gwt.uibinder.client.UiBinder;
51  import com.google.gwt.uibinder.client.UiField;
52  import com.google.gwt.uibinder.client.UiHandler;
53  import com.google.gwt.user.client.ui.Button;
54  import com.google.gwt.user.client.ui.CheckBox;
55  import com.google.gwt.user.client.ui.HTML;
56  import com.google.gwt.user.client.ui.Label;
57  import com.google.gwt.user.client.ui.TabLayoutPanel;
58  import com.google.gwt.user.client.ui.TextBox;
59  
60  /**
61   * Class to demonstrate the use of XPaths to navigate in SVG documents
62   * @author laaglu
63   */
64  public class XPathSample extends SampleBase {
65  	interface XmlCssResource extends CssResource {
66  		public String element();
67  		public String attribute();
68  		public String text();
69  		public String comment();
70  		public String markup();
71  		public String selected();
72  		public String root();
73  	}
74  	interface XPathBundle extends ClientBundle {
75  		public static XPathBundle INSTANCE = GWT.create(XPathBundle.class);
76  		@Source("xml.css")
77  		public XmlCssResource css();
78  		@Validated(validated=false)
79  		@Source("pizza_pepperoni_bw.svg")
80  		public SVGResource pizza();
81  	}
82  	
83  	interface XPathSampleBinder extends UiBinder<TabLayoutPanel, XPathSample> {
84  	}
85  	private static XPathSampleBinder binder = GWT.create(XPathSampleBinder.class);
86  
87  	@UiField(provided=true)
88  	public static XPathBundle xpathBundle = XPathBundle.INSTANCE;	
89  	@UiField(provided=true)
90  	public static MainBundle mainBundle = Main.mainBundle;
91  	/**
92  	 * A Text box to enter XPath expressions
93  	 */
94  	@UiField
95  	TextBox xpathBox;
96  	/**
97  	 * A button to request evaluation of the xpath expression
98  	 */
99  	@UiField
100 	Button evaluateButton;
101 	/**
102 	 * Checkbox to request continuous evaluation of xpath expressions
103 	 * as they are typed
104 	 */
105 	@UiField
106 	CheckBox evaluateCheckBox;
107 	/**
108 	 * A label to display XPath parsing error message
109 	 */
110 	@UiField
111 	Label errorLabel;
112 	/**
113 	 * A sample SVG image
114 	 */
115 	@UiField
116 	SVGImage svgImage;
117 	/**
118 	 * A colorized version of the SVG source XML text
119 	 */
120 	@UiField
121 	HTML xmlContainer;
122 	
123 	/**
124 	 * The root document
125 	 */
126 	private Document doc = Document.get();
127 	/**
128 	 * The CSS used to colorize text
129 	 */
130 	private XmlCssResource css = xpathBundle.css();
131 	/**
132 	 * Maps SVG nodes to bits of XML colorized text
133 	 */
134 	private Map<Node, SpanElement> nodeToSpan = new HashMap<Node, SpanElement>();
135 	/**
136 	 * The bits of XML colorized text corresponding to the SVG
137 	 * nodes selected by the XPath expression
138 	 */
139 	private List<Element> xpathSpans;
140 	/**
141 	 * The SVG elements selected by the XPath expression
142 	 */
143 	private List<Element> svgElements;
144 	/**
145 	 * To resolve namespace prefixes
146 	 */
147 	private SVGPrefixResolver resolver;
148 
149 	@Override
150 	public TabLayoutPanel getPanel() {
151 		if (tabPanel == null) {
152 			tabPanel = binder.createAndBindUi(this);
153 			tabPanel.setTabText(0, "XPath");
154 			createCodeTabs("XPathSample");
155 			
156 			css.ensureInjected();
157 			xpathSpans = new ArrayList<Element>();
158 			svgElements = new ArrayList<Element>();
159 			PreElement pre = doc.createPreElement();
160 			visit(svgImage.getElement(), pre);
161 			pre.addClassName(css.root());
162 			xmlContainer.getElement().appendChild(pre);
163 			resolver = new SVGPrefixResolver() {
164 				{
165 					prefixToUri.put("i", "http://ns.adobe.com/AdobeIllustrator/10.0/");
166 					prefixToUri.put("cc", "http://web.resource.org/cc/");
167 					prefixToUri.put("dc", "http://purl.org/dc/elements/1.1/");
168 					prefixToUri.put("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
169 				}
170 			};
171 			evaluate();
172 		}
173 		return tabPanel;
174 	}
175 	
176 	@UiHandler("evaluateButton")
177 	public void evaluate(ClickEvent event) {
178 		evaluate();
179 	}
180 
181 	@UiHandler("evaluateCheckBox") 
182 	public void checkbox(ClickEvent event) {
183 		evaluateButton.setEnabled(!evaluateCheckBox.getValue());
184 	}
185 	
186 	@UiHandler("xpathBox") 
187 	public void keyUp(KeyUpEvent event) {
188 		if (evaluateCheckBox.getValue()) {
189 			evaluate();
190 		}
191 	}
192 	
193 	public void evaluate() {
194 		String error = "";
195 		try {
196 			for (Element span : xpathSpans) {
197 				span.removeClassName(css.selected());
198 			}
199 			xpathSpans.clear();
200 			for (Element svgElement : svgElements) {
201 				svgElement.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#FFFFFF");
202 			}
203 			svgElements.clear();
204 			
205 			String expr = xpathBox.getText();
206 			GWT.log(expr);
207 			Iterator<Node> iterator = DOMHelper.evaluateNodeListXPath(svgImage.getElement(), expr, resolver);
208 			while (iterator.hasNext()) {
209 				Node node = iterator.next();
210 				if (node.getNodeType() == Node.ELEMENT_NODE) {
211 					svgElements.add(node.<Element>cast());
212 				}
213 				Element span = nodeToSpan.get(node);
214 				xpathSpans.add(span);
215 			}
216 			for (Element span : xpathSpans) {
217 				span.addClassName(css.selected());
218 			}
219 			for (Element svgElement : svgElements) {
220 				svgElement.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "green");
221 			}
222 		} catch(Throwable t) {
223 			error = t.getMessage();
224 		}	
225 		errorLabel.setText(error);
226 	}
227 
228 	private SpanElement createMarkup(String markup) {
229 		SpanElement markupSpan = doc.createSpanElement();
230 		markupSpan.addClassName(css.markup());
231 		markupSpan.appendChild(doc.createTextNode(markup));
232 		return markupSpan;
233 	}
234 	
235 	private void visit(Node node, Element parentSpan) {
236 		SpanElement span = doc.createSpanElement();
237 		nodeToSpan.put(node, span);
238 		
239 		parentSpan.appendChild(span);
240 		NodeList<Node> childNodes = node.getChildNodes();
241 		switch(node.getNodeType()) {
242 			case Node.ELEMENT_NODE:
243 				{
244 					Element element = node.<Element>cast();
245 					span.addClassName(css.element());
246 					
247 					// Populate the span with a start tag
248 					span.appendChild(createMarkup("<"));
249 					String tagName = element.getTagName();
250 					int index = tagName.indexOf(":");
251 					if (index != -1) {
252 						span.appendChild(doc.createTextNode(tagName.substring(0, index)));
253 						span.appendChild(createMarkup(":"));
254 						span.appendChild(doc.createTextNode(tagName.substring(index + 1)));
255 					} else {
256 						span.appendChild(doc.createTextNode(tagName));
257 					}
258 
259 					// Create the attribute nodes spans
260 					NamedNodeMap<Attr> attributes = DOMHelper.getAttributes(element);
261 					for (int i = 0, length = attributes.getLength(); i < length; i++) {
262 						span.appendChild(doc.createTextNode(" "));
263 
264 						SpanElement attrSpan = doc.createSpanElement();
265 						attrSpan.addClassName(css.attribute());
266 						span.appendChild(attrSpan);
267 						Attr attr = attributes.item(i);
268 						nodeToSpan.put(attr, attrSpan);
269 						
270 						String attrName = attr.getName();
271 						index = attrName.indexOf(":");
272 						if (index != -1) {
273 							attrSpan.appendChild(doc.createTextNode(attrName.substring(0, index)));
274 							attrSpan.appendChild(createMarkup(":"));
275 							attrSpan.appendChild(doc.createTextNode(attrName.substring(index + 1)));
276 						} else {
277 							attrSpan.appendChild(doc.createTextNode(attrName));
278 						}
279 						attrSpan.appendChild(createMarkup("=\""));
280 						attrSpan.appendChild(doc.createTextNode(attr.getValue()));
281 						attrSpan.appendChild(createMarkup("\""));
282 					}
283 					span.appendChild(createMarkup(childNodes.getLength() > 0 ? ">" : "/>"));
284 				}
285 				break;
286 			case Node.TEXT_NODE:
287 				{
288 					// Populate span with text
289 					Text text = node.<Text>cast();
290 					span.addClassName(css.text());
291 					span.appendChild(doc.createTextNode(text.getData()));
292 				}
293 				break;
294 		}
295 
296 		for (int i = 0, count = node.getChildCount(); i < count; i++) {
297 			visit(childNodes.getItem(i), span);
298 		}
299 		
300 		if (childNodes.getLength() > 0 && node.getNodeType() == Node.ELEMENT_NODE) {
301 			Element element = node.<Element>cast();
302 			span.addClassName(css.element());
303 			
304 			// Populate the span with a close tag
305 			span.appendChild(createMarkup("</"));
306 			String tagName = element.getTagName();
307 			int index = tagName.indexOf(":");
308 			if (index != -1) {
309 				span.appendChild(doc.createTextNode(tagName.substring(0, index)));
310 				span.appendChild(createMarkup(":"));
311 				span.appendChild(doc.createTextNode(tagName.substring(index + 1)));
312 			} else {
313 				span.appendChild(doc.createTextNode(tagName));
314 			}
315 			span.appendChild(createMarkup(">"));
316 		}
317 	}
318 
319 }