001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.shared.tree;
034:
035: import com.flexive.shared.FxFormatUtils;
036: import com.flexive.shared.SelectableObjectWithName;
037: import com.flexive.shared.SelectableObjectWithLabel;
038: import com.flexive.shared.SelectableObject;
039: import com.flexive.shared.content.FxPK;
040: import com.flexive.shared.security.ACL;
041: import com.flexive.shared.value.FxString;
042:
043: import java.io.Serializable;
044: import java.util.ArrayList;
045: import java.util.List;
046:
047: /**
048: * FxNode implementation for flexive
049: *
050: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
051: */
052: public class FxTreeNode implements Serializable,
053: SelectableObjectWithLabel, SelectableObjectWithName {
054: private static final long serialVersionUID = -1666004845250114348L;
055:
056: private String path = null;
057: private boolean temporary = true;
058: private boolean markForDelete = false;
059: protected FxString label = null;
060: protected FxPK reference;
061: protected FxTreeMode mode;
062: protected int position;
063: protected List<FxTreeNode> children;
064: private List<Long> childIds = null;
065: private boolean dirty;
066: private long id;
067: private long ACLId;
068: protected long parentNodeId;
069: protected String name;
070: private long modifiedAt;
071: protected String template;
072: private int depth;
073: private int totalChildCount;
074: private int directChildCount;
075: private boolean leaf;
076: private boolean mayEdit, mayDelete, mayRelate, mayExport,
077: mayCreate;
078: protected boolean activate = false;
079:
080: //flag indicating that this tree node is only partial loaded
081: private boolean partialLoaded = false;
082: public static final int PARTIAL_LOADED_POS = -42;
083: public static final String PATH_NOT_LOADED = "/not_loaded(partial!)";
084:
085: /**
086: * Constant id of the root node
087: */
088: public final static long ROOT_NODE = 1;
089:
090: /**
091: * Protected constructor to avoid construction
092: */
093: protected FxTreeNode() {
094: }
095:
096: /**
097: * Ctor
098: *
099: * @param mode FxTreeMode
100: * @param id node id
101: * @param parentNodeId id of the parent node
102: * @param reference pk of the referenced content
103: * @param ACLId acl of the referenced content
104: * @param name name (part of the path)
105: * @param path complete path from the root node
106: * @param label label
107: * @param position position
108: * @param children child nodes (only available if loaded with <code>#getTree</code>)
109: * @param childIds ids of the child nodes (only available if loaded with <code>#getTree</code>)
110: * @param depth depth of this node relative to the root node
111: * @param totalChildCount total number of children
112: * @param directChildCount number of children attached to this node directly
113: * @param leaf is this node a leaf?
114: * @param dirty dirty flag
115: * @param modifiedAt timestamp of last modification
116: * @param template template
117: * @param mayEdit edit permission for the calling user
118: * @param mayCreate create permission for the calling user
119: * @param mayDelete delete permission for the calling user
120: * @param mayRelate relate permission for the calling user
121: * @param mayExport export permission for the calling user
122: */
123: public FxTreeNode(FxTreeMode mode, long id, long parentNodeId,
124: FxPK reference, long ACLId, String name, String path,
125: FxString label, int position, List<FxTreeNode> children,
126: List<Long> childIds, int depth, int totalChildCount,
127: int directChildCount, boolean leaf, boolean dirty,
128: long modifiedAt, String template, boolean mayEdit,
129: boolean mayCreate, boolean mayDelete, boolean mayRelate,
130: boolean mayExport) {
131: this .path = FxFormatUtils.escapeTreePath(path);
132: this .label = label;
133: this .reference = reference;
134: this .ACLId = ACLId;
135: this .mode = mode;
136: this .position = position;
137: this .children = children;
138: this .childIds = childIds;
139: this .dirty = dirty;
140: this .id = id;
141: this .parentNodeId = parentNodeId;
142: this .name = FxFormatUtils.escapeTreePath(name);
143: this .modifiedAt = modifiedAt;
144: this .template = template;
145: this .depth = depth;
146: this .totalChildCount = totalChildCount;
147: this .directChildCount = directChildCount;
148: this .leaf = leaf;
149: this .temporary = false;
150: this .markForDelete = false;
151: this .mayCreate = mayCreate;
152: this .mayDelete = mayDelete;
153: this .mayEdit = mayEdit;
154: this .mayExport = mayExport;
155: this .mayRelate = mayRelate;
156: this .partialLoaded = position == PARTIAL_LOADED_POS;
157: }
158:
159: /**
160: * Get the position of this node
161: *
162: * @return position of this node
163: */
164: public int getPosition() {
165: return position;
166: }
167:
168: /**
169: * Is this FxTreeNode only partially loaded?
170: * Loading a (sub)tree is usually performed with partial loading enabled (only the calling
171: * users current language filled in the label)
172: *
173: * @return if this FxTreeNode is only partially loaded
174: */
175: public boolean isPartialLoaded() {
176: return this .partialLoaded;
177: }
178:
179: /**
180: * Is this node "live"?
181: *
182: * @return if the node is "live"
183: */
184: public boolean isLive() {
185: return mode == FxTreeMode.Live;
186: }
187:
188: /**
189: * Is this node flagged as dirty?
190: *
191: * @return node flagged as dirty?
192: */
193: public boolean isDirty() {
194: return dirty;
195: }
196:
197: /**
198: * {@inheritDoc}
199: */
200: public long getId() {
201: return id;
202: }
203:
204: /**
205: * Get the id of the ACL assigned to the referenced content
206: *
207: * @return id of the ACL assigned to the referenced content
208: */
209: public long getACLId() {
210: return ACLId;
211: }
212:
213: /**
214: * Get the id of the parent node
215: *
216: * @return id of the parent node
217: */
218: public long getParentNodeId() {
219: return parentNodeId;
220: }
221:
222: /**
223: * Get the name of this node
224: *
225: * @return name of this node
226: */
227: public String getName() {
228: return name;
229: }
230:
231: /**
232: * Is a reference set for this node (Description)
233: *
234: * @return if a reference is set
235: */
236: public boolean hasReference() {
237: return reference != null;
238: }
239:
240: /**
241: * Get the referenced content
242: *
243: * @return referenced content
244: */
245: public FxPK getReference() {
246: return reference;
247: }
248:
249: /**
250: * Get the timestamp of the last modification
251: *
252: * @return timestamp of the last modification
253: */
254: public long getModifiedAt() {
255: return modifiedAt;
256: }
257:
258: /**
259: * Is a template assigned to this node?
260: *
261: * @return if a template is assigned to this node
262: */
263: public boolean hasTemplate() {
264: return template != null;
265: }
266:
267: /**
268: * Get the template assigned to this node, can be <code>null</code>
269: *
270: * @return template assigned to this node, can be <code>null</code>
271: */
272: public String getTemplate() {
273: return template;
274: }
275:
276: /**
277: * Returns the depth of the node within the complete tree
278: * The root node has depth 1
279: *
280: * @return the depth of the node
281: */
282: public int getDepth() {
283: return depth;
284: }
285:
286: /**
287: * Get the number of child nodes attached to this node and all subchildren
288: *
289: * @return the number of child nodes attached to this node and all subchildren
290: */
291: public int getTotalChildCount() {
292: return totalChildCount;
293: }
294:
295: /**
296: * Get the number of child nodes directly attached to this node
297: *
298: * @return the number of child nodes directly attached to this node
299: */
300: public int getDirectChildCount() {
301: return directChildCount;
302: }
303:
304: /**
305: * Is this a leaf node?
306: *
307: * @return if this node is a leaf node
308: */
309: public boolean isLeaf() {
310: return leaf;
311: }
312:
313: /**
314: * {@inheritDoc}
315: */
316: public synchronized FxString getLabel() {
317: return label;
318: }
319:
320: /**
321: * Get the path of this node
322: *
323: * @return path
324: */
325: public String getPath() {
326: return path;
327: }
328:
329: /**
330: * Returns the children of this node, but only if they are a part of the resultset - use
331: * isLeaf(), getDirectChildCount() and getTotalChildCount() to find out if the node has
332: * children.
333: * This function never returns null, but a empty List when no children are available
334: *
335: * @return the children of this node, but only if they are a part of the resultset
336: */
337: public List<FxTreeNode> getChildren() {
338: return children;
339: }
340:
341: /**
342: * Returns the child Id's of this node, but only if they are a part of the resultset.
343: *
344: * @return the child Id's of this node, but only if they are a part of the resultset
345: */
346: public synchronized List<Long> getChildIds() {
347: if (childIds == null) {
348: childIds = new ArrayList<Long>(children.size());
349: for (FxTreeNode child : getChildren())
350: childIds.add(child.getId());
351: }
352: return childIds;
353: }
354:
355: /**
356: * ACL: Edit permission for the calling user
357: *
358: * @return ACL: Edit permission for the calling user
359: */
360: public boolean isMayEdit() {
361: return mayEdit;
362: }
363:
364: /**
365: * ACL: Delete permission for the calling user
366: *
367: * @return ACL: Delete permission for the calling user
368: */
369: public boolean isMayDelete() {
370: return mayDelete;
371: }
372:
373: /**
374: * ACL: Relate permission for the calling user
375: *
376: * @return ACL: Relate permission for the calling user
377: */
378: public boolean isMayRelate() {
379: return mayRelate;
380: }
381:
382: /**
383: * ACL: Export permission for the calling user
384: *
385: * @return ACL: Export permission for the calling user
386: */
387: public boolean isMayExport() {
388: return mayExport;
389: }
390:
391: /**
392: * ACL: Create permission for the calling user
393: *
394: * @return ACL: Create permission for the calling user
395: */
396: public boolean isMayCreate() {
397: return mayCreate;
398: }
399:
400: /**
401: * Is this node marked to be deleted? (only used in GUI, not persisted!)
402: *
403: * @return marked for delete
404: */
405: public boolean isMarkForDelete() {
406: return markForDelete;
407: }
408:
409: /**
410: * Mark to be deleted for UI (only used in GUI, not persisted!)
411: *
412: * @param markForDelete delete?
413: * @return this
414: */
415: public FxTreeNode setMarkForDelete(boolean markForDelete) {
416: this .markForDelete = markForDelete;
417: return this ;
418: }
419:
420: /**
421: * Set the active flag of this tree node (only used in GUI, not persisted!)
422: *
423: * @param activate activate flag
424: */
425: public void setActivate(boolean activate) {
426: this .activate = activate;
427: }
428:
429: /**
430: * Is the node marked as active? (only used in GUI, not persisted!)
431: *
432: * @return active status
433: */
434: public boolean isActivate() {
435: return activate;
436: }
437:
438: /**
439: * Is this node temporary only? (only used in GUI, not persisted!)
440: *
441: * @return if this node is temporary only
442: */
443: public boolean isTemporary() {
444: return temporary;
445: }
446:
447: /**
448: * {@inheritDoc}
449: */
450: public int hashCode() {
451: return (int) this .getId();
452: }
453:
454: /**
455: * {@inheritDoc}
456: */
457: public boolean equals(Object obj) {
458: if (obj == null || (!(obj instanceof SelectableObject)))
459: return false;
460:
461: SelectableObject comp = (SelectableObject) obj;
462: return this .getId() == comp.getId();
463: }
464:
465: /**
466: * Create a temporary error node to be used in UI
467: *
468: * @param nodeId desired node id
469: * @param message error message
470: * @return FxTreeNode
471: */
472: public static FxTreeNode createErrorNode(long nodeId, String message) {
473: return new FxTreeNode(FxTreeMode.Edit, nodeId, 0, FxPK
474: .createNewPK(), ACL.Category.INSTANCE.getDefaultId(),
475: "Error", message, new FxString(false, "Error"),
476: Integer.MAX_VALUE, new ArrayList<FxTreeNode>(0),
477: new ArrayList<Long>(0), 0, 0, 0, true, true, System
478: .currentTimeMillis(), "", true, true, true,
479: true, true);
480: }
481:
482: /**
483: * Get the tree mode (live or edit)
484: *
485: * @return tree mode
486: */
487: public FxTreeMode getMode() {
488: return mode;
489: }
490:
491: /**
492: * Make this node editable
493: *
494: * @return FxTreeNodeEdit
495: */
496: public FxTreeNodeEdit asEditable() {
497: return new FxTreeNodeEdit(this );
498: }
499:
500: /**
501: * Flag this FxTreeNode as temporary
502: *
503: * @return this
504: */
505: private FxTreeNode flagTemporary() {
506: this .temporary = true;
507: return this ;
508: }
509:
510: /**
511: * Create a temporary node below the given parent node
512: *
513: * @param parentNode the parent node
514: * @return temporary node
515: */
516: public static FxTreeNode createNewTemporaryChildNode(
517: FxTreeNode parentNode) {
518: FxTreeNode n = new FxTreeNode(parentNode.getMode(), (System
519: .currentTimeMillis() * -1), parentNode.getId(), FxPK
520: .createNewPK(), ACL.Category.INSTANCE.getDefaultId(),
521: "@@TMP", "", new FxString(parentNode.getLabel()
522: .isMultiLanguage(), ""), Integer.MAX_VALUE,
523: new ArrayList<FxTreeNode>(0), new ArrayList<Long>(0),
524: 0, 0, 0, true, true, System.currentTimeMillis(), "",
525: true, true, true, true, true).flagTemporary();
526: n.path = parentNode.getPath()
527: + (parentNode.getPath().endsWith("/") ? "" : "/") + "*";
528: return n;
529: }
530:
531: /**
532: * Internal method only used during loading phase of a tree
533: *
534: * @param node child node to add
535: */
536: public synchronized void _addChild(FxTreeNode node) {
537: children.add(node);
538: childIds.add(node.getId());
539: }
540:
541: /**
542: * Internal method to recursively apply a path from the root to all children
543: *
544: * @param path path to apply (includes name already!)
545: */
546: public void _applyPath(String path) {
547: this .path = path;
548: for (FxTreeNode node : this .getChildren())
549: node._applyPath(path
550: + (path.endsWith("/") ? node.getName() : "/"
551: + node.getName()));
552: }
553:
554: /**
555: * Internal method to recursively apply positions
556: *
557: * @param position position to apply to this node
558: */
559: public void _applyPosition(int position) {
560: this .position = position;
561: int pos = 0;
562: for (FxTreeNode node : this.getChildren())
563: node._applyPosition(pos++);
564: }
565: }
|