+++ /dev/null
-/*
- * @(#)Node.java 1.11 2000/08/16
- *
- */
-
-package net.sourceforge.phpdt.tidy;
-
-/**
- *
- * Node
- *
- * (c) 1998-2000 (W3C) MIT, INRIA, Keio University
- * See Tidy.java for the copyright notice.
- * Derived from <a href="http://www.w3.org/People/Raggett/tidy">
- * HTML Tidy Release 4 Aug 2000</a>
- *
- * @author Dave Raggett <dsr@w3.org>
- * @author Andy Quick <ac.quick@sympatico.ca> (translation to Java)
- * @version 1.0, 1999/05/22
- * @version 1.0.1, 1999/05/29
- * @version 1.1, 1999/06/18 Java Bean
- * @version 1.2, 1999/07/10 Tidy Release 7 Jul 1999
- * @version 1.3, 1999/07/30 Tidy Release 26 Jul 1999
- * @version 1.4, 1999/09/04 DOM support
- * @version 1.5, 1999/10/23 Tidy Release 27 Sep 1999
- * @version 1.6, 1999/11/01 Tidy Release 22 Oct 1999
- * @version 1.7, 1999/12/06 Tidy Release 30 Nov 1999
- * @version 1.8, 2000/01/22 Tidy Release 13 Jan 2000
- * @version 1.9, 2000/06/03 Tidy Release 30 Apr 2000
- * @version 1.10, 2000/07/22 Tidy Release 8 Jul 2000
- * @version 1.11, 2000/08/16 Tidy Release 4 Aug 2000
- */
-
-/*
- Used for elements and text nodes
- element name is null for text nodes
- start and end are offsets into lexbuf
- which contains the textual content of
- all elements in the parse tree.
-
- parent and content allow traversal
- of the parse tree in any direction.
- attributes are represented as a linked
- list of AttVal nodes which hold the
- strings for attribute/value pairs.
-*/
-
-public class Node {
-
- public static final short RootNode = 0;
- public static final short DocTypeTag = 1;
- public static final short CommentTag = 2;
- public static final short ProcInsTag = 3;
- public static final short TextNode = 4;
- public static final short StartTag = 5;
- public static final short EndTag = 6;
- public static final short StartEndTag = 7;
- public static final short CDATATag = 8;
- public static final short SectionTag = 9;
- public static final short AspTag = 10;
- public static final short JsteTag = 11;
- public static final short PhpTag = 12;
-
- protected Node parent;
- protected Node prev;
- protected Node next;
- protected Node last;
- protected int start; /* start of span onto text array */
- protected int end; /* end of span onto text array */
- protected byte[] textarray; /* the text array */
- protected short type; /* TextNode, StartTag, EndTag etc. */
- protected boolean closed; /* true if closed by explicit end tag */
- protected boolean implicit; /* true if inferred */
- protected boolean linebreak; /* true if followed by a line break */
- protected Dict was; /* old tag when it was changed */
- protected Dict tag; /* tag's dictionary definition */
- protected String element; /* name (null for text nodes) */
- protected AttVal attributes;
- protected Node content;
-
- public Node()
- {
- this(TextNode, null, 0, 0);
- }
-
- public Node(short type, byte[] textarray, int start, int end)
- {
- this.parent = null;
- this.prev = null;
- this.next = null;
- this.last = null;
- this.start = start;
- this.end = end;
- this.textarray = textarray;
- this.type = type;
- this.closed = false;
- this.implicit = false;
- this.linebreak = false;
- this.was = null;
- this.tag = null;
- this.element = null;
- this.attributes = null;
- this.content = null;
- }
-
- public Node(short type, byte[] textarray, int start, int end, String element, TagTable tt)
- {
- this.parent = null;
- this.prev = null;
- this.next = null;
- this.last = null;
- this.start = start;
- this.end = end;
- this.textarray = textarray;
- this.type = type;
- this.closed = false;
- this.implicit = false;
- this.linebreak = false;
- this.was = null;
- this.tag = null;
- this.element = element;
- this.attributes = null;
- this.content = null;
- if (type == StartTag || type == StartEndTag || type == EndTag)
- tt.findTag(this);
- }
-
- /* used to clone heading nodes when split by an <HR> */
- protected Object clone()
- {
- Node node = new Node();
-
- node.parent = this.parent;
- if (this.textarray != null)
- {
- node.textarray = new byte[this.end - this.start];
- node.start = 0;
- node.end = this.end - this.start;
- if (node.end > 0)
- System.arraycopy(this.textarray, this.start,
- node.textarray, node.start, node.end);
- }
- node.type = this.type;
- node.closed = this.closed;
- node.implicit = this.implicit;
- node.linebreak = this.linebreak;
- node.was = this.was;
- node.tag = this.tag;
- if (this.element != null)
- node.element = this.element;
- if (this.attributes != null)
- node.attributes = (AttVal)this.attributes.clone();
- return node;
- }
-
- public AttVal getAttrByName(String name)
- {
- AttVal attr;
-
- for (attr = this.attributes; attr != null; attr = attr.next)
- {
- if (name != null &&
- attr.attribute != null &&
- attr.attribute.equals(name))
- break;
- }
-
- return attr;
- }
-
- /* default method for checking an element's attributes */
- public void checkAttributes( Lexer lexer )
- {
- AttVal attval;
-
- for (attval = this.attributes; attval != null; attval = attval.next)
- attval.checkAttribute( lexer, this );
- }
-
- public void checkUniqueAttributes(Lexer lexer)
- {
- AttVal attval;
-
- for (attval = this.attributes; attval != null; attval = attval.next) {
- if (attval.asp == null && attval.php == null)
- attval.checkUniqueAttribute(lexer, this);
- }
- }
-
- public void addAttribute(String name, String value)
- {
- AttVal av = new AttVal(null, null, null, null,
- '"', name, value);
- av.dict =
- AttributeTable.getDefaultAttributeTable().findAttribute(av);
-
- if (this.attributes == null)
- this.attributes = av;
- else /* append to end of attributes */
- {
- AttVal here = this.attributes;
-
- while (here.next != null)
- here = here.next;
-
- here.next = av;
- }
- }
-
- /* remove attribute from node then free it */
- public void removeAttribute(AttVal attr)
- {
- AttVal av;
- AttVal prev = null;
- AttVal next;
-
- for (av = this.attributes; av != null; av = next)
- {
- next = av.next;
-
- if (av == attr)
- {
- if (prev != null)
- prev.next = next;
- else
- this.attributes = next;
- }
- else
- prev = av;
- }
- }
-
- /* find doctype element */
- public Node findDocType()
- {
- Node node;
-
- for (node = this.content;
- node != null && node.type != DocTypeTag; node = node.next);
-
- return node;
- }
-
- public void discardDocType()
- {
- Node node;
-
- node = findDocType();
- if (node != null)
- {
- if (node.prev != null)
- node.prev.next = node.next;
- else
- node.parent.content = node.next;
-
- if (node.next != null)
- node.next.prev = node.prev;
-
- node.next = null;
- }
- }
-
- /* remove node from markup tree and discard it */
- public static Node discardElement(Node element)
- {
- Node next = null;
-
- if (element != null)
- {
- next = element.next;
- removeNode(element);
- }
-
- return next;
- }
-
- /* insert node into markup tree */
- public static void insertNodeAtStart(Node element, Node node)
- {
- node.parent = element;
-
- if (element.content == null)
- element.last = node;
- else
- element.content.prev = node; // AQ added 13 Apr 2000
-
- node.next = element.content;
- node.prev = null;
- element.content = node;
- }
-
- /* insert node into markup tree */
- public static void insertNodeAtEnd(Node element, Node node)
- {
- node.parent = element;
- node.prev = element.last;
-
- if (element.last != null)
- element.last.next = node;
- else
- element.content = node;
-
- element.last = node;
- }
-
- /*
- insert node into markup tree in pace of element
- which is moved to become the child of the node
- */
- public static void insertNodeAsParent(Node element, Node node)
- {
- node.content = element;
- node.last = element;
- node.parent = element.parent;
- element.parent = node;
-
- if (node.parent.content == element)
- node.parent.content = node;
-
- if (node.parent.last == element)
- node.parent.last = node;
-
- node.prev = element.prev;
- element.prev = null;
-
- if (node.prev != null)
- node.prev.next = node;
-
- node.next = element.next;
- element.next = null;
-
- if (node.next != null)
- node.next.prev = node;
- }
-
- /* insert node into markup tree before element */
- public static void insertNodeBeforeElement(Node element, Node node)
- {
- Node parent;
-
- parent = element.parent;
- node.parent = parent;
- node.next = element;
- node.prev = element.prev;
- element.prev = node;
-
- if (node.prev != null)
- node.prev.next = node;
-
- if (parent.content == element)
- parent.content = node;
- }
-
- /* insert node into markup tree after element */
- public static void insertNodeAfterElement(Node element, Node node)
- {
- Node parent;
-
- parent = element.parent;
- node.parent = parent;
-
- // AQ - 13Jan2000 fix for parent == null
- if (parent != null && parent.last == element)
- parent.last = node;
- else
- {
- node.next = element.next;
- // AQ - 13Jan2000 fix for node.next == null
- if (node.next != null)
- node.next.prev = node;
- }
-
- element.next = node;
- node.prev = element;
- }
-
- public static void trimEmptyElement(Lexer lexer, Node element)
- {
- TagTable tt = lexer.configuration.tt;
-
- if (lexer.canPrune(element))
- {
- if (element.type != TextNode)
- Report.warning(lexer, element, null, Report.TRIM_EMPTY_ELEMENT);
-
- discardElement(element);
- }
- else if (element.tag == tt.tagP && element.content == null)
- {
- /* replace <p></p> by <br><br> to preserve formatting */
- Node node = lexer.inferredTag("br");
- Node.coerceNode(lexer, element, tt.tagBr);
- Node.insertNodeAfterElement(element, node);
- }
- }
-
- /*
- This maps
- <em>hello </em><strong>world</strong>
- to
- <em>hello</em> <strong>world</strong>
-
- If last child of element is a text node
- then trim trailing white space character
- moving it to after element's end tag.
- */
- public static void trimTrailingSpace(Lexer lexer, Node element, Node last)
- {
- byte c;
- TagTable tt = lexer.configuration.tt;
-
- if (last != null && last.type == Node.TextNode &&
- last.end > last.start)
- {
- c = lexer.lexbuf[last.end - 1];
-
- if (c == 160 || c == (byte)' ')
- {
- /* take care with <td> </td> */
- if (element.tag == tt.tagTd ||
- element.tag == tt.tagTh)
- {
- if (last.end > last.start + 1)
- last.end -= 1;
- }
- else
- {
- last.end -= 1;
-
- if (((element.tag.model & Dict.CM_INLINE) != 0) &&
- !((element.tag.model & Dict.CM_FIELD) != 0))
- lexer.insertspace = true;
-
- /* if empty string then delete from parse tree */
- if (last.start == last.end)
- trimEmptyElement(lexer, last);
- }
- }
- }
- }
-
- /*
- This maps
- <p>hello<em> world</em>
- to
- <p>hello <em>world</em>
-
- Trims initial space, by moving it before the
- start tag, or if this element is the first in
- parent's content, then by discarding the space
- */
- public static void trimInitialSpace(Lexer lexer, Node element, Node text)
- {
- Node prev, node;
-
- // GLP: Local fix to Bug 119789. Remove this comment when parser.c is updated.
- // 31-Oct-00.
- if (text.type == TextNode && text.textarray[text.start] == (byte)' '
- && (text.start < text.end))
- {
- if (((element.tag.model & Dict.CM_INLINE) != 0) &&
- !((element.tag.model & Dict.CM_FIELD) != 0) &&
- element.parent.content != element)
- {
- prev = element.prev;
-
- if (prev != null && prev.type == TextNode)
- {
- if (prev.textarray[prev.end - 1] != (byte)' ')
- prev.textarray[prev.end++] = (byte)' ';
-
- ++element.start;
- }
- else /* create new node */
- {
- node = lexer.newNode();
- // Local fix for bug 228486 (GLP). This handles the case
- // where we need to create a preceeding text node but there are
- // no "slots" in textarray that we can steal from the current
- // element. Therefore, we create a new textarray containing
- // just the blank. When Tidy is fixed, this should be removed.
- if (element.start >= element.end)
- {
- node.start = 0;
- node.end = 1;
- node.textarray = new byte[1];
- }
- else
- {
- node.start = element.start++;
- node.end = element.start;
- node.textarray = element.textarray;
- }
- node.textarray[node.start] = (byte)' ';
- node.prev = prev;
- if (prev != null)
- prev.next = node;
- node.next = element;
- element.prev = node;
- node.parent = element.parent;
- }
- }
-
- /* discard the space in current node */
- ++text.start;
- }
- }
-
- /*
- Move initial and trailing space out.
- This routine maps:
-
- hello<em> world</em>
- to
- hello <em>world</em>
- and
- <em>hello </em><strong>world</strong>
- to
- <em>hello</em> <strong>world</strong>
- */
- public static void trimSpaces(Lexer lexer, Node element)
- {
- Node text = element.content;
- TagTable tt = lexer.configuration.tt;
-
- if (text != null && text.type == Node.TextNode &&
- element.tag != tt.tagPre)
- trimInitialSpace(lexer, element, text);
-
- text = element.last;
-
- if (text != null && text.type == Node.TextNode)
- trimTrailingSpace(lexer, element, text);
- }
-
- public boolean isDescendantOf(Dict tag)
- {
- Node parent;
-
- for (parent = this.parent;
- parent != null; parent = parent.parent)
- {
- if (parent.tag == tag)
- return true;
- }
-
- return false;
- }
-
- /*
- the doctype has been found after other tags,
- and needs moving to before the html element
- */
- public static void insertDocType(Lexer lexer, Node element, Node doctype)
- {
- TagTable tt = lexer.configuration.tt;
-
- Report.warning(lexer, element, doctype, Report.DOCTYPE_AFTER_TAGS);
-
- while (element.tag != tt.tagHtml)
- element = element.parent;
-
- insertNodeBeforeElement(element, doctype);
- }
-
- public Node findBody(TagTable tt)
- {
- Node node;
-
- node = this.content;
-
- while (node != null && node.tag != tt.tagHtml)
- node = node.next;
-
- if (node == null)
- return null;
-
- node = node.content;
-
- while (node != null && node.tag != tt.tagBody)
- node = node.next;
-
- return node;
- }
-
- public boolean isElement()
- {
- return (this.type == StartTag || this.type == StartEndTag ? true : false);
- }
-
- /*
- unexpected content in table row is moved to just before
- the table in accordance with Netscape and IE. This code
- assumes that node hasn't been inserted into the row.
- */
- public static void moveBeforeTable(Node row, Node node, TagTable tt)
- {
- Node table;
-
- /* first find the table element */
- for (table = row.parent; table != null; table = table.parent)
- {
- if (table.tag == tt.tagTable)
- {
- if (table.parent.content == table)
- table.parent.content = node;
-
- node.prev = table.prev;
- node.next = table;
- table.prev = node;
- node.parent = table.parent;
-
- if (node.prev != null)
- node.prev.next = node;
-
- break;
- }
- }
- }
-
- /*
- if a table row is empty then insert an empty cell
- this practice is consistent with browser behavior
- and avoids potential problems with row spanning cells
- */
- public static void fixEmptyRow(Lexer lexer, Node row)
- {
- Node cell;
-
- if (row.content == null)
- {
- cell = lexer.inferredTag("td");
- insertNodeAtEnd(row, cell);
- Report.warning(lexer, row, cell, Report.MISSING_STARTTAG);
- }
- }
-
- public static void coerceNode(Lexer lexer, Node node, Dict tag)
- {
- Node tmp = lexer.inferredTag(tag.name);
- Report.warning(lexer, node, tmp, Report.OBSOLETE_ELEMENT);
- node.was = node.tag;
- node.tag = tag;
- node.type = StartTag;
- node.implicit = true;
- node.element = tag.name;
- }
-
- /* extract a node and its children from a markup tree */
- public static void removeNode(Node node)
- {
- if (node.prev != null)
- node.prev.next = node.next;
-
- if (node.next != null)
- node.next.prev = node.prev;
-
- if (node.parent != null)
- {
- if (node.parent.content == node)
- node.parent.content = node.next;
-
- if (node.parent.last == node)
- node.parent.last = node.prev;
- }
-
- node.parent = node.prev = node.next = null;
- }
-
- public static boolean insertMisc(Node element, Node node)
- {
- if (node.type == CommentTag ||
- node.type == ProcInsTag ||
- node.type == CDATATag ||
- node.type == SectionTag ||
- node.type == AspTag ||
- node.type == JsteTag ||
- node.type == PhpTag)
- {
- insertNodeAtEnd(element, node);
- return true;
- }
-
- return false;
- }
-
- /*
- used to determine how attributes
- without values should be printed
- this was introduced to deal with
- user defined tags e.g. Cold Fusion
- */
- public static boolean isNewNode(Node node)
- {
- if (node != null && node.tag != null)
- {
- return ((node.tag.model & Dict.CM_NEW) != 0);
- }
-
- return true;
- }
-
- public boolean hasOneChild()
- {
- return (this.content != null && this.content.next == null);
- }
-
- /* find html element */
- public Node findHTML(TagTable tt)
- {
- Node node;
-
- for (node = this.content;
- node != null && node.tag != tt.tagHtml; node = node.next);
-
- return node;
- }
-
- public Node findHEAD(TagTable tt)
- {
- Node node;
-
- node = this.findHTML(tt);
-
- if (node != null)
- {
- for (node = node.content;
- node != null && node.tag != tt.tagHead;
- node = node.next);
- }
-
- return node;
- }
-
- public boolean checkNodeIntegrity()
- {
- Node child;
- boolean found = false;
-
- if (this.prev != null)
- {
- if (this.prev.next != this)
- return false;
- }
-
- if (this.next != null)
- {
- if (this.next.prev != this)
- return false;
- }
-
- if (this.parent != null)
- {
- if (this.prev == null && this.parent.content != this)
- return false;
-
- if (this.next == null && this.parent.last != this)
- return false;
-
- for (child = this.parent.content; child != null; child = child.next)
- if (child == this)
- {
- found = true;
- break;
- }
-
- if (!found)
- return false;
- }
-
- for (child = this.content; child != null; child = child.next)
- if (!child.checkNodeIntegrity())
- return false;
-
- return true;
- }
-
- /*
- Add class="foo" to node
- */
- public static void addClass(Node node, String classname)
- {
- AttVal classattr = node.getAttrByName("class");
-
- /*
- if there already is a class attribute
- then append class name after a space
- */
- if (classattr != null)
- {
- classattr.value = classattr.value + " " + classname;
- }
- else /* create new class attribute */
- node.addAttribute("class", classname);
- }
-
- /* --------------------- DEBUG -------------------------- */
-
- private static final String[] nodeTypeString =
- {
- "RootNode",
- "DocTypeTag",
- "CommentTag",
- "ProcInsTag",
- "TextNode",
- "StartTag",
- "EndTag",
- "StartEndTag",
- "SectionTag",
- "AspTag",
- "PhpTag"
- };
-
- public String toString()
- {
- String s = "";
- Node n = this;
-
- while (n != null) {
- s += "[Node type=";
- s += nodeTypeString[n.type];
- s += ",element=";
- if (n.element != null)
- s += n.element;
- else
- s += "null";
- if (n.type == TextNode ||
- n.type == CommentTag ||
- n.type == ProcInsTag) {
- s += ",text=";
- if (n.textarray != null && n.start <= n.end) {
- s += "\"";
- s += Lexer.getString(n.textarray, n.start, n.end - n.start);
- s += "\"";
- } else {
- s += "null";
- }
- }
- s += ",content=";
- if (n.content != null)
- s += n.content.toString();
- else
- s += "null";
- s += "]";
- if (n.next != null)
- s += ",";
- n = n.next;
- }
- return s;
- }
- /* --------------------- END DEBUG ---------------------- */
-
-
- /* --------------------- DOM ---------------------------- */
-
- protected org.w3c.dom.Node adapter = null;
-
- protected org.w3c.dom.Node getAdapter()
- {
- if (adapter == null)
- {
- switch (this.type)
- {
- case RootNode:
- adapter = new DOMDocumentImpl(this);
- break;
- case StartTag:
- case StartEndTag:
- adapter = new DOMElementImpl(this);
- break;
- case DocTypeTag:
- adapter = new DOMDocumentTypeImpl(this);
- break;
- case CommentTag:
- adapter = new DOMCommentImpl(this);
- break;
- case TextNode:
- adapter = new DOMTextImpl(this);
- break;
- case CDATATag:
- adapter = new DOMCDATASectionImpl(this);
- break;
- case ProcInsTag:
- adapter = new DOMProcessingInstructionImpl(this);
- break;
- default:
- adapter = new DOMNodeImpl(this);
- }
- }
- return adapter;
- }
-
- protected Node cloneNode(boolean deep)
- {
- Node node = (Node)this.clone();
- if (deep)
- {
- Node child;
- Node newChild;
- for (child = this.content; child != null; child = child.next)
- {
- newChild = child.cloneNode(deep);
- insertNodeAtEnd(node, newChild);
- }
- }
- return node;
- }
-
-
- protected void setType(short newType)
- {
- this.type = newType;
- }
-
- /* --------------------- END DOM ------------------------ */
-
-}