Source Code Cross Referenced for DomNode.java in  » Testing » htmlunit » com » gargoylesoftware » htmlunit » html » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Testing » htmlunit » com.gargoylesoftware.htmlunit.html 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
0003:         *
0004:         * Redistribution and use in source and binary forms, with or without
0005:         * modification, are permitted provided that the following conditions are met:
0006:         *
0007:         * 1. Redistributions of source code must retain the above copyright notice,
0008:         *    this list of conditions and the following disclaimer.
0009:         * 2. Redistributions in binary form must reproduce the above copyright notice,
0010:         *    this list of conditions and the following disclaimer in the documentation
0011:         *    and/or other materials provided with the distribution.
0012:         * 3. The end-user documentation included with the redistribution, if any, must
0013:         *    include the following acknowledgment:
0014:         *
0015:         *       "This product includes software developed by Gargoyle Software Inc.
0016:         *        (http://www.GargoyleSoftware.com/)."
0017:         *
0018:         *    Alternately, this acknowledgment may appear in the software itself, if
0019:         *    and wherever such third-party acknowledgments normally appear.
0020:         * 4. The name "Gargoyle Software" must not be used to endorse or promote
0021:         *    products derived from this software without prior written permission.
0022:         *    For written permission, please contact info@GargoyleSoftware.com.
0023:         * 5. Products derived from this software may not be called "HtmlUnit", nor may
0024:         *    "HtmlUnit" appear in their name, without prior written permission of
0025:         *    Gargoyle Software Inc.
0026:         *
0027:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
0028:         * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
0029:         * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
0030:         * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
0031:         * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0032:         * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
0033:         * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0034:         * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0035:         * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0036:         * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0037:         */
0038:        package com.gargoylesoftware.htmlunit.html;
0039:
0040:        import java.beans.PropertyChangeListener;
0041:        import java.beans.PropertyChangeSupport;
0042:        import java.io.PrintWriter;
0043:        import java.io.Serializable;
0044:        import java.io.StringWriter;
0045:        import java.util.ArrayList;
0046:        import java.util.Iterator;
0047:        import java.util.List;
0048:        import java.util.NoSuchElementException;
0049:
0050:        import org.apache.commons.logging.Log;
0051:        import org.apache.commons.logging.LogFactory;
0052:        import org.jaxen.JaxenException;
0053:        import org.jaxen.Navigator;
0054:        import org.mozilla.javascript.Function;
0055:        import org.mozilla.javascript.ScriptableObject;
0056:
0057:        import com.gargoylesoftware.htmlunit.Assert;
0058:        import com.gargoylesoftware.htmlunit.IncorrectnessListener;
0059:        import com.gargoylesoftware.htmlunit.Page;
0060:        import com.gargoylesoftware.htmlunit.html.xpath.HtmlUnitXPath;
0061:        import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
0062:
0063:        /**
0064:         * Base class for nodes in the HTML DOM tree. This class is modeled after the
0065:         * W3C DOM specification, but does not implement it.
0066:         *
0067:         * @version $Revision: 2132 $
0068:         * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
0069:         * @author <a href="mailto:gudujarlson@sf.net">Mike J. Bresnahan</a>
0070:         * @author David K. Taylor
0071:         * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
0072:         * @author Chris Erskine
0073:         * @author Mike Williams
0074:         * @author Marc Guillemot
0075:         * @author Denis N. Antonioli
0076:         * @author Daniel Gredler
0077:         * @author Ahmed Ashour
0078:         * @author Rodney Gitzel
0079:         */
0080:        public abstract class DomNode implements  Cloneable, Serializable {
0081:
0082:            /**
0083:             * Node type constant for the <code>Document</code> node.
0084:             * @deprecated use {@link org.w3c.dom.Node#DOCUMENT_NODE} instead.
0085:             */
0086:            public static final short DOCUMENT_NODE = 9;
0087:
0088:            /**
0089:             * Node type constant for <code>Element</code> nodes.
0090:             * @deprecated use {@link org.w3c.dom.Node#ELEMENT_NODE} instead.
0091:             */
0092:            public static final short ELEMENT_NODE = 1;
0093:
0094:            /**
0095:             * Node type constant for <code>Text</code> nodes.
0096:             * @deprecated use {@link org.w3c.dom.Node#TEXT_NODE} instead.
0097:             */
0098:            public static final short TEXT_NODE = 3;
0099:
0100:            /**
0101:             * Node type constant for <code>Attribute</code> nodes.
0102:             * @deprecated use {@link org.w3c.dom.Node#ATTRIBUTE_NODE} instead.
0103:             */
0104:            public static final short ATTRIBUTE_NODE = 2;
0105:
0106:            /**
0107:             * Node type constant for <code>Comment</code> nodes.
0108:             * @deprecated use {@link org.w3c.dom.Node#COMMENT_NODE} instead.
0109:             */
0110:            public static final short COMMENT_NODE = 8;
0111:
0112:            /** A ready state constant for IE (state 1). */
0113:            public static final String READY_STATE_UNINITIALIZED = "uninitialized";
0114:
0115:            /** A ready state constant for IE (state 2). */
0116:            public static final String READY_STATE_LOADING = "loading";
0117:
0118:            /** A ready state constant for IE (state 3). */
0119:            public static final String READY_STATE_LOADED = "loaded";
0120:
0121:            /** A ready state constant for IE (state 4). */
0122:            public static final String READY_STATE_INTERACTIVE = "interactive";
0123:
0124:            /** A ready state constant for IE (state 5). */
0125:            public static final String READY_STATE_COMPLETE = "complete";
0126:
0127:            /** The owning page of this node. */
0128:            private final Page page_;
0129:
0130:            /** The parent node. */
0131:            private DomNode parent_;
0132:
0133:            /**
0134:             * The previous sibling. The first child's <code>previousSibling</code> points
0135:             * to the end of the list
0136:             */
0137:            private DomNode previousSibling_;
0138:
0139:            /**
0140:             * The next sibling. The last child's <code>nextSibling</code> is <code>null</code>
0141:             */
0142:            private DomNode nextSibling_;
0143:
0144:            /** Start of the child list */
0145:            private DomNode firstChild_;
0146:
0147:            /**
0148:             * This is the JavaScript object corresponding to this DOM node. It may
0149:             * be null if there isn't a corresponding JavaScript object.
0150:             */
0151:            private transient ScriptableObject scriptObject_;
0152:
0153:            /** The ready state is is an IE-only value that is available to a large number of elements. */
0154:            private String readyState_;
0155:
0156:            /** We do lazy initialization on this since the vast majority of HtmlElement instances won't need it. */
0157:            private PropertyChangeSupport propertyChangeSupport_;
0158:
0159:            /** The name of the "element" property.  Used when watching property change events. */
0160:            public static final String PROPERTY_ELEMENT = "element";
0161:
0162:            /**
0163:             * The line number in the source page where the DOM node starts.
0164:             */
0165:            private int startLineNumber_;
0166:
0167:            /**
0168:             * The column number in the source page where the DOM node starts.
0169:             */
0170:            private int startColumnNumber_;
0171:
0172:            /**
0173:             * The line number in the source page where the DOM node ends.
0174:             */
0175:            private int endLineNumber_;
0176:
0177:            /**
0178:             * The column number in the source page where the DOM node ends.
0179:             */
0180:            private int endColumnNumber_;
0181:
0182:            private List/* DomChangeListener */domListeners_;
0183:            private final transient Object domListeners_lock_ = new Object();
0184:
0185:            /**
0186:             * Never call this, used for Serialization.
0187:             * @deprecated
0188:             */
0189:            protected DomNode() {
0190:                this (null);
0191:            }
0192:
0193:            /**
0194:             * Creates an instance.
0195:             * @param page The page which contains this node.
0196:             */
0197:            protected DomNode(final Page page) {
0198:                readyState_ = READY_STATE_LOADING;
0199:                page_ = page;
0200:                startLineNumber_ = 0;
0201:                startColumnNumber_ = 0;
0202:                endLineNumber_ = 0;
0203:                endColumnNumber_ = 0;
0204:            }
0205:
0206:            /**
0207:             * Set the line and column numbers in the source page where the
0208:             * DOM node starts.
0209:             *
0210:             * @param startLineNumber The line number where the DOM node starts.
0211:             * @param startColumnNumber The column number where the DOM node starts.
0212:             */
0213:            void setStartLocation(final int startLineNumber,
0214:                    final int startColumnNumber) {
0215:                startLineNumber_ = startLineNumber;
0216:                startColumnNumber_ = startColumnNumber;
0217:            }
0218:
0219:            /**
0220:             * Set the line and column numbers in the source page where the
0221:             * DOM node ends.
0222:             *
0223:             * @param endLineNumber The line number where the DOM node ends.
0224:             * @param endColumnNumber The column number where the DOM node ends.
0225:             */
0226:            void setEndLocation(final int endLineNumber,
0227:                    final int endColumnNumber) {
0228:                endLineNumber_ = endLineNumber;
0229:                endColumnNumber_ = endColumnNumber;
0230:            }
0231:
0232:            /**
0233:             * Get the line number in the source page where the DOM node starts.
0234:             *
0235:             * @return See above.
0236:             */
0237:            public int getStartLineNumber() {
0238:                return startLineNumber_;
0239:            }
0240:
0241:            /**
0242:             * Get the column number in the source page where the DOM node starts.
0243:             *
0244:             * @return See above.
0245:             */
0246:            public int getStartColumnNumber() {
0247:                return startColumnNumber_;
0248:            }
0249:
0250:            /**
0251:             * Get the line number in the source page where the DOM node ends.
0252:             *
0253:             * @return See above.
0254:             */
0255:            public int getEndLineNumber() {
0256:                return endLineNumber_;
0257:            }
0258:
0259:            /**
0260:             * Get the column number in the source page where the DOM node ends.
0261:             *
0262:             * @return See above.
0263:             */
0264:            public int getEndColumnNumber() {
0265:                return endColumnNumber_;
0266:            }
0267:
0268:            /**
0269:             * Return the HtmlPage that contains this node
0270:             *
0271:             * @return See above
0272:             */
0273:            public HtmlPage getPage() {
0274:                return (HtmlPage) page_;
0275:            }
0276:
0277:            /**
0278:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0279:             * Returns the Page interface, should be removed, and use {@link #getPage()} instead.
0280:             *
0281:             * @return the Page interface.
0282:             */
0283:            public Page getNativePage() {
0284:                return page_;
0285:            }
0286:
0287:            /**
0288:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0289:             *
0290:             * Set the javascript object that corresponds to this node.  This is not
0291:             * guaranteed to be set even if there is a javascript object for this
0292:             * DOM node.
0293:             * @param scriptObject The javascript object.
0294:             */
0295:            public void setScriptObject(final ScriptableObject scriptObject) {
0296:                scriptObject_ = scriptObject;
0297:            }
0298:
0299:            /**
0300:             * Get the last child node.
0301:             * @return The last child node or null if the current node has
0302:             * no children.
0303:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0304:             * different.  Use getLastDomChild instead.
0305:             */
0306:            public DomNode getLastChild() {
0307:                return getLastDomChild();
0308:            }
0309:
0310:            /**
0311:             * Get the last child DomNode.
0312:             * @return The last child node or null if the current node has
0313:             * no children.
0314:             */
0315:            public DomNode getLastDomChild() {
0316:                if (firstChild_ != null) {
0317:                    // last child is stored as the previous sibling of first child
0318:                    return firstChild_.previousSibling_;
0319:                } else {
0320:                    return null;
0321:                }
0322:            }
0323:
0324:            /**
0325:             * @return the parent of this node, which may be <code>null</code> if this
0326:             * is the root node
0327:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0328:             * different.  Use getParentDomNode instead.
0329:             */
0330:            public DomNode getParentNode() {
0331:                return getParentDomNode();
0332:            }
0333:
0334:            /**
0335:             * @return the parent DomNode of this node, which may be <code>null</code> if this
0336:             * is the root node
0337:             */
0338:            public DomNode getParentDomNode() {
0339:                return parent_;
0340:            }
0341:
0342:            /**
0343:             * Set the parent node
0344:             * @param parent the parent node
0345:             */
0346:            protected void setParentNode(final DomNode parent) {
0347:                parent_ = parent;
0348:            }
0349:
0350:            /**
0351:             * @return the previous sibling of this node, or <code>null</code> if this is
0352:             * the first node
0353:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0354:             * different.  Use getPreviousDomSibling instead.
0355:             */
0356:            public DomNode getPreviousSibling() {
0357:                return getPreviousDomSibling();
0358:            }
0359:
0360:            /**
0361:             * @return the previous sibling of this node, or <code>null</code> if this is
0362:             * the first node
0363:             */
0364:            public DomNode getPreviousDomSibling() {
0365:                if (parent_ == null || this  == parent_.firstChild_) {
0366:                    // previous sibling of first child points to last child
0367:                    return null;
0368:                } else {
0369:                    return previousSibling_;
0370:                }
0371:            }
0372:
0373:            /**
0374:             * @return the next sibling
0375:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0376:             * different.  Use getNextDomSibling instead.
0377:             */
0378:            public DomNode getNextSibling() {
0379:                return getNextDomSibling();
0380:            }
0381:
0382:            /**
0383:             * @return the next sibling
0384:             */
0385:            public DomNode getNextDomSibling() {
0386:                return nextSibling_;
0387:            }
0388:
0389:            /**
0390:             * @return the previous sibling
0391:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0392:             * different.  Use getFirstDomChild instead.
0393:             */
0394:            public DomNode getFirstChild() {
0395:                return getFirstDomChild();
0396:            }
0397:
0398:            /**
0399:             * @return the previous sibling
0400:             */
0401:            public DomNode getFirstDomChild() {
0402:                return firstChild_;
0403:            }
0404:
0405:            /**
0406:             * Returns <tt>true</tt> if this node is an ancestor of the specified node.
0407:             *
0408:             * @param node the node to check
0409:             * @return <tt>true</tt> if this node is an ancestor of the specified node
0410:             */
0411:            public boolean isAncestorOf(DomNode node) {
0412:                while (node != null) {
0413:                    if (node == this ) {
0414:                        return true;
0415:                    }
0416:                    node = node.getParentDomNode();
0417:                }
0418:                return false;
0419:            }
0420:
0421:            /** @param previous set the previousSibling field value */
0422:            protected void setPreviousSibling(final DomNode previous) {
0423:                previousSibling_ = previous;
0424:            }
0425:
0426:            /** @param next set the nextSibling field value */
0427:            protected void setNextSibling(final DomNode next) {
0428:                nextSibling_ = next;
0429:            }
0430:
0431:            /**
0432:             * Get the type of the current node.
0433:             * @return The node type
0434:             */
0435:            public abstract short getNodeType();
0436:
0437:            /**
0438:             * Get the name for the current node.
0439:             * @return The node name
0440:             */
0441:            public abstract String getNodeName();
0442:
0443:            /**
0444:             * The namespace URI of this node, or null if it is unspecified (see ).  This is not a
0445:             * computed value that is the result of a namespace lookup based on an examination of the
0446:             * namespace declarations in scope.  It is merely the namespace URI given at creation time.
0447:             * For nodes of any type other than ELEMENT_NODE and ATTRIBUTE_NODE and nodes created with
0448:             * a DOM Level 1 method, such as Document.createElement(), this is always null.
0449:             * @return The URI that identifies an XML namespace.
0450:             */
0451:            public String getNamespaceURI() {
0452:                return null;
0453:            }
0454:
0455:            /**
0456:             * Returns the local part of the qualified name of this node.  For nodes of any
0457:             * type other than ELEMENT_NODE and ATTRIBUTE_NODE and nodes created with a DOM Level 1
0458:             * method, such as Document.createElement(), this is always null.
0459:             * @return The local name (without prefix).
0460:             */
0461:            public String getLocalName() {
0462:                return null;
0463:            }
0464:
0465:            /**
0466:             * The namespace prefix of this node, or null if it is unspecified.
0467:             * @return The Namespace prefix.
0468:             */
0469:            public String getPrefix() {
0470:                return null;
0471:            }
0472:
0473:            /**
0474:             * Set the namespace prefix of this node, or null if it is unspecified.  When it is defined
0475:             * to be null, setting it has no effect, including if the node is read-only.  Note that setting
0476:             * this attribute, when permitted, changes the nodeName attribute, which holds the qualified
0477:             * name, as well as the tagName and name attributes of the Element and Attr interfaces, when
0478:             * applicable.  Setting the prefix to null makes it unspecified, setting it to an empty string
0479:             * is implementation dependent.  Note also that changing the prefix of an attribute that is
0480:             * known to have a default value, does not make a new attribute with the default value and the
0481:             * original prefix appear, since the namespaceURI and localName do not change.  For nodes of
0482:             * any type other than ELEMENT_NODE and ATTRIBUTE_NODE and nodes created with a DOM Level 1
0483:             * method, such as createElement from the Document interface, this is always null.
0484:             * @param prefix The namespace prefix of this node, or null if it is unspecified.
0485:             */
0486:            public void setPrefix(final String prefix) {
0487:            }
0488:
0489:            /**
0490:             * Return whether this node has any attributes.
0491:             *
0492:             * @return true if the node has attributes, false otherwise.
0493:             */
0494:            public boolean hasAttributes() {
0495:                return false;
0496:            }
0497:
0498:            /**
0499:             * Returns a flag indicating whether or not this node itself results
0500:             * in any space taken up in the browser windows; for instance, "<b>"
0501:             * affects the specified text, but does not use up any space itself
0502:             *
0503:             * @return The flag
0504:             */
0505:            protected boolean isRenderedVisible() {
0506:                return false;
0507:            }
0508:
0509:            /**
0510:             * Returns a flag indicating whether or not this node should
0511:             * have any leading and trailing whitespace removed when asText()
0512:             * is called; mostly this should be true, but must be false for
0513:             * such things as text formatting tags
0514:             *
0515:             * @return The flag
0516:             */
0517:            protected boolean isTrimmedText() {
0518:                return true;
0519:            }
0520:
0521:            /**
0522:             *  Returns a text representation of this element that represents what would
0523:             *  be visible to the user if this page was shown in a web browser. For
0524:             *  example, a single-selection select element would return the currently selected
0525:             *  value as text.
0526:             *
0527:             * @return The element as text.
0528:             */
0529:            public String asText() {
0530:                String text = getChildrenAsText();
0531:                text = reduceWhitespace(text);
0532:
0533:                if (isTrimmedText()) {
0534:                    text = text.trim();
0535:                }
0536:
0537:                return text;
0538:            }
0539:
0540:            /**
0541:             *  Return a text string that represents all the child elements as they
0542:             *  would be visible in a web browser
0543:             *
0544:             * @return See above
0545:             * @see #asText()
0546:             */
0547:            protected final String getChildrenAsText() {
0548:                final StringBuffer buffer = new StringBuffer();
0549:                final Iterator childIterator = getChildIterator();
0550:
0551:                if (!childIterator.hasNext()) {
0552:                    return "";
0553:                }
0554:                boolean previousNodeWasText = false;
0555:                final StringBuffer textBuffer = new StringBuffer();
0556:                while (childIterator.hasNext()) {
0557:                    final DomNode node = (DomNode) childIterator.next();
0558:                    if (node instanceof  DomText) {
0559:                        textBuffer.append(((DomText) node).getData());
0560:                        previousNodeWasText = true;
0561:                    } else {
0562:                        if (previousNodeWasText) {
0563:                            // Whitespace between adjacent text nodes should reamin as a single
0564:                            // space.  So, append raw adjacent text and reduce it as a whole.
0565:                            buffer.append(reduceWhitespace(textBuffer
0566:                                    .toString()));
0567:                            textBuffer.setLength(0);
0568:                            previousNodeWasText = false;
0569:                        }
0570:
0571:                        if (node.isRenderedVisible()) {
0572:                            buffer.append(" ");
0573:                            buffer.append(node.asText());
0574:                            buffer.append(" ");
0575:                        } else if (node.getNodeName().equals("p")) {
0576:                            // this is a bit kludgey, but we can't add the space
0577:                            //  inside the node's asText(), since it doesn't belong
0578:                            //  with the contents of the 'p' tag
0579:                            buffer.append(" ");
0580:                            buffer.append(node.asText());
0581:                        } else {
0582:                            buffer.append(node.asText());
0583:                        }
0584:                    }
0585:                }
0586:                if (previousNodeWasText) {
0587:                    // we ended with text
0588:                    buffer.append(textBuffer.toString());
0589:                }
0590:
0591:                return buffer.toString();
0592:            }
0593:
0594:            /**
0595:             * Removes extra whitespace from a string similar to what a browser does
0596:             * when it displays text.
0597:             * @param text The text to clean up.
0598:             * @return The cleaned up text.
0599:             */
0600:            protected static String reduceWhitespace(final String text) {
0601:                final StringBuffer buffer = new StringBuffer(text.length());
0602:                final int length = text.length();
0603:                boolean whitespace = false;
0604:                for (int i = 0; i < length; i++) {
0605:                    final char ch = text.charAt(i);
0606:
0607:                    // Translate non-breaking space to regular space.
0608:                    if (ch == (char) 160) {
0609:                        buffer.append(' ');
0610:                        whitespace = false;
0611:                    } else {
0612:                        if (whitespace) {
0613:                            if (!Character.isWhitespace(ch)) {
0614:                                buffer.append(ch);
0615:                                whitespace = false;
0616:                            }
0617:                        } else {
0618:                            if (Character.isWhitespace(ch)) {
0619:                                whitespace = true;
0620:                                buffer.append(' ');
0621:                            } else {
0622:                                buffer.append(ch);
0623:                            }
0624:                        }
0625:                    }
0626:                }
0627:                return buffer.toString();
0628:            }
0629:
0630:            /**
0631:             * Return the log object for this element.
0632:             * @return The log object for this element.
0633:             */
0634:            protected final Log getLog() {
0635:                return LogFactory.getLog(getClass());
0636:            }
0637:
0638:            /**
0639:             * Return a string representation of the xml document from this element and all
0640:             * it's children (recursively).
0641:             *
0642:             * @return The xml string.
0643:             */
0644:            public String asXml() {
0645:                final StringWriter stringWriter = new StringWriter();
0646:                final PrintWriter printWriter = new PrintWriter(stringWriter);
0647:                printXml("", printWriter);
0648:                printWriter.close();
0649:                return stringWriter.toString();
0650:            }
0651:
0652:            /**
0653:             * recursively write the XML data for the node tree starting at <code>node</code>
0654:             *
0655:             * @param indent white space to indent child nodes
0656:             * @param printWriter writer where child nodes are written
0657:             */
0658:            protected void printXml(final String indent,
0659:                    final PrintWriter printWriter) {
0660:                printWriter.println(indent + this );
0661:                printChildrenAsXml(indent, printWriter);
0662:            }
0663:
0664:            /**
0665:             * recursively write the XML data for the node tree starting at <code>node</code>
0666:             *
0667:             * @param indent white space to indent child nodes
0668:             * @param printWriter writer where child nodes are written
0669:             */
0670:            protected void printChildrenAsXml(final String indent,
0671:                    final PrintWriter printWriter) {
0672:                DomNode child = getFirstDomChild();
0673:                while (child != null) {
0674:                    child.printXml(indent + "  ", printWriter);
0675:                    child = child.getNextDomSibling();
0676:                }
0677:            }
0678:
0679:            /**
0680:             * Get the value for the current node.
0681:             * @return The node value
0682:             */
0683:            public String getNodeValue() {
0684:                return null;
0685:            }
0686:
0687:            /**
0688:             * @param x The new value
0689:             */
0690:            public void setNodeValue(final String x) {
0691:                // Default behavior is to do nothing, overridden in some subclasses
0692:            }
0693:
0694:            /**
0695:             * Make a clone of this node
0696:             *
0697:             * @param deep if <code>true</code>, the clone will be propagated to the whole subtree
0698:             * below this one. Otherwise, the new node will not have any children. The page reference
0699:             * will always be the same as this node's.
0700:             * @return a new node
0701:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0702:             * different.  Use cloneDomNode instead.
0703:             */
0704:            public DomNode cloneNode(final boolean deep) {
0705:                return cloneDomNode(deep);
0706:            }
0707:
0708:            /**
0709:             * Make a clone of this node
0710:             *
0711:             * @param deep if <code>true</code>, the clone will be propagated to the whole subtree
0712:             * below this one. Otherwise, the new node will not have any children. The page reference
0713:             * will always be the same as this node's.
0714:             * @return a new node
0715:             */
0716:            public DomNode cloneDomNode(final boolean deep) {
0717:                final DomNode newnode;
0718:                try {
0719:                    newnode = (DomNode) clone();
0720:                } catch (final CloneNotSupportedException e) {
0721:                    throw new IllegalStateException(
0722:                            "Clone not supported for node [" + this  + "]");
0723:                }
0724:
0725:                newnode.parent_ = null;
0726:                newnode.nextSibling_ = null;
0727:                newnode.previousSibling_ = null;
0728:                newnode.firstChild_ = null;
0729:                newnode.scriptObject_ = null;
0730:
0731:                // if deep, clone the kids too.
0732:                if (deep) {
0733:                    for (DomNode child = firstChild_; child != null; child = child.nextSibling_) {
0734:                        newnode.appendDomChild(child.cloneDomNode(true));
0735:                    }
0736:                }
0737:                return newnode;
0738:            }
0739:
0740:            /**
0741:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0742:             *
0743:             * The logic of when and where the js object is created needs a clean up: functions using
0744:             * a js object of a dom node should not have to look if they should create it first
0745:             * Return the javascript object that corresponds to this node.
0746:             * @return The javascript object that corresponds to this node building it if necessary.
0747:             */
0748:            public ScriptableObject getScriptObject() {
0749:                if (scriptObject_ == null) {
0750:                    if (this  == page_) {
0751:                        throw new IllegalStateException(
0752:                                "No script object associated with the Page");
0753:                    }
0754:                    scriptObject_ = ((SimpleScriptable) ((DomNode) page_)
0755:                            .getScriptObject()).makeScriptableFor(this );
0756:                }
0757:                return scriptObject_;
0758:            }
0759:
0760:            /**
0761:             * append a child node to the end of the current list
0762:             * @param node the node to append
0763:             * @return the node added
0764:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0765:             * different.  Use appendDomChild instead.
0766:             */
0767:            public DomNode appendChild(final DomNode node) {
0768:                return appendDomChild(node);
0769:            }
0770:
0771:            /**
0772:             * append a child node to the end of the current list
0773:             * @param node the node to append
0774:             * @return the node added
0775:             */
0776:            public DomNode appendDomChild(final DomNode node) {
0777:                if (node instanceof  DomDocumentFragment) {
0778:                    final DomDocumentFragment fragment = (DomDocumentFragment) node;
0779:                    for (final Iterator iterator = fragment.getChildIterator(); iterator
0780:                            .hasNext();) {
0781:                        final DomNode child = (DomNode) iterator.next();
0782:                        appendDomChild(child);
0783:                    }
0784:                } else {
0785:                    //clean up the new node, in case it is being moved
0786:                    if (node != this ) {
0787:                        node.basicRemove();
0788:                    }
0789:                    if (firstChild_ == null) {
0790:                        firstChild_ = node;
0791:                        firstChild_.previousSibling_ = node;
0792:                    } else {
0793:                        final DomNode last = getLastDomChild();
0794:
0795:                        last.nextSibling_ = node;
0796:                        node.previousSibling_ = last;
0797:                        node.nextSibling_ = null; //safety first
0798:                        firstChild_.previousSibling_ = node; //new last node
0799:                    }
0800:                    node.parent_ = this ;
0801:
0802:                    //TODO: should be
0803:                    //  if (!(this instanceof DomDocumentFragment) && getPage() instanceof HtmlPage)
0804:                    if (!(this  instanceof  DomDocumentFragment)
0805:                            && (page_ instanceof  HtmlPage || this  instanceof  HtmlPage)) {
0806:                        getPage().notifyNodeAdded(node);
0807:                    }
0808:                    fireNodeAdded(this , node);
0809:                }
0810:                return node;
0811:            }
0812:
0813:            /**
0814:             * Inserts a new child node before this node into the child relationship this node is a
0815:             * part of. If the specified node is this node, this method is a no-op.
0816:             *
0817:             * @param newNode the new node to insert
0818:             * @throws IllegalStateException if this node is not a child of any other node
0819:             */
0820:            public void insertBefore(final DomNode newNode)
0821:                    throws IllegalStateException {
0822:
0823:                if (previousSibling_ == null) {
0824:                    throw new IllegalStateException();
0825:                }
0826:
0827:                if (newNode == this ) {
0828:                    return;
0829:                }
0830:
0831:                //clean up the new node, in case it is being moved
0832:                newNode.basicRemove();
0833:
0834:                if (parent_.firstChild_ == this ) {
0835:                    parent_.firstChild_ = newNode;
0836:                } else {
0837:                    previousSibling_.nextSibling_ = newNode;
0838:                }
0839:                newNode.previousSibling_ = previousSibling_;
0840:                newNode.nextSibling_ = this ;
0841:                previousSibling_ = newNode;
0842:                newNode.parent_ = parent_;
0843:
0844:                getPage().notifyNodeAdded(newNode);
0845:                fireNodeAdded(this , newNode);
0846:            }
0847:
0848:            /**
0849:             * Removes this node from all relationships with other nodes.
0850:             * @throws IllegalStateException if this node is not a child of any other node
0851:             */
0852:            public void remove() throws IllegalStateException {
0853:                if (previousSibling_ == null) {
0854:                    throw new IllegalStateException();
0855:                }
0856:                final DomNode exParent = parent_;
0857:                basicRemove();
0858:
0859:                if (getNativePage() instanceof  HtmlPage) {
0860:                    getPage().notifyNodeRemoved(this );
0861:                }
0862:
0863:                fireNodeDeleted(exParent, this );
0864:                //ask ex-parent to fire event (because we don't have parent now)
0865:                exParent.fireNodeDeleted(exParent, this );
0866:            }
0867:
0868:            /**
0869:             * Cuts off all relationships this node has with siblings and parents.
0870:             */
0871:            private void basicRemove() {
0872:                if (parent_ != null && parent_.firstChild_ == this ) {
0873:                    parent_.firstChild_ = nextSibling_;
0874:                } else if (previousSibling_ != null
0875:                        && previousSibling_.nextSibling_ == this ) {
0876:                    previousSibling_.nextSibling_ = nextSibling_;
0877:                }
0878:                if (nextSibling_ != null
0879:                        && nextSibling_.previousSibling_ == this ) {
0880:                    nextSibling_.previousSibling_ = previousSibling_;
0881:                }
0882:                if (parent_ != null && this  == parent_.getLastDomChild()) {
0883:                    parent_.firstChild_.previousSibling_ = previousSibling_;
0884:                }
0885:
0886:                nextSibling_ = null;
0887:                previousSibling_ = null;
0888:                parent_ = null;
0889:            }
0890:
0891:            /**
0892:             * Replaces this node with another node. If the specified node is this node, this
0893:             * method is a no-op.
0894:             *
0895:             * @param newNode the node to replace this one
0896:             * @throws IllegalStateException if this node is not a child of any other node
0897:             */
0898:            public void replace(final DomNode newNode)
0899:                    throws IllegalStateException {
0900:                if (newNode != this ) {
0901:                    insertBefore(newNode);
0902:                    remove();
0903:                }
0904:            }
0905:
0906:            /**
0907:             * Lifecycle method invoked whenever a node is added to a page. Intended to
0908:             * be overridden by nodes which need to perform custom logic when they are
0909:             * added to a page. This method is recursive, so if you override it, please
0910:             * be sure to call <tt>super.onAddedToPage()</tt>.
0911:             */
0912:            protected void onAddedToPage() {
0913:                if (firstChild_ != null) {
0914:                    for (final Iterator i = getChildIterator(); i.hasNext();) {
0915:                        final DomNode child = (DomNode) i.next();
0916:                        child.onAddedToPage();
0917:                    }
0918:                }
0919:            }
0920:
0921:            /**
0922:             * Lifecycle method invoked after a node and all its children have been added to a page, during
0923:             * parsing of the HTML. Intended to be overridden by nodes which need to perform custom logic
0924:             * after they and all their child nodes have been processed by the HTML parser. This method is
0925:             * not recursive, and the default implementation is empty, so there is no need to call
0926:             * <tt>super.onAllChildrenAddedToPage()</tt> if you implement this method.
0927:             */
0928:            protected void onAllChildrenAddedToPage() {
0929:                // Empty by default.
0930:            }
0931:
0932:            /**
0933:             * @return an iterator over the children of this node
0934:             */
0935:            public Iterator getChildIterator() {
0936:                return new ChildIterator();
0937:            }
0938:
0939:            // TODO: remove event handlers methods! Nothing to do in DomNode!
0940:            /**
0941:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0942:             * Return a Function to be executed when a given event occurs.
0943:             * @param eventName Name of event such as "onclick" or "onblur", etc.
0944:             * @return A rhino javascript executable Function, or null if no event
0945:             * handler has been defined
0946:             */
0947:            public Function getEventHandler(final String eventName) {
0948:                return null;
0949:            }
0950:
0951:            /**
0952:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0953:             * Register a Function as an event handler.
0954:             * @param eventName Name of event such as "onclick" or "onblur", etc.
0955:             * @param eventHandler A rhino javascript executable Function
0956:             */
0957:            public void setEventHandler(final String eventName,
0958:                    final Function eventHandler) {
0959:            }
0960:
0961:            /**
0962:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0963:             * Register a snippet of javascript code as an event handler.  The javascript code will
0964:             * be wrapped inside a unique function declaration which provides one argument named
0965:             * "event"
0966:             * @param eventName Name of event such as "onclick" or "onblur", etc.
0967:             * @param jsSnippet executable javascript code
0968:             */
0969:            public void setEventHandler(final String eventName,
0970:                    final String jsSnippet) {
0971:            }
0972:
0973:            /**
0974:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0975:             * Removes the specified event handler.
0976:             * @param eventName Name of the event such as "onclick" or "onblur", etc.
0977:             */
0978:            public void removeEventHandler(final String eventName) {
0979:            }
0980:
0981:            /**
0982:             * Add a property change listener to this node.
0983:             * @param listener The new listener.
0984:             * @deprecated Not used
0985:             */
0986:            //deprecated after 1.11
0987:            public final synchronized void addPropertyChangeListener(
0988:                    final PropertyChangeListener listener) {
0989:                Assert.notNull("listener", listener);
0990:                if (propertyChangeSupport_ == null) {
0991:                    propertyChangeSupport_ = new PropertyChangeSupport(this );
0992:                }
0993:                propertyChangeSupport_.addPropertyChangeListener(listener);
0994:            }
0995:
0996:            /**
0997:             * Remove a property change listener from this node.
0998:             * @param listener The listener.
0999:             * @deprecated Not used
1000:             */
1001:            //deprecated after 1.11
1002:            public final synchronized void removePropertyChangeListener(
1003:                    final PropertyChangeListener listener) {
1004:                Assert.notNull("listener", listener);
1005:                if (propertyChangeSupport_ != null) {
1006:                    propertyChangeSupport_
1007:                            .removePropertyChangeListener(listener);
1008:                }
1009:            }
1010:
1011:            /**
1012:             * Fire a property change event
1013:             * @param propertyName The name of the property.
1014:             * @param oldValue The old value.
1015:             * @param newValue The new value.
1016:             * @deprecated Not used
1017:             */
1018:            //deprecated after 1.11
1019:            protected final synchronized void firePropertyChange(
1020:                    final String propertyName, final Object oldValue,
1021:                    final Object newValue) {
1022:
1023:                if (propertyChangeSupport_ != null) {
1024:                    propertyChangeSupport_.firePropertyChange(propertyName,
1025:                            oldValue, newValue);
1026:                }
1027:            }
1028:
1029:            /**
1030:             * an iterator over all children of this node
1031:             */
1032:            protected class ChildIterator implements  Iterator {
1033:
1034:                private DomNode nextNode_ = firstChild_;
1035:                private DomNode currentNode_ = null;
1036:
1037:                /** @return whether there is a next object */
1038:                public boolean hasNext() {
1039:                    return nextNode_ != null;
1040:                }
1041:
1042:                /** @return the next object */
1043:                public Object next() {
1044:                    if (nextNode_ != null) {
1045:                        currentNode_ = nextNode_;
1046:                        nextNode_ = nextNode_.nextSibling_;
1047:                        return currentNode_;
1048:                    } else {
1049:                        throw new NoSuchElementException();
1050:                    }
1051:                }
1052:
1053:                /** remove the current object */
1054:                public void remove() {
1055:                    if (currentNode_ == null) {
1056:                        throw new IllegalStateException();
1057:                    }
1058:                    currentNode_.remove();
1059:                }
1060:            }
1061:
1062:            /**
1063:             * Return an iterator that will recursively iterate over every child element
1064:             * below this one.
1065:             * @return The iterator.
1066:             */
1067:            public Iterator getAllHtmlChildElements() {
1068:                return new DescendantElementsIterator();
1069:            }
1070:
1071:            /**
1072:             * An iterator over all HtmlElement descendants in document order.
1073:             */
1074:            protected class DescendantElementsIterator implements  Iterator {
1075:
1076:                private HtmlElement nextElement_ = getFirstChildElement(DomNode.this );
1077:
1078:                /** @return is there a next one? */
1079:                public boolean hasNext() {
1080:                    return nextElement_ != null;
1081:                }
1082:
1083:                /** @return the next one */
1084:                public Object next() {
1085:                    return nextElement();
1086:                }
1087:
1088:                /** @throws UnsupportedOperationException always */
1089:                public void remove() throws UnsupportedOperationException {
1090:                    throw new UnsupportedOperationException();
1091:                }
1092:
1093:                /** @return is there a next one? */
1094:                public HtmlElement nextElement() {
1095:                    final HtmlElement result = nextElement_;
1096:                    setNextElement();
1097:                    return result;
1098:                }
1099:
1100:                private void setNextElement() {
1101:                    HtmlElement next = getFirstChildElement(nextElement_);
1102:                    if (next == null) {
1103:                        next = getNextDomSibling(nextElement_);
1104:                    }
1105:                    if (next == null) {
1106:                        next = getNextElementUpwards(nextElement_);
1107:                    }
1108:                    nextElement_ = next;
1109:                }
1110:
1111:                private HtmlElement getNextElementUpwards(
1112:                        final DomNode startingNode) {
1113:                    if (startingNode == DomNode.this ) {
1114:                        return null;
1115:                    }
1116:
1117:                    final DomNode parent = startingNode.getParentDomNode();
1118:                    if (parent == DomNode.this ) {
1119:                        return null;
1120:                    }
1121:
1122:                    DomNode next = parent.getNextDomSibling();
1123:                    while (next != null && next instanceof  HtmlElement == false) {
1124:                        next = next.getNextDomSibling();
1125:                    }
1126:
1127:                    if (next == null) {
1128:                        return getNextElementUpwards(parent);
1129:                    } else {
1130:                        return (HtmlElement) next;
1131:                    }
1132:                }
1133:
1134:                private HtmlElement getFirstChildElement(final DomNode parent) {
1135:                    if (parent instanceof  HtmlNoScript
1136:                            && getPage().getWebClient().isJavaScriptEnabled()) {
1137:                        return null;
1138:                    }
1139:                    DomNode node = parent.getFirstDomChild();
1140:                    while (node != null && node instanceof  HtmlElement == false) {
1141:                        node = node.getNextDomSibling();
1142:                    }
1143:                    return (HtmlElement) node;
1144:                }
1145:
1146:                private HtmlElement getNextDomSibling(final HtmlElement element) {
1147:                    DomNode node = element.getNextDomSibling();
1148:                    while (node != null && node instanceof  HtmlElement == false) {
1149:                        node = node.getNextDomSibling();
1150:                    }
1151:                    return (HtmlElement) node;
1152:                }
1153:            }
1154:
1155:            /**
1156:             * Return this node's ready state (IE only).
1157:             * @return This node's ready state.
1158:             */
1159:            public String getReadyState() {
1160:                return readyState_;
1161:            }
1162:
1163:            /**
1164:             * Sets this node's ready state (IE only).
1165:             * @param state This node's ready state.
1166:             */
1167:            public void setReadyState(final String state) {
1168:                readyState_ = state;
1169:            }
1170:
1171:            /**
1172:             * Remove all the children of this node.
1173:             *
1174:             */
1175:            public void removeAllChildren() {
1176:                if (getFirstDomChild() == null) {
1177:                    return;
1178:                }
1179:                final Iterator it = getChildIterator();
1180:                DomNode child;
1181:                while (it.hasNext()) {
1182:                    child = (DomNode) it.next();
1183:                    child.removeAllChildren();
1184:                    it.remove();
1185:                }
1186:            }
1187:
1188:            /**
1189:             * Facility to evaluate an xpath from the current node. The current node is considered as the
1190:             * document root for the evaluation therefore parent nodes can't be reached.
1191:             * @param xpathExpr the xpath expression
1192:             * @return See {@link XPath#selectNodes(Object)}
1193:             * @throws JaxenException if the xpath expression can't be parsed/evaluated
1194:             */
1195:            public List getByXPath(final String xpathExpr)
1196:                    throws JaxenException {
1197:                if (xpathExpr == null) {
1198:                    throw new NullPointerException(
1199:                            "Null is not a valid xpath expression");
1200:                }
1201:
1202:                final Navigator navigator = HtmlUnitXPath
1203:                        .buildSubtreeNavigator(this );
1204:                final HtmlUnitXPath xpath = new HtmlUnitXPath(xpathExpr,
1205:                        navigator);
1206:                return xpath.selectNodes(this );
1207:            }
1208:
1209:            /**
1210:             * Facility to evaluate an xpath from the current node and get the first result.
1211:             * The current node is considered as the document root for the evaluation
1212:             * therefore parent nodes can't be reached.
1213:             * @param xpathExpr the xpath expression
1214:             * @return <code>null</code> if no result is found, the first one otherwise
1215:             * @throws JaxenException if the xpath expression can't be parsed/evaluated
1216:             */
1217:            public Object getFirstByXPath(final String xpathExpr)
1218:                    throws JaxenException {
1219:                final List results = getByXPath(xpathExpr);
1220:                if (results.isEmpty()) {
1221:                    return null;
1222:                } else {
1223:                    return results.get(0);
1224:                }
1225:            }
1226:
1227:            /**
1228:             * Facility to notify the registered {@link IncorrectnessListener} of something that is not fully correct.
1229:             * @param message the notification
1230:             */
1231:            protected void notifyIncorrectness(final String message) {
1232:                final IncorrectnessListener incorrectnessListener = getPage()
1233:                        .getWebClient().getIncorrectnessListener();
1234:                incorrectnessListener.notify(message, this );
1235:            }
1236:
1237:            /**
1238:             * Adds a DomChangeListener to the listener list.
1239:             * The listener is registered for all children nodes of this DomNode,
1240:             * as well as the descendant nodes.
1241:             *
1242:             * @param listener the dom structure change listener to be added.
1243:             * @see #removeDomChangeListener(DomChangeListener)
1244:             */
1245:            public void addDomChangeListener(final DomChangeListener listener) {
1246:                Assert.notNull("listener", listener);
1247:                synchronized (domListeners_lock_) {
1248:                    if (domListeners_ == null) {
1249:                        domListeners_ = new ArrayList();
1250:                    }
1251:                    if (!domListeners_.contains(listener)) {
1252:                        domListeners_.add(listener);
1253:                    }
1254:                }
1255:            }
1256:
1257:            /**
1258:             * Removes an DomChangeListener from the listener list.
1259:             * This method should be used to remove DomChangeListener that were registered
1260:             * for all children nodes and descendant nodes of this DomNode.
1261:             *
1262:             * @param listener the dom structure change listener to be removed.
1263:             * @see #addDomChangeListener(DomChangeListener)
1264:             */
1265:            public void removeDomChangeListener(final DomChangeListener listener) {
1266:                Assert.notNull("listener", listener);
1267:                synchronized (domListeners_lock_) {
1268:                    if (domListeners_ != null) {
1269:                        domListeners_.remove(listener);
1270:                    }
1271:                }
1272:            }
1273:
1274:            /**
1275:             * Support for reporting DOM changes. This method can be called when a node has been added and it
1276:             * will send the appropriate {@link DomChangeEvent} to any registered {@link DomChangeListener}s.
1277:             *
1278:             * Note that this method recursively calls this node's parent's {@link #fireNodeAdded(DomNode, DomNode)}.
1279:             *
1280:             * @param parentNode the parent of the node that was added.
1281:             * @param addedNode the node that was added.
1282:             */
1283:            protected void fireNodeAdded(final DomNode parentNode,
1284:                    final DomNode addedNode) {
1285:                final List listeners = safeGetDomListeners();
1286:                if (listeners != null) {
1287:                    final DomChangeEvent event = new DomChangeEvent(parentNode,
1288:                            addedNode);
1289:                    for (final Iterator iterator = listeners.iterator(); iterator
1290:                            .hasNext();) {
1291:                        final DomChangeListener listener = (DomChangeListener) iterator
1292:                                .next();
1293:                        listener.nodeAdded(event);
1294:                    }
1295:                }
1296:                if (parent_ != null) {
1297:                    parent_.fireNodeAdded(parentNode, addedNode);
1298:                }
1299:            }
1300:
1301:            /**
1302:             * Support for reporting DOM changes. This method can be called when a node has been deleted and it
1303:             * will send the appropriate {@link DomChangeEvent} to any registered {@link DomChangeListener}s.
1304:             *
1305:             * Note that this method recursively calls this node's parent's {@link #fireNodeDeleted(DomNode, DomNode)}.
1306:             *
1307:             * @param parentNode the parent of the node that was deleted.
1308:             * @param deletedNode the node that was deleted.
1309:             */
1310:            protected void fireNodeDeleted(final DomNode parentNode,
1311:                    final DomNode deletedNode) {
1312:                final List listeners = safeGetDomListeners();
1313:                if (listeners != null) {
1314:                    final DomChangeEvent event = new DomChangeEvent(parentNode,
1315:                            deletedNode);
1316:                    for (final Iterator iterator = listeners.iterator(); iterator
1317:                            .hasNext();) {
1318:                        final DomChangeListener listener = (DomChangeListener) iterator
1319:                                .next();
1320:                        listener.nodeDeleted(event);
1321:                    }
1322:                }
1323:                if (parent_ != null) {
1324:                    parent_.fireNodeDeleted(parentNode, deletedNode);
1325:                }
1326:            }
1327:
1328:            private List safeGetDomListeners() {
1329:                synchronized (domListeners_lock_) {
1330:                    if (domListeners_ != null) {
1331:                        return new ArrayList(domListeners_);
1332:                    } else {
1333:                        return null;
1334:                    }
1335:                }
1336:            }
1337:
1338:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.