package textbender.o.rhinohide.core; // Copyright 2006-2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import netscape.javascript.JSObject; import org.w3c.dom.*; import textbender.g.lang.ThreadSafe; import textbender.o.rhinohide.*; import textbender.o.rhinohide.events.RhiEventTarget; /** A node implemented as an overlay of a JavaScript node. * *

BUG: flaky-insert

*
* http://reluk.ca/var/cache/textbender-javadoc/textbender/o/rhinohide/core/RhiNode.html#flaky-insert *
*

* Affects IE * and particular orderings of node insert/append. * When populating a newly created node, * you may have to insert/append its children * prior to inserting the new node in its own, higher parent. * Otherwise the insert/append may throw a JSException, * "Unexpected call to method or property access." * See for example the source code of populateAndInsert, * in RhiDocumentFragment_1. *

*

* Suspect Java Plug-In bug, specific to IE. *

*/ public abstract @ThreadSafe class RhiNode extends RhiEventTarget implements Node { /** Constructs a RhiNode. * * @param window global object * @param jsObject bridge to underlying JavaScript node, * per {@linkplain Rhinohide#jsObject() jsObject}() * * @return node, or null if jsObject is null * * @throws StunnedRhinoException if jsObject is non-null, * and has no 'nodeType' property */ public static RhiNode wrapNode( RhiWindow window, JSObject jsObject ) { if( jsObject == null ) return null; return wrapNode( window, jsObject, getNodeType(new Rhinohide(window,jsObject)) ); } /** Constructs a RhiNode of a specified node type. * * @param window global object * @param jsObject bridge to underlying JavaScript node, * per {@linkplain Rhinohide#jsObject() jsObject}() * @param nodeType jsObject's node type */ public static RhiNode wrapNode( RhiWindow window, JSObject jsObject, short nodeType ) { final RhiNode node; switch( nodeType ) { case ATTRIBUTE_NODE: node = RhiAttr.wrapAttr( window, jsObject, /*doTypeCheck*/false ); break; case CDATA_SECTION_NODE: node = RhiCDATASection.wrapCDATASection( window, jsObject, /*doTypeCheck*/false ); break; case COMMENT_NODE: node = RhiComment.wrapComment( window, jsObject, /*doTypeCheck*/false ); break; case DOCUMENT_NODE: node = RhiDocument.wrapDocument( window, jsObject, /*doTypeCheck*/false ); break; case DOCUMENT_FRAGMENT_NODE: node = RhiDocumentFragment.wrapDocumentFragment( window, jsObject, /*doTypeCheck*/false ); break; case DOCUMENT_TYPE_NODE: node = RhiDocumentType.wrapDocumentType( window, jsObject, /*doTypeCheck*/false ); break; case ELEMENT_NODE: node = RhiElement.wrapElement( window, jsObject, /*doTypeCheck*/false ); break; case ENTITY_NODE: node = RhiEntity.wrapEntity( window, jsObject, /*doTypeCheck*/false ); break; case ENTITY_REFERENCE_NODE: node = RhiEntityReference.wrapEntityReference( window, jsObject, /*doTypeCheck*/false ); break; case NOTATION_NODE: node = RhiNotation.wrapNotation( window, jsObject, /*doTypeCheck*/false ); break; case PROCESSING_INSTRUCTION_NODE: node = RhiProcessingInstruction.wrapProcessingInstruction( window, jsObject, /*doTypeCheck*/false ); break; case TEXT_NODE: node = RhiText.wrapText( window, jsObject, /*doTypeCheck*/false ); break; default: throw new IllegalArgumentException( "unrecognized node type: " + nodeType ); } return node; } RhiNode( RhiWindow window, JSObject jsObject ) { super( window, jsObject ); } // - N o d e -------------------------------------------------------------------------- public final Node appendChild( Node newChild ) { callV( "appendChild", toJSObject((RhiNode)newChild) ); return newChild; } public final Node cloneNode( boolean deep ) { return wrapNode( window, (JSObject)callV( "cloneNode", deep )); } /** Not yet coded. * * @throws UnsupportedOperationException */ public final short compareDocumentPosition( Node other ) { throw new UnsupportedOperationException(); } public final NamedNodeMap getAttributes() { return RhiNamedNodeMap.wrapNamedNodeMap( window, (JSObject)getMemberV( "attributes" )); } /** Untested. */ public final String getBaseURI() { return (String)getMember( "baseURI" ); } public final NodeList getChildNodes() { return RhiNodeList.wrapNodeList( window, (JSObject)getMemberV( "childNodes" )); } /** Not yet coded. * * @throws UnsupportedOperationException */ public final Object getFeature( String feature, String version ) { throw new UnsupportedOperationException(); } public final Node getFirstChild() { return wrapNode( window, (JSObject)getMember( "firstChild" )); } public final Node getLastChild() { return wrapNode( window, (JSObject)getMember( "lastChild" )); } public String getLocalName() { return (String)getMember( "localName" ); } public final String getNamespaceURI() { return (String)getMember( "namespaceURI" ); } public final String getNodeName() { return (String)getMember( "nodeName" ); } public final short getNodeType() { return getNodeType( RhiNode.this ); } /** Returns the node type of a JavaScript node. * * @param rhinohide of JavaScript node * * @throws StunnedRhinoException if jsObject is non-null, * and has no 'nodeType' property */ public static short getNodeType( Rhinohide rhinohide ) { Object sNodeType = rhinohide.getMemberV( "nodeType" ); return ((Number)sNodeType).shortValue(); // not Double on IE, as expected (per txt on JSObject) } public final String getNodeValue() { return (String)getMember( "nodeValue" ); } public final void setNodeValue( String value ) { setMember( "nodeValue", value ); } public final Document getOwnerDocument() { return RhiDocument.wrapDocument ( window, (JSObject)getMember( "ownerDocument" ), /*doTypeCheck*/false ); } public final Node getParentNode() { return wrapNode( window, (JSObject)getMember( "parentNode" )); } /** Untested. */ public final String getPrefix() { return (String)getMember( "prefix" ); } /** Not yet coded. * * @throws UnsupportedOperationException */ public final void setPrefix( String prefix ) { throw new UnsupportedOperationException(); } public final Node getPreviousSibling() { return wrapNode( window, (JSObject)getMember( "previousSibling" )); } public final Node getNextSibling() { return wrapNode( window, (JSObject)getMember( "nextSibling" )); } /** Untested. */ public final String getTextContent() { return (String)getMember( "textContent" ); } /** Not yet coded. * * @throws UnsupportedOperationException */ public final void setTextContent( String textContent ) { throw new UnsupportedOperationException(); } /** Untested. */ public final boolean hasAttributes() { return (Boolean)callV( "hasAttributes" ); } public final boolean hasChildNodes() { return (Boolean)callV( "hasChildNodes" ); } public final Node insertBefore( Node newChild, Node refChild ) { callV( "insertBefore", toJSObject((RhiNode)newChild), toJSObject((RhiNode)refChild) ); return newChild; } public final boolean isDefaultNamespace( String namespaceURI ) { return (Boolean)callV( "isDefaultNamespace", namespaceURI ); } /** Untested. */ public final boolean isEqualNode( Node arg ) { return (Boolean)callV( "isEqualNode", toJSObject((RhiNode)arg) ); } public final boolean isSameNode( Node other ) { return (Boolean)callV( "isSameNode", toJSObject((RhiNode)other) ); } public final boolean isSupported( String feature, String version ) { RhiDOMImplementation.checkFormatOfFeatureVersion( version ); return (Boolean)callV( "isSupported", feature, version ); } /** Untested. */ public final String lookupNamespaceURI( String prefix ) { return (String)call( "lookupNamespaceURI", prefix ); } public final String lookupPrefix( String namespaceURI ) { return (String)call( "lookupPrefix", namespaceURI ); } public final void normalize() { call( "normalize" ); } public final Node removeChild( Node oldChild ) { callV( "removeChild", toJSObject((RhiNode)oldChild) ); return oldChild; } public final Node replaceChild( Node newChild, Node oldChild ) { callV( "replaceChild", toJSObject((RhiNode)newChild), toJSObject((RhiNode)oldChild) ); return newChild; } /** Not yet coded. * * @throws UnsupportedOperationException */ public final Object getUserData( String key ) { throw new UnsupportedOperationException(); } // see ._/userData_SNode.java /** Not yet coded. * * @throws UnsupportedOperationException */ public final Object setUserData( String key, Object data, UserDataHandler handler ) { throw new UnsupportedOperationException(); } // - O b j e c t ---------------------------------------------------------------------- /** Returns true iff o is a RhiNode, and wraps the same JavaScript node, * according to Node.isSameNode(). */ public @Override boolean equals( Object o ) { if( !(o instanceof RhiNode )) return false; return this.isSameNode( (RhiNode)o ); // instead of JSObject.equals(), which returns false for same nodes (in comparison between nodes of a Range and a TreeWalker in textbender.a.u.transfer.InPageFactory, Plug-In 1.5) // cf. com.sun.org.apache.xerces.internal.dom.NodeImpl, effectively the same according to its isSameNode() } /** Not yet coded, non-trivial. * Needs a reasonably efficient algorithm, compatible with equals(). * * @throws UnsupportedOperationException */ public @Override int hashCode() { throw new UnsupportedOperationException(); } //// P r i v a t e /////////////////////////////////////////////////////////////////////// /** @throws IllegalArgumentException if jsObject is not a DOM node of nodeType */ protected static void ensureNodeType( Rhinohide rhinohide, short nodeType ) { short jsObjectNodeType = getNodeType( rhinohide ); if( jsObjectNodeType != nodeType ) throw new IllegalArgumentException( "wrong node type: " + jsObjectNodeType ); } }