001: /*
002: ItsNat Java Web Application Framework
003: Copyright (C) 2007 Innowhere Software Services S.L., Spanish Company
004: Author: Jose Maria Arranz Santamaria
005:
006: This program is free software: you can redistribute it and/or modify
007: it under the terms of the GNU Affero General Public License as published by
008: the Free Software Foundation, either version 3 of the License, or
009: (at your option) any later version. See the GNU Affero General Public
010: License for more details. See the copy of the GNU Affero General Public License
011: included in this program. If not, see <http://www.gnu.org/licenses/>.
012: */
013:
014: package org.itsnat.impl.core.domutil;
015:
016: import org.itsnat.core.domutil.ItsNatDOMUtil;
017: import org.itsnat.core.domutil.ItsNatTreeWalker;
018: import org.w3c.dom.Attr;
019: import org.w3c.dom.Document;
020: import org.w3c.dom.DocumentFragment;
021: import org.w3c.dom.Element;
022: import org.w3c.dom.NamedNodeMap;
023: import org.w3c.dom.Node;
024: import org.w3c.dom.NodeList;
025: import org.w3c.dom.Text;
026: import org.w3c.dom.html.HTMLBodyElement;
027: import org.w3c.dom.html.HTMLDocument;
028: import org.w3c.dom.html.HTMLHeadElement;
029: import org.w3c.dom.html.HTMLHtmlElement;
030: import org.w3c.dom.traversal.DocumentTraversal;
031: import org.w3c.dom.traversal.NodeFilter;
032: import org.w3c.dom.traversal.TreeWalker;
033:
034: /**
035: *
036: * @author jmarranz
037: */
038: public class ItsNatDOMUtilInternal {
039:
040: /**
041: * Creates a new instance of ItsNatDOMUtilInternal
042: */
043: public ItsNatDOMUtilInternal() {
044: }
045:
046: public static void setAttribute(Element elem, String name,
047: String value) {
048: // Hacemos esto para evitar lo más posible los mutation events inútiles
049: // pues al parecer aunque el valor sea el mismo Xerces genera un mutation event
050: // que supondrá enviar código a lo tonto al cliente cuando nada ha cambiado
051: String oldValue = elem.getAttribute(name);
052: if (value.equals(oldValue))
053: return;
054: elem.setAttribute(name, value);
055: }
056:
057: public static void setAttribute(Element elem, String name,
058: boolean value) {
059: if (value)
060: setAttribute(elem, name, name);
061: else
062: elem.removeAttribute(name);
063: }
064:
065: public static String toString(Object obj) {
066: return obj != null ? obj.toString() : null;
067: }
068:
069: public static boolean isChildOrSame(Node node,
070: Element parentElement, Element topLimitParent) {
071: if (node == null)
072: return false;
073:
074: if (node == parentElement)
075: return true;
076:
077: return isChild(node, parentElement, topLimitParent);
078: }
079:
080: public static boolean isChild(Node node, Element parentElement,
081: Element topLimitParent) {
082: if (node == null)
083: return false;
084:
085: if (node == parentElement)
086: return false;
087:
088: Node nodeParent = getChildTopMostContainingNode(node,
089: parentElement, topLimitParent);
090: return (nodeParent != null); // Es que es hijo
091: }
092:
093: public static Node getChildTopMostContainingNode(Node node,
094: Element parentElement) {
095: return getChildTopMostContainingNode(node, parentElement, null);
096: }
097:
098: public static Node getChildTopMostContainingNode(Node node,
099: Element parentElement, Element topLimitParent) {
100: // Devuelve el nodo padre de "node" que a su vez es hijo directo de parentElement,
101: // si node no está dentro de parentElement se devuelve null
102: // topLimitParent sirve para evitar subir más arriba inútilmente si no está dentro
103: // de parentElement pues sabemos que pasará por topLimitParent necesariamente, topLimitParent ha de ser un elemento del cual tenemos la seguridad
104: // de que node es hijo, sirve para acelerar la búsqueda en caso fallido, puede ser null o el propio parentElement
105: // no tiene sentido que sea hijo de parentElement
106: if (node == null)
107: return null;
108:
109: Node parent = node.getParentNode();
110: while ((parent != null) && (parent != parentElement)
111: && (parent != topLimitParent)) {
112: node = parent;
113: parent = node.getParentNode();
114: }
115:
116: if (parent == parentElement)
117: return node;
118:
119: return null; // No es hijo, resto de los casos
120: }
121:
122: public static HTMLHeadElement getHead(HTMLDocument doc) {
123: HTMLHtmlElement html = (HTMLHtmlElement) doc
124: .getDocumentElement();
125: Node node = html.getFirstChild();
126: while (node != null) {
127: if (node instanceof HTMLHeadElement)
128: return (HTMLHeadElement) node;
129:
130: node = node.getNextSibling();
131: }
132: return null;
133: }
134:
135: public static boolean hasContainedNodeMatching(Node parent,
136: NodeConstraints rules) {
137: return (getFirstContainedNodeMatching(parent, rules) != null);
138: }
139:
140: public static Node getFirstContainedNodeMatching(Node parent,
141: NodeConstraints rules) {
142: // El propio nodo no se considera
143: Node child = parent.getFirstChild();
144: while (child != null) {
145: if (rules.match(child))
146: return child;
147: Node result = getFirstContainedNodeMatching(child, rules);
148: if (result != null)
149: return result;
150: child = child.getNextSibling();
151: }
152: return null;
153: }
154:
155: public static boolean isHeadChild(Node node) {
156: Node parent = node.getParentNode();
157: if (parent == null)
158: return false;
159: HTMLDocument doc = (HTMLDocument) node.getOwnerDocument();
160: HTMLHeadElement head = getHead(doc);
161: while (parent != null) {
162: if (parent == head)
163: return true;
164:
165: parent = parent.getParentNode();
166: }
167:
168: return false;
169: }
170:
171: public static boolean isBodyChild(Node node) {
172: Node parent = node.getParentNode();
173: if (parent == null)
174: return false;
175: HTMLDocument doc = (HTMLDocument) node.getOwnerDocument();
176: HTMLBodyElement body = (HTMLBodyElement) doc.getBody();
177: while (parent != null) {
178: if (parent == body)
179: return true;
180:
181: parent = parent.getParentNode();
182: }
183:
184: return false;
185: }
186:
187: public static void replaceContent(Element parentElem, Node newChild) {
188: // newChild puede ser Element, DocumentFragment, Text etc
189: // Intentamos evitar cambiar el árbol si no es necesario, así evitamos tráfico de red
190: if (newChild instanceof DocumentFragment) {
191: DocumentFragment docFrag = (DocumentFragment) newChild;
192: if (ItsNatDOMUtilInternal
193: .isSameContent(parentElem, docFrag))
194: return; // Nada que hacer
195: } else {
196: Node child = parentElem.getFirstChild();
197: if ((child.getNextSibling() == null)
198: && ItsNatDOMUtilInternal.isSameContent(
199: (Node) newChild, child))
200: return; // Nada que hacer pues queremos sustituir el mismo elemento ya presente
201: }
202:
203: // Eliminamos el contenido del elemento antes de insertar
204: ItsNatDOMUtil.removeAllChildren(parentElem);
205: parentElem.appendChild((Node) newChild);
206: }
207:
208: public static void removeAllChildNotElement(Element parentElem) {
209: Node node = parentElem.getFirstChild();
210: while (node != null) {
211: if (node.getNodeType() != Node.ELEMENT_NODE) {
212: Node next = node.getNextSibling();
213: parentElem.removeChild(node);
214: node = next;
215: } else {
216: node = node.getNextSibling();
217: }
218: }
219: }
220:
221: public static void removeAllChildTextNodes(Element parentElem) {
222: Node node = parentElem.getFirstChild();
223: while (node != null) {
224: if (node.getNodeType() == Node.TEXT_NODE) {
225: Node next = node.getNextSibling();
226: parentElem.removeChild(node);
227: node = next;
228: } else {
229: node = node.getNextSibling();
230: }
231: }
232: }
233:
234: public static Text getFirstTextNode(Element elem) {
235: Node child = elem.getFirstChild();
236: while (child != null) {
237: if (child.getNodeType() == Node.TEXT_NODE)
238: return (Text) child;
239:
240: Text textNode = getFirstTextNode((Element) child);
241: if (textNode != null)
242: return textNode;
243:
244: child = child.getNextSibling();
245: }
246:
247: return null;
248: }
249:
250: public static String getTextFirstTextNode(Element elem) {
251: // NO SE usa
252: Text textNode = getFirstTextNode(elem);
253: if (textNode != null)
254: return textNode.getData();
255: return "";
256: }
257:
258: public static void setTextFirstTextNode(Element elem, String value) {
259: Text textNode = getFirstTextNode(elem);
260: if (textNode != null) {
261: if (value == null)
262: value = "";
263: textNode.setData(value); // Nos interesa mantener el objeto Text aunque contenga "" para saber donde ha de escribirse
264: } else {
265: // Creamos un nuevo nodo de texto en el lugar más bajo
266: // bajo el primer elemento si existe o directamente debajo del elemento.
267:
268: Node parent = elem;
269: Node child = parent.getFirstChild();
270: while (child != null) {
271: parent = child;
272: child = child.getFirstChild();
273: }
274:
275: // No hay nodo de texto, lo creamos (si value tiene algo)
276: if ((value != null) && !value.equals("")) {
277: child = parent.getOwnerDocument().createTextNode(value);
278: parent.appendChild(child);
279: }
280: }
281: }
282:
283: public static boolean isSameContent(Element parentElem,
284: DocumentFragment docFrag) {
285: if (!parentElem.hasChildNodes() && !docFrag.hasChildNodes())
286: return true;
287: NodeList children1 = parentElem.getChildNodes();
288: NodeList children2 = docFrag.getChildNodes();
289: if (children1.getLength() != children2.getLength())
290: return false;
291: int len = children1.getLength();
292: for (int i = 0; i < len; i++) {
293: Node childNode1 = children1.item(i);
294: Node childNode2 = children2.item(i);
295: if (!isSameContent(childNode1, childNode2))
296: return false;
297: }
298: return true;
299: }
300:
301: public static boolean isSameContent(Attr attr1, Attr attr2) {
302: if (!attr1.getName().equals(attr2.getName()))
303: return false;
304: if (!attr1.getValue().equals(attr2.getValue()))
305: return false;
306: if (attr1.getNamespaceURI() != null)
307: if (attr1.getNamespaceURI().equals(attr2.getNamespaceURI()))
308: return false;
309: return true;
310: }
311:
312: public static boolean isSameContent(Node node1, Node node2) {
313: // Útil para detectar que un nodo es un clon de otro
314:
315: if (node1 == node2)
316: return true;
317:
318: if (node1.getNodeType() != node2.getNodeType())
319: return false;
320:
321: int type = node1.getNodeType();
322: if (type == Node.ELEMENT_NODE) {
323: if (!((Element) node1).getTagName().equals(
324: ((Element) node2).getTagName()))
325: return false;
326:
327: if (node1.getNamespaceURI() != null)
328: if (node1.getNamespaceURI().equals(
329: node2.getNamespaceURI()))
330: return false;
331:
332: if (node1.hasAttributes() != node1.hasAttributes())
333: return false;
334:
335: if (node1.hasAttributes()) {
336: NamedNodeMap attribs1 = node1.getAttributes();
337: NamedNodeMap attribs2 = node2.getAttributes();
338: if (attribs1.getLength() != attribs2.getLength())
339: return false;
340: int len = attribs1.getLength();
341: for (int i = 0; i < len; i++) {
342: Attr attr1 = (Attr) attribs1.item(i);
343: Attr attr2 = (Attr) attribs2.item(i);
344: if (!isSameContent(attr1, attr2))
345: return false;
346: }
347: }
348: }
349:
350: if (node1.hasChildNodes() != node1.hasChildNodes())
351: return false;
352:
353: if (node1.hasChildNodes()) {
354: NodeList childList1 = node1.getChildNodes();
355: NodeList childList2 = node2.getChildNodes();
356: if (childList1.getLength() != childList2.getLength())
357: return false;
358: int len = childList1.getLength();
359: for (int i = 0; i < len; i++) {
360: Node child1 = childList1.item(i);
361: Node child2 = childList2.item(i);
362: if (!isSameContent(child1, child2))
363: return false;
364: }
365: }
366:
367: return true;
368: }
369:
370: public static TreeWalker createTreeWalker(Element node) {
371: DocumentTraversal doc = (DocumentTraversal) node
372: .getOwnerDocument();
373: return doc.createTreeWalker(node, NodeFilter.SHOW_ELEMENT,
374: null, true);
375: }
376:
377: public static boolean isNodeBoundToDocumentTree(Node node) {
378: // NO se usa, lo dejo para un futuro
379: if (node == null)
380: return false;
381:
382: Node parent = node;
383: do {
384: node = parent;
385: parent = node.getParentNode();
386: } while (parent != null);
387:
388: return (node.getNodeType() == Node.DOCUMENT_NODE);
389: }
390:
391: public static DocumentFragment extractChildrenToDocFragment(
392: Node parentNode) {
393: Node child = parentNode.getFirstChild();
394:
395: Document doc = parentNode.getOwnerDocument();
396: DocumentFragment docFrag = doc.createDocumentFragment();
397: while (child != null) {
398: parentNode.removeChild(child); // realmente no hace falta pues al añadir al DocumentFragment se quita del árbol
399: docFrag.appendChild(child);
400: child = parentNode.getFirstChild();
401: }
402: return docFrag; // Nunca es null
403: }
404: }
|