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.ItsNatException;
017: import org.itsnat.core.domutil.ElementTreeNodeRenderer;
018: import org.itsnat.core.domutil.ElementTreeNode;
019: import org.itsnat.core.domutil.ElementTreeNodeList;
020: import org.itsnat.core.domutil.ElementTreeNodeStructure;
021: import org.itsnat.impl.core.ItsNatDocumentImpl;
022: import org.w3c.dom.DocumentFragment;
023: import org.w3c.dom.Element;
024: import org.w3c.dom.Node;
025:
026: /**
027: *
028: * @author jmarranz
029: */
030: public abstract class ElementTreeNodeImpl extends ElementGroupImpl
031: implements ElementTreeNode {
032: protected final ElementTreeNodeStructure structure; // No se puede cambiar pues se utiliza en tiempo de creación del objeto (en el constructor), por tanto no tiene sentido un método "set"
033: protected ElementTreeNodeRenderer renderer;
034: protected ElementTreeNodeListImpl parentList;
035: protected ElementTreeNodeListImpl childElemList;
036: protected Element parentElement;
037: protected int index; // -1 cuando parentTreeNode es null
038: protected int row = -1; // 0 si es el root, -1 es el valor inicial que indica que no ha sido calculado, se calcula la primera vez que se necesite
039: protected Object auxObject; // Usado por el componente ItsNatTree, no usar para otro fin, no hacer público
040: protected boolean usePatternMarkupToRender;
041: protected DocumentFragment labelContentPatternFragment; // Será recordado como patrón
042:
043: /**
044: * Creates a new instance of ElementTreeNodeImpl
045: */
046: public ElementTreeNodeImpl(ItsNatDocumentImpl itsNatDoc,
047: ElementTreeNodeListImpl parentList, int index,
048: Element parentElement, ElementTreeNodeStructure structure,
049: ElementTreeNodeRenderer renderer) {
050: super (itsNatDoc);
051:
052: this .parentList = parentList; // Puede ser null en el caso de root no removible, lo cual no es posible en TreeTables (nunca null)
053: this .index = index;
054: this .parentElement = parentElement;
055: if (structure == null)
056: throw new ItsNatException(
057: "No tree node structure was registered");
058: this .structure = structure;
059: this .renderer = renderer;
060:
061: if (parentList != null)
062: this .usePatternMarkupToRender = parentList
063: .isUsePatternMarkupToRender();
064: else
065: this .usePatternMarkupToRender = itsNatDoc
066: .isUsePatternMarkupToRender(); // Root
067: }
068:
069: public boolean isUsePatternMarkupToRender() {
070: return usePatternMarkupToRender;
071: }
072:
073: public void setUsePatternMarkupToRender(
074: boolean usePatternMarkupToRender) {
075: this .usePatternMarkupToRender = usePatternMarkupToRender;
076: }
077:
078: public Object getAuxObject() {
079: return auxObject;
080: }
081:
082: public void setAuxObject(Object auxObject) {
083: this .auxObject = auxObject;
084: }
085:
086: public ElementTreeNodeListImpl getParentList() {
087: return parentList;
088: }
089:
090: public int getIndex() {
091: return index;
092: }
093:
094: public void setIndex(int index) {
095: // Interna, no hacer pública
096: this .index = index;
097: }
098:
099: public Element getParentElement() {
100: return parentElement;
101: }
102:
103: public Element getTreeContainerElement() {
104: // Devuelve el elemento DOM más alto posible que englobe todo
105: // el árbol, sea el nodo removible o no
106: if (parentList != null)
107: return parentList.getTreeContainerElement();
108: else
109: return getParentElement(); // Es un root no removible (lo más alto)
110: }
111:
112: public ElementTreeNodeStructure getElementTreeNodeStructure() {
113: return structure;
114: }
115:
116: public ElementTreeNode getElementTreeNodeParent() {
117: // Devuelve null si es el root
118: if (parentList == null)
119: return null; // Caso de root no removible, cuando el root es no removible no tiene lista contenedora
120: return parentList.getElementTreeNodeParent(); // Si devuelve null es el root removible o rootless tree
121: }
122:
123: public ElementTreeNode getPreviousSiblingTreeNode() {
124: ElementTreeNodeListImpl parentList = getParentList();
125: if (parentList == null) // Caso de root no removible
126: return null;
127: return parentList.getTreeNodeAt(getIndex() - 1);
128: }
129:
130: public ElementTreeNode getPreviousTreeNode() {
131: ElementTreeNodeImpl prevSibling = (ElementTreeNodeImpl) getPreviousSiblingTreeNode();
132: if (prevSibling == null)
133: return getElementTreeNodeParent(); // puede ser null
134:
135: return prevSibling.getDeepMostLastTreeNode();
136: }
137:
138: public ElementTreeNodeImpl getDeepMostLastTreeNode() {
139: ElementTreeNode lastChild = getChildTreeNodeList()
140: .getLastTreeNode();
141: if (lastChild == null)
142: return this ; // Sí mismo, no tiene hijos
143:
144: ElementTreeNode prevChild;
145: do {
146: prevChild = lastChild;
147: lastChild = lastChild.getChildTreeNodeList()
148: .getLastTreeNode();
149: } while (lastChild != null);
150:
151: return (ElementTreeNodeImpl) prevChild;
152: }
153:
154: public ElementTreeNode getNextSiblingTreeNode() {
155: ElementTreeNodeListImpl parentList = getParentList();
156: if (parentList == null) // Caso de root no removible
157: return null;
158: return parentList.getTreeNodeAt(getIndex() + 1);
159: }
160:
161: public ElementTreeNode getNextTreeNode() {
162: ElementTreeNode result = getChildTreeNodeList()
163: .getFirstTreeNode();
164: if (result != null)
165: return result;
166:
167: result = getNextSiblingTreeNode();
168: if (result != null)
169: return result;
170:
171: // return parent's 1st sibling.
172: ElementTreeNode parent = getElementTreeNodeParent();
173: while (parent != null) {
174: result = parent.getNextSiblingTreeNode();
175: if (result != null)
176: return result;
177: else
178: parent = parent.getElementTreeNodeParent();
179: }
180:
181: // end , return null
182: return null;
183: }
184:
185: public static int[] createRowIterator() {
186: int[] currRow = new int[1];
187: currRow[0] = -1;
188: return currRow;
189: }
190:
191: public ElementTreeNodeImpl getElementTreeNodeFromRow(int row) {
192: int[] currRow = createRowIterator();
193:
194: return getElementTreeNodeFromRow(row, currRow);
195: }
196:
197: public ElementTreeNodeImpl getElementTreeNodeFromRow(int row,
198: int[] currRow) {
199: currRow[0] += 1;
200: if (row == currRow[0])
201: return this ;
202: else {
203: ElementTreeNodeListImpl childList = (ElementTreeNodeListImpl) getChildTreeNodeList();
204: return childList.getElementTreeNodeFromRow(row, currRow);
205: }
206: }
207:
208: public ElementTreeNodeRenderer getElementTreeNodeRenderer() {
209: return renderer;
210: }
211:
212: public void setElementTreeNodeRenderer(
213: ElementTreeNodeRenderer renderer) {
214: this .renderer = renderer;
215: }
216:
217: public Element getContentElement() {
218: return getElementTreeNodeStructure().getContentElement(this ,
219: getParentElement());
220: }
221:
222: public Element getHandleElement() {
223: return getElementTreeNodeStructure().getHandleElement(this ,
224: getParentElement());
225: }
226:
227: public Element getIconElement() {
228: return getElementTreeNodeStructure().getIconElement(this ,
229: getParentElement());
230: }
231:
232: public Element getLabelElement() {
233: return getElementTreeNodeStructure().getLabelElement(this ,
234: getParentElement());
235: }
236:
237: public Element getChildListElement() {
238: return getElementTreeNodeStructure().getChildListElement(this ,
239: getParentElement());
240: }
241:
242: public void setValue(Object value) {
243: setValue(value, false);
244: }
245:
246: public void setValue(Object value, boolean isNew) {
247: Element labelElem = getLabelElement();
248: prepareRendering(labelElem, isNew);
249: ElementTreeNodeRenderer renderer = getElementTreeNodeRenderer();
250: if (renderer != null)
251: renderer.renderTreeNode(this , value, labelElem, isNew);
252: }
253:
254: public void prepareRendering(Element labelElem, boolean isNew) {
255: if (!isNew && isUsePatternMarkupToRender()) // Si es nuevo el markup es ya el del patrón
256: {
257: // Es una actualización en donde tenemos que usar el markup pattern en vez del contenido actual
258: restorePatternMarkupWhenRendering(labelElem,
259: getLabelContentPatternFragment());
260: }
261: }
262:
263: public ElementTreeNode getElementTreeNodeFromNode(Node node) {
264: return getElementTreeNodeFromNode(node,
265: getTreeContainerElement());
266: }
267:
268: public ElementTreeNode getElementTreeNodeFromNode(Node node,
269: Element treeContainerElem) {
270: if (getParentElement() == node)
271: return this ;
272:
273: Element contentElem = getContentElement();
274: if (contentElem != null) {
275: if (ItsNatDOMUtilInternal.isChildOrSame(node, contentElem,
276: treeContainerElem)) // El treeContainerElem es para acelerar la búsqueda en caso fallido
277: {
278: // "node" forma parte del "contenido" del nodo
279: return this ;
280: }
281: } else // Caso raro
282: {
283: Element handleElem = getHandleElement();
284: if ((handleElem != null)
285: && ItsNatDOMUtilInternal.isChildOrSame(node,
286: handleElem, treeContainerElem)) // El treeContainerElem es para acelerar la búsqueda en caso fallido
287: return this ; // "node" forma parte del "contenido" del nodo
288:
289: Element iconElem = getIconElement();
290: if ((iconElem != null)
291: && ItsNatDOMUtilInternal.isChildOrSame(node,
292: iconElem, treeContainerElem)) // El treeContainerElem es para acelerar la búsqueda en caso fallido
293: return this ; // "node" forma parte del "contenido" del nodo
294:
295: Element labelElem = getIconElement();
296: if ((labelElem != null)
297: && ItsNatDOMUtilInternal.isChildOrSame(node,
298: labelElem, treeContainerElem)) // El treeContainerElem es para acelerar la búsqueda en caso fallido
299: return this ; // "node" forma parte del "contenido" del nodo
300: }
301:
302: ElementTreeNodeListImpl childElemList = getElementTreeNodeList();
303: if (childElemList != null) // puede no tener hijos
304: return childElemList.getElementTreeNodeFromNode(node,
305: treeContainerElem);
306: return null;
307: // Si null en este caso "node" no pertenece a este TreeNode o bien se ha pulsado un nodo de texto entre los hijos o una zona entre el elemento DOM padre de los hijos y los hijos
308: }
309:
310: public static String toString(int[] path) {
311: StringBuffer res = new StringBuffer();
312: for (int i = 0; i < path.length; i++) {
313: if (i > 0)
314: res.append(",");
315: res.append(path[i]);
316: }
317: return res.toString();
318: }
319:
320: public ElementTreeNode getElementTreeNodeFromPath(int[] path) {
321: // No se usa, para un posible uso recuperar.
322: return getElementTreeNodeFromPath(path, 0);
323: }
324:
325: public ElementTreeNode getElementTreeNodeFromPath(int[] path,
326: int fromIndex) {
327: // No se usa, para un posible uso recuperar.
328: if (fromIndex == path.length)
329: return this ;
330: int childIndex = path[fromIndex];
331: if (childIndex == -1) // Puede serlo si viene de llamar a TreeModel.getIndexOfChild
332: return null;
333: ElementTreeNodeImpl childNode = (ElementTreeNodeImpl) getChildTreeNodeList()
334: .getTreeNodeAt(childIndex);
335: if (childNode == null)
336: throw new ItsNatException("Tree node not found: "
337: + toString(path));
338:
339: return childNode
340: .getElementTreeNodeFromPath(path, fromIndex + 1);
341: }
342:
343: public ElementTreeNodeList getChildTreeNodeList() {
344: return getElementTreeNodeList();
345: }
346:
347: public abstract ElementTreeNodeListImpl getElementTreeNodeList();
348:
349: public int getRow() {
350: if (this .row != -1)
351: return row;
352:
353: // Devuelve el índice de la fila visto el árbol como una lista
354: // Hay que tener en cuenta el caso "rootless" en donde un nodo sin
355: // nodo padre no tiene por qué ser el root, luego eso no vale como criterio "el root"
356: // para calcular la fila 0, el no existir nodo previo si vale en todos los casos.
357: ElementTreeNode prevTreeNode = getPreviousTreeNode();
358: if (prevTreeNode == null)
359: this .row = 0; // El root
360: else
361: this .row = prevTreeNode.getRow() + 1;
362:
363: return row;
364: }
365:
366: public void setRow(int row) {
367: this .row = row;
368: }
369:
370: public int getDeepLevel() {
371: ElementTreeNode parentNode = getElementTreeNodeParent();
372: if (parentNode == null)
373: return 0; // Sea o no el root (pues existe el caso de rootless) no hay más niveles por encima
374: return parentNode.getDeepLevel() + 1;
375: }
376:
377: public boolean isLeaf() {
378: return getChildTreeNodeList().isEmpty();
379: }
380:
381: public DocumentFragment getLabelContentPatternFragment() {
382: if (labelContentPatternFragment == null) {
383: Element labelElem;
384: ElementTreeNodeListImpl parentList = getParentList(); // No puede ser null
385: if (parentList == null) {
386: // En este caso obtenemos el pattern a través del markup original del root
387: // antes de que se renderice por vez primera
388: labelElem = structure.getLabelElement(this ,
389: getParentElement());
390: } else {
391: Element treeNodePattern = parentList
392: .getChildPatternElement();
393: ElementTreeNodeStructure structure = getElementTreeNodeStructure();
394: labelElem = structure.getLabelElement(this ,
395: treeNodePattern);
396: }
397:
398: if (labelElem != null) // Si no se puede no se puede
399: {
400: Element clonedLabelElem = (Element) labelElem
401: .cloneNode(true); // Necesitamos clonar porque al extraer los nodos hijos se vaciará el contenido
402: this.labelContentPatternFragment = ItsNatDOMUtilInternal
403: .extractChildrenToDocFragment(clonedLabelElem);
404: }
405: }
406:
407: return labelContentPatternFragment;
408: }
409: }
|