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 nodeClasses = new HashMap();
056    //final HashMap attribClasses = new HashMap();
057    final LinkedList buildStack = new LinkedList();
058
059    final HashSet ignoreClasses = new HashSet();
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    /** Creates a new instance of SVGLoader */
078    public SVGLoader(URI xmlBase, SVGUniverse universe)
079    {
080        this(xmlBase, universe, false);
081    }
082    
083    public SVGLoader(URI xmlBase, SVGUniverse universe, boolean verbose)
084    {
085        this.verbose = verbose;
086        
087        diagram = new SVGDiagram(xmlBase, universe);
088
089        //Compile a list of important builder classes
090        nodeClasses.put("a", A.class);
091        nodeClasses.put("animate", Animate.class);
092        nodeClasses.put("animatecolor", AnimateColor.class);
093        nodeClasses.put("animatemotion", AnimateMotion.class);
094        nodeClasses.put("animatetransform", AnimateTransform.class);
095        nodeClasses.put("circle", Circle.class);
096        nodeClasses.put("clippath", ClipPath.class);
097        nodeClasses.put("defs", Defs.class);
098        nodeClasses.put("desc", Desc.class);
099        nodeClasses.put("ellipse", Ellipse.class);
100        nodeClasses.put("filter", Filter.class);
101        nodeClasses.put("font", Font.class);
102        nodeClasses.put("font-face", FontFace.class);
103        nodeClasses.put("g", Group.class);
104        nodeClasses.put("glyph", Glyph.class);
105        nodeClasses.put("hkern", Hkern.class);
106        nodeClasses.put("image", ImageSVG.class);
107        nodeClasses.put("line", Line.class);
108        nodeClasses.put("lineargradient", LinearGradient.class);
109        nodeClasses.put("marker", Marker.class);
110        nodeClasses.put("metadata", Metadata.class);
111        nodeClasses.put("missing-glyph", MissingGlyph.class);
112        nodeClasses.put("path", Path.class);
113        nodeClasses.put("pattern", PatternSVG.class);
114        nodeClasses.put("polygon", Polygon.class);
115        nodeClasses.put("polyline", Polyline.class);
116        nodeClasses.put("radialgradient", RadialGradient.class);
117        nodeClasses.put("rect", Rect.class);
118        nodeClasses.put("set", SetSmil.class);
119        nodeClasses.put("shape", ShapeElement.class);
120        nodeClasses.put("stop", Stop.class);
121        nodeClasses.put("style", Style.class);
122        nodeClasses.put("svg", SVGRoot.class);
123        nodeClasses.put("symbol", Symbol.class);
124        nodeClasses.put("text", Text.class);
125        nodeClasses.put("title", Title.class);
126        nodeClasses.put("tspan", Tspan.class);
127        nodeClasses.put("use", Use.class);
128
129        ignoreClasses.add("midpointstop");
130
131        //attribClasses.put("clip-path", StyleUrl.class);
132        //attribClasses.put("color", StyleColor.class);
133
134        helper = new SVGLoaderHelper(xmlBase, universe, diagram);
135    }
136
137    private String printIndent(int indent, String indentStrn)
138    {
139        StringBuffer sb = new StringBuffer();
140        for (int i = 0; i < indent; i++)
141        {
142            sb.append(indentStrn);
143        }
144        return sb.toString();
145    }
146    
147    public void startDocument() throws SAXException
148    {
149//        System.err.println("Start doc");
150
151//        buildStack.clear();
152    }
153
154    public void endDocument() throws SAXException
155    {
156//        System.err.println("End doc");
157    }
158
159    public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException
160    {
161        if (verbose)
162        {
163            System.err.println(printIndent(indent, " ") + "Starting parse of tag " + sName+ ": " + namespaceURI);
164        }
165        indent++;
166        
167        if (skipNonSVGTagDepth != 0 || (!namespaceURI.equals("") && !namespaceURI.equals(SVGElement.SVG_NS)))
168        {
169            skipNonSVGTagDepth++;
170            return;
171        }
172        
173        sName = sName.toLowerCase();
174
175//javax.swing.JOptionPane.showMessageDialog(null, sName);
176
177        Object obj = nodeClasses.get(sName);
178        if (obj == null)
179        {
180            if (!ignoreClasses.contains(sName))
181            {
182                if (verbose)
183                {
184                    System.err.println("SVGLoader: Could not identify tag '" + sName + "'");
185                }
186            }
187            return;
188        }
189
190//Debug info tag depth
191//for (int i = 0; i < buildStack.size(); i++) System.err.print(" ");
192//System.err.println("+" + sName);
193
194        try {
195            Class cls = (Class)obj;
196            SVGElement svgEle = (SVGElement)cls.newInstance();
197
198            SVGElement parent = null;
199            if (buildStack.size() != 0) parent = (SVGElement)buildStack.getLast();
200            svgEle.loaderStartElement(helper, attrs, parent);
201
202            buildStack.addLast(svgEle);
203        }
204        catch (Exception e)
205        {
206            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 
207                "Could not load", e);
208            throw new SAXException(e);
209        }
210
211    }
212
213    public void endElement(String namespaceURI, String sName, String qName)
214        throws SAXException
215    {
216        indent--;
217        if (verbose)
218        {
219            System.err.println(printIndent(indent, " ") + "Ending parse of tag " + sName+ ": " + namespaceURI);
220        }
221        
222        if (skipNonSVGTagDepth != 0)
223        {
224            skipNonSVGTagDepth--;
225            return;
226        }
227        
228        sName = sName.toLowerCase();
229
230        Object obj = nodeClasses.get(sName);
231        if (obj == null) return;
232
233//Debug info tag depth
234//for (int i = 0; i < buildStack.size(); i++) System.err.print(" ");
235//System.err.println("-" + sName);
236
237        try {
238            SVGElement svgEle = (SVGElement)buildStack.removeLast();
239
240            svgEle.loaderEndElement(helper);
241
242            SVGElement parent = null;
243            if (buildStack.size() != 0)
244            {
245                parent = (SVGElement)buildStack.getLast();
246            }
247            //else loadRoot = (SVGElement)svgEle;
248
249            if (parent != null)
250            {
251                parent.loaderAddChild(helper, svgEle);
252            }
253            else
254            {
255                diagram.setRoot((SVGRoot)svgEle);
256            }
257
258        }
259        catch (Exception e)
260        {
261            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 
262                "Could not parse", e);
263            throw new SAXException(e);
264        }
265    }
266
267    public void characters(char buf[], int offset, int len)
268        throws SAXException
269    {
270        if (skipNonSVGTagDepth != 0)
271        {
272            return;
273        }
274
275        if (buildStack.size() != 0)
276        {
277            SVGElement parent = (SVGElement)buildStack.getLast();
278            String s = new String(buf, offset, len);
279            parent.loaderAddText(helper, s);
280        }
281    }
282
283    public void processingInstruction(String target, String data)
284        throws SAXException
285    {
286        //Check for external style sheet
287    }
288    
289//    public SVGElement getLoadRoot() { return loadRoot; }
290    public SVGDiagram getLoadedDiagram() { return diagram; }
291}