0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026: package com.sun.perseus.model;
0027:
0028: import com.sun.perseus.util.RunnableQueue;
0029: import com.sun.perseus.util.RunnableQueue.RunnableHandler;
0030:
0031: import com.sun.perseus.j2d.GraphicsProperties;
0032: import com.sun.perseus.j2d.RenderGraphics;
0033: import com.sun.perseus.j2d.TextProperties;
0034: import com.sun.perseus.j2d.Transform;
0035:
0036: import com.sun.perseus.builder.DefaultFontFace;
0037:
0038: import com.sun.perseus.parser.ColorParser;
0039: import com.sun.perseus.parser.ClockParser;
0040: import com.sun.perseus.parser.LengthParser;
0041: import com.sun.perseus.parser.NumberListParser;
0042: import com.sun.perseus.parser.PathParser;
0043: import com.sun.perseus.parser.TimeConditionParser;
0044: import com.sun.perseus.parser.TransformListParser;
0045: import com.sun.perseus.parser.UnicodeParser;
0046: import com.sun.perseus.parser.ViewBoxParser;
0047:
0048: import org.w3c.dom.Document;
0049: import org.w3c.dom.Element;
0050: import org.w3c.dom.Node;
0051: import org.w3c.dom.DOMException;
0052:
0053: import java.io.PrintStream;
0054:
0055: import java.util.Enumeration;
0056: import java.util.Hashtable;
0057: import java.util.Hashtable;
0058: import java.util.Vector;
0059:
0060: import com.sun.perseus.util.SVGConstants;
0061:
0062: /**
0063: * A <code>DocumentNode</code> represents the root of an SVG document model.
0064: * A DocumentNode is a <code>Viewport</code> and centralizes some functions such
0065: * as the font data base, tree updates management and event dispatching.
0066: * The <code>DocumentNode</code> also manages the <code>RunnableQueue</code>
0067: * used to synchronize all access to the SVG document.
0068: *
0069: * @version $Id: DocumentNode.java,v 1.24 2006/06/29 10:47:30 ln156897 Exp $
0070: */
0071: public class DocumentNode extends Viewport implements Document {
0072: /**
0073: * Default spatial resolution value (96dpi);
0074: */
0075: public static final float DEFAULT_PIXEL_MM_SIZE = 0.26458333333333333333333333333333f; // 96dpi
0076:
0077: /**
0078: * Size of a pixel, in millimeters. This is needed in unit
0079: * conversion as well.
0080: */
0081: protected float pxMMSize = DEFAULT_PIXEL_MM_SIZE;
0082:
0083: /**
0084: * Coordinate in user space coordinates. This value is used as a working
0085: * array for all nodes in the document tree.
0086: */
0087: protected float[] upt = { 0, 0 };
0088:
0089: /**
0090: * A map itself containing map of ElementHandlers
0091: * namespaceURI -> Hashtable
0092: *
0093: * the contained Hashtable maps:
0094: * localName -> ElementNode prototype.
0095: */
0096: protected Hashtable namespaceMap = new Hashtable();
0097:
0098: /**
0099: * Used to store additional traits NS., i.e., traits
0100: * which are not naturally supported by the element.
0101: * elt -> Hashtable.
0102: *
0103: * The contained Hashtable maps:
0104: * namespace -> Hashtable.
0105: *
0106: * The contained Hashtable maps:
0107: * localname -> value.
0108: */
0109: protected Hashtable unknownTraitsNS = null;
0110:
0111: /**
0112: * Parser used to convert clock values
0113: */
0114: protected final ClockParser clockParser = new ClockParser();
0115:
0116: /**
0117: * Parser used to convert unit and unitless floating point
0118: * values to floats.
0119: */
0120: protected final LengthParser lengthParser = new LengthParser();
0121:
0122: /**
0123: * Parser used to convert time condition values
0124: */
0125: protected final TimeConditionParser timeConditionParser = new TimeConditionParser();
0126:
0127: /**
0128: * Parser used to convert number list values
0129: */
0130: protected NumberListParser numberListParser = new NumberListParser();
0131:
0132: /**
0133: * Parser used to convert transform values
0134: */
0135: protected TransformListParser transformListParser = new TransformListParser();
0136:
0137: /**
0138: * Parser used to convert color values
0139: */
0140: protected final ColorParser colorParser = new ColorParser();
0141:
0142: /**
0143: * Parser used to convert viewBox values
0144: */
0145: protected final ViewBoxParser viewBoxParser = new ViewBoxParser();
0146:
0147: /**
0148: * Parser used to convert path data
0149: */
0150: protected final PathParser pathParser = new PathParser();
0151:
0152: /**
0153: * Parser used to convert unicode ranges.
0154: */
0155: protected UnicodeParser unicodeParser = new UnicodeParser();
0156:
0157: /**
0158: * The <code>ImageLoader</code> handles the desired policy
0159: * for loading image resources.
0160: */
0161: protected ImageLoader imageLoader;
0162:
0163: /**
0164: * The default FontFace used by this RenderGraphics.
0165: * @see com.sun.perseus.builder.DefaultFontFace
0166: */
0167: protected FontFace defaultFontFace;
0168:
0169: /**
0170: * Exceptions can be delayed and later thrown. This is used, for example,
0171: * to keep processing an invalid d attribute on a <path> element and only
0172: * throw the exception after the corresponding model node has been hooked
0173: * to the tree (while the exception was thrown by the path parser during the
0174: * processing of the d attribute). This is only used when loading a document
0175: * to comply to the error processing required for <path>, <polyline> and
0176: * <polygon> processing of the 'd' and 'points' attributes.
0177: */
0178: protected DOMException delayedException;
0179:
0180: /**
0181: * The default namespace URI for elements created in that document,
0182: * in case the namespace URI in createElementNS is empty.
0183: */
0184: protected String defaultNamespaceURI = SVGConstants.SVG_NAMESPACE_URI;
0185:
0186: /**
0187: * The Document URI
0188: */
0189: protected String docURI;
0190:
0191: /**
0192: * The set of initial <code>FontFace</code> used by this
0193: * <code>RenderingGraphics</code>.
0194: * <br />
0195: * <b>NOTE</b><br />It is the responsibility of the user of the
0196: * Perseus software to make sure that the <code>RenderGraphics</code>
0197: * initial <code>fontFamiliy</code> value is indeed the same as that
0198: * of the various fonts in the <code>initialFontFaces</code> array.
0199: * <br />
0200: * If there is a mismatch, then text content that does not
0201: * specify the <code>font-family</code> property will not match against the
0202: * <code>initialFontFaces</code> instances and only the defaultFont will be
0203: * used.
0204: *
0205: * @see com.sun.perseus.builder.DefaultFontFace
0206: */
0207: protected FontFace[] initialFontFaces;
0208:
0209: /**
0210: * The FontFace data base used by this RenderGraphics.
0211: * The Hashtable maps font-family names to a Vector
0212: * of fonts with that font-family value.
0213: */
0214: protected Hashtable fontFaceDB = null;
0215:
0216: /**
0217: * Set of currently active TraitAnimations.
0218: */
0219: protected Vector activeTraitAnims = new Vector(0);
0220:
0221: /**
0222: * Set of currently active MediaElements.
0223: */
0224: protected Vector activeMediaElements = new Vector(0);
0225:
0226: /**
0227: * Controls whether the document is 'playing'. This is used to disable the
0228: * media elements like audio when the document is not playing and allows
0229: * sampling the document without having the audio stream played.
0230: */
0231: protected boolean playing;
0232:
0233: /**
0234: * EventSupport is used for listener registration and event
0235: * dispatching.
0236: */
0237: protected EventSupport eventSupport = new EventSupport();
0238:
0239: /**
0240: * The associated <code>UpdateListener</code> is notified of
0241: * all mutation events on this <code>DocumentNode</code> tree
0242: */
0243: protected UpdateListener updateListener;
0244:
0245: /**
0246: * The associated <code>RunnableQueue</code>, if any, is
0247: * managing updates to the <code>DocumentNode</code> and any of
0248: * its descendants. In effect, the updateQueue provides the
0249: * synchronization needed for updates to a document tree.
0250: */
0251: protected RunnableQueue updateQueue;
0252:
0253: /**
0254: * The associated <code>RunnableHandler</code> which should be
0255: * notified when Runnable acting on this DocumentNode tree are
0256: * run.
0257: */
0258: protected RunnableHandler runHandler;
0259:
0260: /**
0261: * Maps ids to ElementNode instances
0262: */
0263: protected Hashtable idToElement = new Hashtable();
0264:
0265: /**
0266: * Used to store nodes with ids before they are inserted into
0267: * the document tree.
0268: */
0269: protected Hashtable reservedIds = new Hashtable();
0270:
0271: /**
0272: * Maps prefixes to namespace entries.
0273: */
0274: protected Hashtable prefixes = new Hashtable();
0275:
0276: /**
0277: * Maps namespaces to prefixes.
0278: */
0279: protected Hashtable namespaces = new Hashtable();
0280:
0281: /**
0282: * A DocumentNode is a root container. This object provides
0283: * support for root time container behavior.
0284: */
0285: protected TimeContainerRootSupport timeContainerRootSupport = new TimeContainerRootSupport();
0286:
0287: /**
0288: * Map of unresolved IDRefs (id -> Vector of referencing IDRefs)
0289: */
0290: protected Hashtable unresolvedIDRefs = new Hashtable();
0291:
0292: /**
0293: * Vector of animations. This is used only at parse time.
0294: * @see #validate
0295: */
0296: protected Vector animations = new Vector(0);
0297:
0298: /**
0299: * We use a single instance of ModelEvent per DocumentNode instance.
0300: * This allows multiple instances of DocumentNodes to co-exist.
0301: */
0302: protected ModelEvent engineEvent = null;
0303:
0304: /**
0305: * Transform used to perform hit detection on text chunks.
0306: */
0307: protected Transform hitChunkTxf = new Transform(null);
0308:
0309: /**
0310: * Transform used to compute bounding boxes on text chunks.
0311: */
0312: protected Transform bboxChunkTxf = new Transform(null);
0313:
0314: /**
0315: * Transform used to render text chunks.
0316: */
0317: protected Transform paintChunkTxf = new Transform(null);
0318:
0319: /**
0320: * Transform used to render glyphs.
0321: */
0322: protected Transform paintGlyphTxf = new Transform(null);
0323:
0324: /**
0325: * Transform used to compute bounding boxes on glyphs.
0326: */
0327: protected Transform bboxGlyphTxf = new Transform(null);
0328:
0329: /**
0330: * Transform used to perform hit testing on glyphs
0331: */
0332: protected Transform hitGlyphTxf = new Transform(null);
0333:
0334: /**
0335: * Default constructor
0336: */
0337: public DocumentNode() {
0338: ownerDocument = this ;
0339: engineEvent = new ModelEvent("", this );
0340:
0341: // Clear the 'in document tree' bit
0342: canRenderState &= CAN_RENDER_IN_DOCUMENT_TREE_MASK;
0343:
0344: // Clear the renderable bit
0345: canRenderState &= CAN_RENDER_RENDERABLE_MASK;
0346: }
0347:
0348: /**
0349: * Returns the initial value of the given Object-valued property.
0350: * @return the initial value of the given property, null if the property is
0351: * unknown.
0352: */
0353: protected Object getInitialPropertyState(final int propertyIndex) {
0354: switch (propertyIndex) {
0355: case GraphicsNode.PROPERTY_FILL:
0356: return GraphicsProperties.INITIAL_FILL;
0357: case GraphicsNode.PROPERTY_STROKE:
0358: return GraphicsProperties.INITIAL_STROKE;
0359: case GraphicsNode.PROPERTY_COLOR:
0360: return GraphicsProperties.INITIAL_COLOR;
0361: case GraphicsNode.PROPERTY_STROKE_DASH_ARRAY:
0362: return GraphicsProperties.INITIAL_STROKE_DASH_ARRAY;
0363: case TextNode.PROPERTY_FONT_FAMILY:
0364: return TextNode.INITIAL_FONT_FAMILY;
0365: default:
0366: return null;
0367: }
0368: }
0369:
0370: /**
0371: * Returns the initial value of the given float-valued property.
0372: * @return the initial value of the given property, 0 if the property is
0373: * unknown.
0374: */
0375: protected float getInitialFloatPropertyState(final int propertyIndex) {
0376: switch (propertyIndex) {
0377: case GraphicsNode.PROPERTY_STROKE_WIDTH:
0378: return GraphicsNode.INITIAL_STROKE_WIDTH;
0379: case GraphicsNode.PROPERTY_STROKE_MITER_LIMIT:
0380: return GraphicsNode.INITIAL_STROKE_MITER_LIMIT;
0381: case GraphicsNode.PROPERTY_STROKE_DASH_OFFSET:
0382: return GraphicsNode.INITIAL_STROKE_DASH_OFFSET;
0383: case TextNode.PROPERTY_FONT_SIZE:
0384: return TextNode.INITIAL_FONT_SIZE;
0385: default:
0386: return 0;
0387: }
0388: }
0389:
0390: /**
0391: * Returns the initial value of the given packed property.
0392: * @return the initial value of the given packed property, zero if the
0393: * property is unknown.
0394: */
0395: protected int getInitialPackedPropertyState(final int propertyIndex) {
0396: switch (propertyIndex) {
0397: case GraphicsNode.PROPERTY_FILL_RULE:
0398: return CompositeGraphicsNode.INITIAL_FILL_RULE_IMPL;
0399: case GraphicsNode.PROPERTY_STROKE_LINE_JOIN:
0400: return CompositeGraphicsNode.INITIAL_STROKE_LINE_JOIN_IMPL;
0401: case GraphicsNode.PROPERTY_STROKE_LINE_CAP:
0402: return CompositeGraphicsNode.INITIAL_STROKE_LINE_CAP_IMPL;
0403: case GraphicsNode.PROPERTY_DISPLAY:
0404: return CompositeGraphicsNode.INITIAL_DISPLAY_IMPL;
0405: case GraphicsNode.PROPERTY_VISIBILITY:
0406: return CompositeGraphicsNode.INITIAL_VISIBILITY_IMPL;
0407: case GraphicsNode.PROPERTY_FILL_OPACITY:
0408: return CompositeGraphicsNode.INITIAL_FILL_OPACITY_IMPL;
0409: case GraphicsNode.PROPERTY_STROKE_OPACITY:
0410: return CompositeGraphicsNode.INITIAL_STROKE_OPACITY_IMPL;
0411: case GraphicsNode.PROPERTY_OPACITY:
0412: return CompositeGraphicsNode.INITIAL_OPACITY_IMPL;
0413: case TextNode.PROPERTY_FONT_STYLE:
0414: return StructureNode.INITIAL_FONT_STYLE_IMPL;
0415: case TextNode.PROPERTY_FONT_WEIGHT:
0416: return StructureNode.INITIAL_FONT_WEIGHT_IMPL;
0417: case TextNode.PROPERTY_TEXT_ANCHOR:
0418: return StructureNode.INITIAL_TEXT_ANCHOR_IMPL;
0419:
0420: default:
0421: return 0;
0422: }
0423: }
0424:
0425: /**
0426: * Returns the value of the given Object-valued property.
0427: *
0428: * @return the value of the given property, null if the property is
0429: * unknown.
0430: */
0431: protected Object getPropertyState(final int propertyIndex) {
0432: return getInitialPropertyState(propertyIndex);
0433: }
0434:
0435: /**
0436: * Returns the value of the given float property.
0437: *
0438: * @return the value of the given property, null if the property is
0439: * unknown.
0440: */
0441: protected float getFloatPropertyState(final int propertyIndex) {
0442: return getInitialFloatPropertyState(propertyIndex);
0443: }
0444:
0445: /**
0446: * Returns the value of the given packed property.
0447: *
0448: * @return the value of the given property, null if the property is
0449: * unknown.
0450: */
0451: protected int getPackedPropertyState(final int propertyIndex) {
0452: return getInitialPackedPropertyState(propertyIndex);
0453: }
0454:
0455: /**
0456: * Paints this node into the input <code>RenderGraphics</code>.
0457: *
0458: * @param rg the <tt>RenderGraphics</tt> where the node should paint itself
0459: */
0460: public void paint(final RenderGraphics rg) {
0461: if (canRenderState != 0) {
0462: return;
0463: }
0464:
0465: paint(getFirstChildNode(), rg);
0466: }
0467:
0468: /**
0469: * Initializes the ModelEvent's singleton object with the input data.
0470: *
0471: * @param type the event type
0472: * @param target the event target
0473: * @param time the event time
0474: */
0475: ModelEvent initEngineEvent(final String type, final ModelNode target) {
0476: ModelEvent engineEvent = new ModelEvent(type, target);
0477: engineEvent.type = type;
0478: engineEvent.target = target;
0479: engineEvent.currentTarget = null;
0480: engineEvent.anchor = null;
0481: engineEvent.stopPropagation = false;
0482: engineEvent.repeatCount = 0;
0483: engineEvent.keyChar = ModelEvent.CHAR_UNDEFINED;
0484:
0485: return engineEvent;
0486: }
0487:
0488: /**
0489: * When the document finishes loading, it notifies the ImageLoader so that
0490: * it can start loading raster images, in case it waited for the load
0491: * completion to start (e.g., as in SVGImageLoader).
0492: *
0493: * @param isLoaded the new loaded state
0494: */
0495: public final void setLoaded(final boolean isLoaded) {
0496: super .setLoaded(isLoaded);
0497: if (isLoaded == true) {
0498: getImageLoader().documentLoaded(this );
0499: }
0500: }
0501:
0502: /**
0503: * @return true if the DocumentNode is in the playing state.
0504: */
0505: public boolean isPlaying() {
0506: return playing;
0507: }
0508:
0509: /**
0510: * @param isPlaying the new playing state.
0511: */
0512: public void setPlaying(final boolean isPlaying) {
0513: this .playing = isPlaying;
0514: }
0515:
0516: /**
0517: * @throws DOMException the delayed exception if one was set.
0518: */
0519: public void checkDelayedException() throws DOMException {
0520: if (delayedException != null) {
0521: throw delayedException;
0522: }
0523: }
0524:
0525: /**
0526: * @return the delayed exception, if any
0527: */
0528: public DOMException getDelayedException() {
0529: return delayedException;
0530: }
0531:
0532: /**
0533: * @param de the new delayedException
0534: */
0535: protected void setDelayedException(DOMException de) {
0536: delayedException = de;
0537: }
0538:
0539: /**
0540: * @return the size of a px CSS unit in millimeters.
0541: */
0542: public float getPixelMMSize() {
0543: return pxMMSize;
0544: }
0545:
0546: /**
0547: * Controls the size of a pixel in millimeters
0548: * @param newPxMMSize the new pixel size value, in millimeter
0549: */
0550: public void setPixelMMSize(final float newPxMMSize) {
0551: this .pxMMSize = newPxMMSize;
0552: }
0553:
0554: /**
0555: * @return true if this node is hooked to the document tree, i.e., if it top
0556: * most ancestor is the DocumentNode.
0557: */
0558: boolean inDocumentTree() {
0559: return true;
0560: }
0561:
0562: /**
0563: * Invoked by <code>IDRef</code> instances when they need the given
0564: * input id reference to be resolved to an <code>ElementNode</code>
0565: * reference. If there is a known <code>ElementNode</code> with the
0566: * requested id, the <code>IDRef</code>'s <code>resolve()</code>
0567: * method is invoked immediately. Otherwise, the method will be called
0568: * as soon as the id reference is resolved.
0569: *
0570: * @param idRef the IDRef which needs to be resolved.
0571: * @param id the id the IDRef needs to resolve to an
0572: * <code>ElementNode</code> reference.
0573: */
0574: public void resolveIDRef(final IDRef idRef, final String id) {
0575: ElementNode ref = (ElementNode) getElementById(id);
0576: if (ref != null) {
0577: idRef.resolveTo(ref);
0578: } else {
0579: if (unresolvedIDRefs != null) {
0580: Vector idRefs = (Vector) unresolvedIDRefs.get(id);
0581: if (idRefs == null) {
0582: idRefs = new Vector(1);
0583: unresolvedIDRefs.put(id, idRefs);
0584: }
0585: idRefs.addElement(idRef);
0586: }
0587: }
0588: }
0589:
0590: /**
0591: * Should be called when a document node is no longer needed
0592: * and could be garbage collected.
0593: */
0594: public void dispose() {
0595: clearLayouts();
0596: }
0597:
0598: /**
0599: * Gets the <code>ImageLoader</code> instance.
0600: *
0601: * @return the <code>ImageLoader</code> associated to this
0602: * <code>DocumentNode</code>.
0603: */
0604: public ImageLoader getImageLoader() {
0605: if (imageLoader == null) {
0606: imageLoader = new DefaultImageLoader();
0607: }
0608: return imageLoader;
0609: }
0610:
0611: /**
0612: * Sets the <code>ImageLoader</code> for this document.
0613: *
0614: * @param imageLoader the new <code>ImageLoader</code> this
0615: * <code>DocumentNode</code> should use.
0616: */
0617: public void setImageLoader(final ImageLoader imageLoader) {
0618: this .imageLoader = imageLoader;
0619: }
0620:
0621: /**
0622: * A <code>DocumentNode</code> has no expanded content, so this
0623: * returns null.
0624: *
0625: * @return a reference to the node's last expanded child, or null if there
0626: * are no expanded children. This forces the computation of expanded
0627: * content if needed.
0628: */
0629: ModelNode getLastExpandedChild() {
0630: return null;
0631: }
0632:
0633: /**
0634: * A <code>DocumentNode</code> has no expanded content, so this
0635: * returns null.
0636: *
0637: * @return a reference to the node's first expanded child, or null if there
0638: * are no expanded children. This forces the computation of expanded
0639: * content if needed.
0640: */
0641: ModelNode getFirstExpandedChild() {
0642: return null;
0643: }
0644:
0645: /**
0646: * Some node types (such as <code>ElementNodeProxy</code>) have
0647: * expanded children that they compute in some specific
0648: * way depending on the implementation.
0649: *
0650: * @return a reference to the node's first expanded child, or null if there
0651: * are no expanded children.
0652: */
0653: public ModelNode getFirstComputedExpandedChild() {
0654: return null;
0655: }
0656:
0657: /**
0658: * Utility method. Unhooks the expanded content.
0659: */
0660: protected void unhookExpandedQuiet() {
0661: }
0662:
0663: /**
0664: * The node's URI base to use to resolve URI references
0665: * If a URI base value was set on this node, then that value
0666: * is returned. Otherwise, this method returns the parent's
0667: * URI base. If there is not URI base on this node and if there
0668: * is not parent, then this method returns null.
0669: *
0670: * @return the node's URI base to use to resolve relative URI references.
0671: */
0672: public String getURIBase() {
0673: return docURI;
0674: }
0675:
0676: /**
0677: * Sets this document's URI
0678: *
0679: * @param docURI the new document URI
0680: */
0681: public void setDocumentURI(final String docURI) {
0682: if (ElementNode.equal(docURI, this .docURI)) {
0683: return;
0684: }
0685: modifyingNode();
0686: this .docURI = docURI;
0687: modifiedNode();
0688: }
0689:
0690: /**
0691: * @return the <code>UpdateListener</code> associated with this viewport.
0692: * @see #setUpdateListener
0693: */
0694: public UpdateListener getUpdateListener() {
0695: if (parent == null || parent == this ) {
0696: return updateListener;
0697: } else {
0698: return parent.getUpdateListener();
0699: }
0700: }
0701:
0702: /**
0703: * @return the <code>RunnableQueue</code> which managers updates to this
0704: * <code>DocumentNode</code> hierarchy.
0705: */
0706: public RunnableQueue getUpdateQueue() {
0707: return updateQueue;
0708: }
0709:
0710: /**
0711: * @return the <code>RunnableQueue.RunnableHandler</code> which is notified
0712: * of <code>Runnable</code> instances ran against this DocumentNode.
0713: */
0714: public RunnableHandler getRunnableHandler() {
0715: return runHandler;
0716: }
0717:
0718: /**
0719: * @param updateQueue the <code>RunnableQueue</code> which manages
0720: * updates to this document tree.
0721: */
0722: public void setUpdateQueue(final RunnableQueue updateQueue) {
0723: this .updateQueue = updateQueue;
0724: }
0725:
0726: /**
0727: * @param runHandler the <code>RunnableHandler</code> which listens to
0728: * <code>Runnable</code> execution for this
0729: * <code>DocumentNode</code>.
0730: */
0731: public void setRunnableHandler(final RunnableHandler runHandler) {
0732: this .runHandler = runHandler;
0733: }
0734:
0735: /**
0736: * Sets the <code>UpdateListener</code> associated with this viewport.
0737: * All updates made to this tree will be reported to the input
0738: * <code>UpdateListener</code>
0739: *
0740: * @param updateListener the new <code>UpdateListener</code> which will
0741: * receive notifications for updates on this tree.
0742: */
0743: public void setUpdateListener(final UpdateListener updateListener) {
0744: this .updateListener = updateListener;
0745: }
0746:
0747: /**
0748: * @return The <code>EventSupport</code> instance associated
0749: * with this <code>DocumentNode</code>
0750: */
0751: public EventSupport getEventSupport() {
0752: return eventSupport;
0753: }
0754:
0755: /**
0756: * If no such element exists, this returns null.
0757: * If more than one element has an id attribute with that value, what
0758: * is returned is undefined.
0759: *
0760: */
0761: public Element getElementById(final String id) {
0762: return (ElementNode) idToElement.get(id);
0763: }
0764:
0765: /**
0766: * Return the <code>Element</code> in the current document with
0767: * the given unique ID. The difference with getElementById is
0768: * that this method may return a node that is not inserted in
0769: * the document tree. This is used in ElementNode.setId().
0770: *
0771: * @param id the ID of the object to be retrieved.
0772: * @return the Element that matches with the given ID or
0773: * <code>null</code> if the ID is not present.
0774: *
0775: * @throws NullPointerException if id is null
0776: */
0777: Element getElementByIdAll(final String id) {
0778: ElementNode n = (ElementNode) idToElement.get(id);
0779: if (n == null) {
0780: n = (ElementNode) reservedIds.get(id);
0781: }
0782:
0783: return n;
0784: }
0785:
0786: /**
0787: * Adds the input element to the list of identified nodes.
0788: *
0789: * @param element the new element with a non-null identifier
0790: *
0791: * @throws NullPointerException if the input element is null or if its
0792: * id is null.
0793: */
0794: void addIdentifiedNode(final ElementNode element) {
0795: // Add element to id map
0796: idToElement.put(element.getId(), element);
0797:
0798: // Check if there are any unresolved references for
0799: // the newly identified node.
0800: if (unresolvedIDRefs != null) {
0801: Vector idRefs = (Vector) unresolvedIDRefs.get(element
0802: .getId());
0803: if (idRefs != null) {
0804: int n = idRefs.size();
0805: for (int i = 0; i < n; i++) {
0806: IDRef idRef = (IDRef) idRefs.elementAt(i);
0807: idRef.resolveTo(element);
0808: }
0809: unresolvedIDRefs.remove(element.getId());
0810: }
0811: }
0812:
0813: // If the id was in the reserved map, remove it.
0814: reservedIds.remove(element.getId());
0815: }
0816:
0817: /**
0818: * Reserves the given id. This is used to be able to check for
0819: * duplicate identifiers.
0820: *
0821: * @param element the element reserving the id. Should not be null.
0822: * The element id should not be null.
0823: */
0824: void reserveId(final ElementNode element) {
0825: reservedIds.put(element.getId(), element);
0826: }
0827:
0828: /**
0829: * Remove element from the list of identified nodes
0830: *
0831: * @param element the element to remove from the list of identified nodes
0832: * @throws NullPointerException if element is null or if its id is null.
0833: */
0834: void removeIdentifiedNode(final ElementNode element) {
0835: String id = element.getId();
0836: if (idToElement.get(id) == element) {
0837: idToElement.remove(element.getId());
0838: }
0839: }
0840:
0841: /**
0842: * Schedules the given Runnable object for a later invocation in
0843: * the document's update thread, and returns.
0844: * <br />
0845: * If there is no <code>updateQueue</code> the <code>Runnable</code>
0846: * is run before returning. Otherwise, the <code>Runnable</code>
0847: * is scheduled in the associated <code>updateQueue</code>
0848: *
0849: * @param r the <code>Runnable</code> to put at the end of the
0850: * execution list.
0851: * @throws IllegalStateException if there is an associated
0852: * <code>RunnableQueue</code> but that one has exited
0853: * or was not started.
0854: */
0855: public void invokeLater(final Runnable r) {
0856: if (updateQueue == null) {
0857: r.run();
0858: } else {
0859: updateQueue.invokeLater(r, runHandler);
0860: }
0861: }
0862:
0863: /**
0864: * Waits until the given Runnable's <tt>run()</tt> has returned.
0865: * <em>Note: <tt>invokeAndWait()</tt> must not be called from the
0866: * current thread (for example from the <tt>run()</tt> method of the
0867: * argument)</em>.
0868: *
0869: * @param r the <code>Runnable</code> to put at the end of the
0870: * execution list.
0871: * @throws IllegalStateException if there is an associated RunnableQueue
0872: * which has exited or was not started.
0873: * @throws InterruptedException if the thread is interrupted while
0874: * waiting for the input <code>Runnable</code> to complete
0875: * its execution.
0876: */
0877: public void invokeAndWait(final Runnable r)
0878: throws InterruptedException {
0879:
0880: if (updateQueue == null) {
0881: r.run();
0882: } else {
0883: updateQueue.invokeAndWait(r, runHandler);
0884: }
0885: }
0886:
0887: /**
0888: * Waits until the given Runnable's <tt>run()</tt> has returned.
0889: * <em>Note: <tt>safeInvokeAndWait()</tt> may be called from any thread.
0890: * This method checks if this thread is the update thread, in which case
0891: * the Runnable is invoked directly. Otherwise, it delegates to the
0892: * invokeAndWait method.
0893: *
0894: * @param r the <code>Runnable</code> to put at the end of the
0895: * execution list. Should not be null.
0896: * @param runHandler the <code>RunnableHandler</code> to notify
0897: * once the <code>Runnable</code> has finished executing.
0898: * Should not be null.
0899: * @throws IllegalStateException if getThread() is null or if the
0900: * thread returned by getThread() is the current one.
0901: */
0902: public void safeInvokeAndWait(final Runnable r) {
0903: if (updateQueue == null) {
0904: r.run();
0905: } else {
0906: updateQueue.safeInvokeAndWait(r, runHandler);
0907: }
0908: }
0909:
0910: /**
0911: * This is where font matching happens. This method compares the
0912: * font attributes (such as 'font-family' or 'font-weight') with
0913: * the corresponding attributes in the FontFace set. At a minimum,
0914: * this method returns a single font in the list: the defaultFontFace.
0915: *
0916: * This process follows the algorithm described in section
0917: * 15.5 of the CSS2 specification (http://www.w3.org/TR/REC-CSS2/)
0918: *
0919: * In SVG Tiny, the font attributes on Text are:
0920: * - font-family
0921: * - font-size
0922: * - font-style
0923: * - font-weight
0924: *
0925: * Therefore, font matching is limited to these attributes (i.e.,
0926: * font-variant is not used).
0927: *
0928: * IMPORTANT NOTE: The FontFaces data base is built when the
0929: * setFontFaceSet method is invoked, from the input array. The
0930: * input array is expected to be in document order and the
0931: * fontFaceDB hashtable's value Vectors contain values which are
0932: * in the same order as in the array passed to setFontFaceSet.
0933: * Therefore, the resolveFontFaces returns matches in document
0934: * order. This is important for situations where a less specific
0935: * FontFace (font-family:Arial, font-style: any) would appear
0936: * before (in document order) a more specific FontFace (font-family:Arial,
0937: * font-style: italic). In that case, only the less specific FontFace
0938: * will be the match for a text with font-family set to Arial
0939: * and font-style set to italic.
0940: *
0941: * @param tp The <code>TextProperties</code> containing the font selection
0942: * properties.
0943: * @return a chain of <code>FontFace.Match</code>. The first element is
0944: * <b>always</b> the default font face. The next element (if any),
0945: * is the first match, ordered according to the CSS2 font matching
0946: * rules.
0947: */
0948: protected FontFace.Match resolveFontFaces(final TextProperties tp) {
0949: // If no default font face has been set, set the default one now.
0950: if (defaultFontFace == null) {
0951: setDefaultFontFace(DefaultFontFace.getDefaultFontFace());
0952: }
0953:
0954: // If no initial font face has been set, set the default one now.
0955: if (initialFontFaces == null) {
0956: setInitialFontFaces(DefaultFontFace.getInitialFontFaces());
0957: }
0958:
0959: // Build the font face data base if it has not been
0960: // built already
0961: if (fontFaceDB == null) {
0962: setFontFaceSetSilent(null);
0963: }
0964:
0965: // Iterate over each matching font-family name
0966: Vector fontFamilyMatch = null;
0967: String[] fontFamily = tp.getFontFamily();
0968: int nff = 0;
0969: if (fontFamily != null) {
0970: nff = fontFamily.length;
0971: }
0972:
0973: FontFace.Match firstMatch = new FontFace.Match(defaultFontFace);
0974: FontFace.Match lastMatch = firstMatch;
0975: for (int i = 0; i < nff; i++) {
0976: fontFamilyMatch = (Vector) fontFaceDB.get(fontFamily[i]);
0977: lastMatch = matchFontFaces(fontFamilyMatch, lastMatch, tp);
0978: }
0979:
0980: return firstMatch;
0981: }
0982:
0983: /**
0984: * Matches values in the input fontFamily map against the
0985: * current context values.
0986: *
0987: * @param fontFamily the 'candidate' <tt>FontFace</tt>s
0988: * @param lastMatch the last <code>FontFace.Match</code>. Any new
0989: * match sub-chain (i.e., for the input fontFamily) will be
0990: * chained after <code>lastMatch</code>
0991: * @param tp the <tt>TextProperties</tt> defining the applicable
0992: * font selection properties
0993: *
0994: * @return the first <code>FontFace.Match</code> node, head of
0995: * the chain of matches, linked in precedence order.
0996: *
0997: * @see #resolveFontFaces
0998: */
0999: protected FontFace.Match matchFontFaces(final Vector fontFamily,
1000: final FontFace.Match lastMatch, final TextProperties tp) {
1001: if (fontFamily == null) {
1002: return lastMatch;
1003: }
1004:
1005: int n = fontFamily.size();
1006: int fontStyle = tp.getFontStyle();
1007: float fontSize = tp.getFontSize();
1008: int fontWeight = tp.getFontWeight();
1009:
1010: FontFace.Match match = null, firstMatch = null;
1011: for (int i = 0; i < n; i++) {
1012: FontFace ff = (FontFace) fontFamily.elementAt(i);
1013:
1014: // First, match on font-style.
1015: if ((ff.getFontStyles() & fontStyle) != 0
1016: || ((fontStyle == TextNode.FONT_STYLE_ITALIC) && ((ff
1017: .getFontStyles() & TextNode.FONT_STYLE_OBLIQUE) != 0))) {
1018:
1019: // Match on font-size
1020: if (ff.getFontSizes() == null) {
1021: match = new FontFace.Match(ff);
1022: match.distance = ff.fontWeightDistance(fontWeight);
1023: firstMatch = addMatch(firstMatch, match);
1024: } else {
1025: float[] fs = ff.getFontSizes();
1026: for (int j = 0; j < fs.length; j++) {
1027: if (fs[j] == fontSize) {
1028: match = new FontFace.Match(ff);
1029: match.distance = ff
1030: .fontWeightDistance(fontWeight);
1031: firstMatch = addMatch(firstMatch, match);
1032: break;
1033: }
1034: }
1035: }
1036: }
1037: }
1038:
1039: if (match == null) {
1040: return lastMatch;
1041: } else {
1042: if (lastMatch != null) {
1043: lastMatch.next = firstMatch;
1044: }
1045:
1046: while (match.next != null) {
1047: match = match.next;
1048: }
1049: return match;
1050: }
1051: }
1052:
1053: /**
1054: * Chains <code>newMatch</code> into the chain starting with
1055: * <code>firstMatch. This inserts the new matching font face
1056: * according to the CSS2 font matching rules.
1057: *
1058: * @param firstMatch the first match in the chain of matching
1059: * <code>FontFace.Match</code> instances.
1060: * @param newMatch the new <code>FontFace.Match</code> object to
1061: * insert into the chain.
1062: * @return the head of the <code>FontFace.Match</code> chain after
1063: * insertion of the new match.
1064: */
1065: protected FontFace.Match addMatch(final FontFace.Match firstMatch,
1066: final FontFace.Match newMatch) {
1067: if (firstMatch == null) {
1068: return newMatch;
1069: }
1070:
1071: FontFace.Match curMatch = firstMatch;
1072: FontFace.Match prevMatch = null;
1073: while (curMatch != null
1074: && curMatch.distance <= newMatch.distance) {
1075: prevMatch = curMatch;
1076: curMatch = curMatch.next;
1077: }
1078:
1079: if (curMatch == null) {
1080: // We reached the end of the list, simply append newMatch
1081: prevMatch.next = newMatch;
1082: return firstMatch;
1083: } else {
1084: // We need to insert newMatch before curMatch
1085: if (prevMatch == null) {
1086: // Inserted at the start of the list
1087: newMatch.next = firstMatch;
1088: return newMatch;
1089: } else {
1090: // Insert somewhere in the middle of the list
1091: prevMatch.next = newMatch;
1092: newMatch.next = curMatch;
1093: return firstMatch;
1094: }
1095: }
1096: }
1097:
1098: /**
1099: * Sets the default FontFace, i.e., the FontFace which
1100: * will always be the last element in the
1101: * FontFace array returned from <code>resolveFontFaces</code>
1102: *
1103: * @param newDefaultFontFace the fall back font
1104: * @throws IllegalArgumentException if defaultFontFace is null
1105: */
1106: public void setDefaultFontFace(final FontFace newDefaultFontFace) {
1107: if (newDefaultFontFace == null) {
1108: throw new IllegalArgumentException();
1109: }
1110:
1111: if (defaultFontFace == newDefaultFontFace) {
1112: return;
1113: }
1114:
1115: this .defaultFontFace = newDefaultFontFace;
1116: clearLayouts();
1117: }
1118:
1119: /**
1120: * Sets the intial set of FontFaces. The intent for this property
1121: * is to provide a set of FontFaces that match the initial
1122: * font-family property for varying values of font-weight and
1123: * font-style. However, this can also be used to provide support
1124: * for the logical font-faces.
1125: *
1126: * The RenderGraphics will keep a reference to the input array which
1127: * should not be modified after it has been passed to this RenderGraphics.
1128: *
1129: * @param newInitialFontFaces should not be null and should not contain
1130: * null values
1131: *
1132: * @throws IllegalArgumentException if initialFontFaces is null or if
1133: * one of the array values is null.
1134: */
1135: public void setInitialFontFaces(final FontFace[] newInitialFontFaces) {
1136: if (newInitialFontFaces == null) {
1137: throw new IllegalArgumentException();
1138: }
1139:
1140: if (initialFontFaces == newInitialFontFaces) {
1141: return;
1142: }
1143:
1144: for (int i = 0; i < newInitialFontFaces.length; i++) {
1145: if (newInitialFontFaces[i] == null) {
1146: throw new IllegalArgumentException();
1147: }
1148: }
1149:
1150: this .initialFontFaces = newInitialFontFaces;
1151: clearLayouts();
1152: }
1153:
1154: /**
1155: * @return The default FontFace used by this RenderGraphics
1156: */
1157: public FontFace getDefaultFontFace() {
1158: return defaultFontFace;
1159: }
1160:
1161: /**
1162: * @return The set of FontFaces used to match the initial font-family
1163: * value
1164: */
1165: public FontFace[] getInitialFontFaces() {
1166: return initialFontFaces;
1167: }
1168:
1169: /**
1170: * Sets the set of FontFaces that this Document uses
1171: * to display text.
1172: *
1173: * @param fontFaceSet the set of font faces to use
1174: */
1175: public void setFontFaceSet(final FontFace[] fontFaceSet) {
1176: setFontFaceSetSilent(fontFaceSet);
1177: clearLayouts();
1178: }
1179:
1180: /**
1181: * Sets the set of FontFaces this document uses but does
1182: * not generate a modifyingNode notification.
1183: *
1184: * @param fontFaceSet the set of font faces to use
1185: */
1186: protected void setFontFaceSetSilent(final FontFace[] fontFaceSet) {
1187: fontFaceDB = new Hashtable();
1188: growFontFaceDB(fontFaceDB, fontFaceSet);
1189: growFontFaceDB(fontFaceDB, initialFontFaces);
1190: }
1191:
1192: /**
1193: * Adds a new <code>FontFace</code> to the data base
1194: * available to the document.
1195: *
1196: * @param fontFace the new <code>FontFace</code> which is
1197: * now available.
1198: */
1199: public void addFontFace(final FontFace fontFace) {
1200: if (fontFaceDB == null) {
1201: // This initializes the data base
1202: setFontFaceSetSilent(null);
1203: }
1204:
1205: growFontFaceDB(fontFaceDB, new FontFace[] { fontFace });
1206: clearLayouts();
1207: }
1208:
1209: /**
1210: * Builds a Hashtable of font faces to map font family
1211: * names to FontFace instances.
1212: *
1213: * @param ffDB the font data base to grow
1214: * @param fontFaceSet array of <tt>FontFace</tt> instances to add
1215: * to the font data base.
1216: * @see #setFontFaceSet
1217: */
1218: protected void growFontFaceDB(final Hashtable ffDB,
1219: final FontFace[] fontFaceSet) {
1220: if (fontFaceSet == null) {
1221: return;
1222: }
1223:
1224: // Grow the font data base from the new fontFaceSet
1225: for (int i = 0; i < fontFaceSet.length; i++) {
1226: FontFace ff = fontFaceSet[i];
1227: String[] fontFamilies = ff.getFontFamilies();
1228: if (fontFamilies != null) {
1229: // null is actually a CSS error. But CSS is silent
1230: // about errors, so we should not break or halt on
1231: // such a condition which should be expected
1232: for (int j = 0; j < fontFamilies.length; j++) {
1233: Vector v = (Vector) ffDB.get(fontFamilies[j]);
1234: if (v == null) {
1235: v = new Vector(1);
1236: ffDB.put(fontFamilies[j], v);
1237: v.addElement(ff);
1238: } else {
1239: if (!v.contains(ff)) {
1240: v.addElement(ff);
1241: }
1242: }
1243: }
1244: }
1245: }
1246: }
1247:
1248: /**
1249: * Returns the current time for the document, i.e., the time at which the
1250: * document was last sampled.
1251: *
1252: * @return the time at which the container was last sampled.
1253: */
1254: public Time getCurrentTime() {
1255: return timeContainerRootSupport.lastSampleTime;
1256: }
1257:
1258: /**
1259: * Applies all currently active animations.
1260: */
1261: public void applyAnimations() {
1262: int n = activeTraitAnims.size();
1263: // System.err.println(">>>>>>>>>>>>>>>>>> There are : " + n
1264: // + " active animations");
1265: for (int i = 0; i < n; i++) {
1266: // System.err.println("Applying TraitAnim [" + i + "]");
1267: ((TraitAnim) activeTraitAnims.elementAt(i)).apply();
1268: }
1269: }
1270:
1271: /**
1272: * Applies all currently active media if this document is playing.
1273: */
1274: void applyMedia() {
1275: /*
1276: if (playing) {
1277: int n = activeMediaElements.size();
1278: for (int i = n - 1; i >= 0; i--) {
1279: ((MediaElement) activeMediaElements.elementAt(i)).playMedia();
1280: }
1281: } else {
1282: int n = activeMediaElements.size();
1283: for (int i = n - 1; i >= 0; i--) {
1284: ((MediaElement) activeMediaElements.elementAt(i)).endMedia();
1285: }
1286: }
1287: */
1288: }
1289:
1290: /**
1291: * This method is typically called by this element's time container
1292: * when it samples.
1293: *
1294: * Note that if this element is not in the waiting or playing
1295: * state, this does nothing. This method assumes that successive
1296: * calls are made with increasing time values.
1297: *
1298: * @param currentTime the time at which this element should be
1299: * sampled.
1300: */
1301: public void sample(final Time currentTime) {
1302: timeContainerRootSupport.sample(currentTime);
1303: // System.err.println(">>>>>>>>>>>>>>>>>> currentTime : "
1304: // + currentTime);
1305: // timeContainerRootSupport.dump();
1306: }
1307:
1308: /**
1309: * Increments the animation or media timeline for this SVGImage (in
1310: * seconds). As the name implies, this method is intended to move only
1311: * forward in the timeline and typically should be used to animate SVG
1312: * content in the "one-shot" rendering mode. Setting negative values will
1313: * throw an Exception. It is important to note that setting large increments
1314: * of time would result in dropping or skipping of frames as per the SVG
1315: * animation model.
1316: *
1317: * @throws IllegalArgumentException if the specified time is negative.
1318: */
1319: public void incrementTime(float seconds) {
1320: if (seconds < 0) {
1321: throw new IllegalArgumentException();
1322: }
1323:
1324: long lastSampleTime = timeContainerRootSupport.lastSampleTime.value;
1325: timeContainerRootSupport.sample(new Time(lastSampleTime
1326: + (long) (seconds * 1000)));
1327: }
1328:
1329: /**
1330: * Initializes the timing engine.
1331: */
1332: public void initializeTimingEngine() {
1333: timeContainerRootSupport.initialize();
1334: }
1335:
1336: /**
1337: * Debug helper
1338: *
1339: * @return a textual description of this viewport object
1340: */
1341: /*
1342: public String toString() {
1343: return "[Document(zoomPan=" + zoomAndPan + ", width="
1344: + width + " height=" + height
1345: + " txf=" + transform
1346: + "]";
1347: }
1348: */
1349:
1350: /**
1351: * Traces this viewport tree
1352: */
1353: public void dump() {
1354: dump(this , "", System.err);
1355: }
1356:
1357: /**
1358: * Debug: traces the input ModelNode, using the input prefix
1359: *
1360: * @param n the node to dump
1361: * @param prefix the string used to prefix the node information
1362: * @param out the stream where the node structure is dumped.
1363: */
1364: static void dump(final ModelNode n, final String prefix,
1365: final PrintStream out) {
1366: out.print(prefix + " " + n);
1367: if (n instanceof ElementNode) {
1368: ElementNode e = (ElementNode) n;
1369: String pfx = n.ownerDocument.toPrefix(e.getNamespaceURI(),
1370: e);
1371: if (pfx == null || pfx.length() == 0) {
1372: out.println(" <" + e.getLocalName() + ">");
1373: } else {
1374: out.println(" <" + pfx + ":" + e.getLocalName() + ">");
1375: }
1376: } else {
1377: out.println();
1378: }
1379: ModelNode child = n.getFirstChildNode();
1380: while (child != null) {
1381: dump(child, prefix + "+-->", out);
1382: child = child.nextSibling;
1383: }
1384:
1385: child = n.getFirstExpandedChild();
1386: while (child != null) {
1387: dump(child, prefix + "*~~>", out);
1388: child = child.nextSibling;
1389: }
1390: }
1391:
1392: /**
1393: * @return null as per the DOM Level 2 specification for Document
1394: * nodes.
1395: */
1396: public String getNamespaceURI() {
1397: return null;
1398: }
1399:
1400: /**
1401: * @return returns the unprefixed node name. For an SVGElement, this returns
1402: * the tag name without a prefix. In case of the Document node, string
1403: * <code>null/code> is returned.
1404: */
1405: public String getLocalName() {
1406: return null;
1407: }
1408:
1409: // JAVADOC COMMENT ELIDED
1410: public Element createElementNS(String namespaceURI,
1411: final String qualifiedName) throws DOMException {
1412: if (namespaceURI == null || qualifiedName == null) {
1413: throw new NullPointerException();
1414: }
1415:
1416: // Extract qualified name from the local name.
1417: String localName = qualifiedName;
1418: int pi = qualifiedName.indexOf(':');
1419: if (pi != -1) {
1420: if (pi == localName.length() - 1) {
1421: // Namepace prefix separator is at the end of the qualified name
1422: localName = "";
1423: } else {
1424: localName = qualifiedName.substring(pi + 1);
1425: }
1426: }
1427:
1428: //
1429: // If the namespaceURI is empty, we have two cases:
1430: // 1. (extremely rare) the content maps some prefix, or the default
1431: // namespace, to the "" namespace URI.
1432: // 2. (extremely common) the content does not have an xmlns declaration
1433: // on its root element and the parser reported the root element
1434: // and children as being in the "" namespace URI.
1435: //
1436: // The following code means that the situation 1. is _not_ handled. If
1437: // the author mapped a prefix or the default prefix to the
1438: // empty string URI, that will be overriddent by the default namespace.
1439: if (namespaceURI.length() == 0) {
1440: namespaceURI = defaultNamespaceURI;
1441: }
1442:
1443: Hashtable lmap = (Hashtable) namespaceMap.get(namespaceURI);
1444: ElementNode en = null;
1445: if (lmap != null) {
1446: en = (ElementNode) lmap.get(localName);
1447: if (en != null) {
1448: en = en.newInstance(this );
1449: }
1450: }
1451:
1452: if (en == null) {
1453: checkNCName(localName);
1454: en = new GenericElementNode(namespaceURI, localName, this );
1455: }
1456:
1457: return en;
1458: }
1459:
1460: /**
1461: * @param name the trait name
1462: * @return a DOMException describing the unsupported trait error.
1463: */
1464: protected DOMException unsupportedTrait(final String name) {
1465: return new DOMException(DOMException.NOT_SUPPORTED_ERR,
1466: Messages.formatMessage(
1467: Messages.ERROR_UNSUPPORTED_TRAIT, new String[] {
1468: name, null, getLocalName(),
1469: getNamespaceURI() }));
1470: }
1471:
1472: /**
1473: * Checks if the input trait name is valid and throws a DOMException
1474: * with error code NOT_SUPPORTED_ERR if not.
1475: *
1476: * @param name the name whose syntax should be checked.
1477: * @throws DOMException with error code NOT_SUPPORTED_ERR if the
1478: * trait name is syntactically incorrect (e.g., null or containing
1479: * characters not conforming to the Namespaces in XML specification.
1480: *
1481: * @see http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName
1482: */
1483: final void checkNCName(final String name) throws DOMException {
1484: if (name == null || name.length() == 0) {
1485: throw unsupportedTrait(name);
1486: }
1487:
1488: // NCName ::= (Letter | '_') (NCNameChar)*
1489: // NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar
1490: // | Extender
1491: char c = name.charAt(0);
1492: if (!isLetter(c) && c != '_') {
1493: throw unsupportedTrait(name);
1494: }
1495:
1496: for (int i = 1; i < name.length(); i++) {
1497: c = name.charAt(i);
1498: if (!isNCNameChar(c)) {
1499: throw unsupportedTrait(name);
1500: }
1501: }
1502: }
1503:
1504: final static boolean isNCNameChar(final char c) {
1505: return isLetter(c) || Character.isDigit(c) || c == '.'
1506: || c == '-' || c == '_' || isCombiningChar(c)
1507: || isExtender(c);
1508: }
1509:
1510: final static boolean isExtender(final int c) {
1511: return c == 0x00B7 || c == 0x02D0 || c == 0x02D1 || c == 0x0387
1512: || c == 0x0640 || c == 0x0E46 || c == 0x0EC6
1513: || c == 0x3005 || (c >= 0x3031 && c <= 0x3035)
1514: || (c >= 0x309D && c <= 0x309E)
1515: || (c >= 0x30FC && c <= 0x30FE);
1516: }
1517:
1518: final static boolean isCombiningChar(final int c) {
1519: return (c >= 0x0300 && c <= 0x0345)
1520: || (c >= 0x0360 && c <= 0x0361)
1521: || (c >= 0x0483 && c <= 0x0486)
1522: || (c >= 0x0591 && c <= 0x05A1)
1523: || (c >= 0x05A3 && c <= 0x05B9)
1524: || (c >= 0x05BB && c <= 0x05BD) || c == 0x05BF
1525: || (c >= 0x05C1 && c <= 0x05C2) || c == 0x05C4
1526: || (c >= 0x064B && c <= 0x0652) || c == 0x0670
1527: || (c >= 0x06D6 && c <= 0x06DC)
1528: || (c >= 0x06DD && c <= 0x06DF)
1529: || (c >= 0x06E0 && c <= 0x06E4)
1530: || (c >= 0x06E7 && c <= 0x06E8)
1531: || (c >= 0x06EA && c <= 0x06ED)
1532: || (c >= 0x0901 && c <= 0x0903) || c == 0x093C
1533: || (c >= 0x093E && c <= 0x094C) || c == 0x094D
1534: || (c >= 0x0951 && c <= 0x0954)
1535: || (c >= 0x0962 && c <= 0x0963)
1536: || (c >= 0x0981 && c <= 0x0983) || c == 0x09BC
1537: || c == 0x09BE || c == 0x09BF
1538: || (c >= 0x09C0 && c <= 0x09C4)
1539: || (c >= 0x09C7 && c <= 0x09C8)
1540: || (c >= 0x09CB && c <= 0x09CD) || c == 0x09D7
1541: || (c >= 0x09E2 && c <= 0x09E3) || c == 0x0A02
1542: || c == 0x0A3C || c == 0x0A3E || c == 0x0A3F
1543: || (c >= 0x0A40 && c <= 0x0A42)
1544: || (c >= 0x0A47 && c <= 0x0A48)
1545: || (c >= 0x0A4B && c <= 0x0A4D)
1546: || (c >= 0x0A70 && c <= 0x0A71)
1547: || (c >= 0x0A81 && c <= 0x0A83) || c == 0x0ABC
1548: || (c >= 0x0ABE && c <= 0x0AC5)
1549: || (c >= 0x0AC7 && c <= 0x0AC9)
1550: || (c >= 0x0ACB && c <= 0x0ACD)
1551: || (c >= 0x0B01 && c <= 0x0B03) || c == 0x0B3C
1552: || (c >= 0x0B3E && c <= 0x0B43)
1553: || (c >= 0x0B47 && c <= 0x0B48)
1554: || (c >= 0x0B4B && c <= 0x0B4D)
1555: || (c >= 0x0B56 && c <= 0x0B57)
1556: || (c >= 0x0B82 && c <= 0x0B83)
1557: || (c >= 0x0BBE && c <= 0x0BC2)
1558: || (c >= 0x0BC6 && c <= 0x0BC8)
1559: || (c >= 0x0BCA && c <= 0x0BCD) || c == 0x0BD7
1560: || (c >= 0x0C01 && c <= 0x0C03)
1561: || (c >= 0x0C3E && c <= 0x0C44)
1562: || (c >= 0x0C46 && c <= 0x0C48)
1563: || (c >= 0x0C4A && c <= 0x0C4D)
1564: || (c >= 0x0C55 && c <= 0x0C56)
1565: || (c >= 0x0C82 && c <= 0x0C83)
1566: || (c >= 0x0CBE && c <= 0x0CC4)
1567: || (c >= 0x0CC6 && c <= 0x0CC8)
1568: || (c >= 0x0CCA && c <= 0x0CCD)
1569: || (c >= 0x0CD5 && c <= 0x0CD6)
1570: || (c >= 0x0D02 && c <= 0x0D03)
1571: || (c >= 0x0D3E && c <= 0x0D43)
1572: || (c >= 0x0D46 && c <= 0x0D48)
1573: || (c >= 0x0D4A && c <= 0x0D4D) || c == 0x0D57
1574: || c == 0x0E31 || (c >= 0x0E34 && c <= 0x0E3A)
1575: || (c >= 0x0E47 && c <= 0x0E4E) || c == 0x0EB1
1576: || (c >= 0x0EB4 && c <= 0x0EB9)
1577: || (c >= 0x0EBB && c <= 0x0EBC)
1578: || (c >= 0x0EC8 && c <= 0x0ECD)
1579: || (c >= 0x0F18 && c <= 0x0F19) || c == 0x0F35
1580: || c == 0x0F37 || c == 0x0F39 || c == 0x0F3E
1581: || c == 0x0F3F || (c >= 0x0F71 && c <= 0x0F84)
1582: || (c >= 0x0F86 && c <= 0x0F8B)
1583: || (c >= 0x0F90 && c <= 0x0F95) || c == 0x0F97
1584: || (c >= 0x0F99 && c <= 0x0FAD)
1585: || (c >= 0x0FB1 && c <= 0x0FB7) || c == 0x0FB9
1586: || (c >= 0x20D0 && c <= 0x20DC) || c == 0x20E1
1587: || (c >= 0x302A && c <= 0x302F) || c == 0x3099
1588: || c == 0x309A;
1589: }
1590:
1591: final static boolean isLetter(final int c) {
1592: return isIdeographic(c) || isBaseChar(c);
1593: }
1594:
1595: final static boolean isIdeographic(final int c) {
1596: return (c >= 0x4E00 && c <= 0x9FA5) || c == 0x3007
1597: || (c >= 0x3021 && c <= 0x3029);
1598: }
1599:
1600: final static boolean isBaseChar(final int c) {
1601: return (c >= 0x0041 && c <= 0x005A)
1602: || (c >= 0x0061 && c <= 0x007A)
1603: || (c >= 0x00C0 && c <= 0x00D6)
1604: || (c >= 0x00D8 && c <= 0x00F6)
1605: || (c >= 0x00F8 && c <= 0x00FF)
1606: || (c >= 0x0100 && c <= 0x0131)
1607: || (c >= 0x0134 && c <= 0x013E)
1608: || (c >= 0x0141 && c <= 0x0148)
1609: || (c >= 0x014A && c <= 0x017E)
1610: || (c >= 0x0180 && c <= 0x01C3)
1611: || (c >= 0x01CD && c <= 0x01F0)
1612: || (c >= 0x01F4 && c <= 0x01F5)
1613: || (c >= 0x01FA && c <= 0x0217)
1614: || (c >= 0x0250 && c <= 0x02A8)
1615: || (c >= 0x02BB && c <= 0x02C1) || c == 0x0386
1616: || (c >= 0x0388 && c <= 0x038A) || c == 0x038C
1617: || (c >= 0x038E && c <= 0x03A1)
1618: || (c >= 0x03A3 && c <= 0x03CE)
1619: || (c >= 0x03D0 && c <= 0x03D6) || c == 0x03DA
1620: || c == 0x03DC || c == 0x03DE || c == 0x03E0
1621: || (c >= 0x03E2 && c <= 0x03F3)
1622: || (c >= 0x0401 && c <= 0x040C)
1623: || (c >= 0x040E && c <= 0x044F)
1624: || (c >= 0x0451 && c <= 0x045C)
1625: || (c >= 0x045E && c <= 0x0481)
1626: || (c >= 0x0490 && c <= 0x04C4)
1627: || (c >= 0x04C7 && c <= 0x04C8)
1628: || (c >= 0x04CB && c <= 0x04CC)
1629: || (c >= 0x04D0 && c <= 0x04EB)
1630: || (c >= 0x04EE && c <= 0x04F5)
1631: || (c >= 0x04F8 && c <= 0x04F9)
1632: || (c >= 0x0531 && c <= 0x0556) || c == 0x0559
1633: || (c >= 0x0561 && c <= 0x0586)
1634: || (c >= 0x05D0 && c <= 0x05EA)
1635: || (c >= 0x05F0 && c <= 0x05F2)
1636: || (c >= 0x0621 && c <= 0x063A)
1637: || (c >= 0x0641 && c <= 0x064A)
1638: || (c >= 0x0671 && c <= 0x06B7)
1639: || (c >= 0x06BA && c <= 0x06BE)
1640: || (c >= 0x06C0 && c <= 0x06CE)
1641: || (c >= 0x06D0 && c <= 0x06D3) || c == 0x06D5
1642: || (c >= 0x06E5 && c <= 0x06E6)
1643: || (c >= 0x0905 && c <= 0x0939) || c == 0x093D
1644: || (c >= 0x0958 && c <= 0x0961)
1645: || (c >= 0x0985 && c <= 0x098C)
1646: || (c >= 0x098F && c <= 0x0990)
1647: || (c >= 0x0993 && c <= 0x09A8)
1648: || (c >= 0x09AA && c <= 0x09B0) || c == 0x09B2
1649: || (c >= 0x09B6 && c <= 0x09B9)
1650: || (c >= 0x09DC && c <= 0x09DD)
1651: || (c >= 0x09DF && c <= 0x09E1)
1652: || (c >= 0x09F0 && c <= 0x09F1)
1653: || (c >= 0x0A05 && c <= 0x0A0A)
1654: || (c >= 0x0A0F && c <= 0x0A10)
1655: || (c >= 0x0A13 && c <= 0x0A28)
1656: || (c >= 0x0A2A && c <= 0x0A30)
1657: || (c >= 0x0A32 && c <= 0x0A33)
1658: || (c >= 0x0A35 && c <= 0x0A36)
1659: || (c >= 0x0A38 && c <= 0x0A39)
1660: || (c >= 0x0A59 && c <= 0x0A5C) || c == 0x0A5E
1661: || (c >= 0x0A72 && c <= 0x0A74)
1662: || (c >= 0x0A85 && c <= 0x0A8B) || c == 0x0A8D
1663: || (c >= 0x0A8F && c <= 0x0A91)
1664: || (c >= 0x0A93 && c <= 0x0AA8)
1665: || (c >= 0x0AAA && c <= 0x0AB0)
1666: || (c >= 0x0AB2 && c <= 0x0AB3)
1667: || (c >= 0x0AB5 && c <= 0x0AB9) || c == 0x0ABD
1668: || c == 0x0AE0 || (c >= 0x0B05 && c <= 0x0B0C)
1669: || (c >= 0x0B0F && c <= 0x0B10)
1670: || (c >= 0x0B13 && c <= 0x0B28)
1671: || (c >= 0x0B2A && c <= 0x0B30)
1672: || (c >= 0x0B32 && c <= 0x0B33)
1673: || (c >= 0x0B36 && c <= 0x0B39) || c == 0x0B3D
1674: || (c >= 0x0B5C && c <= 0x0B5D)
1675: || (c >= 0x0B5F && c <= 0x0B61)
1676: || (c >= 0x0B85 && c <= 0x0B8A)
1677: || (c >= 0x0B8E && c <= 0x0B90)
1678: || (c >= 0x0B92 && c <= 0x0B95)
1679: || (c >= 0x0B99 && c <= 0x0B9A) || c == 0x0B9C
1680: || (c >= 0x0B9E && c <= 0x0B9F)
1681: || (c >= 0x0BA3 && c <= 0x0BA4)
1682: || (c >= 0x0BA8 && c <= 0x0BAA)
1683: || (c >= 0x0BAE && c <= 0x0BB5)
1684: || (c >= 0x0BB7 && c <= 0x0BB9)
1685: || (c >= 0x0C05 && c <= 0x0C0C)
1686: || (c >= 0x0C0E && c <= 0x0C10)
1687: || (c >= 0x0C12 && c <= 0x0C28)
1688: || (c >= 0x0C2A && c <= 0x0C33)
1689: || (c >= 0x0C35 && c <= 0x0C39)
1690: || (c >= 0x0C60 && c <= 0x0C61)
1691: || (c >= 0x0C85 && c <= 0x0C8C)
1692: || (c >= 0x0C8E && c <= 0x0C90)
1693: || (c >= 0x0C92 && c <= 0x0CA8)
1694: || (c >= 0x0CAA && c <= 0x0CB3)
1695: || (c >= 0x0CB5 && c <= 0x0CB9) || c == 0x0CDE
1696: || (c >= 0x0CE0 && c <= 0x0CE1)
1697: || (c >= 0x0D05 && c <= 0x0D0C)
1698: || (c >= 0x0D0E && c <= 0x0D10)
1699: || (c >= 0x0D12 && c <= 0x0D28)
1700: || (c >= 0x0D2A && c <= 0x0D39)
1701: || (c >= 0x0D60 && c <= 0x0D61)
1702: || (c >= 0x0E01 && c <= 0x0E2E) || c == 0x0E30
1703: || (c >= 0x0E32 && c <= 0x0E33)
1704: || (c >= 0x0E40 && c <= 0x0E45)
1705: || (c >= 0x0E81 && c <= 0x0E82) || c == 0x0E84
1706: || (c >= 0x0E87 && c <= 0x0E88) || c == 0x0E8A
1707: || c == 0x0E8D || (c >= 0x0E94 && c <= 0x0E97)
1708: || (c >= 0x0E99 && c <= 0x0E9F)
1709: || (c >= 0x0EA1 && c <= 0x0EA3) || c == 0x0EA5
1710: || c == 0x0EA7 || (c >= 0x0EAA && c <= 0x0EAB)
1711: || (c >= 0x0EAD && c <= 0x0EAE) || c == 0x0EB0
1712: || (c >= 0x0EB2 && c <= 0x0EB3) || c == 0x0EBD
1713: || (c >= 0x0EC0 && c <= 0x0EC4)
1714: || (c >= 0x0F40 && c <= 0x0F47)
1715: || (c >= 0x0F49 && c <= 0x0F69)
1716: || (c >= 0x10A0 && c <= 0x10C5)
1717: || (c >= 0x10D0 && c <= 0x10F6) || c == 0x1100
1718: || (c >= 0x1102 && c <= 0x1103)
1719: || (c >= 0x1105 && c <= 0x1107) || c == 0x1109
1720: || (c >= 0x110B && c <= 0x110C)
1721: || (c >= 0x110E && c <= 0x1112) || c == 0x113C
1722: || c == 0x113E || c == 0x1140 || c == 0x114C
1723: || c == 0x114E || c == 0x1150
1724: || (c >= 0x1154 && c <= 0x1155) || c == 0x1159
1725: || (c >= 0x115F && c <= 0x1161) || c == 0x1163
1726: || c == 0x1165 || c == 0x1167 || c == 0x1169
1727: || (c >= 0x116D && c <= 0x116E)
1728: || (c >= 0x1172 && c <= 0x1173) || c == 0x1175
1729: || c == 0x119E || c == 0x11A8 || c == 0x11AB
1730: || (c >= 0x11AE && c <= 0x11AF)
1731: || (c >= 0x11B7 && c <= 0x11B8) || c == 0x11BA
1732: || (c >= 0x11BC && c <= 0x11C2) || c == 0x11EB
1733: || c == 0x11F0 || c == 0x11F9
1734: || (c >= 0x1E00 && c <= 0x1E9B)
1735: || (c >= 0x1EA0 && c <= 0x1EF9)
1736: || (c >= 0x1F00 && c <= 0x1F15)
1737: || (c >= 0x1F18 && c <= 0x1F1D)
1738: || (c >= 0x1F20 && c <= 0x1F45)
1739: || (c >= 0x1F48 && c <= 0x1F4D)
1740: || (c >= 0x1F50 && c <= 0x1F57) || c == 0x1F59
1741: || c == 0x1F5B || c == 0x1F5D
1742: || (c >= 0x1F5F && c <= 0x1F7D)
1743: || (c >= 0x1F80 && c <= 0x1FB4)
1744: || (c >= 0x1FB6 && c <= 0x1FBC) || c == 0x1FBE
1745: || (c >= 0x1FC2 && c <= 0x1FC4)
1746: || (c >= 0x1FC6 && c <= 0x1FCC)
1747: || (c >= 0x1FD0 && c <= 0x1FD3)
1748: || (c >= 0x1FD6 && c <= 0x1FDB)
1749: || (c >= 0x1FE0 && c <= 0x1FEC)
1750: || (c >= 0x1FF2 && c <= 0x1FF4)
1751: || (c >= 0x1FF6 && c <= 0x1FFC) || c == 0x2126
1752: || (c >= 0x212A && c <= 0x212B) || c == 0x212E
1753: || (c >= 0x2180 && c <= 0x2182)
1754: || (c >= 0x3041 && c <= 0x3094)
1755: || (c >= 0x30A1 && c <= 0x30FA)
1756: || (c >= 0x3105 && c <= 0x312C)
1757: || (c >= 0xAC00 && c <= 0xD7A3);
1758: }
1759:
1760: /**
1761: * To support the creation of elements in the <code>createElementNS</code>
1762: * method, we supply prototypes to the <code>DocumentNode</code> so that it
1763: * can create nodes of specific data types for the prototype's namespaceURI
1764: * and localName.
1765: *
1766: * @param prototypeElement the <code>ElementNode</code> which will be used
1767: * as a prototype in <code>createElementNS</code>. Should not be null.
1768: * @throws IllegalArgumentException If there is already a prototype node for
1769: * the given namespace and local name.
1770: */
1771: public void addPrototype(final ElementNode prototypeElement) {
1772: // Get the namespace map
1773: String namespaceURI = prototypeElement.getNamespaceURI();
1774:
1775: Hashtable lmap = (Hashtable) namespaceMap.get(namespaceURI);
1776:
1777: if (lmap == null) {
1778: lmap = new Hashtable();
1779: namespaceMap.put(namespaceURI, lmap);
1780: }
1781:
1782: lmap.put(prototypeElement.getLocalName(), prototypeElement);
1783: }
1784:
1785: /**
1786: * For SVG files this must be
1787: * <code>SVGSVGElement</code>, but return type is Element for DOM Core
1788: * compatibility and to allow for future extensions. Return null if
1789: * document does not have an element child.
1790: *
1791: */
1792:
1793: public Element getDocumentElement() {
1794: return firstChild;
1795: }
1796:
1797: /**
1798: * Returns the parent <code>Node</code> of this <code>Node</code>.
1799: *
1800: * @return the parent node or null if there is no parent (i.e. if a node has
1801: * just been created and not yet added to the tree, or if it has been
1802: * removed from the tree, this is null).
1803: */
1804: public Node getParentNode() {
1805: return null;
1806: }
1807:
1808: /**
1809: * @return false, as DocumentNode does not support removing children.
1810: */
1811: protected boolean isRemoveChildSupported() {
1812: return false;
1813: }
1814:
1815: /**
1816: * Only and SVG child is allowed under a DocumentNode.
1817: *
1818: * @param node the candidate child node.
1819: * @return true if the input node can be inserted under this CompositeNode
1820: */
1821: protected boolean isAllowedChild(final ElementNode node) {
1822: if (node instanceof SVG) {
1823: return true;
1824: }
1825: return false;
1826: }
1827:
1828: // =========================================================================
1829: // Namespace prefix management. Note that this is designed to be minimal
1830: // in terms of memory and is not optimized for speed.
1831: // =========================================================================
1832:
1833: /**
1834: * Adds a new prefix to namespace mapping. The scope is provided by
1835: * the node parameter.
1836: *
1837: * @param prefix the new namespace prefix.
1838: * @param uri the new namespace URI which maps to the prefix.
1839: * @param node the scope of the namespace prefix mapping. The mapping
1840: * applies to all children, unless overridden.
1841: */
1842: public void addNamespacePrefix(final String prefix,
1843: final String namespaceURI, final ModelNode node) {
1844: if (prefix == null) {
1845: throw new NullPointerException();
1846: }
1847:
1848: // First, check if there are already mappings for the
1849: // prefix.
1850: Object[][] namespaceEntry = (Object[][]) prefixes.get(prefix);
1851:
1852: if (namespaceEntry == null) {
1853: // Simple case: there is not entry yet. Create a new one.
1854: namespaceEntry = new Object[][] { { namespaceURI, node } };
1855: } else {
1856: // There is an existing entry. Add the new one ahead of the
1857: // other ones.
1858: Object[][] newNamespaceEntry = new Object[namespaceEntry.length + 1][];
1859: newNamespaceEntry[0] = new Object[] { namespaceURI, node };
1860: System.arraycopy(namespaceEntry, 0, newNamespaceEntry, 1,
1861: namespaceEntry.length);
1862: namespaceEntry = newNamespaceEntry;
1863: }
1864:
1865: prefixes.put(prefix, namespaceEntry);
1866:
1867: // Now, update the namespaces map.
1868: Object[][] prefixEntry = (Object[][]) namespaces
1869: .get(namespaceURI);
1870:
1871: if (prefixEntry == null) {
1872: // Simple case: there is no entry yet. Create a new one.
1873: prefixEntry = new Object[][] { { prefix, node } };
1874: } else {
1875: // There is an existing entry. Add the new one ahead of the other
1876: // ones.
1877: Object[][] newPrefixEntry = new Object[prefixEntry.length + 1][];
1878: newPrefixEntry[0] = new Object[] { prefix, node };
1879: System.arraycopy(prefixEntry, 0, newPrefixEntry, 1,
1880: prefixEntry.length);
1881: prefixEntry = newPrefixEntry;
1882: }
1883:
1884: namespaces.put(namespaceURI, prefixEntry);
1885: }
1886:
1887: /**
1888: * Maps the input prefix name to a namespace value.
1889: *
1890: * @param prefix the prefix to map.
1891: * @param node the node for which the prefix needs to be mapped.
1892: * @return the namespace the prefix maps to for the node, or null if
1893: * there is no such namespace prefix.
1894: */
1895: String toNamespace(final String prefix, final ModelNode node) {
1896: Object[][] namespaceEntry = (Object[][]) prefixes.get(prefix);
1897: if (namespaceEntry == null) {
1898: // No namespace entry
1899: return null;
1900: } else {
1901: // Note that namespaceEntry.length == 0 should _never_ happen.
1902:
1903: // There are multiple prefix that map to entries.
1904: // Walk up the parent tree and match with the
1905: // first parent that is found in an entry.
1906: ModelNode cur = node;
1907: final int n = namespaceEntry.length;
1908: while (cur != null && cur != this ) {
1909: for (int i = 0; i < n; i++) {
1910: if (namespaceEntry[i][1] == cur) {
1911: return (String) namespaceEntry[i][0];
1912: }
1913: }
1914: cur = cur.parent;
1915: }
1916:
1917: // If we are here, it means we have not found a
1918: // matching namespace in the document tree. Check
1919: // if there are any default mapping on the document
1920: // node itself.
1921: for (int i = 0; i < n; i++) {
1922: if (namespaceEntry[i][1] == this ) {
1923: return (String) namespaceEntry[i][0];
1924: }
1925: }
1926:
1927: // Did not find any matching namespace prefix.
1928: return null;
1929: }
1930: }
1931:
1932: /**
1933: * Maps the input namespace value to a prefix.
1934: *
1935: * @param namespaceURI the URI to map.
1936: * @param node the node for which the namespace needs to be mapped.
1937: * @return the namespace the prefix maps to for the node, or null if
1938: * there is no such namespace prefix.
1939: */
1940: public String toPrefix(final String namespaceURI, final Element node) {
1941: Object[][] prefixEntry = (Object[][]) namespaces
1942: .get(namespaceURI);
1943: if (prefixEntry == null) {
1944: // No prefix entry
1945: return null;
1946: } else {
1947: // Note that prefixEntry.length == 0 should _never_ happen.
1948:
1949: // There are multiple prefixEntries that map to entries.
1950: // Walk up the parent tree and match with the
1951: // first parent that is found in an entry.
1952: ModelNode cur = (ElementNode) node;
1953: final int n = prefixEntry.length;
1954: while (cur != null && cur != this ) {
1955: for (int i = 0; i < n; i++) {
1956: if (prefixEntry[i][1] == cur) {
1957: return (String) prefixEntry[i][0];
1958: }
1959: }
1960: cur = cur.parent;
1961: }
1962:
1963: // If we are here, it means we have not found a
1964: // matching prefix in the document tree. Check
1965: // if there are any default mapping on the document
1966: // node itself.
1967: for (int i = 0; i < n; i++) {
1968: if (prefixEntry[i][1] == this ) {
1969: return (String) prefixEntry[i][0];
1970: }
1971: }
1972:
1973: // Did not find any matching prefix.
1974: return null;
1975: }
1976: }
1977:
1978: /**
1979: * Invoked at the end of the parsing stage to validate things which cannot
1980: * be validated earlier, such as unresolved use references or invalid
1981: * animation settings.
1982: *
1983: * @throws DOMException if there are validation errors.
1984: */
1985: public void validate() throws DOMException {
1986: // First, check unresolved ID references.
1987: if (unresolvedIDRefs != null && unresolvedIDRefs.size() > 0) {
1988: // There are unresolved ID references, this is a validation error.
1989: Enumeration iter = unresolvedIDRefs.keys();
1990: StringBuffer buf = new StringBuffer();
1991: while (iter.hasMoreElements()) {
1992: buf.append('[');
1993: buf.append(iter.nextElement());
1994: buf.append(']');
1995: }
1996:
1997: String message = Messages.formatMessage(
1998: Messages.ERROR_UNRESOLVED_REFERENCES,
1999: new Object[] { buf.toString() });
2000:
2001: throw new DOMException(DOMException.INVALID_ACCESS_ERR,
2002: message);
2003: }
2004:
2005: unresolvedIDRefs = null;
2006:
2007: // Now, validate animation elements. At this stage, we know that the
2008: // animation elements either had no xlink:href or had one which has
2009: // been resolved.
2010: if (animations != null) {
2011: int n = animations.size();
2012: for (int i = 0; i < n; i++) {
2013: Animation animation = (Animation) animations
2014: .elementAt(i);
2015: if (animation.parent != null) {
2016: // The prototypes Set may have a null parent, so we account
2017: // for that situation here.
2018: animation.validate();
2019: }
2020: }
2021: }
2022:
2023: animations = null;
2024: }
2025:
2026: /**
2027: * Implementation helper. Checks that the unknownTraitNS map is not null for
2028: * the given ElementNode before using it, for the requested namespaceURI.
2029: *
2030: * @param element the ElementNode for which a table should be created for
2031: * the given namespace URI.
2032: * @param namespaceURI the trait's namespace URI
2033: * @param traitName the trait's local name.
2034: * @param value the trait value.
2035: * @return the namespaceURI's unknown traits table.
2036: */
2037: void setUnknownTraitsNS(final ElementNode element,
2038: final String namespaceURI, final String traitName,
2039: final String value) {
2040: // Make sure we do have a table for storing unknown traits.
2041: if (unknownTraitsNS == null) {
2042: unknownTraitsNS = new Hashtable();
2043: }
2044:
2045: // Make sure we do have a table for storing unknown traits for
2046: // the requested element.
2047: Hashtable eltMap = (Hashtable) unknownTraitsNS.get(element);
2048: if (eltMap == null) {
2049: eltMap = new Hashtable();
2050: unknownTraitsNS.put(element, eltMap);
2051: }
2052:
2053: // If there is already a map for the given namespace, use that.
2054: // Otherwise, create a new map.
2055: Hashtable nsMap = (Hashtable) eltMap.get(namespaceURI);
2056: if (nsMap == null) {
2057: nsMap = new Hashtable();
2058: eltMap.put(namespaceURI, nsMap);
2059: }
2060:
2061: nsMap.put(traitName, value);
2062: }
2063:
2064: /**
2065: * Implementation helper. Returns the ElementNode's trait value if
2066: * it was ever set.
2067: *
2068: * @param element the ElementNode on which the trait might be set.
2069: * @param namespaceURI the trait's namespace URI
2070: * @param traitName the trait's local name.
2071: * @return the trait value or null if the value was never set.
2072: */
2073: String getUnknownTraitsNS(final ElementNode element,
2074: final String namespaceURI, final String traitName) {
2075: if (unknownTraitsNS == null) {
2076: return null;
2077: }
2078:
2079: Hashtable eltMap = (Hashtable) unknownTraitsNS.get(element);
2080: if (eltMap == null) {
2081: return null;
2082: }
2083:
2084: Hashtable nsMap = (Hashtable) eltMap.get(namespaceURI);
2085: if (nsMap == null) {
2086: return null;
2087: }
2088:
2089: return (String) nsMap.get(traitName);
2090: }
2091:
2092: }
|