/********************************************** * Copyright (C) 2009 Lukas Laag * This file is part of lib-gwt-svg-samples. * * libgwtsvg-samples is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * libgwtsvg-samples is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with libgwtsvg-samples. If not, see http://www.gnu.org/licenses/ **********************************************/ package org.vectomatic.svg.samples.client.xpath; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.vectomatic.dom.svg.impl.Attr; import org.vectomatic.dom.svg.impl.NamedNodeMap; import org.vectomatic.dom.svg.ui.SVGImage; import org.vectomatic.dom.svg.ui.SVGResource; import org.vectomatic.dom.svg.ui.SVGResource.Validated; import org.vectomatic.dom.svg.utils.DOMHelper; import org.vectomatic.dom.svg.utils.SVGConstants; import org.vectomatic.dom.svg.utils.SVGPrefixResolver; import org.vectomatic.svg.samples.client.Main; import org.vectomatic.svg.samples.client.Main.MainBundle; import org.vectomatic.svg.samples.client.SampleBase; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.NodeList; import com.google.gwt.dom.client.PreElement; import com.google.gwt.dom.client.SpanElement; import com.google.gwt.dom.client.Text; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TabLayoutPanel; import com.google.gwt.user.client.ui.TextBox; /** * Class to demonstrate the use of XPaths to navigate in SVG documents * @author laaglu */ public class XPathSample extends SampleBase { interface XmlCssResource extends CssResource { public String element(); public String attribute(); public String text(); public String comment(); public String markup(); public String selected(); public String root(); } interface XPathBundle extends ClientBundle { public static XPathBundle INSTANCE = GWT.create(XPathBundle.class); @Source("xml.css") public XmlCssResource css(); @Validated(validated=false) @Source("pizza_pepperoni_bw.svg") public SVGResource pizza(); } interface XPathSampleBinder extends UiBinder<TabLayoutPanel, XPathSample> { } private static XPathSampleBinder binder = GWT.create(XPathSampleBinder.class); @UiField(provided=true) public static XPathBundle xpathBundle = XPathBundle.INSTANCE; @UiField(provided=true) public static MainBundle mainBundle = Main.mainBundle; /** * A Text box to enter XPath expressions */ @UiField TextBox xpathBox; /** * A button to request evaluation of the xpath expression */ @UiField Button evaluateButton; /** * Checkbox to request continuous evaluation of xpath expressions * as they are typed */ @UiField CheckBox evaluateCheckBox; /** * A label to display XPath parsing error message */ @UiField Label errorLabel; /** * A sample SVG image */ @UiField SVGImage svgImage; /** * A colorized version of the SVG source XML text */ @UiField HTML xmlContainer; /** * The root document */ private Document doc = Document.get(); /** * The CSS used to colorize text */ private XmlCssResource css = xpathBundle.css(); /** * Maps SVG nodes to bits of XML colorized text */ private Map<Node, SpanElement> nodeToSpan = new HashMap<Node, SpanElement>(); /** * The bits of XML colorized text corresponding to the SVG * nodes selected by the XPath expression */ private List<Element> xpathSpans; /** * The SVG elements selected by the XPath expression */ private List<Element> svgElements; /** * To resolve namespace prefixes */ private SVGPrefixResolver resolver; @Override public TabLayoutPanel getPanel() { if (tabPanel == null) { tabPanel = binder.createAndBindUi(this); tabPanel.setTabText(0, "XPath"); createCodeTabs("XPathSample"); css.ensureInjected(); xpathSpans = new ArrayList<Element>(); svgElements = new ArrayList<Element>(); PreElement pre = doc.createPreElement(); visit(svgImage.getElement(), pre); pre.addClassName(css.root()); xmlContainer.getElement().appendChild(pre); resolver = new SVGPrefixResolver() { { prefixToUri.put("i", "http://ns.adobe.com/AdobeIllustrator/10.0/"); prefixToUri.put("cc", "http://web.resource.org/cc/"); prefixToUri.put("dc", "http://purl.org/dc/elements/1.1/"); prefixToUri.put("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); } }; evaluate(); } return tabPanel; } @UiHandler("evaluateButton") public void evaluate(ClickEvent event) { evaluate(); } @UiHandler("evaluateCheckBox") public void checkbox(ClickEvent event) { evaluateButton.setEnabled(!evaluateCheckBox.getValue()); } @UiHandler("xpathBox") public void keyUp(KeyUpEvent event) { if (evaluateCheckBox.getValue()) { evaluate(); } } public void evaluate() { String error = ""; try { for (Element span : xpathSpans) { span.removeClassName(css.selected()); } xpathSpans.clear(); for (Element svgElement : svgElements) { svgElement.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "#FFFFFF"); } svgElements.clear(); String expr = xpathBox.getText(); GWT.log(expr); Iterator<Node> iterator = DOMHelper.evaluateNodeListXPath(svgImage.getElement(), expr, resolver); while (iterator.hasNext()) { Node node = iterator.next(); if (node.getNodeType() == Node.ELEMENT_NODE) { svgElements.add(node.<Element>cast()); } Element span = nodeToSpan.get(node); xpathSpans.add(span); } for (Element span : xpathSpans) { span.addClassName(css.selected()); } for (Element svgElement : svgElements) { svgElement.setAttribute(SVGConstants.SVG_FILL_ATTRIBUTE, "green"); } } catch(Throwable t) { error = t.getMessage(); } errorLabel.setText(error); } private SpanElement createMarkup(String markup) { SpanElement markupSpan = doc.createSpanElement(); markupSpan.addClassName(css.markup()); markupSpan.appendChild(doc.createTextNode(markup)); return markupSpan; } private void visit(Node node, Element parentSpan) { SpanElement span = doc.createSpanElement(); nodeToSpan.put(node, span); parentSpan.appendChild(span); NodeList<Node> childNodes = node.getChildNodes(); switch(node.getNodeType()) { case Node.ELEMENT_NODE: { Element element = node.<Element>cast(); span.addClassName(css.element()); // Populate the span with a start tag span.appendChild(createMarkup("<")); String tagName = element.getTagName(); int index = tagName.indexOf(":"); if (index != -1) { span.appendChild(doc.createTextNode(tagName.substring(0, index))); span.appendChild(createMarkup(":")); span.appendChild(doc.createTextNode(tagName.substring(index + 1))); } else { span.appendChild(doc.createTextNode(tagName)); } // Create the attribute nodes spans NamedNodeMap<Attr> attributes = DOMHelper.getAttributes(element); for (int i = 0, length = attributes.getLength(); i < length; i++) { span.appendChild(doc.createTextNode(" ")); SpanElement attrSpan = doc.createSpanElement(); attrSpan.addClassName(css.attribute()); span.appendChild(attrSpan); Attr attr = attributes.item(i); nodeToSpan.put(attr, attrSpan); String attrName = attr.getName(); index = attrName.indexOf(":"); if (index != -1) { attrSpan.appendChild(doc.createTextNode(attrName.substring(0, index))); attrSpan.appendChild(createMarkup(":")); attrSpan.appendChild(doc.createTextNode(attrName.substring(index + 1))); } else { attrSpan.appendChild(doc.createTextNode(attrName)); } attrSpan.appendChild(createMarkup("=\"")); attrSpan.appendChild(doc.createTextNode(attr.getValue())); attrSpan.appendChild(createMarkup("\"")); } span.appendChild(createMarkup(childNodes.getLength() > 0 ? ">" : "/>")); } break; case Node.TEXT_NODE: { // Populate span with text Text text = node.<Text>cast(); span.addClassName(css.text()); span.appendChild(doc.createTextNode(text.getData())); } break; } for (int i = 0, count = node.getChildCount(); i < count; i++) { visit(childNodes.getItem(i), span); } if (childNodes.getLength() > 0 && node.getNodeType() == Node.ELEMENT_NODE) { Element element = node.<Element>cast(); span.addClassName(css.element()); // Populate the span with a close tag span.appendChild(createMarkup("</")); String tagName = element.getTagName(); int index = tagName.indexOf(":"); if (index != -1) { span.appendChild(doc.createTextNode(tagName.substring(0, index))); span.appendChild(createMarkup(":")); span.appendChild(doc.createTextNode(tagName.substring(index + 1))); } else { span.appendChild(doc.createTextNode(tagName)); } span.appendChild(createMarkup(">")); } } }