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 java.util.ArrayList;
017: import org.itsnat.core.ItsNatException;
018: import org.itsnat.core.domutil.ElementTreeNodeRenderer;
019: import org.itsnat.core.domutil.ElementTreeNode;
020: import org.itsnat.core.domutil.ElementTreeNodeList;
021: import org.itsnat.core.domutil.ElementTreeNodeStructure;
022: import org.itsnat.impl.core.ItsNatDocumentImpl;
023: import org.w3c.dom.Element;
024: import org.w3c.dom.Node;
025:
026: /**
027: *
028: * @author jmarranz
029: */
030: public abstract class ElementTreeNodeListImpl extends ElementGroupImpl
031: implements ElementTreeNodeList {
032: protected ElementTreeNodeStructure structure; // La que usarán los nuevos hijos, puede cambiarse
033: protected ElementTreeNodeRenderer renderer; // El que usarán los nuevos hijos
034: protected ElementTreeNodeImpl parentTreeNode;
035: protected ArrayList childTreeNodes;
036: protected boolean usePatternMarkupToRender;
037:
038: /**
039: * Creates a new instance of ElementTreeNodeListImpl
040: */
041: public ElementTreeNodeListImpl(ItsNatDocumentImpl itsNatDoc,
042: ElementTreeNodeImpl parentTreeNode,
043: ElementTreeNodeStructure structure,
044: ElementTreeNodeRenderer renderer) {
045: super (itsNatDoc);
046:
047: this .parentTreeNode = parentTreeNode; // Puede ser nulo, caso de la lista contenedora del root o bien rootless tree
048: if (structure == null)
049: throw new ItsNatException(
050: "No tree node structure was registered");
051: this .structure = structure;
052: this .renderer = renderer;
053:
054: if (parentTreeNode != null)
055: this .usePatternMarkupToRender = parentTreeNode
056: .isUsePatternMarkupToRender();
057: else
058: this .usePatternMarkupToRender = itsNatDoc
059: .isUsePatternMarkupToRender(); // Root
060: }
061:
062: public boolean isUsePatternMarkupToRender() {
063: return usePatternMarkupToRender;
064: }
065:
066: public void setUsePatternMarkupToRender(
067: boolean usePatternMarkupToRender) {
068: this .usePatternMarkupToRender = usePatternMarkupToRender;
069: }
070:
071: protected ArrayList getInternalTreeNodeList() {
072: // En el caso de TreeTable puede no usarse nunca
073: if (childTreeNodes == null)
074: this .childTreeNodes = new ArrayList();
075: return childTreeNodes;
076: }
077:
078: public ElementTreeNodeImpl getElementTreeNodeParent() {
079: return parentTreeNode;
080: }
081:
082: public ElementTreeNodeStructure getElementTreeNodeStructure() {
083: return structure;
084: }
085:
086: public void setElementTreeNodeStructure(
087: ElementTreeNodeStructure structure) {
088: this .structure = structure;
089: }
090:
091: public ElementTreeNodeRenderer getElementTreeNodeRenderer() {
092: return renderer;
093: }
094:
095: public void setElementTreeNodeRenderer(
096: ElementTreeNodeRenderer renderer) {
097: this .renderer = renderer;
098: }
099:
100: public abstract ElementTreeNodeImpl createTreeNode(int index,
101: Element childElemElement);
102:
103: public boolean isOutOfRange(int index) {
104: return (index < 0)
105: || (index >= getInternalTreeNodeList().size());
106: }
107:
108: public ElementTreeNode getTreeNodeAt(int index) {
109: if (isOutOfRange(index))
110: return null;
111:
112: return (ElementTreeNode) getInternalTreeNodeList().get(index);
113: }
114:
115: public ElementTreeNode getFirstTreeNode() {
116: return getTreeNodeAt(0);
117: }
118:
119: public ElementTreeNode getLastTreeNode() {
120: return getTreeNodeAt(getLength() - 1);
121: }
122:
123: protected abstract Element addTreeNodeDOMElementAt();
124:
125: public ElementTreeNode addTreeNode() {
126: Element newNode = addTreeNodeDOMElementAt();
127:
128: int index = getLength();
129: ElementTreeNodeImpl treeNode = createTreeNode(index, newNode);
130: getInternalTreeNodeList().add(treeNode);
131:
132: // No es necesario recalcular el index porque es el último pero sí el row
133: recalcForwardIndices(index + 1, +1);
134:
135: return treeNode;
136: }
137:
138: public ElementTreeNode addTreeNode(Object value) {
139: ElementTreeNodeImpl childNode = (ElementTreeNodeImpl) addTreeNode();
140: childNode.setValue(value, true);
141: return childNode;
142: }
143:
144: protected abstract Element insertTreeNodeDOMElementAt(int index);
145:
146: public ElementTreeNode insertTreeNodeAt(int index) {
147: Element newNode = insertTreeNodeDOMElementAt(index);
148:
149: ElementTreeNodeImpl treeNode = createTreeNode(index, newNode);
150: getInternalTreeNodeList().add(index, treeNode);
151:
152: recalcForwardIndices(index + 1, +1);
153:
154: return treeNode;
155: }
156:
157: public ElementTreeNode insertTreeNodeAt(int index, Object value) {
158: ElementTreeNodeImpl treeNode = (ElementTreeNodeImpl) insertTreeNodeAt(index);
159: treeNode.setValue(value, true);
160: return treeNode;
161: }
162:
163: protected abstract Element removeTreeNodeDOMElementAt(int index);
164:
165: public void unrenderTreeNode(int index) {
166: ElementTreeNodeImpl treeNode = (ElementTreeNodeImpl) getTreeNodeAt(index);
167: if (treeNode == null)
168: return;
169:
170: ElementTreeNodeRenderer renderer = treeNode
171: .getElementTreeNodeRenderer();
172: if (renderer != null) {
173: Element labelElem = treeNode.getLabelElement();
174: renderer.unrenderTreeNode(treeNode, labelElem);
175: }
176:
177: ElementTreeNodeListImpl childList = treeNode
178: .getElementTreeNodeList();
179: if (childList == null)
180: return;
181:
182: int len = childList.getLength();
183: for (int i = 0; i < len; i++)
184: childList.unrenderTreeNode(i);
185: }
186:
187: public ElementTreeNode removeTreeNodeAt(int index) {
188: unrenderTreeNode(index);
189:
190: Element removedElem = removeTreeNodeDOMElementAt(index);
191: if (removedElem == null)
192: return null; // out of bounds.
193:
194: ArrayList childTreeNodes = getInternalTreeNodeList();
195: ElementTreeNodeImpl treeNodeRemoved = (ElementTreeNodeImpl) childTreeNodes
196: .remove(index);
197:
198: recalcForwardIndices(index, -1);
199:
200: return treeNodeRemoved;
201: }
202:
203: public void removeTreeNodeRange(int fromIndex, int toIndex) {
204: // Hay que actualizar rows etc está la complicación del tree-table
205: // demasiadas cosas para intentar hacer el borrado del DOM primero
206: // con un posible removeTreeNodeDOMElementRange y de las auxiliares después (daba errores ese enfoque)
207: // Lo hacemos uno a uno y así aseguramos y del final al principio
208: // para que no ralentice la actualización de la row (hacia adelante) de nodos que luego van a ser eliminados
209: for (int i = toIndex; i >= fromIndex; i--)
210: removeTreeNodeAt(i);
211: }
212:
213: public void recalcForwardIndices(int fromChildIndex, int count) {
214: // Corregimos los índices
215: ArrayList childTreeNodes = getInternalTreeNodeList();
216: int newLen = childTreeNodes.size();
217: for (int i = fromChildIndex; i < newLen; i++) {
218: ElementTreeNodeImpl nodeAfter = (ElementTreeNodeImpl) childTreeNodes
219: .get(i);
220: nodeAfter.setIndex(nodeAfter.getIndex() + count);
221: }
222:
223: // El recálculo de las rows es mucho más complicado que los índices de los hijos inmediatos
224: // porque el borrado o inserción de un nuevo nodo puede suponer el borrado o adición
225: // también de nodos hijo. Por eso lo que hacemos es invalidar la row de los siguientes
226: // nodos a los borrados/añadidos y de esa manera cuando se necesite obtenerse se calculará automáticamente
227: // "bajo demanda".
228:
229: ElementTreeNodeImpl nodeAfter = null;
230: if (fromChildIndex < newLen)
231: nodeAfter = (ElementTreeNodeImpl) childTreeNodes
232: .get(fromChildIndex);
233: else {
234: // Es el caso en el que se ha añadido el nuevo nodo como el último
235: // hijo, o bien se ha eliminado el último nodo hijo (o todos los hijos) y newLen es cero
236: // en dicho caso el siguiente es el siguiente del padre, es decir de este nodo,
237: // salvo que sea una lista contenedora del Root (no hay TreeNode)
238: // en dicho caso no hay siguiente claro pues el único posible nodo hijo añadido o quitado es el Root
239: ElementTreeNodeImpl this TreeNode = getElementTreeNodeParent();
240: if (this TreeNode != null)
241: nodeAfter = (ElementTreeNodeImpl) this TreeNode
242: .getNextSiblingTreeNode();
243: }
244:
245: while (nodeAfter != null) {
246: nodeAfter.setRow(-1);
247:
248: nodeAfter = (ElementTreeNodeImpl) nodeAfter
249: .getNextTreeNode();
250: }
251: }
252:
253: public void removeAllTreeNodes() {
254: int len = getLength();
255: if (len == 0)
256: return;
257:
258: removeTreeNodeRange(0, len - 1);
259: }
260:
261: public boolean isEmpty() {
262: return getInternalTreeNodeList().isEmpty();
263: }
264:
265: public int getLength() {
266: return getInternalTreeNodeList().size();
267: }
268:
269: public void setLength(int len) {
270: if (len < 0)
271: throw new ItsNatException("Length can not be negative:"
272: + len);
273: int currentSize = getLength();
274: int diff = len - currentSize;
275: if (diff > 0)
276: for (int i = 0; i < diff; i++)
277: addTreeNode();
278: else if (diff < 0)
279: for (int i = currentSize - 1; i >= len; i--)
280: removeTreeNodeAt(i);
281: }
282:
283: public ElementTreeNode getElementTreeNodeFromNode(Node node) {
284: return getElementTreeNodeFromNode(node,
285: getTreeContainerElement());
286: }
287:
288: public ElementTreeNode getElementTreeNodeFromNode(Node node,
289: Element treeContainerElem) {
290: Element parentElem = getParentElement();
291: if (!ItsNatDOMUtilInternal.isChildOrSame(node, parentElem,
292: treeContainerElem)) {
293: // Podemos ahorrar mucho tiempo en este caso, evitando iterar por el árbol de los hijos sobre todo si hay muchos
294: // En el caso de tree-table salvo la primera vez es redundante (si no es el caso nulo).
295: return null;
296: }
297:
298: ArrayList childList = getInternalTreeNodeList();
299: int len = childList.size();
300: for (int i = 0; i < len; i++) {
301: ElementTreeNodeImpl childTreeNode = (ElementTreeNodeImpl) childList
302: .get(i);
303: ElementTreeNode result = childTreeNode
304: .getElementTreeNodeFromNode(node, treeContainerElem);
305: if (result != null)
306: return result;
307: }
308: return 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
309: }
310:
311: public ElementTreeNodeImpl getElementTreeNodeFromRow(int row) {
312: int[] currRow = ElementTreeNodeImpl.createRowIterator();
313:
314: return getElementTreeNodeFromRow(row, currRow);
315: }
316:
317: public ElementTreeNodeImpl getElementTreeNodeFromRow(int row,
318: int[] currRow) {
319: ArrayList childList = getInternalTreeNodeList();
320: int size = childList.size();
321: for (int i = 0; i < size; i++) {
322: ElementTreeNodeImpl childTreeNode = (ElementTreeNodeImpl) childList
323: .get(i);
324: ElementTreeNodeImpl treeNodeRes = childTreeNode
325: .getElementTreeNodeFromRow(row, currRow);
326: if (treeNodeRes != null)
327: return treeNodeRes;
328: }
329:
330: return null;
331: }
332:
333: public abstract Element getTreeContainerElement();
334:
335: public ElementTreeNode[] getTreeNodes() {
336: ArrayList childList = getInternalTreeNodeList();
337: return (ElementTreeNode[]) childList
338: .toArray(new ElementTreeNode[childList.size()]);
339: }
340:
341: }
|