001/* 002 * SVG Salamander 003 * Copyright (c) 2004, Mark McKay 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or 007 * without modification, are permitted provided that the following 008 * conditions are met: 009 * 010 * - Redistributions of source code must retain the above 011 * copyright notice, this list of conditions and the following 012 * disclaimer. 013 * - Redistributions in binary form must reproduce the above 014 * copyright notice, this list of conditions and the following 015 * disclaimer in the documentation and/or other materials 016 * provided with the distribution. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 021 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 022 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 026 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 027 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 029 * OF THE POSSIBILITY OF SUCH DAMAGE. 030 * 031 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other 032 * projects can be found at http://www.kitfox.com 033 * 034 * Created on February 18, 2004, 5:09 PM 035 */ 036 037package com.kitfox.svg; 038 039 040import java.util.*; 041import java.net.*; 042import org.xml.sax.*; 043import org.xml.sax.helpers.DefaultHandler; 044 045import com.kitfox.svg.animation.*; 046import java.util.logging.Level; 047import java.util.logging.Logger; 048 049/** 050 * @author Mark McKay 051 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a> 052 */ 053public class SVGLoader extends DefaultHandler 054{ 055 final HashMap<String, Class<?>> nodeClasses = new HashMap<String, Class<?>>(); 056 //final HashMap attribClasses = new HashMap(); 057 final LinkedList<SVGElement> buildStack = new LinkedList<SVGElement>(); 058 059 final HashSet<String> ignoreClasses = new HashSet<String>(); 060 061 final SVGLoaderHelper helper; 062 063 /** 064 * The diagram that represents the base of this SVG document we're loading. 065 * Will be augmented to include node indexing info and other useful stuff. 066 */ 067 final SVGDiagram diagram; 068 069// SVGElement loadRoot; 070 071 //Used to keep track of document elements that are not part of the SVG namespace 072 int skipNonSVGTagDepth = 0; 073 int indent = 0; 074 075 final boolean verbose; 076 077 /** 078 * Creates a new instance of SVGLoader 079 * @param xmlBase 080 * @param universe 081 */ 082 public SVGLoader(URI xmlBase, SVGUniverse universe) 083 { 084 this(xmlBase, universe, false); 085 } 086 087 public SVGLoader(URI xmlBase, SVGUniverse universe, boolean verbose) 088 { 089 this.verbose = verbose; 090 091 diagram = new SVGDiagram(xmlBase, universe); 092 093 //Compile a list of important builder classes 094 nodeClasses.put("a", A.class); 095 nodeClasses.put("animate", Animate.class); 096 nodeClasses.put("animatecolor", AnimateColor.class); 097 nodeClasses.put("animatemotion", AnimateMotion.class); 098 nodeClasses.put("animatetransform", AnimateTransform.class); 099 nodeClasses.put("circle", Circle.class); 100 nodeClasses.put("clippath", ClipPath.class); 101 nodeClasses.put("defs", Defs.class); 102 nodeClasses.put("desc", Desc.class); 103 nodeClasses.put("ellipse", Ellipse.class); 104 nodeClasses.put("filter", Filter.class); 105 nodeClasses.put("font", Font.class); 106 nodeClasses.put("font-face", FontFace.class); 107 nodeClasses.put("g", Group.class); 108 nodeClasses.put("glyph", Glyph.class); 109 nodeClasses.put("hkern", Hkern.class); 110 nodeClasses.put("image", ImageSVG.class); 111 nodeClasses.put("line", Line.class); 112 nodeClasses.put("lineargradient", LinearGradient.class); 113 nodeClasses.put("marker", Marker.class); 114 nodeClasses.put("metadata", Metadata.class); 115 nodeClasses.put("missing-glyph", MissingGlyph.class); 116 nodeClasses.put("path", Path.class); 117 nodeClasses.put("pattern", PatternSVG.class); 118 nodeClasses.put("polygon", Polygon.class); 119 nodeClasses.put("polyline", Polyline.class); 120 nodeClasses.put("radialgradient", RadialGradient.class); 121 nodeClasses.put("rect", Rect.class); 122 nodeClasses.put("set", SetSmil.class); 123 nodeClasses.put("shape", ShapeElement.class); 124 nodeClasses.put("stop", Stop.class); 125 nodeClasses.put("style", Style.class); 126 nodeClasses.put("svg", SVGRoot.class); 127 nodeClasses.put("symbol", Symbol.class); 128 nodeClasses.put("text", Text.class); 129 nodeClasses.put("title", Title.class); 130 nodeClasses.put("tspan", Tspan.class); 131 nodeClasses.put("use", Use.class); 132 133 ignoreClasses.add("midpointstop"); 134 135 //attribClasses.put("clip-path", StyleUrl.class); 136 //attribClasses.put("color", StyleColor.class); 137 138 helper = new SVGLoaderHelper(xmlBase, universe, diagram); 139 } 140 141 private String printIndent(int indent, String indentStrn) 142 { 143 StringBuffer sb = new StringBuffer(); 144 for (int i = 0; i < indent; i++) 145 { 146 sb.append(indentStrn); 147 } 148 return sb.toString(); 149 } 150 151 @Override 152 public void startDocument() throws SAXException 153 { 154// System.err.println("Start doc"); 155 156// buildStack.clear(); 157 } 158 159 @Override 160 public void endDocument() throws SAXException 161 { 162// System.err.println("End doc"); 163 } 164 165 @Override 166 public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException 167 { 168 if (verbose) 169 { 170 System.err.println(printIndent(indent, " ") + "Starting parse of tag " + sName+ ": " + namespaceURI); 171 } 172 indent++; 173 174 if (skipNonSVGTagDepth != 0 || (!namespaceURI.equals("") && !namespaceURI.equals(SVGElement.SVG_NS))) 175 { 176 skipNonSVGTagDepth++; 177 return; 178 } 179 180 sName = sName.toLowerCase(); 181 182//javax.swing.JOptionPane.showMessageDialog(null, sName); 183 184 Object obj = nodeClasses.get(sName); 185 if (obj == null) 186 { 187 if (!ignoreClasses.contains(sName)) 188 { 189 if (verbose) 190 { 191 System.err.println("SVGLoader: Could not identify tag '" + sName + "'"); 192 } 193 } 194 return; 195 } 196 197//Debug info tag depth 198//for (int i = 0; i < buildStack.size(); i++) System.err.print(" "); 199//System.err.println("+" + sName); 200 201 try { 202 Class<?> cls = (Class<?>)obj; 203 SVGElement svgEle = (SVGElement)cls.newInstance(); 204 205 SVGElement parent = null; 206 if (buildStack.size() != 0) parent = (SVGElement)buildStack.getLast(); 207 svgEle.loaderStartElement(helper, attrs, parent); 208 209 buildStack.addLast(svgEle); 210 } 211 catch (Exception e) 212 { 213 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 214 "Could not load", e); 215 throw new SAXException(e); 216 } 217 218 } 219 220 @Override 221 public void endElement(String namespaceURI, String sName, String qName) 222 throws SAXException 223 { 224 indent--; 225 if (verbose) 226 { 227 System.err.println(printIndent(indent, " ") + "Ending parse of tag " + sName+ ": " + namespaceURI); 228 } 229 230 if (skipNonSVGTagDepth != 0) 231 { 232 skipNonSVGTagDepth--; 233 return; 234 } 235 236 sName = sName.toLowerCase(); 237 238 Object obj = nodeClasses.get(sName); 239 if (obj == null) return; 240 241//Debug info tag depth 242//for (int i = 0; i < buildStack.size(); i++) System.err.print(" "); 243//System.err.println("-" + sName); 244 245 try { 246 SVGElement svgEle = (SVGElement)buildStack.removeLast(); 247 248 svgEle.loaderEndElement(helper); 249 250 SVGElement parent = null; 251 if (buildStack.size() != 0) 252 { 253 parent = (SVGElement)buildStack.getLast(); 254 } 255 //else loadRoot = (SVGElement)svgEle; 256 257 if (parent != null) 258 { 259 parent.loaderAddChild(helper, svgEle); 260 } 261 else 262 { 263 diagram.setRoot((SVGRoot)svgEle); 264 } 265 266 } 267 catch (Exception e) 268 { 269 Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 270 "Could not parse", e); 271 throw new SAXException(e); 272 } 273 } 274 275 @Override 276 public void characters(char buf[], int offset, int len) 277 throws SAXException 278 { 279 if (skipNonSVGTagDepth != 0) 280 { 281 return; 282 } 283 284 if (buildStack.size() != 0) 285 { 286 SVGElement parent = (SVGElement)buildStack.getLast(); 287 String s = new String(buf, offset, len); 288 parent.loaderAddText(helper, s); 289 } 290 } 291 292 @Override 293 public void processingInstruction(String target, String data) 294 throws SAXException 295 { 296 //Check for external style sheet 297 } 298 299// public SVGElement getLoadRoot() { return loadRoot; } 300 public SVGDiagram getLoadedDiagram() { return diagram; } 301}