/* * $RCSfile: ElementNode.java,v $ * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program 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 version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.perseus.model; import com.sun.perseus.platform.MathSupport; import com.sun.perseus.util.SimpleTokenizer; import com.sun.perseus.util.SVGConstants; import com.sun.perseus.parser.Length; import org.w3c.dom.DOMException; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.events.EventListener; import org.w3c.dom.svg.SVGElement; import org.w3c.dom.svg.SVGLocatableElement; import org.w3c.dom.svg.SVGMatrix; import org.w3c.dom.svg.SVGPath; import org.w3c.dom.svg.SVGRect; import org.w3c.dom.svg.SVGRGBColor; import java.util.Hashtable; import java.util.Vector; import com.sun.perseus.j2d.RGB; import com.sun.perseus.j2d.Path; import com.sun.perseus.j2d.PaintDef; import com.sun.perseus.j2d.PaintTarget; import com.sun.perseus.j2d.PaintServer; import com.sun.perseus.j2d.Transform; import com.sun.perseus.j2d.Box; /** * <code>ElementNode</code> models <code>ModelNodes</code which * cna have children. * * <p>A <code>ElementNode</code> can have either <code>ElementNode</code> * children and text content children * (see the {@link #appendTextChild} appendTextChild} method).</p> * * <p>In addition, <code>Element</code>s can have proxies (see * {@link com.sun.perseus.model.ElementNodeProxy ElementNodeProxy}). * The proxies are used for implementing the behavior of the * <code><use></code> element and the <code><text></code> * element. The <code>buildProxy</code> and <code>removeProxy</code> * methods are used to add or remove proxies from a node.</p> * * <p>Finally, the <code>ElementNode</code> class provides support * for the common XML attributes supported by elements, such as * the <code>id</code>, <code>uriBase</code> and conditional attributes.</p> * * @version $Id: ElementNode.java,v 1.27 2006/06/29 10:47:30 ln156897 Exp $ */ public abstract class ElementNode extends CompositeNode implements SVGElement { /* * Constants for trait types. */ /** * String trait type. */ static final String TRAIT_TYPE_STRING = "string"; /** * Float trait type. */ static final String TRAIT_TYPE_FLOAT = "float"; /** * SVGMatrix trait type. */ static final String TRAIT_TYPE_SVG_MATRIX = "SVGMatrix"; /** * SVGPath trait type. */ static final String TRAIT_TYPE_SVG_PATH = "SVGPath"; /** * SVGRect trait type */ static final String TRAIT_TYPE_SVG_RECT = "SVGRect"; /** * SVGRGBColor trait type. */ static final String TRAIT_TYPE_SVG_RGB_COLOR = "SVGRGBColor"; /** * See the SVG 1.1 specification */ public static final int XML_SPACE_PRESERVE = 0; /** * See the SVG 1.1 specification */ public static final int XML_SPACE_DEFAULT = 1; /** * Use the parent node's xml:space setting */ public static final int XML_SPACE_INHERIT = 2; /** * Constant used to identify the per-element partition * (sometimes called anonymous) namespace. */ static final String NULL_NS = "#!null/ns@!"; /** * This node's id */ protected String id = null; /** * This node's URI base */ protected String uriBase = null; /** * This node's conditional attributes. */ protected String[][] conditionalAttributes; /** * Index for requiredFeatures in conditionalAttributes. */ public static final int REQUIRED_FEATURES_INDEX = 0; /** * Index for requiredExtensions in conditionalAttributes */ public static final int REQUIRED_EXTENSIONS_INDEX = 1; /** * Index for systemLanguage in conditionalAttributes */ public static final int SYSTEM_LANGUAGE_INDEX = 2; /** * Number of conditional attributes. */ public static final int CONDITIONAl_ATTRIBUTES_LENGTH = 3; /** * The node's text white space handling policy */ protected int xmlSpace = XML_SPACE_INHERIT; /** * Controls whether or not the node needs to be fully loaded * before it can be painted. */ protected boolean paintNeedsLoad = false; /** * First node proxy. May be null. */ protected ElementNodeProxy firstProxy; /** * Last node proxy. May be null. */ protected ElementNodeProxy lastProxy; /** * Used to detect circular references when building * proxy chains. */ protected boolean buildingProxy = false; /** * Maps namespaces to a map of (localName, TraitAnim) */ protected Hashtable traitAnimsNS = null; /** * Only constructor. * * @param ownerDocument the document this node belongs to. * @throws IllegalArgumentException if the input ownerDocument is null */ public ElementNode(final DocumentNode ownerDocument) { if (ownerDocument == null) { throw new IllegalArgumentException(); } this.ownerDocument = ownerDocument; } /** * Returns the <code>ModelNode</code>, if any, hit by the * point at coordinate x/y in the proxy tree starting at * proxy. * * @param pt the x/y coordinate. Should never be null and be * of size two. If not, the behavior is unspecified. * The coordinates are in viewport space. * @param proxy the root of the proxy tree to test. * @return the <tt>ModelNode</tt> hit at the given point or null * if none was hit. */ ModelNode proxyNodeHitAt(final float[] pt, final ElementNodeProxy proxy) { return null; } /** * Recomputes the transform cache on proxy nodes. */ protected void recomputeProxyTransformState() { ElementNodeProxy proxy = firstProxy; while (proxy != null) { proxy.recomputeTransformState(); proxy = proxy.nextProxy; } } /** * Invoked before an element is added to the document tree, to let the * element perform any validation it needs. For example, the use element * overrides this method to check that its reference is resolved. * * Note that the validate() method defined in some element implementations * is meant to perform validation after the nodes has been inserted into the * document tree. */ protected void preValidate() { // By default, do nothing. } /** * By default, an <code>ElementNode</code> has no expanded content, so this * returns null. * * @return a reference to the node's first expanded child, or null if there * are no expanded children. This forces the computation of expanded * content if needed. */ ModelNode getFirstExpandedChild() { return null; } /** * Some node types (such as <code>ElementNodeProxy</code>) have * expanded children that they compute in some specific * way depending on the implementation. * * @return a reference to the node's first expanded child, or null if there * are no expanded children. */ public ModelNode getFirstComputedExpandedChild() { return null; } /** * By default, an <code>ElementNode</code> has no expanded content, so this * returns null. * * @return a reference to the node's last expanded child, or null if there * are no expanded children. This forces the computation of expanded * content if needed. */ ModelNode getLastExpandedChild() { return null; } /** * Utility method. Unhooks the expanded content. */ protected void unhookExpandedQuiet() { // No expanded content by default. } /** * Utility method. Unhooks the children. */ protected void unhookChildrenQuiet() { super.unhookChildrenQuiet(); // Need to clear the expanded content on proxies ElementNodeProxy proxy = firstProxy; while (proxy != null) { proxy.unhookExpandedQuiet(); proxy = proxy.nextProxy; } } /** * By default, appending a text child does not do anything. * * @param text the text child to append to this node. */ public void appendTextChild(final String text) { } /** * Adds a proxy to this node. When this node is modified, the * <code>ElementNodeProxy</code>'s corresponding modification * methods will be called so that modifications also gets reported * on the proxy. * * @param proxy new <code>ElementNodeProxy</code> * @throws NullPointerException if the input proxy is null. * * @see UpdateListener#modifiedNode * @see UpdateListener#modifyingNode * @see UpdateListener#nodeInserted */ protected void addProxy(final ElementNodeProxy proxy) { if (proxy == null) { throw new NullPointerException(); } if (firstProxy == null) { firstProxy = proxy; lastProxy = proxy; } else { lastProxy.nextProxy = proxy; proxy.prevProxy = lastProxy; lastProxy = proxy; } } /** * Removes a proxy from this node. If the input proxy is null, * or is not an existing proxy, this does nothing. * * @param proxy the <code>ElementNodeProxy</code> to remove. * * @see #addProxy */ void removeProxy(final ElementNodeProxy proxy) { if (proxy == null || firstProxy == null) { return; } // The proxy may be the first one, the last // one, or in the middle if (proxy == firstProxy) { firstProxy = proxy.nextProxy; } if (proxy == lastProxy) { lastProxy = proxy.prevProxy; } if (proxy.prevProxy != null) { proxy.prevProxy.nextProxy = proxy.nextProxy; } if (proxy.nextProxy != null) { proxy.nextProxy.prevProxy = proxy.prevProxy; } } /** * Used to notify the <code>UpdateListener</code>, if any, of * an upcoming node modification * */ protected void modifyingNode() { UpdateListener updateListener = getUpdateListener(); if (updateListener != null) { updateListener.modifyingNode(this); } // During progressive rendering, a proxy may be hooked into // the tree while the referenced node is not. This is why we // need to notify proxies even if the node is not hooked into // the tree yet. ElementNodeProxy proxy = firstProxy; while (proxy != null) { proxy.modifyingProxied(); proxy = proxy.nextProxy; } } /** * Used to notify the <code>UpdateListener</code>, if any, of * a completed node modification * */ protected void modifiedNode() { UpdateListener updateListener = getUpdateListener(); if (updateListener != null) { updateListener.modifiedNode(this); } // See comment in #modifyingNode. ElementNodeProxy proxy = firstProxy; while (proxy != null) { proxy.modifiedProxied(); proxy = proxy.nextProxy; } } /** * Appends an element at the end of the list * * @param element the node to add to this <tt>CompositeNode</tt> * @throws NullPointerException if the input argument is null. */ public void add(final ElementNode element) { super.add(element); ElementNodeProxy proxy = firstProxy; while (proxy != null) { proxy.proxiedChildAdded(element); proxy = proxy.nextProxy; } } /** * @return an adequate <code>ElementNodeProxy</code> for this node. */ ElementNodeProxy buildProxy() { return new ElementNodeProxy(this); } /** * @return an adequate <code>ElementNodeProxy</code> for this node and * makes sure the content is expanded before returning. */ protected ElementNodeProxy buildExpandedProxy() { if (buildingProxy) { // We ran into a circular reference. throw new IllegalStateException(); } buildingProxy = true; ElementNodeProxy proxy = buildProxy(); proxy.expand(); buildingProxy = false; return proxy; } /** * @return the node's identifier */ public String getId() { return id; } /** * @param newId the node's identifier * * @throws DOMException - with error code NO_MODIFICATION_ALLOWED_ERR is * raised if an attempt is made to change an existing Id. * @throws DOMException - with error code INVALID_ACCESS_ERR is raised if * the Id is not unique i.e. if this Id already exists in the document. * * @throws java.lang.NullPointerException - if Id is null. */ public void setId(final String newId) { // Null ids are disallowed. if (newId == null) { throw new NullPointerException(); } // If the id was already set, we cannot let it be modified. if (id != null) { throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, Messages.formatMessage (Messages.ERROR_CANNOT_MODIFY_ID, new String[] {newId, id, getLocalName(), getNamespaceURI()})); } // Now, check if there is any element with that id already. if (ownerDocument.getElementByIdAll(newId) != null) { ElementNode duplicateElement = (ElementNode) ownerDocument.getElementByIdAll(newId); throw new DOMException( DOMException.INVALID_ACCESS_ERR, Messages.formatMessage( Messages.ERROR_DUPLICATE_ID_VALUE, new String[] { newId, getLocalName(), getNamespaceURI(), duplicateElement.getLocalName(), duplicateElement.getNamespaceURI() })); } modifyingNode(); id = newId; // We only declare the id in the global document // scope, i.e., we only consider the id to be // resolved, once the element is loaded, i.e., ready // to render. If the element is not loaded, // see the buildComplete method: it adds the element // to the list of identified nodes when loaded is // set to true. if (loaded && isInDocumentTree()) { ownerDocument.addIdentifiedNode(this); } else { ownerDocument.reserveId(this); } modifiedNode(); } /** * When an Element is hooked into the document tree, it needs * to register as an identified node if it does have an id. */ void nodeHookedInDocumentTree() { super.nodeHookedInDocumentTree(); if (id != null) { ownerDocument.addIdentifiedNode(this); } } /** * The node's URI base to use to resolve URI references * If a URI base value was set on this node, then that value * is returned. Otherwise, this method returns the parent's * URI base. * * @return the node's URI base to use to resolve relative URI references. */ public String getURIBase() { if (uriBase == null) { if (parent != null) { return parent.getURIBase(); } return null; } else { if (uriBase.indexOf(":") != -1 || parent == null) { // This is not a relative URI, we can return this // value // - or - // There is no parent, return this relative URI // as the baseURI return uriBase; } else { // There is no scheme in this node's uri base. // We concatenate the uriBase to the one of the // parent. // This is done according to RFC 2396 // (http://www.faqs.org/rfcs/rfc2396.html) String parentURIBase = parent.getURIBase(); if (parentURIBase != null) { if (uriBase.startsWith("/")) { // uriBase is "absolute", we need to strip parentURIBase // up to the first slash after ":[//]". int slashIndex; int endSchemePartIndex = parentURIBase.indexOf(':'); if (endSchemePartIndex != -1) { if (parentURIBase.substring(endSchemePartIndex+1).startsWith("//")) { endSchemePartIndex += 2; } } slashIndex = parentURIBase.indexOf('/', endSchemePartIndex + 1); if (slashIndex != -1) { parentURIBase = parentURIBase.substring(0, slashIndex); } return parentURIBase + uriBase; } else { // Concatenate the strings, take care of the last slash // of parentURIBase. int lastSlashIndex = parentURIBase.lastIndexOf('/'); if (lastSlashIndex != -1) { parentURIBase = parentURIBase.substring(0, lastSlashIndex); } return parentURIBase + '/' + uriBase; } } else { return uriBase; } } } } /** * @param newUriBase the node's new URI base. The uriBase is used * to resolve relative URIs */ public void setURIBase(final String newUriBase) { if (equal(newUriBase, uriBase)) { return; } modifyingNode(); uriBase = newUriBase; modifiedNode(); } /** * Controls how the node handles white spaces * * @param newXmlSpace should be one of XML_SPACE_DEFAULT, * XML_SPACE_PRESERVE, or XML_SPACE_INHERIT. Otherwise, an * IllegalArgumentException is thrown. */ public void setXMLSpace(final int newXmlSpace) { if (newXmlSpace == xmlSpace) { return; } switch (newXmlSpace) { case XML_SPACE_DEFAULT: case XML_SPACE_PRESERVE: case XML_SPACE_INHERIT: modifyingNode(); xmlSpace = newXmlSpace; modifiedNode(); break; default: throw new IllegalArgumentException(); } } /** * Defines how the node handles white spaces. Note that * if the value is set to null, the node should return * the value of its parent, as the xml:space attribute is * inherited. * * @return one of XML_SPACE_DEFAULT, XML_SPACE_PRESERVE */ public int getXMLSpace() { if (xmlSpace != XML_SPACE_INHERIT) { return xmlSpace; } else { ModelNode ancestor = parent; while (ancestor != null) { if (ancestor instanceof ElementNode) { return ((ElementNode) parent).getXMLSpace(); } ancestor = ancestor.parent; } return XML_SPACE_DEFAULT; } } /** * Returns true if the condition at the given index is equal to the input * value. */ final boolean conditionEquals(final int index, final String[] conditionValue) { if (conditionValue == null) { return (conditionalAttributes == null) || (conditionalAttributes[index] == null); } else { return (conditionalAttributes != null) && (equal(conditionalAttributes[index], conditionValue)); } } /** * The node will only render if the requiredFeatures string * evaluates to true. A null value will evaluate to true. * * @param newRequiredFeatures the set of features required for rendering * this node. */ public void setRequiredFeatures(final String[] newRequiredFeatures) { setConditionalAttribute(REQUIRED_FEATURES_INDEX, newRequiredFeatures); } /** * Sets the new value for the given conditional attribute. * * @param index the conditional attribute index. * @param value the new conditional attribute value. */ void setConditionalAttribute(final int index, final String[] newValue) { if (conditionEquals(index, newValue)) { return; } modifyingNode(); if (newValue == null) { if (conditionalAttributes != null) { conditionalAttributes[index] = null; } } else { if (conditionalAttributes == null) { conditionalAttributes = new String[CONDITIONAl_ATTRIBUTES_LENGTH][]; } conditionalAttributes[index] = newValue; } switch (index) { case REQUIRED_FEATURES_INDEX: { computeCanRenderRequiredFeaturesBit(newValue); ElementNodeProxy p = firstProxy; while (p != null) { p.computeCanRenderRequiredFeaturesBit(newValue); p = p.nextProxy; } } break; case REQUIRED_EXTENSIONS_INDEX: { computeCanRenderRequiredExtensionsBit(newValue); ElementNodeProxy p = firstProxy; while (p != null) { p.computeCanRenderRequiredExtensionsBit(newValue); p = p.nextProxy; } } break; default: { computeCanRenderSystemLanguageBit(newValue); ElementNodeProxy p = firstProxy; while (p != null) { p.computeCanRenderSystemLanguageBit(newValue); p = p.nextProxy; } } break; } modifiedNode(); } /** * The node will only render if the required feature is * supported by Perseus. * * @return the array of features required for this node to * render. * @see #setRequiredFeatures */ public String[] getRequiredFeatures() { return getConditionalAttribute(REQUIRED_FEATURES_INDEX); } /** * Returns the value of the conditional attribute with the given * index. * * @param index the conditional attribute index. * @return the conditional attribute value. */ String[] getConditionalAttribute(final int index) { if (conditionalAttributes != null) { return conditionalAttributes[index]; } else { return null; } } /** * The node will only render if the requiredExtensions string * evaluates to true. A null value evaluates to true. * * @param newRequiredExtensions the extensions which will be considered * a match for any document required extension. * @see #getRequiredExtensions */ public void setRequiredExtensions (final String[] newRequiredExtensions) { setConditionalAttribute(REQUIRED_EXTENSIONS_INDEX, newRequiredExtensions); } /** * The node will only render if the required extension is supported * by Perseus. * * @return the extensions required to render this node * @see #setRequiredExtensions */ public String[] getRequiredExtensions() { return getConditionalAttribute(REQUIRED_EXTENSIONS_INDEX); } /** * The node will only render if the Perseus user language matches * one of the values in this comma separated list. A null value is * a match. * * @param newSystemLanguage an array of languages which will be matched * against any document system language value */ public void setSystemLanguage(final String[] newSystemLanguage) { setConditionalAttribute(SYSTEM_LANGUAGE_INDEX, newSystemLanguage); } /** * The node will only render if the Perseus user language matches * one of the values in this comma separated list. A null value is a * match. * @return the set of system languages. * @see #setSystemLanguage */ public String[] getSystemLanguage() { return getConditionalAttribute(SYSTEM_LANGUAGE_INDEX); } /** * @return true if the node needs to be fully loaded before it * can be painted */ public boolean getPaintNeedsLoad() { return paintNeedsLoad; } /** * @param paintNeedsLoad if true, the node can only be painted after its * children have been loaded. This can be used by a renderer * implementing progressive rendering of an SVG document. */ public void setPreferedPaintNeedsLoad(final boolean paintNeedsLoad) { this.paintNeedsLoad = paintNeedsLoad; } /** * @return the namespace URI of the Node. By default, this returns * SVGConstants.SVG_NAMESPACE_URI. */ public String getNamespaceURI() { return SVGConstants.SVG_NAMESPACE_URI; } /** * Returns the parent <code>Node</code> of this <code>Node</code>. * * @return the parent node or null if there is no parent (i.e. if a node has * just been created and not yet added to the tree, or if it has been * removed from the tree, this is null). * @throws SecurityException if the application does not have the necessary * privilege rights to access this (SVG) content. */ public Node getParentNode() { return (Node) parent; } /** * Used by <code>DocumentNode</code> to create a new instance from * a prototype <code>ElementNode</code>. * * @param doc the <code>DocumentNode</code> for which a new node is * should be created. * @return a new <code>ElementNode</code> for the requested document. */ public abstract ElementNode newInstance(final DocumentNode doc); /** * @return the first child element node of this element. <code>null</code> * if this element has no child elements. */ public Element getFirstElementChild() { return (Element) firstChild; } /** * @return the next sibling element node of this element. <code>null</code> * if this element has no element sibling nodes that come after this one in * the document tree. */ public Element getNextElementSibling() { // Casting is safe here because ElementNodes can only have ElementNode // siblings. return (Element) nextSibling; } /** * @return the previous sibling element node of this element. null if this * element has no element sibling nodes that come before this one in the * document tree. */ public Element getPreviousElementSibling() { // Casting is safe here because ElementNodes can only have ElementNode // siblings. return (Element) prevSibling; } /** * @return the last child element node of this element. null if this element * has no child elements. */ public Element getLastElementChild() { return (Element) lastChild; } /** * @param traitName the name of the trait which the element may support. * @return true if this element supports the given trait in one of the * trait accessor methods (such as <code>getTrait</code> or * <code>setFloatTrait</code>. */ boolean supportsTrait(final String traitName) { if (SVGConstants.SVG_ID_ATTRIBUTE == traitName || SVGConstants.SVG_REQUIRED_FEATURES_ATTRIBUTE == traitName || SVGConstants.SVG_REQUIRED_EXTENSIONS_ATTRIBUTE == traitName || SVGConstants.SVG_SYSTEM_LANGUAGE_ATTRIBUTE == traitName) { return true; } return false; } /** * Checks if the input trait name is valid and throws a DOMException * with error code NOT_SUPPORTED_ERR if not. * * @param name the name whose syntax should be checked. * @throws DOMException with error code NOT_SUPPORTED_ERR if the * trait name is syntactically incorrect (e.g., null or containing * characters not conforming to the Namespaces in XML specification. * * @see http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName */ final void checkNCName(final String name) throws DOMException { if (name == null || name.length() == 0) { throw unsupportedTrait(name); } // We should really validate that the name has the conforming syntax // But this slows down the load time considerably. // // NCName ::= (Letter | '_') (NCNameChar)* // NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar // | Extender } /** * @param traitNamespace the namespace of the trait for which a TraitAnim * is requested. * @param traitName the trait for which there may be a TraitAnimation. * @return the TraitAnim for the requested trait name. */ TraitAnim getSafeTraitAnimNS(final String traitNamespace, final String traitName) { TraitAnim traitAnim = getTraitAnimNS(traitNamespace, traitName); if (traitAnim == null) { traitAnim = createTraitAnimNS(traitNamespace, traitName); } return traitAnim; } /** * @param traitNamespace the namespace of the trait for which a TraitAnim * is requested. * @param traitName the trait for which there may be a TraitAnimation. * @return the TraitAnim for the requested trait name. */ TraitAnim getTraitAnimNS(String traitNamespace, final String traitName) { if (traitName == null) { throw new NullPointerException(); } if (traitAnimsNS == null) { return null; } if (traitNamespace == null || traitNamespace.length() == 0) { traitNamespace = NULL_NS; } Hashtable nsTraitAnims = (Hashtable) traitAnimsNS.get(traitNamespace); if (nsTraitAnims != null) { return (TraitAnim) nsTraitAnims.get(traitName); } return null; } /** * @return an array of traits that are required by this element. */ public String[] getRequiredTraits() { return null; } /** * @return an array of namespaceURI, localName trait pairs required by * this element. */ public String[][] getRequiredTraitsNS() { return null; } /** * @return an array of trait default values, used if this element * requires that the default trait value be explicitly * set through a setTrait call. This happens, for example, * with the begin trait value on animation elements. */ public String[][] getDefaultTraits() { return null; } /** * @return an array of trait aliases. These are used when the * value of a trait can be used to set the value of another trait. * For example, on a <rect>, if the rx trait is not specified in the * original XML document, the value fot eh ry trait should be used. */ public String[][] getTraitAliases() { return null; } /** * Validates the input trait value. * * @param namespaceURI the trait's namespace URI. * @param traitName the name of the trait to be validated. * @param value the value to be validated * @param reqNamespaceURI the namespace of the element requesting * validation. * @param reqLocalName the local name of the element requesting validation. * @param reqTraitNamespace the namespace of the trait which has the values * value on the requesting element. * @param reqTraitName the name of the trait which has the values value on * the requesting element. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is incompatible with the given trait. * * @return the computed trait value. */ String validateTraitNS(final String namespaceURI, final String traitName, final String value, final String reqNamespaceURI, final String reqLocalName, final String reqTraitNamespace, final String reqTraitName) throws DOMException { /* throw new InternalError( "Trying to validate unknown trait: " + "namespaceURI : " + namespaceURI + "\n" + "traitName : " + traitName + "\n" + "value : " + value + "\n" + "reqNamespaceURI : " + reqNamespaceURI + "\n" + "reqLocalName : " + reqLocalName + "\n" + "reqTraitNamespace : " + reqTraitNamespace + "\n" + "reqTraitName : " + reqTraitName); */ return value; } /** * Validates the input trait value. * * @param traitName the name of the trait to be validated. * @param value the value to be validated * @param reqNamespaceURI the namespace of the element requesting * validation. * @param reqLocalName the local name of the element requesting validation. * @param reqTraitNamespace the namespace of the trait which has the values * value on the requesting element. * @param reqTraitName the name of the trait which has the values value on * the requesting element. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is incompatible with the given trait. */ float[][] validateFloatArrayTrait( final String traitName, final String value, final String reqNamespaceURI, final String reqLocalName, final String reqTraitNamespace, final String reqTraitName) throws DOMException { // Throw an error because this should _never_ happen, as a float // array anim should only happen on a known trait. If validation is // requested, the element implementation should know the trait. throw new Error(); } /** * @param namespaceURI the trait's namespace URI. * @param traitName the name of the trait which the element may support. * @return true if this element supports the given trait in one of the * trait accessor methods. */ boolean supportsTraitNS(final String namespaceURI, final String traitName) { if (SVGConstants.PERSEUS_NAMESPACE_URI == namespaceURI && SVGConstants.PERSEUS_CHILDREN_REQUIRED_ATTRIBUTE == traitName) { return true; } else if (SVGConstants.XML_NAMESPACE_URI == namespaceURI && (SVGConstants.XML_BASE_ATTRIBUTE_LOCAL_NAME == traitName || SVGConstants.XML_SPACE_ATTRIBUTE_LOCAL_NAME == traitName)) { return true; } if (namespaceURI == null) { return supportsTrait(traitName); } else { return false; } } /** * The traits supported by default are: externalResourcesRequired, * xml:base, xml:space, requiredFeatures, requiredExtensions and * systemLanguage. * * Returns the trait value as String. In SVG Tiny only certain traits can be * obtained as a String value. Syntax of the returned String matches the * syntax of the corresponding attribute. This element is exactly equivalent * to {@link org.w3c.dom.svg.SVGElement#getTraitNS getTraitNS} with * namespaceURI set to null. * * The method is meant to be overridden by derived classes. The * implementation pattern is that derived classes will override the method * and call their super class' implementation. If the ElementNode * implementation is called, it means that the trait is either not supported * or that it cannot be seen as a String. * * @param name the requested trait name. * @return the trait value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to a String (SVG Tiny only). */ public final String getTrait(String traitName) throws DOMException { traitName = intern(traitName); if (!supportsTrait(traitName)) { throw unsupportedTrait(traitName); } TraitAnim anim = getTraitAnimNS(NULL_NS, traitName); if (anim == null || !anim.active) { return getTraitImpl(traitName); } // Get the computed trait value from the trait animation. return anim.getTrait(TRAIT_TYPE_STRING); } /** * Returns the specified trait value as String. In SVG Tiny only certain * traits can be obtained as a String value. Syntax of the returned String * matches the syntax of the corresponding attribute. This element is * exactly equivalent to {@link org.w3c.dom.svg.SVGElement#getTraitNS * getTraitNS} with namespaceURI set to null. * * The method is meant to be overridden by derived classes. The * implementation pattern is that derived classes will override the method * and call their super class' implementation. If the ElementNode * implementation is called, it means that the trait is either not supported * or that it cannot be seen as a String. * * @param traitNamespace the requested trait's namespace. * @param traitName the requested trait name. * @return the trait value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to a String (SVG Tiny only). */ String getSpecifiedTraitNSImpl(final String traitNamespace, final String traitName) throws DOMException { if (traitNamespace == null || traitNamespace == NULL_NS) { return getSpecifiedTraitImpl(traitName); } // Only xml:base behaves differently because the computed value // may be different from the specified value. if (SVGConstants.XML_NAMESPACE_URI == traitNamespace && SVGConstants.XML_BASE_ATTRIBUTE_LOCAL_NAME == traitName) { return uriBase; } return getTraitNSImpl(traitNamespace, traitName); } /** * Returns the specified trait value as String. In SVG Tiny only certain * traits can be obtained as a String value. Syntax of the returned String * matches the syntax of the corresponding attribute. This element is * exactly equivalent to {@link org.w3c.dom.svg.SVGElement#getTraitNS * getTraitNS} with namespaceURI set to null. * * The method is meant to be overridden by derived classes. The * implementation pattern is that derived classes will override the method * and call their super class' implementation. If the ElementNode * implementation is called, it means that the trait is either not supported * or that it cannot be seen as a String. * * @param traitName the requested trait name. * @return the trait value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to a String (SVG Tiny only). */ String getSpecifiedTraitImpl(final String traitName) throws DOMException { return getTraitImpl(traitName); } /** * The traits supported by default are: externalResourcesRequired, * xml:base, xml:space, requiredFeatures, requiredExtensions and * systemLanguage. * * Returns the trait value as String. In SVG Tiny only certain traits can be * obtained as a String value. Syntax of the returned String matches the * syntax of the corresponding attribute. This element is exactly equivalent * to {@link org.w3c.dom.svg.SVGElement#getTraitNS getTraitNS} with * namespaceURI set to null. * * The method is meant to be overridden by derived classes. The * implementation pattern is that derived classes will override the method * and call their super class' implementation. If the ElementNode * implementation is called, it means that the trait is either not supported * or that it cannot be seen as a String. * * @param name the requested trait name. * @return the trait value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to a String (SVG Tiny only). */ public String getTraitImpl(final String name) throws DOMException { if (SVGConstants.SVG_ID_ATTRIBUTE == name) { return getId(); } else if (SVGConstants.SVG_REQUIRED_FEATURES_ATTRIBUTE == name) { return toStringTrait(getRequiredFeatures(), " "); } else if (SVGConstants.SVG_REQUIRED_EXTENSIONS_ATTRIBUTE == name) { return toStringTrait(getRequiredExtensions(), " "); } else if (SVGConstants.SVG_SYSTEM_LANGUAGE_ATTRIBUTE == name) { return toStringTrait(getSystemLanguage(), ","); } else { if (!supportsTrait(name)) { if (name == null) { throw unsupportedTrait(name); } String unknownTraitValue = ownerDocument.getUnknownTraitsNS(this, NULL_NS, name); if (unknownTraitValue != null) { return unknownTraitValue; } else { return ""; } } else { throw unsupportedTraitType(name, TRAIT_TYPE_STRING); } } } /** * Implementation method. * * Creates a TraitAnim for the requested trait. This method does not * check whether or not there is an existing TraitAnim for the trait. * Instead, it creates a new TraitAnim and associates it with the * given trait. After this call, any call to getSafeTraitAnimNS or * getTraitAnimNS will return this new object. * * @param traitName the trait name. * @param traitNamespace the trait's namespace. Should not be null. */ TraitAnim createTraitAnimNS(String traitNamespace, final String traitName) { if (traitNamespace == null || traitNamespace.length() == 0) { traitNamespace = NULL_NS; } TraitAnim traitAnim = null; if (NULL_NS == traitNamespace) { traitAnim = createTraitAnimImpl(traitName); } else { traitAnim = createTraitAnimNSImpl(traitNamespace, traitName); } if (traitAnimsNS == null) { traitAnimsNS = new Hashtable(); } Hashtable nsTraitAnims = (Hashtable) traitAnimsNS.get(traitNamespace); if (nsTraitAnims == null) { nsTraitAnims = new Hashtable(); traitAnimsNS.put(traitNamespace, nsTraitAnims); } nsTraitAnims.put(traitName, traitAnim); return traitAnim; } /** * To be overridden by derived classes. Should create the proper * TraitAnim type for the given trait in the anonymous namespace. * * @param traitName the trait name. */ TraitAnim createTraitAnimImpl(final String traitName) { // // If the trait is supported but the element did not create // a TraitAnim in its implementation of createTraitAnimImpl, // it means the trait is not animatable. // if (supportsTrait(traitName)) { throw notAnimatable(null, traitName); } return new StringTraitAnim(this, NULL_NS, traitName); } /** * To be overridden by derived classes. Should create the proper * TraitAnim type for the given trait in the desired namespace. * * @param traitName the trait name. */ TraitAnim createTraitAnimNSImpl(final String traitNamespace, final String traitName) { // // If the trait is supported but the element did not create // a TraitAnim in its implementation of createTraitAnimImpl, // it means the trait is not animatable. // if (supportsTraitNS(traitNamespace, traitName)) { throw notAnimatable(traitNamespace, traitName); } return new StringTraitAnim(this, traitNamespace, traitName); } /** * Same as {@link org.w3c.dom.svg.SVGElement#getTrait getTrait}, but for * namespaced traits. Parameter name must be a non-qualified trait name, * i.e. without prefix. * * @param namespaceURI the requested trait's namespace. * @param name the requested trait's local name (un-prefixed, e.g, 'href') * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to a String (SVG Tiny only). * @throws SecurityException if the application does not have the necessary * privilege rights to access this (SVG) content. */ public final String getTraitNS(String namespaceURI, String name) throws DOMException { if (namespaceURI == null) { return getTrait(name); } name = intern(name); namespaceURI = intern(namespaceURI); if (!supportsTraitNS(namespaceURI, name)) { throw unsupportedTraitNS(name, namespaceURI); } StringTraitAnim anim = (StringTraitAnim) getTraitAnimNS(namespaceURI, name); if (anim == null || !anim.active) { return getTraitNSImpl(namespaceURI, name); } return anim.getTrait(TRAIT_TYPE_STRING); } /** * Same as {@link org.w3c.dom.svg.SVGElement#getTrait getTrait}, but for * namespaced traits. Parameter name must be a non-qualified trait name, * i.e. without prefix. * * @param namespaceURI the requested trait's namespace. * @param name the requested trait's local name (un-prefixed, e.g, 'href') * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to a String (SVG Tiny only). * @throws SecurityException if the application does not have the necessary * privilege rights to access this (SVG) content. */ String getTraitNSImpl(String namespaceURI, String name) { if (SVGConstants.PERSEUS_NAMESPACE_URI == namespaceURI) { if (SVGConstants.PERSEUS_CHILDREN_REQUIRED_ATTRIBUTE == name) { if (paintNeedsLoad) { return SVGConstants.SVG_TRUE_VALUE; } else { return SVGConstants.SVG_FALSE_VALUE; } } } else if (SVGConstants.XML_NAMESPACE_URI == namespaceURI) { if (SVGConstants.XML_BASE_ATTRIBUTE_LOCAL_NAME == name) { return getURIBase(); } else if (SVGConstants.XML_SPACE_ATTRIBUTE_LOCAL_NAME == name) { switch (getXMLSpace()) { case XML_SPACE_DEFAULT: return SVGConstants.XML_DEFAULT_VALUE; case XML_SPACE_PRESERVE: default: return SVGConstants.XML_PRESERVE_VALUE; } } } if (!supportsTraitNS(namespaceURI, name)) { String unknownTraitValue = ownerDocument.getUnknownTraitsNS(this, namespaceURI, name); if (unknownTraitValue != null) { return unknownTraitValue; } return ""; } else { throw unsupportedTraitTypeNS(name, namespaceURI, TRAIT_TYPE_STRING); } } /** * @param name the requested trait's name. * @return the trait value as float. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to a float */ public final float getFloatTrait(String name) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } TraitAnim anim = getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { return getFloatTraitImpl(name); } // Get the computed value from the trait animation. return parseFloatTrait(name, anim.getTrait(TRAIT_TYPE_FLOAT)); } /** * @param name the requested trait's name. * @return the trait value as float. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to a float */ float getFloatTraitImpl(final String name) { throw unsupportedTraitType(name, TRAIT_TYPE_FLOAT); } /** * Returns the trait value as {@link org.w3c.dom.svg.SVGMatrix SVGMatrix}. * The returned object is a copy of the actual trait value and will not * change ifthe corresponding trait changes. * * @param name matrix trait name. * @return the trait value corresponding to name as SVGMatrix. * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to {@link * org.w3c.dom.svg.SVGMatrix SVGMatrix} */ public final SVGMatrix getMatrixTrait(String name) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } TraitAnim anim = getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { return getMatrixTraitImpl(name); } // Get the computed value from the trait animation SVGMatrix m = parseTransformTrait(name, anim.getTrait(TRAIT_TYPE_SVG_MATRIX)); if (m == null) { m = new Transform(1, 0, 0, 1, 0, 0); } return m; } /** * Returns the trait value as {@link org.w3c.dom.svg.SVGMatrix SVGMatrix}. * The returned object is a copy of the actual trait value and will not * change ifthe corresponding trait changes. * * @param name matrix trait name. * @return the trait value corresponding to name as SVGMatrix. * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to {@link * org.w3c.dom.svg.SVGMatrix SVGMatrix} */ SVGMatrix getMatrixTraitImpl(final String name) throws DOMException { throw unsupportedTraitType(name, TRAIT_TYPE_SVG_MATRIX); } /** * @param name the trait's name. * @return the trait value as {@link org.w3c.dom.svg.SVGRect SVGRect}. The * returned object is a copy of the actual trait value and will not change * if the corresponding trait changes. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to {@link * org.w3c.dom.svg.SVGRect SVGRect} */ public final SVGRect getRectTrait(String name) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } TraitAnim anim = getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { return getRectTraitImpl(name); } // Get the computed value from the trait animation. return toSVGRect(toViewBox(name, anim.getTrait(TRAIT_TYPE_SVG_RECT))); } /** * @param name the trait's name. * @return the trait value as {@link org.w3c.dom.svg.SVGRect SVGRect}. The * returned object is a copy of the actual trait value and will not change * if the corresponding trait changes. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to {@link * org.w3c.dom.svg.SVGRect SVGRect} */ SVGRect getRectTraitImpl(final String name) throws DOMException { throw unsupportedTraitType(name, TRAIT_TYPE_SVG_RECT); } /** * Returns the trait value as {@link org.w3c.dom.svg.SVGPath SVGPath}. The * returned object is a copy of the actual trait value and will not change * if the corresponding trait changes. * * @param name the trait's name. * @return the trait's value, as an <code>SVGPath</code> object. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to {@link * org.w3c.dom.svg.SVGPath SVGPath} */ public final SVGPath getPathTrait(String name) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } TraitAnim anim = getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { return getPathTraitImpl(name); } // Get the computed value from the trait animation. return parsePathTrait(name, anim.getTrait(TRAIT_TYPE_SVG_PATH)); } /** * Returns the trait value as {@link org.w3c.dom.svg.SVGPath SVGPath}. The * returned object is a copy of the actual trait value and will not change * if the corresponding trait changes. * * @param name the trait's name. * @return the trait's value, as an <code>SVGPath</code> object. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to {@link * org.w3c.dom.svg.SVGPath SVGPath} */ SVGPath getPathTraitImpl(final String name) throws DOMException { throw unsupportedTraitType(name, TRAIT_TYPE_SVG_PATH); } /** * Returns the trait value as {@link org.w3c.dom.svg.SVGRGBColor * SVGRGBColor}. The returned object is a copy of the trait value and will * not change if the corresponding trait changes. If the actual trait value * is not an RGBColor (i.e. "none"), this method will return null. * * @param name the requested trait name. * @return the trait value, as an <code>SVGRGBColor</code> object. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to {@link * org.w3c.dom.svg.SVGRGBColor SVGRGBColor} */ public final SVGRGBColor getRGBColorTrait(String name) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } TraitAnim anim = getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { return getRGBColorTraitImpl(name); } // Get the computed value from the trait animation return toSVGRGBColor( name, parseColorTrait(name, anim.getTrait(TRAIT_TYPE_SVG_RGB_COLOR))); } /** * Returns the trait value as {@link org.w3c.dom.svg.SVGRGBColor * SVGRGBColor}. The returned object is a copy of the trait value and will * not change if the corresponding trait changes. If the actual trait value * is not an RGBColor (i.e. "none"), this method will return null. * * @param name the requested trait name. * @return the trait value, as an <code>SVGRGBColor</code> object. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if requested * trait's computed value cannot be converted to {@link * org.w3c.dom.svg.SVGRGBColor SVGRGBColor} */ SVGRGBColor getRGBColorTraitImpl(final String name) throws DOMException { throw unsupportedTraitType(name, TRAIT_TYPE_SVG_RGB_COLOR); } /** * Adds a new attribute. * * @param name - the name of the attribute to add. * @param value - the value to set. * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified name * contains an illegal character. NO_MODIFICATION_ALLOWED_ERR: Raised if * this node is readonly. */ public final void setAttribute(String name, String value) throws DOMException { checkNCName(name); if (value == null) { throw illegalTraitValue(name, value); } name = name.intern(); setTraitImpl(name, value); } /** * Returns the requested attribute. * * @param name - the name of the attribute to add. * @return the attribute value or empty string if the attribute is not * specified. * @throws DOMException INVALID_CHARACTER_ERR: Raised if the specified name * contains an illegal character. */ public final String getAttribute(String name) throws DOMException { checkNCName(name); name = name.intern(); return getTraitImpl(name); } /** * The traits supported by default are: externalResourcesRequired, * xml:base, xml:space, requiredFeatures, requiredExtensions and * systemLanguage. * * @param name the trait's name. * @param value the trait's value. * * @throws DOMException with error code NOT_SUPPORTED_ERR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as a String * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if * attempt is made to change readonly trait. */ public final void setTrait(String name, String value) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } if (value == null) { throw illegalTraitValue(name, value); } TraitAnim anim = (TraitAnim) getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { setTraitImpl(name, value); } else { anim.setTrait(value, TRAIT_TYPE_STRING); } } /** * The traits supported by default are: externalResourcesRequired, * xml:base, xml:space, requiredFeatures, requiredExtensions and * systemLanguage. * * @param name the trait's name. * @param value the trait's value. * * @throws DOMException with error code NOT_SUPPORTED_ERR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as a String * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if * attempt is made to change readonly trait. */ public void setTraitImpl(final String name, final String value) throws DOMException { if (SVGConstants.SVG_ID_ATTRIBUTE == name) { try { setId(value); } catch (IllegalArgumentException iae) { iae.printStackTrace(); throw illegalTraitValue(name, value); } } else if (SVGConstants.SVG_REQUIRED_FEATURES_ATTRIBUTE == name) { if (value == null) { throw illegalTraitValue(name, value); } setRequiredFeatures(parseStringArrayTrait(name, value, " ")); } else if (SVGConstants.SVG_REQUIRED_EXTENSIONS_ATTRIBUTE == name) { if (value == null) { throw illegalTraitValue(name, value); } setRequiredExtensions(parseStringArrayTrait(name, value, " ")); } else if (SVGConstants.SVG_SYSTEM_LANGUAGE_ATTRIBUTE == name) { if (value == null) { throw illegalTraitValue(name, value); } setSystemLanguage(parseStringArrayTrait(name, value, ",")); } else { // The trait is not handled by this element as a string. // There are two situations. If this trait is supported // by the element, then it means that the String type is // not supported for the trait, and the following throws an // exception. Otherwise, this means the trait is just unknown, // and it goes into the generic trait map. if (supportsTrait(name)) { throw unsupportedTraitType(name, TRAIT_TYPE_STRING); } else { if (name == null) { throw unsupportedTrait(name); } if (value == null) { throw illegalTraitValue(name, value); } ownerDocument.setUnknownTraitsNS(this, NULL_NS, name, value); } } } /** * Adds a new attribute. * * @param namespaceURI - the namespace URI of the attribute to create or * alter. * @param name - the local name of the attribute to create or alter. * @param value - the value to set in string form. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if * attempt is made to change readonly trait. */ public final void setAttributeNS(String namespaceURI, String name, String value) throws DOMException { if (namespaceURI == null || namespaceURI.length() == 0) { setAttribute(name, value); return; } checkNCName(name); if (value == null) { throw illegalTraitValue(name, namespaceURI, value); } namespaceURI = namespaceURI.intern(); name = name.intern(); setTraitNSImpl(namespaceURI, name, value); } /** * Returns the requested attribute in the specified namespace. * * @param namespaceURI - the namespace URI of the attribute to create or * alter. * @param name - the local name of the attribute to create or alter. * @return the attribute value as a string, or the empty string if the * attribute was not specified. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. */ public final String getAttributeNS(String namespaceURI, String name) throws DOMException { if (namespaceURI == null || namespaceURI.length() == 0) { return getAttribute(name); } checkNCName(name); namespaceURI = namespaceURI.intern(); name = name.intern(); return getTraitNSImpl(namespaceURI, name); } /* * Same as {@link org.w3c.dom.svg.SVGElement#setTrait setTrait}, but for * namespaced traits. Parameter name must be a non-qualified trait name, * i.e. without prefix. * * @param namespaceURI the trait's namespace. * @param name the trait's local name (un-prefixed). * @param value the trait's value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as a String * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if * attempt is made to change readonly trait. */ public final void setTraitNS(String namespaceURI, String name, String value) throws DOMException { if (namespaceURI == null || namespaceURI.length() == 0) { setTrait(name, value); return; } namespaceURI = intern(namespaceURI); name = intern(name); if (!supportsTraitNS(namespaceURI, name)) { throw unsupportedTraitNS(name, namespaceURI); } StringTraitAnim anim = (StringTraitAnim) getTraitAnimNS(namespaceURI, name); if (anim == null || !anim.active) { setTraitNSImpl(namespaceURI, name, value); } else { anim.setTrait(value, TRAIT_TYPE_STRING); } } /** * Same as {@link org.w3c.dom.svg.SVGElement#setTrait setTrait}, but for * namespaced traits. Parameter name must be a non-qualified trait name, * i.e. without prefix. * * @param namespaceURI the trait's namespace. * @param name the trait's local name (un-prefixed). * @param value the trait's value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as a String * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if * attempt is made to change readonly trait. */ public void setTraitNSImpl(final String namespaceURI, final String name, final String value) throws DOMException { if (SVGConstants.PERSEUS_NAMESPACE_URI == namespaceURI) { if (SVGConstants.PERSEUS_CHILDREN_REQUIRED_ATTRIBUTE == name) { if (SVGConstants.SVG_TRUE_VALUE.equals(value)) { setPreferedPaintNeedsLoad(true); return; } else if (SVGConstants.SVG_FALSE_VALUE.equals(value)) { setPreferedPaintNeedsLoad(false); return; } else { throw illegalTraitValue(namespaceURI, name, value); } } } else if (SVGConstants.XML_NAMESPACE_URI == namespaceURI) { if (SVGConstants.XML_BASE_ATTRIBUTE_LOCAL_NAME == name) { if (value == null) { throw illegalTraitValue(name, value); } setURIBase(value); } else if (SVGConstants.XML_SPACE_ATTRIBUTE_LOCAL_NAME == name) { if (SVGConstants.XML_DEFAULT_VALUE.equals(value) || (value != null && value.length() == 0)) { setXMLSpace(XML_SPACE_DEFAULT); return; } else if (SVGConstants.XML_PRESERVE_VALUE.equals(value)) { setXMLSpace(XML_SPACE_PRESERVE); return; } else { throw illegalTraitValue(name, value); } } } // The trait is not handled by this element as a string. // There are two situations. If this trait is supported // by the element, then it means that the String type is // not supported for the trait which should never happen // because all traits have to be supported as a string. // So that causes an internal error. // Otherwise, this means the trait is just unknown, // and it goes into the generic trait map. if (supportsTraitNS(name, value)) { throw new Error(); } // Trait is unknown, treat as a generic string trait. ownerDocument.setUnknownTraitsNS(this, namespaceURI, name, value); } /** * Set the trait value as float. * * @param name the trait's name. * @param value the trait's value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as a float * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait. */ public final void setFloatTrait(String name, float value) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } TraitAnim anim = (TraitAnim) getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { setFloatTraitImpl(name, value); } else { anim.setTrait(Float.toString(value), TRAIT_TYPE_FLOAT); } } /** * Converts the input float array value to a String value. * * @param traitName the name of the trait to convert. * @param value the float trait value to convert. */ String toStringTrait(final String traitName, final float[][] value) { throw new Error(traitName); } /** * Conversts the input PaintServer value to a String trait value. * * @param value the PaintServer value to convert. */ String toString(final PaintServer paintServer) { if (paintServer == null) { return SVGConstants.CSS_NONE_VALUE; } return paintServer.toString(); } /** * Set the trait value as float. * * @param name the trait's name. * @param value the trait's value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as a float * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait. */ void setFloatArrayTrait(final String name, final float[][] value) throws DOMException { throw new Error(name); } /** * Set the trait value as a float. * * @param name the trait's name. * @param value the trait's value. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as a float * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait. */ void setFloatTraitImpl(final String name, final float value) { throw unsupportedTraitType(name, TRAIT_TYPE_FLOAT); } /** * Set the trait value as {@link org.w3c.dom.svg.SVGMatrix SVGMatrix}. * Values in SVGMarix are copied in the trait so subsequent changes to the * givenSVGMatrix have no effect on the value of the trait. * * @param name name of trait to set * @param matrix SVGMatrix value of trait * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as an {@link org.w3c.dom.svg.SVGMatrix * SVGMatrix} * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if * attempt is made to change readonly trait. */ public final void setMatrixTrait(String name, final SVGMatrix matrix) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } if (matrix == null) { throw illegalTraitValue(name, null); } TraitAnim anim = (TraitAnim) getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { setMatrixTraitImpl(name, new Transform(matrix)); } else { anim.setTrait(toStringTrait((Transform) matrix), TRAIT_TYPE_SVG_MATRIX); } } /** * Set the trait value as {@link org.w3c.dom.svg.SVGMatrix SVGMatrix}. * Values in SVGMarix are copied in the trait so subsequent changes to the * givenSVGMatrix have no effect on the value of the trait. * * @param name name of trait to set * @param matrix Transform value of trait * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as an {@link org.w3c.dom.svg.SVGMatrix * SVGMatrix} * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if * attempt is made to change readonly trait. */ void setMatrixTraitImpl(final String name, final Transform matrix) throws DOMException { throw unsupportedTraitType(name, TRAIT_TYPE_SVG_MATRIX); } /** * Set the trait value as {@link org.w3c.dom.svg.SVGRect SVGRect}. Values in * SVGRect are copied in the trait so subsequent changes to the given * SVGRect have no effect on the value of the trait. * * @param name the trait name. * @param rect the trait value, as an <code>SVGRect</code> object. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as an {@link org.w3c.dom.svg.SVGRect * SVGRect} * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. SVGRect is * invalid if the width or height values are set to negative. */ public final void setRectTrait(String name, final SVGRect rect) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } TraitAnim anim = (TraitAnim) getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { setRectTraitImpl(name, rect); } else { if (rect == null) { throw illegalTraitValue(name, null); } float[] vb = {rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()}; anim.setTrait(toStringTrait(vb), TRAIT_TYPE_SVG_RECT); } } /** * Set the trait value as {@link org.w3c.dom.svg.SVGRect SVGRect}. Values in * SVGRect are copied in the trait so subsequent changes to the given * SVGRect have no effect on the value of the trait. * * @param name the trait name. * @param rect the trait value, as an <code>SVGRect</code> object. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as an {@link org.w3c.dom.svg.SVGRect * SVGRect} * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. SVGRect is * invalid if the width or height values are set to negative. */ public void setRectTraitImpl(final String name, final SVGRect rect) throws DOMException { throw unsupportedTraitType(name, TRAIT_TYPE_SVG_RECT); } /** * Set the trait value as {@link org.w3c.dom.svg.SVGPath SVGPath}. Values in * SVGPath are copied in the trait so subsequent changes to the given * SVGPath have no effect on the value of the trait. * * @param name the trait name. * @param path the trait value, as an <code>SVGPath</code> object. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as an {@link org.w3c.dom.svg.SVGPath * SVGPath} * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. SVGPath is * invalid if it does not begin with a MOVE_TO segment. */ public final void setPathTrait(String name, final SVGPath path) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } if (path == null) { throw illegalTraitValue(name, null); } TraitAnim anim = (TraitAnim) getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { setPathTraitImpl(name, path); } else { if (path == null) { throw illegalTraitValue(name, null); } anim.setTrait(((Path) path).toString(), TRAIT_TYPE_SVG_PATH); } } /** * Set the trait value as {@link org.w3c.dom.svg.SVGPath SVGPath}. Values in * SVGPath are copied in the trait so subsequent changes to the given * SVGPath have no effect on the value of the trait. * * @param name the trait name. * @param path the trait value, as an <code>SVGPath</code> object. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as an {@link org.w3c.dom.svg.SVGPath * SVGPath} * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. SVGPath is * invalid if it does not begin with a MOVE_TO segment. */ void setPathTraitImpl(final String name, final SVGPath path) throws DOMException { throw unsupportedTraitType(name, TRAIT_TYPE_SVG_PATH); } /** * Set the trait value as {@link org.w3c.dom.svg.SVGRGBColor SVGRGBColor}. * * @param name the trait name. * @param color the trait value, as a color. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as an {@link * org.w3c.dom.svg.SVGRGBColor SVGRGBColor} * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is null. */ public final void setRGBColorTrait(String name, final SVGRGBColor color) throws DOMException { name = intern(name); if (!supportsTrait(name)) { throw unsupportedTrait(name); } if (color == null) { throw illegalTraitValue(name, null); } TraitAnim anim = (TraitAnim) getTraitAnimNS(NULL_NS, name); if (anim == null || !anim.active) { setRGBColorTraitImpl(name, color); } else { anim.setTrait(color.toString(), TRAIT_TYPE_SVG_RGB_COLOR); } } /** * Set the trait value as {@link org.w3c.dom.svg.SVGRGBColor SVGRGBColor}. * * @param name the trait name. * @param color the trait value, as a color. * * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested * trait is not supported on this element or null. * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested * trait's value cannot be specified as an {@link * org.w3c.dom.svg.SVGRGBColor SVGRGBColor} * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is null. */ void setRGBColorTraitImpl(final String name, final SVGRGBColor color) throws DOMException { throw unsupportedTraitType(name, TRAIT_TYPE_SVG_RGB_COLOR); } /** * Parses the input value and converts it to a String array. * * @param value the value to convert to a String array * @param name the name of the trait being converted. * @param seperators the string of characters which are seperators * between array values. */ protected final String[] parseStringArrayTrait(final String name, final String value, final String seperators) throws DOMException { // Don't accept null trait values. if (value == null) { throw illegalTraitValue(name, value); } SimpleTokenizer st = new SimpleTokenizer(value, seperators); int n = st.countTokens(); String[] result = new String[n]; for (int i = 0; i < n; i++) { result[i] = st.nextToken().trim().intern(); } return result; } /** * Parses the input value and converts it to a float value. * * @param name the name of the trait to convert to a float. * @param value the value to convert to a float. * @return the value converted to a float value. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid float value or null. */ protected final float parseFloatTrait( final String name, final String value) throws DOMException { try { return ownerDocument.lengthParser.parseNumber(value); } catch (IllegalArgumentException iae) { throw illegalTraitValue(name, value); } } /** * Parses the input value and converts it to a float array value. * * @param name the name of the trait to convert to a float array. * @param value the value to convert to a float. * @return the value converted to a float array value. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid float value or null. */ protected final float[] parsePositiveFloatArrayTrait( final String name, final String value) throws DOMException { try { SimpleTokenizer st = new SimpleTokenizer(value, ", "); float[] da = null; int n = st.countTokens(); float totalLength = 0; if ((n % 2) == 0) { da = new float[n]; for (int i = 0; i < da.length; i++) { da[i] = ownerDocument .lengthParser.parseNumber(st.nextToken()); if (Float.isNaN(da[i]) || da[i] < 0) { // The CSS number was invalid. // Do not set the value throw new IllegalArgumentException(); } totalLength += da[i]; } } else { da = new float[2 * n]; for (int i = 0; i < n; i++) { da[i] = ownerDocument .lengthParser.parseNumber(st.nextToken()); da[n + i] = da[i]; if (Float.isNaN(da[i]) || da[i] < 0) { // The CSS number was invalid. // Do not set the value throw new IllegalArgumentException(); } totalLength += da[i]; } } if (totalLength > 0) { return da; } else { return null; } } catch (IllegalArgumentException iae) { throw illegalTraitValue(name, value); } catch (NullPointerException iae) { throw illegalTraitValue(name, value); } } /** * Parses the input value and converts it to a positive float value. * * @param name the trait name. * @param value the value to convert to a float. * @return the value converted to a float value. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid positive float value or null. */ protected final float parsePositiveFloatTrait( final String name, final String value) throws DOMException { try { float v = ownerDocument.lengthParser.parseNumber(value); if (v < 0) { throw new IllegalArgumentException(); } return v; } catch (IllegalArgumentException iae) { throw illegalTraitValue(name, value); } } /** * Parses the input value and converts it to a float. * * @param name the trait name. * @param value the value to convert to a float. * @param isHorizontal controls whether this is a horizontal length or not. * @return the value converted to a float. * @throws DOMException if the trait value is invalid. */ protected final float parseLengthTrait( final String name, final String value, boolean isHorizontal) throws DOMException { try { Length l = ownerDocument.lengthParser.parseLength(value); switch (l.unit) { case Length.SVG_LENGTHTYPE_NUMBER: return l.value; case Length.SVG_LENGTHTYPE_IN: return (l.value * 25.4f / ownerDocument.getPixelMMSize()); case Length.SVG_LENGTHTYPE_CM: return (l.value * 10f / ownerDocument.getPixelMMSize()); case Length.SVG_LENGTHTYPE_MM: return (l.value / ownerDocument.getPixelMMSize()); case Length.SVG_LENGTHTYPE_PT: return (l.value * 25.4f / (72f * ownerDocument.getPixelMMSize())); case Length.SVG_LENGTHTYPE_PC: return (l.value * 25.4f / (6f * ownerDocument.getPixelMMSize())); case Length.SVG_LENGTHTYPE_PERCENTAGE: if (isHorizontal) { return ownerDocument.width * l.value / 100f; } else { return ownerDocument.height * l.value / 100f; } default: // This should never happen. throw new Error(); } } catch (IllegalArgumentException iae) { throw illegalTraitValue(name, value); } } /** * Parses the input value and converts it to a float. * @param name the trait name. * @param value the value to convert to a float. * @param isHorizontal controls whether this is a horizontal length or not. * @return the value converted to a float. * @throws DOMException if the trait value is invalid. */ protected final float parsePositiveLengthTrait( final String name, final String value, final boolean isHorizontal) throws DOMException { float v = parseLengthTrait(name, value, isHorizontal); if (v < 0) { throw illegalTraitValue(name, value); } return v; } /** * Parses the input value and converts it to a Path value. * * @param name the trait name. * @param value the value to convert. * @throws DOMException if the input value is invalid. */ protected final Path parsePathTrait(final String name, final String value) throws DOMException { try { return ownerDocument.pathParser.parsePath(value); } catch (IllegalArgumentException iae) { DOMException de = illegalTraitValue(name, value); if (!loaded) { ownerDocument.setDelayedException(de); return ownerDocument.pathParser.getPath(); } else { throw de; } } } /** * Parses the input points value and converts it to a Path value. * * @param name the trait name. * @param value the value to convert. * @throws DOMException if the input value is invalid. */ protected final Path parsePointsTrait(final String name, final String value) throws DOMException { try { return ownerDocument.pathParser.parsePoints(value); } catch (IllegalArgumentException iae) { DOMException de = illegalTraitValue(name, value); if (!loaded) { ownerDocument.setDelayedException(de); return ownerDocument.pathParser.getPath(); } else { throw de; } } } /** * Parses the input value and converts it to an Transform value. * * @param name the trait's name. * @param value the value to convert to a transform. * @return the value converted to an Transform object. * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid transform trait value. */ protected final Transform parseTransformTrait(final String name, final String value) throws DOMException { try { return ownerDocument.transformListParser.parseTransformList(value); } catch (IllegalArgumentException iae) { throw illegalTraitValue(name, value); } } /** * Parses the input value and converts it to an RGB value * * @param traitName the name of the color trait being parsed. * @param value the value to convert to an RGB * @return the value converted to a RGB object * * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid color trait value. */ protected final RGB parseColorTrait( final String traitName, final String value) throws DOMException { try { return ownerDocument.colorParser.parseColor(value); } catch (IllegalArgumentException e) { throw illegalTraitValue(traitName, value); } } /** * Parses the input value and converts it to a Paint value * * @param traitName the name of the color trait being parsed. * @param paintTarget the PaintTarget requesting the PaintServer. * @param value the value to convert to a Paint * @return the value converted to a Paint object * * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid color trait value. */ protected final PaintServer parsePaintTrait( final String traitName, final PaintTarget paintTarget, final String value) throws DOMException { if (value == null) { throw illegalTraitValue(traitName, value); } if (value.startsWith("url(#")) { if (value.length() < 7 || value.charAt(value.length() - 1) != ')') { throw illegalTraitValue(traitName, value); } String idRef = value.substring(5, value.length() - 1); return PaintServerReference.resolve(ownerDocument, paintTarget, traitName, idRef); } else { try { return ownerDocument.colorParser.parseColor(value); } catch (IllegalArgumentException e) { throw illegalTraitValue(traitName, value); } } } /** * Parses the input value and converts it to a Time value. * * @param traitName the name of the clock trait being parsed. * @param value the value to convert to a Time instance. * @return the value converted to a Time object. * @throws DOMException if the input value is invalid. */ protected final Time parseClockTrait( final String traitName, final String value) throws DOMException { if (SVGConstants.SVG_INDEFINITE_VALUE.equals(value)) { return Time.INDEFINITE; } try { return new Time(ownerDocument.clockParser.parseClock(value)); } catch (IllegalArgumentException iae) { throw illegalTraitValue(traitName, value); } } /** * Parses the input value and converts it to a Time value. If there * is a syntax error or if the value is invalid for the given usage * (has to be [0, infinite[ for min, and ]0, infinite[ for max, * then the default is used. For min, the default is 0. For max, the * default is 'indefinite' * * @param traitName the name of the clock trait being parsed. * @param value the value to convert to a Time instance. * @param isMin should * @return the value converted to a Time object. * @throws DOMException if the input value is invalid. */ protected final Time parseMinMaxClock( final String traitName, final String value, final boolean isMin) throws DOMException { if (SVGConstants.SVG_INDEFINITE_VALUE.equals(value)) { return Time.INDEFINITE; } if (value == null) { throw illegalTraitValue(traitName, value); } try { long v = ownerDocument.clockParser.parseClock(value); if (v < 0) { throw new IllegalArgumentException(); } if (v == 0 && !isMin) { throw new IllegalArgumentException(); } return new Time(v); } catch (IllegalArgumentException iae) { if (isMin) { return new Time(0); } return Time.INDEFINITE; } } /** * Utility method. This should be used for XML attribute values converted * to float arrays. * * @param traitName the name of the trait to parse * @param ctx the build context * @return the attribute value, converted to float * @throws DOMException if the value represents an invalid * floating point array value. */ public final float[] parseFloatArrayTrait(final String traitName, final String value) throws DOMException { return parseFloatArrayTrait(traitName, value, ','); } /** * Utility method. This should be used for XML attribute values converted * to float arrays. * * @param traitName the name of the trait to parse * @param value the value to parse * @param sep the number separator in the input value list of numbers. * @return the attribute value, converted to float * @throws DOMException if the value represents an invalid * floating point array value. */ public final float[] parseFloatArrayTrait(final String traitName, final String value, final char sep) throws DOMException { try { return ownerDocument.numberListParser.parseNumberList(value, sep); } catch (IllegalArgumentException e) { e.printStackTrace(); throw illegalTraitValue(traitName, value); } } /** * CSS 2 Specification (section 15.3.2) and SVG 1.1 specification * (20.8.3): * * all | [ normal | italic | oblique ] [, [normal | italic | oblique] ]* * * @param name the name of the trait to parse * @param value the trait value * @return the font style in the FontFace.FONT_STYLE_XXX value set. * @throws DOMException if the value is not a legal one for this trait. */ public final int parseFontStylesTrait(final String name, final String value) { if (value == null) { throw illegalTraitValue(name, value); } if (SVGConstants.CSS_ALL_VALUE.equals(value)) { return FontFace.FONT_STYLE_ALL; } SimpleTokenizer st = new SimpleTokenizer(value, SVGConstants.COMMA_STR); if (st.countTokens() < 1) { throw illegalTraitValue(name, value); } int styles = 0; while (st.hasMoreTokens()) { String t = st.nextToken().trim(); if (SVGConstants.CSS_NORMAL_VALUE.equals(t)) { styles |= TextNode.FONT_STYLE_NORMAL; } else if (SVGConstants.CSS_ITALIC_VALUE.equals(t)) { styles |= TextNode.FONT_STYLE_ITALIC; } else if (SVGConstants.CSS_OBLIQUE_VALUE.equals(t)) { styles |= TextNode.FONT_STYLE_OBLIQUE; } else { throw illegalTraitValue(name, value); } } return styles; } /** * CSS 2 specification ((section 15.3.2) and SVG 1.1 specification * (20.8.3): * * all | [normal | bold |100 | 200 | 300 | 400 | 500 | 600 | * 700 | 800 | 900] [, [normal | bold |100 | 200 | 300 | * 400 | 500 | 600 | 700 | 800 | 900]]* * * @param name the name of the trait to parse * @param value the trait value. * @return the font weight as an int value in the * FontFace.FONT_WEIGHT_XXX set. * @throws DOMException if the value is not a legal one for this trait. */ protected final int parseFontWeightsTrait(final String name, final String value) { if (value == null) { throw illegalTraitValue(name, value); } if (SVGConstants.CSS_ALL_VALUE.equals(value)) { return FontFace.FONT_WEIGHT_ALL; } SimpleTokenizer st = new SimpleTokenizer(value, SVGConstants.COMMA_STR); if (st.countTokens() < 1) { throw illegalTraitValue(name, value); } int weights = 0; while (st.hasMoreTokens()) { String t = st.nextToken().trim(); if (SVGConstants.CSS_NORMAL_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_NORMAL; } else if (SVGConstants.CSS_BOLD_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_BOLD; } else if (SVGConstants.CSS_100_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_100; } else if (SVGConstants.CSS_200_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_200; } else if (SVGConstants.CSS_300_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_300; } else if (SVGConstants.CSS_400_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_400; } else if (SVGConstants.CSS_500_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_500; } else if (SVGConstants.CSS_600_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_600; } else if (SVGConstants.CSS_700_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_700; } else if (SVGConstants.CSS_800_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_800; } else if (SVGConstants.CSS_900_VALUE.equals(t)) { weights |= TextNode.FONT_WEIGHT_900; } else { throw illegalTraitValue(name, value); } } return weights; } /** * Parses the input value assuming it has the folloing syntax: * Value: [ <family-name> | <generic-family> ] [, [<family-name> | * <generic-family> ]]* * @param name the name of the trait being parsed. * @param value the font family value to be parsed. Should not be null. * @return an array of font-family strings */ public String[] parseFontFamilyTrait(final String name, final String value) { if (value == null) { throw illegalTraitValue(name, value); } SimpleTokenizer st = new SimpleTokenizer(value, SVGConstants.COMMA_STR); String[] fontFamily = new String[st.countTokens()]; int i = 0; while (st.hasMoreTokens()) { fontFamily[i] = st.nextToken(); // Remove leading and trailing white spaces fontFamily[i] = fontFamily[i].trim(); // // Now, take care of quotes // // <!> NOTE // // According to the CSS spec., if font family values are not // quoted, then the spaces should be consolidated. The following // code does not do that. // if (fontFamily[i].length() > 0) { if (fontFamily[i].charAt(0) == '\'') { // If there is a trailing quote, remove the quotes if (fontFamily[i].charAt(fontFamily[i].length() - 1) == '\'') { fontFamily[i] = fontFamily[i].substring(1, fontFamily[i].length() - 1); } } } i++; } return fontFamily; } /** * Parses the input value, assuming a unicode range syntax, as for the * <code><hkern></code> element's u1 and u2 attributes. * * @param name the trait name. * @param value the trait value. * @return an array of unicode range pairs, as integer pairs. * @throws DOMException if the input trait value is invalid. */ protected final int[][] parseUnicodeRangeTrait(final String name, final String value) throws DOMException { try { return ownerDocument.unicodeParser.parseUnicode(value); } catch (IllegalArgumentException iae) { throw illegalTraitValue(name, value); } } /** * Throws a DOMException if the element is not loading, i.e., if * its loaded field is set to true. * * @param name the name of the trait that is accessed. */ protected void checkWriteLoading(final String name) throws DOMException { if (loaded && isInDocumentTree()) { throw readOnlyTraitError(name); } } /** * Throws a DOMException if the input float trait value is * strictly negative. * * @param name the trait name. * @param value the trait float value. */ protected void checkPositive(final String name, final float value) { if (value < 0) { throw illegalTraitValue(name, Float.toString(value)); } } /** * @param name the trait name. * @return a DOMException describing the type mismatch error. */ protected DOMException unsupportedTraitType(final String name, final String type) { if (name == null) { return unsupportedTrait(name); } return new DOMException(DOMException.TYPE_MISMATCH_ERR, Messages.formatMessage (Messages.ERROR_TRAIT_TYPE_MISMATCH, new String[] {name, type, getLocalName(), getNamespaceURI()})); } /** * @param name the trait name. * @return a DOMException describing the type mismatch error. */ protected DOMException unsupportedTraitTypeNS(final String name, final String namespaceURI, final String type) { return new DOMException(DOMException.TYPE_MISMATCH_ERR, Messages.formatMessage (Messages.ERROR_TRAIT_TYPE_NS_MISMATCH, new String[] {name, namespaceURI, type, getLocalName(), getNamespaceURI()})); } /** * @param name the trait name * @return a DOMException describing the unsupported trait error. */ protected DOMException unsupportedTrait(final String name) { return new DOMException(DOMException.NOT_SUPPORTED_ERR, Messages.formatMessage (Messages.ERROR_UNSUPPORTED_TRAIT, new String[] {name, null, getLocalName(), getNamespaceURI()})); } /** * @param name the trait name * @param namespaceURI the trait's namespace URI. * @return a DOMException describing the unsupported trait error. */ protected DOMException unsupportedTraitNS(final String name, final String namespaceURI) { return new DOMException(DOMException.NOT_SUPPORTED_ERR, Messages.formatMessage (Messages.ERROR_UNSUPPORTED_TRAIT, new String[] {name, namespaceURI, getLocalName(), getNamespaceURI()})); } /** * @param name the name of the trait * @param value the illegal value. * @return a DOMException describing an illegal trait value */ DOMException illegalTraitValue(final String name, final String value) { return new DOMException( DOMException.INVALID_ACCESS_ERR, Messages.formatMessage( Messages.ERROR_INVALID_TRAIT_VALUE, new String[] { name, value, getLocalName() + "(" + getId() + ")", getNamespaceURI() })); } /** * @param name the name of the trait * @param value the illegal value. * @return a DOMException describing an illegal trait value */ DOMException notAnimatable(final String traitNamespace, final String traitName) { return new DOMException(DOMException.NOT_SUPPORTED_ERR, Messages.formatMessage (Messages.ERROR_TRAIT_NOT_ANIMATABLE, new String[] {traitNamespace, traitName, getLocalName(), getNamespaceURI()})); } /** * @param targetId the target element's id (may be null) * @param traitNamespace the animated trait's namespace. * @param traitName the animated trait's name. * @param targetNamespace the target element's namespace. * @param targetName the target element's name * @param animationId the animation id (may be null) * @param animationNamespace the animation's namespace * @param animationLocalName the animation's local name. * @param errorDescription the animation error's description. */ protected DOMException animationError(final String targetId, final String traitNamespace, final String traitName, final String targetNamespace, final String targetName, final String animationId, final String animationNamespace, final String animationLocalName, final String errorDescription) { return new DOMException(DOMException.INVALID_STATE_ERR, Messages.formatMessage (Messages.ERROR_INVALID_ANIMATION_CONFIGURATION, new String[] {targetId, traitNamespace, traitName, targetNamespace, targetName, animationId, animationNamespace, animationLocalName, animationLocalName, errorDescription})); } /** * @param name the name of the trait * @param namespaceURI the trait's namespace URI. * @param value the illegal value. * @return a DOMException describing an illegal trait value */ protected DOMException illegalTraitValue(final String name, final String namespaceURI, final String value) { return illegalTraitValue(name + "(" + namespaceURI + ")", value); } /** * @param name the trait name. * @return DOMException describing the write error on a read-only attribute. */ protected DOMException readOnlyTraitError(final String name) { return new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, Messages.formatMessage (Messages.ERROR_READ_ONLY_TRAIT, new String[] {name, getLocalName(), getNamespaceURI()})); } /** * Converts an animated float[][] value into an AWT color value. * * @param name the trait's name. * @param value the float[][] to convert. * @return the color converted to an AWT <code>Color</code> object. */ protected RGB toRGB(final String name, final float[][] v) throws DOMException { if (v != null) { return new RGB((int) v[0][0], (int) v[0][1], (int) v[0][2]); } else { return null; } } /** * Converts an animated float[][] value into an String rgb value. * * @param name the trait's name. * @param value the float[][] to convert. * @return the color converted to a string. */ protected String toRGBString(final String name, final float[][] v) throws DOMException { if (v != null) { return "rgb(" + ((int) v[0][0]) + "," + ((int) v[0][1]) + "," + ((int) v[0][2]) + ")"; } else { return "none"; } } /** * Converts an J2D RGB to an SVG DOM RGBColor * * @param traitName the name of the trait whose value is convereted. * @param paint the PaintServer to convert. Should not be null. * @return the color, converted to an <code>SVGRGBColor</code> instance. * * @throws DOMException with error code TYPE_MISMATCH_ERR if the * paint is not an instance of Colorl */ protected SVGRGBColor toSVGRGBColor(final String traitName, final PaintServer paint) { if (paint == null) { return null; } if (!(paint instanceof SVGRGBColor)) { throw unsupportedTraitType(traitName, TRAIT_TYPE_SVG_RGB_COLOR); } return (SVGRGBColor) paint; } /** * Converts a viewBox array to an SVGRect value. * * @param name the name of the trait. * @param viewBox the viewbox value to convet. */ protected SVGRect toSVGRect(final float[][] viewBox) { if (viewBox == null) { return null; } SVGRect r = new Box(viewBox[0][0], viewBox[0][1], viewBox[1][0], viewBox[2][0]); return r; } /** * Converts an comma seperated list of floats to a viewBox array * * @param name the trait name * @param value the trait value to be converted to an SVGRect. * The expected syntax is * "float comma-wsp float comma-wsp float comma-wsp float comma-wsp" * @return an array of four floats. * * @throws DOMException with error code INVALID_ACCESS_ERR if the input * value is an invalid value for the given trait or null. */ protected float[][] toViewBox(final String name, final String value) throws DOMException { if (value == null) { throw illegalTraitValue(name, value); } try { return ownerDocument.viewBoxParser.parseViewBox(value); } catch (IllegalArgumentException iae) { throw illegalTraitValue(name, value); } } /** * Converts a Java String array to a trait string array with the format: * "str1, str2, .., strN" * * @param array the string array to be converted. * @return a string with the value "" if the array is null or * "float1,float2,..,floatN" */ protected String toStringTrait(final String[] array) { return toStringTrait(array, ","); } /** * Converts a Java String array to a trait string array with the format: * "str1, str2, .., strN" * * @param array the string array to be converted. * @param sep seperator to use in the output string array. * * @return a string with the value "" if the array is null or * "float1,float2,..,floatN" */ protected String toStringTrait(final String[] array, final String sep) { if (array == null || array.length < 1) { return ""; } StringBuffer sb = new StringBuffer(); for (int i = 0; i < array.length - 1; i++) { sb.append(array[i]); sb.append(sep); } sb.append(array[array.length - 1]); return sb.toString(); } /** * Converts a Java String array to a trait string array with the format: * "str1, str2, .., strN". In addition, the string values are put inside * single quotes if there are spaces in the string values. This is needed * for values such as the CSS 2 font-family attribute. * * @param array the string array to be converted. * * @return a string with the value "" if the array is null or * "float1,float2,..,floatN" */ protected String toStringTraitQuote(final String[] array) { if (array == null || array.length < 1) { return ""; } StringBuffer sb = new StringBuffer(); for (int i = 0; i < array.length - 1; i++) { if (array[i].indexOf(' ') != -1) { sb.append('\''); sb.append(array[i]); sb.append('\''); } else { sb.append(array[i]); } sb.append(','); } if (array[array.length - 1].indexOf(' ') != -1) { sb.append('\''); sb.append(array[array.length - 1]); sb.append('\''); } else { sb.append(array[array.length - 1]); } return sb.toString(); } /** * Helper method. Converts the input array value to a string trait value. * * @param array the float array to be converted. * @return a string with the value "none" if the array is null or * "float1,float2,..,floatN" */ protected String toStringTrait(final float[] array) { return toStringTrait(array, ','); } /** * Helper method. Converts the input array value to a string trait value. * * @param array the float array to be converted. * @return a string with the value "none" if the array is null or * "float1,float2,..,floatN" */ protected String toStringTrait(final float[] array, final char sep) { if (array == null) { return SVGConstants.CSS_NONE_VALUE; } StringBuffer sb = new StringBuffer(); for (int i = 0; i < array.length - 1; i++) { sb.append(array[i]); sb.append(sep); } sb.append(array[array.length - 1]); return sb.toString(); } /** * Helper method. Convertst the input array of float arrays into a * string trait value. * * @param array the array of float arrays to convert. * @return a string the the value "" if the array is null or * "f01 f02 f03 f04; f11 f12 f13 f14; ...;fn1 fn2 fn3 fn4" * @throws NullPointerException if one of the array values is null * @throws ArrayIndexOutOfBoundsException if one of the array values is of * length 0. */ protected String toStringTrait(final float[][] array) { if (array == null || array.length == 0) { return ""; } StringBuffer sb = new StringBuffer(); for (int i = 0; i < array.length; i++) { for (int j = 0; j < array[i].length - 1; j++) { sb.append(array[i][j]); sb.append(SVGConstants.COMMA); } sb.append(array[i][array[i].length - 1]); sb.append(SVGConstants.SEMI_COLON); } String value = sb.toString(); // Trim trailing ';' return value.substring(0, value.length() - 1); } /** * Helper method. Converts the input <code>Transform</code> * to an SVGMatrix trait value. * * @param transform the transform to convert. * @return the SVGMatrix equivalent value. */ protected SVGMatrix toSVGMatrixTrait(final Transform transform) { if (transform == null) { return new Transform(1, 0, 0, 1, 0, 0); } else { return new Transform(transform); } } /** * Converts an align value to a preserveAspectRatio string trait * value. * * @param align one of StructureNode.ALIGN_NONE, * StructureNode.ALIGN_XMIDYMID */ protected static String alignToStringTrait(final int align) { switch (align) { case StructureNode.ALIGN_XMIDYMID: return SVGConstants.SVG_IMAGE_PRESERVE_ASPECT_RATIO_DEFAULT_VALUE; default: return SVGConstants.SVG_NONE_VALUE; } } /** * Converts a FontFace's font-styles to a String trait. * * @param styles the FontFace type styles value. */ protected String fontStylesToStringTrait(final int styles) { if (styles == FontFace.FONT_STYLE_ALL) { return SVGConstants.CSS_ALL_VALUE; } StringBuffer sb = new StringBuffer(); if ((styles & TextNode.FONT_STYLE_NORMAL) != 0) { sb.append(SVGConstants.CSS_NORMAL_VALUE); sb.append(SVGConstants.COMMA); } if ((styles & TextNode.FONT_STYLE_ITALIC) != 0) { sb.append(SVGConstants.CSS_ITALIC_VALUE); sb.append(SVGConstants.COMMA); } if ((styles & TextNode.FONT_STYLE_OBLIQUE) != 0) { sb.append(SVGConstants.CSS_OBLIQUE_VALUE); sb.append(SVGConstants.COMMA); } if (sb.length() > 0) { return sb.toString().substring(0, sb.length() - 1); } return sb.toString(); } /** * Converts an FontFace's font-weights to a String trait. * * @param weight the FontFace type' weights value. */ protected String fontWeightsToStringTrait(final int weight) { if (weight == FontFace.FONT_WEIGHT_ALL) { return SVGConstants.CSS_ALL_VALUE; } StringBuffer sb = new StringBuffer(); if ((weight & TextNode.FONT_WEIGHT_100) != 0) { sb.append(SVGConstants.CSS_100_VALUE); sb.append(SVGConstants.COMMA); } if ((weight & TextNode.FONT_WEIGHT_200) != 0) { sb.append(SVGConstants.CSS_200_VALUE); sb.append(SVGConstants.COMMA); } if ((weight & TextNode.FONT_WEIGHT_300) != 0) { sb.append(SVGConstants.CSS_300_VALUE); sb.append(SVGConstants.COMMA); } if ((weight & TextNode.FONT_WEIGHT_400) != 0) { sb.append(SVGConstants.CSS_400_VALUE); sb.append(SVGConstants.COMMA); } if ((weight & TextNode.FONT_WEIGHT_500) != 0) { sb.append(SVGConstants.CSS_500_VALUE); sb.append(SVGConstants.COMMA); } if ((weight & TextNode.FONT_WEIGHT_600) != 0) { sb.append(SVGConstants.CSS_600_VALUE); sb.append(SVGConstants.COMMA); } if ((weight & TextNode.FONT_WEIGHT_700) != 0) { sb.append(SVGConstants.CSS_700_VALUE); sb.append(SVGConstants.COMMA); } if ((weight & TextNode.FONT_WEIGHT_800) != 0) { sb.append(SVGConstants.CSS_800_VALUE); sb.append(SVGConstants.COMMA); } if ((weight & TextNode.FONT_WEIGHT_900) != 0) { sb.append(SVGConstants.CSS_900_VALUE); sb.append(SVGConstants.COMMA); } if (sb.length() > 0) { return sb.toString().substring(0, sb.length() - 1); } return sb.toString(); } /** * Helper method. Converts the input <code>Transform</code> * to a string trait value. * * @param transform the transform to convert. * @return the string trait value. */ protected static String toStringTrait(final Transform transform) { if (transform == null) { return Transformable.IDENTITY_TRANSFORM_TRAIT; } else { StringBuffer sb = new StringBuffer(); sb.append("matrix("); sb.append(transform.getComponent(0)); sb.append(","); sb.append(transform.getComponent(1)); sb.append(","); sb.append(transform.getComponent(2)); sb.append(","); sb.append(transform.getComponent(3)); sb.append(","); sb.append(transform.getComponent(4)); sb.append(","); sb.append(transform.getComponent(5)); sb.append(")"); return sb.toString(); } } /** * Helper method. Converts the input unicode range into a String * trait, with the following syntax: * "U+b1-e1,U+b2-e2,...,U+bn-en" * * @param u the unicode range to convert. * @return the string value with the unicode range syntax. */ protected String unicodeRangeToStringTrait(final int[][] u) { if (u == null) { return null; } if (u.length == 0) { return SVGConstants.EMPTY; } StringBuffer sb = new StringBuffer(); for (int i = 0; i < u.length - 1; i++) { if (u[i] == null || u[i].length != 2) { throw new IllegalArgumentException(); } sb.append("U+"); sb.append(Integer.toHexString(u[i][0])); sb.append('-'); sb.append(Integer.toHexString(u[i][1])); sb.append(','); } if (u[u.length - 1] == null || u[u.length - 1].length != 2) { throw new IllegalArgumentException(); } sb.append("U+"); sb.append(Integer.toHexString(u[u.length - 1][0])); sb.append('-'); sb.append(Integer.toHexString(u[u.length - 1][1])); return sb.toString(); } /** * Converts the input float value to an animated float array trait. * * @param v the float value to wrap in a float[][] array. * @return the wrapped value. */ float[][] toAnimatedFloatArray(final float v) { return new float[][] {{v}}; } /** * Converts the input float[] array to an animated float array trait. * * @param a the float array to wrap in a float[][] array. Should not * be null. * @return the wrapped value. */ float[][] toAnimatedFloatArray(final float[] a) { float[][] v = new float[a.length][]; // This assumes that each value in the input array are separate // components. for (int i = 0; i < a.length; i++) { v[i] = new float[] {a[i]}; } return v; } /** * Utility method to convert a Path to an animatable float array. * * @param p the path to convert * @return the converted value. */ float[][] toAnimatedFloatArray(final Path path) { return new float[][] {path.getData()}; } /** * Converts an animated float array to a trait float array value. * This is used for multi-component trait values, such as * stroke-dasharray, or the text's x, y or rotate values. * * @param value the animated value to convert. * @return an float array trait value. */ float[] toTraitFloatArray(final float[][] value) { float[] v = new float[value.length]; for (int i = 0; i < v.length; i++) { v[i] = value[i][0]; } return v; } /** * @return a text description of this node including the node * ID if one was set. */ public String toString() { if (getId() != null && getId().length() > 0) { return "ElementNode[" + getId() + "] [" + super.toString() + "]"; } else { return super.toString(); } } // ========================================================================= // Type comparison utilities. // /** * @param objA first object to compare. * @param objB second object to compare. * @return true if the objects are both null or if they are Object.equals() */ public static boolean equal(final Object objA, final Object objB) { if (objA == objB) { return true; } if (objA == null || objB == null) { return false; } return objA.equals(objB); } /** * @param faa first float array to compare * @param faab second float array to compare * @return true if the objects are both null or if they are equal */ public static boolean equal(final float[] faa, final float[] fab) { if (faa == fab) { return true; } if (faa == null || fab == null || faa.length != fab.length) { return false; } int n = faa.length; for (int i = 0; i < n; i++) { if (faa[i] != fab[i]) { return false; } } return true; } /** * @param saa first string array to compare * @param sab second string array to compare * @return true if the objects are both null or if they are equal */ public static boolean equal(final String[] saa, final String[] sab) { if (saa == sab) { return true; } if (saa == null || sab == null || saa.length != sab.length) { return false; } int n = saa.length; for (int i = 0; i < n; i++) { if (!equal(saa[i], sab[i])) { return false; } } return true; } /** * @param iaa first int array to compare * @param iab second int array to compare * @return true if the objects are both null or if they are equal */ public static boolean equal(final int[][] iaa, final int[][] iab) { if (iaa == iab) { return true; } if (iaa == null || iab == null || iaa.length != iab.length) { return false; } int n = iaa.length; for (int i = 0; i < n; i++) { if (iaa[i] != iab[i]) { if (iaa[i] == null || iab[i] == null || iaa[i].length != iab[i].length) { return false; } int m = iaa[i].length; for (int j = 0; j < m; j++) { if (iaa[i][j] != iab[i][j]) { return false; } } } } return true; } /** * @param faa first float array to compare * @param fab second float array to compare * @return true if the objects are both null or if they are equal */ public static boolean equal(final float[][] faa, final float[][] fab) { if (faa == fab) { return true; } if (faa == null || fab == null || faa.length != fab.length) { return false; } int n = faa.length; for (int i = 0; i < n; i++) { if (faa[i] != fab[i]) { if (faa[i] == null || fab[i] == null || faa[i].length != fab[i].length) { return false; } int m = faa[i].length; for (int j = 0; j < m; j++) { if (faa[i][j] != fab[i][j]) { return false; } } } } return true; } /** * Utility method. * * @param str the String object to intern. * @return null if the input string is null. The interned string otherwise. */ public static String intern(final String str) { if (str == null) { return null; } return str.intern(); } }