001: /*
002: The contents of this file are subject to the Common Public Attribution License
003: Version 1.0 (the "License"); you may not use this file except in compliance with
004: the License. You may obtain a copy of the License at
005: http://www.projity.com/license . The License is based on the Mozilla Public
006: License Version 1.1 but Sections 14 and 15 have been added to cover use of
007: software over a computer network and provide for limited attribution for the
008: Original Developer. In addition, Exhibit A has been modified to be consistent
009: with Exhibit B.
010:
011: Software distributed under the License is distributed on an "AS IS" basis,
012: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
013: specific language governing rights and limitations under the License. The
014: Original Code is OpenProj. The Original Developer is the Initial Developer and
015: is Projity, Inc. All portions of the code written by Projity are Copyright (c)
016: 2006, 2007. All Rights Reserved. Contributors Projity, Inc.
017:
018: Alternatively, the contents of this file may be used under the terms of the
019: Projity End-User License Agreeement (the Projity License), in which case the
020: provisions of the Projity License are applicable instead of those above. If you
021: wish to allow use of your version of this file only under the terms of the
022: Projity License and not to allow others to use your version of this file under
023: the CPAL, indicate your decision by deleting the provisions above and replace
024: them with the notice and other provisions required by the Projity License. If
025: you do not delete the provisions above, a recipient may use your version of this
026: file under either the CPAL or the Projity License.
027:
028: [NOTE: The text of this license may differ slightly from the text of the notices
029: in Exhibits A and B of the license at http://www.projity.com/license. You should
030: use the latest text at http://www.projity.com/license for your modifications.
031: You may not remove this license text from the source files.]
032:
033: Attribution Information: Attribution Copyright Notice: Copyright � 2006, 2007
034: Projity, Inc. Attribution Phrase (not exceeding 10 words): Powered by OpenProj,
035: an open source solution from Projity. Attribution URL: http://www.projity.com
036: Graphic Image as provided in the Covered Code as file: openproj_logo.png with
037: alternatives listed on http://www.projity.com/logo
038:
039: Display of Attribution Information is required in Larger Works which are defined
040: in the CPAL as a work which combines Covered Code or portions thereof with code
041: not governed by the terms of the CPAL. However, in addition to the other notice
042: obligations, all copies of the Covered Code in Executable and Source Code form
043: distributed must, as a form of attribution of the original author, include on
044: each user interface screen the "OpenProj" logo visible to all users. The
045: OpenProj logo should be located horizontally aligned with the menu bar and left
046: justified on the top left of the screen adjacent to the File menu. The logo
047: must be at least 100 x 25 pixels. When users click on the "OpenProj" logo it
048: must direct them back to http://www.projity.com.
049: */
050: package com.projity.grouping.core.model;
051:
052: import java.util.ArrayList;
053: import java.util.Collection;
054: import java.util.Comparator;
055: import java.util.Enumeration;
056: import java.util.HashMap;
057: import java.util.HashSet;
058: import java.util.Iterator;
059: import java.util.LinkedList;
060: import java.util.List;
061: import java.util.ListIterator;
062: import java.util.Map;
063: import java.util.Set;
064:
065: import javax.swing.event.TreeModelListener;
066: import javax.swing.tree.TreePath;
067: import javax.swing.undo.UndoableEdit;
068: import javax.swing.undo.UndoableEditSupport;
069:
070: import com.projity.association.AssociationList;
071: import com.projity.document.Document;
072: import com.projity.field.Field;
073: import com.projity.field.FieldContext;
074: import com.projity.field.FieldParseException;
075: import com.projity.grouping.core.Node;
076: import com.projity.grouping.core.NodeBridge;
077: import com.projity.grouping.core.NodeFactory;
078: import com.projity.grouping.core.VoidNodeImpl;
079: import com.projity.grouping.core.hierarchy.HierarchyUtils;
080: import com.projity.grouping.core.hierarchy.MutableNodeHierarchy;
081: import com.projity.grouping.core.hierarchy.NodeHierarchy;
082: import com.projity.pm.assignment.Assignment;
083: import com.projity.pm.assignment.AssignmentService;
084: import com.projity.pm.assignment.HasAssignments;
085: import com.projity.pm.dependency.Dependency;
086: import com.projity.pm.resource.ResourceImpl;
087: import com.projity.pm.task.NormalTask;
088: import com.projity.pm.task.Task;
089: import com.projity.undo.ModelFieldEdit;
090: import com.projity.undo.NodeCreationEdit;
091: import com.projity.undo.NodeDeletionEdit;
092: import com.projity.undo.NodeImplChangeAndValueSetEdit;
093: import com.projity.undo.NodeImplChangeEdit;
094: import com.projity.undo.NodePasteEdit;
095: import com.projity.undo.NodeUndoInfo;
096: import com.projity.undo.UndoController;
097:
098: /**
099: *
100: */
101: public class DefaultNodeModel implements NodeModel {
102:
103: protected NodeHierarchy hierarchy;
104: protected NodeModelDataFactory dataFactory = null;
105:
106: /**
107: *
108: */
109: public DefaultNodeModel() {
110: hierarchy = new MutableNodeHierarchy();
111: }
112:
113: public DefaultNodeModel(NodeModelDataFactory dataFactory) {
114: this ();
115: this .dataFactory = dataFactory;
116: }
117:
118: //for clone()
119: DefaultNodeModel(NodeHierarchy hierarchy,
120: NodeModelDataFactory dataFactory) {
121: this .hierarchy = hierarchy;
122: this .dataFactory = dataFactory;
123: }
124:
125: public void addBefore(LinkedList siblings, Node newNode,
126: int actionType) {
127: Node previous, next, parent;
128: boolean firstChild;
129: if (siblings.size() == 0) {
130: return;
131: } else if (siblings.size() == 1) {
132: previous = null;
133: next = (Node) siblings.removeLast();
134: parent = null;
135: firstChild = true;
136: } else {
137: previous = (Node) siblings.removeFirst(); //no need to clone, list used only here, see CommonSpreadSheetModel
138: next = (Node) siblings.removeLast();
139: parent = (Node) next.getParent();
140: firstChild = (parent == previous);
141: if (firstChild)
142: parent = previous;
143: else
144: parent = (Node) previous.getParent();
145: remove(siblings, NodeModel.SILENT);
146: }
147: siblings.add(newNode);
148: add(parent, siblings, (firstChild) ? 0 : (parent
149: .getIndex(previous) + 1), NodeModel.SILENT);
150:
151: getDataFactory().setGroupDirty(true);
152: //TODO need undo here
153: }
154:
155: public void addBefore(Node sibling, Node newNode, int actionType) {
156: Node parent = (Node) sibling.getParent();
157: add(parent, newNode, parent.getIndex(sibling), actionType);
158: }
159:
160: public void addBefore(Node sibling, List newNodes, int actionType) {
161: Node parent = (Node) sibling.getParent();
162: add(parent, newNodes, parent.getIndex(sibling), actionType);
163: }
164:
165: public void add(Node parent, Node child, int actionType) {
166: add(parent, child, -1, actionType);
167: }
168:
169: public void add(Node parent, Node child, int position,
170: int actionType) {
171: ArrayList children = new ArrayList();
172: children.add(child);
173: add(parent, children, position, actionType);
174: //hierarchy.add(parent,child,position,actionType);
175: }
176:
177: public void add(Node parent, List children, int actionType) {
178: add(parent, children, -1, actionType);
179: //hierarchy.add(parent,children,actionType);
180: }
181:
182: public void add(Node parent, List children, int position,
183: int actionType) {
184: hierarchy.add(parent, children, position, actionType);
185: //Undo
186: if (isUndo(actionType))
187: postEdit(new NodeCreationEdit(this , parent, children,
188: position));
189:
190: }
191:
192: public void add(Node child, int actionType) {
193: add((Node) hierarchy.getRoot(), child, actionType);
194: }
195:
196: public Node newNode(Node parent, int position, int actionType) {
197: //check if position is correct
198: Node node;
199: int p = position;
200: int i = 0;
201: for (Enumeration e = parent.children(); e.hasMoreElements(); i++) {
202: node = (Node) e.nextElement();
203: if (i == p) {
204: if (node.getImpl() instanceof Assignment)
205: p++;
206: else {
207: Node newNode = NodeFactory.getInstance()
208: .createVoidNode();
209: add(parent, newNode, p, NodeModel.NORMAL);
210: return newNode;
211: }
212: }
213: }
214: Node newNode = NodeFactory.getInstance().createVoidNode();
215: add(parent, newNode, -1, NodeModel.NORMAL);
216: return newNode;
217:
218: }
219:
220: public void paste(Node parent, List nodes, int position,
221: int actionType) {
222: //nodes=copy(nodes,NodeModel.SILENT); //make an other copy, in case it is copied more than one time
223: //done in transfert handler
224:
225: hierarchy.paste(parent, nodes, position, this , actionType);
226: //Undo
227: if (isUndo(actionType))
228: postEdit(new NodePasteEdit(this , parent, nodes, position));
229: }
230:
231: public boolean isAncestor(Node parent, Node child) {
232: if (child == null)
233: return false;
234: if (parent == child)
235: return true;
236: return isAncestor(parent, getParent(child));
237: }
238:
239: public boolean isAncestorOrDescendant(Node one, Node two) {
240: return isAncestor(one, two) || isAncestor(two, one);
241: }
242:
243: public boolean testAncestorOrDescendant(Node one, List nodes) {
244: Iterator i = nodes.iterator();
245: while (i.hasNext()) {
246: if (isAncestorOrDescendant(one, (Node) i.next()))
247: return false;
248: }
249: return true;
250: }
251:
252: public void move(Node parent, List nodes, int position,
253: int actionType) {
254: if (!testAncestorOrDescendant(parent, nodes)) // don't allow circular
255: return;
256: List cutNodes = cut(nodes, false, actionType);
257: //List cutNodes=cut(nodes,actionType&UNDO); //TODO fixes bug 225 but it's probably breaking undo
258: paste(parent, cutNodes, position, actionType);
259: }
260:
261: /**
262: * Convenience method to add a collection of objects (not nodes) to the node model
263: * @param parent
264: * @param collection
265: */
266: public void addImplCollection(Node parent, Collection collection,
267: int actionType) {
268: Iterator i = collection.iterator();
269: Node child;
270: while (i.hasNext()) {
271: child = NodeFactory.getInstance().createNode(i.next());
272: add(parent, child, actionType);
273: }
274:
275: }
276:
277: public void remove(Node node, int actionType) {
278: remove(node, actionType, true, true);
279: }
280:
281: public void remove(Node node, int actionType,
282: boolean removeDependencies) {
283: remove(node, actionType, true, removeDependencies);
284: }
285:
286: protected void remove(Node node, int actionType,
287: boolean filterAssignments, boolean removeDependencies) {
288: ArrayList nodes = new ArrayList();
289: nodes.add(node);
290: remove(nodes, actionType, filterAssignments, removeDependencies);
291: //hierarchy.remove(node,this,actionType);
292: //it calls back removeApartFromHierarchy for each node to remove
293: }
294:
295: public void remove(List nodes, int actionType) {
296: remove(nodes, actionType, true);
297: }
298:
299: public void remove(List nodes, int actionType,
300: boolean removeDependencies) {
301: remove(nodes, actionType, true, removeDependencies);
302: }
303:
304: protected void remove(List nodes, int actionType,
305: boolean filterAssignments, boolean removeDependencies) {
306: if (undoController != null && isUndo(actionType)) {
307: undoController.getEditSupport().beginUpdate();
308: }
309: try {
310: ArrayList roots = new ArrayList();
311: HierarchyUtils.extractParents(nodes, roots);
312: if (filterAssignments)
313: for (Iterator i = roots.iterator(); i.hasNext();) {
314: Node node = (Node) i.next();
315: if (node.getImpl() instanceof Assignment) {
316: i.remove();
317: AssignmentService.getInstance().remove(node,
318: this , true);
319: }
320: }
321: boolean containsSubprojects = false;
322: List parents = new ArrayList();
323: List positions = new ArrayList();
324: NodeBridge node, parent;
325: for (Iterator i = roots.iterator(); i.hasNext();) {
326: node = (NodeBridge) i.next();
327: if (NodeModelUtil.nodeIsSubproject(node))
328: containsSubprojects = true;
329: parent = (NodeBridge) node.getParent();
330: parents.add(parent);
331: positions.add(new Integer(parent.getIndex(node)));
332: }
333: if (!confirmRemove(roots))
334: return;
335:
336: if (undoController != null && isUndo(actionType)) {
337: if (containsSubprojects)
338: undoController.clear();
339: }
340:
341: hierarchy.remove(roots, this , actionType,
342: removeDependencies);
343: //it calls back removeApartFromHierarchy for each node to remove
344: hierarchy.checkEndVoidNodes(actionType);
345:
346: //Undo
347: if (undoController != null) {
348: if (!containsSubprojects && isUndo(actionType)) {
349: postEdit(new NodeDeletionEdit(this , parents, roots,
350: positions));
351: }
352: }
353: } finally {
354: if (undoController != null) {
355: if (isUndo(actionType)) {
356: undoController.getEditSupport().endUpdate();
357: }
358: }
359: }
360: }
361:
362: public void removeAll(int actionType) {
363: hierarchy.removeAll(this , actionType);
364: }
365:
366: public boolean removeApartFromHierarchy(Node node,
367: boolean cleanAssignment, int actionType,
368: boolean removeDependencies) {
369: if (!isEvent(actionType))
370: return true;
371: // try {
372: // beginUpdate();
373: if (node.getImpl() instanceof Assignment) {
374: Assignment assignment = (Assignment) node.getImpl();
375: // if (cleanAssignment)
376: AssignmentService.getInstance().remove(assignment,
377: cleanAssignment, this , isUndo(actionType)); //LC 8/4/2006 - hk 7/8/2006 changed null to this so event will be fired
378: // else if (assignment.getResource()!=ResourceImpl.getUnassignedInstance()){
379: // assignment.getResource().removeAssignment(assignment);
380: // }
381:
382: //AssignmentService.getInstance().remove((Assignment)node.getImpl(),this);
383: } else if (dataFactory != null && !node.isVoid())
384: dataFactory.remove(node.getImpl(), this , false,
385: isUndo(actionType), removeDependencies); //TODO make this work properly with subproject
386: // } finally {
387: // endUpdate();
388: // }
389: return true;
390: }
391:
392: public List cut(List nodes, int actionType) {
393: return cut(nodes, true, actionType);
394: }
395:
396: public List cut(List nodes, boolean clone, int actionType) {
397: List newNodes = copy(nodes, clone, actionType);
398: remove(nodes, actionType);
399: return newNodes;
400: // ArrayList parentNodes=new ArrayList(nodes.size());
401: // HierarchyUtils.extractParents(nodes,parentNodes);
402: // remove(parentNodes,actionType);
403: // //TODO check parent is null
404: // return parentNodes;
405: }
406:
407: public List copy(List nodes, int actionType) {
408: return copy(nodes, true, actionType);
409: }
410:
411: public List copy(List nodes, boolean clone, int actionType) {
412: ArrayList parentNodes = new ArrayList(nodes.size());
413: HierarchyUtils.extractParents(nodes, parentNodes);
414: if (!clone)
415: return parentNodes;
416: Set assignedNodes = new HashSet();
417: Map implMap = new HashMap();
418: Set<Dependency> predecessors = new HashSet<Dependency>();
419: Set<Dependency> successors = new HashSet<Dependency>();
420: for (ListIterator i = parentNodes.listIterator(); i.hasNext();) {
421: Node parent = (Node) i.next();
422: Node newParent = cloneNode(parent, null, implMap,
423: predecessors, successors);
424: cloneBranch(parent, newParent, assignedNodes, implMap,
425: predecessors, successors);
426: i.remove();
427: i.add(newParent);
428: }
429:
430: //rebuild dependencies
431: for (Dependency dependency : predecessors) {
432: if (successors.contains(dependency)) {
433: Task predecessor = (Task) implMap.get(dependency
434: .getPredecessor());
435: Task successor = (Task) implMap.get(dependency
436: .getSuccessor());
437: if (predecessor != null && successor != null) {
438: Dependency d = Dependency.getInstance(predecessor,
439: successor, dependency.getDependencyType(),
440: dependency.getLag());
441: //Serializer.connectDependency(dependency, predecessor, successor);
442: predecessor.getDependencyList(false).add(d);
443: successor.getDependencyList(true).add(d);
444: }
445: }
446: }
447:
448: for (Iterator i = assignedNodes.iterator(); i.hasNext();) {
449: addAssignments((Node) i.next());
450: }
451:
452: for (ListIterator i = parentNodes.listIterator(); i.hasNext();) {
453: Node node = (Node) i.next();
454: cleanBranch(node);
455: }
456:
457: return parentNodes;
458: }
459:
460: private void cloneBranch(Node parent, Node newParent,
461: Set assignedNodes, Map implMap,
462: Set<Dependency> predecessors, Set<Dependency> successors) {
463: for (Iterator i = parent.childrenIterator(); i.hasNext();) {
464: Node child = (Node) i.next();
465: if (child.getImpl() instanceof Assignment) {
466: assignedNodes.add(newParent);
467: } else {
468: Node newChild = cloneNode(child, newParent, implMap,
469: predecessors, successors);
470: cloneBranch(child, newChild, assignedNodes, implMap,
471: predecessors, successors);
472: }
473: }
474: }
475:
476: private Node cloneNode(Node oldNode, Node newParent, Map implMap,
477: Set<Dependency> predecessors, Set<Dependency> successors) {
478: Object oldNodeImpl = oldNode.getImpl();
479: Object newNodeImpl = cloneNodeImpl(oldNodeImpl);
480: implMap.put(oldNodeImpl, newNodeImpl);
481: if (oldNodeImpl instanceof Task) {
482: Task t = (Task) oldNodeImpl;
483: predecessors.addAll(t.getDependencyList(true));
484: successors.addAll(t.getDependencyList(false));
485: }
486: Object parentImpl = (newParent == null) ? null : newParent
487: .getImpl();
488: NodeModelDataFactory factory = getFactory(parentImpl);
489:
490: factory.addUnvalidatedObject(newNodeImpl, this , parentImpl);
491:
492: Node newNode = NodeFactory.getInstance()
493: .createNode(newNodeImpl);
494: if (newParent != null)
495: newParent.add(newNode);
496: if (parentImpl != null && parentImpl instanceof Task)
497: ((Task) parentImpl).setWbsChildrenNodes(getHierarchy()
498: .getChildren(newParent)); //rebuild children task's wbs cache
499: return newNode;
500: }
501:
502: private Object cloneNodeImpl(Object impl) {
503: if (impl instanceof VoidNodeImpl) {
504: return new VoidNodeImpl();
505: } else if (impl instanceof NormalTask) {
506: return ((NormalTask) impl).clone();
507: } else if (impl instanceof ResourceImpl) {
508: return ((ResourceImpl) impl).clone();
509: }//TOTO assignments
510: return null;
511: }
512:
513: private void cleanBranch(Node parent) {
514: for (Iterator i = parent.childrenIterator(); i.hasNext();) {
515: Node child = (Node) i.next();
516: cleanNodeImpl(child.getImpl());
517: cleanBranch(child);
518: }
519: }
520:
521: private void cleanNodeImpl(Object impl) {
522: if (impl instanceof NormalTask) {
523: ((NormalTask) impl).cleanClone();
524: } else if (impl instanceof ResourceImpl) {
525: ((ResourceImpl) impl).cleanClone();
526: }
527: }
528:
529: private void addAssignments(Node node) {
530: if (node.getImpl() instanceof HasAssignments) {
531: AssociationList assignments = ((HasAssignments) node
532: .getImpl()).getAssignments();
533: if (assignments == null)
534: return;
535: for (ListIterator i = assignments.listIterator(assignments
536: .size()); i.hasPrevious();) {
537: Assignment assignment = (Assignment) i.previous();
538: if (assignment.isDefault())
539: continue;
540: Node assignmentNode = NodeFactory.getInstance()
541: .createNode(assignment);
542: node.insert(assignmentNode, 0);
543: }
544: }
545: }
546:
547: public Object clone() {
548: return new DefaultNodeModel((NodeHierarchy) hierarchy.clone(),
549: dataFactory);
550: }
551:
552: public Iterator iterator() {
553: return hierarchy.iterator();
554: }
555:
556: public Iterator iterator(Node rootNode) {
557: return hierarchy.iterator(rootNode);
558: }
559:
560: public Iterator shallowIterator(int maxLevel, boolean returnRoot) {
561: return hierarchy.shallowIterator(maxLevel, returnRoot);
562: }
563:
564: /**
565: * @return Returns the hierarchy.
566: */
567: public NodeHierarchy getHierarchy() {
568: return hierarchy;
569: }
570:
571: /**
572: * @param hierarchy The hierarchy to set.
573: */
574: public void setHierarchy(NodeHierarchy hierarchy) {
575: this .hierarchy = hierarchy;
576: }
577:
578: /* (non-Javadoc)
579: * @see com.projity.grouping.core.NodeModel#hasChildren(com.projity.grouping.core.Node)
580: */
581: public boolean hasChildren(Node node) {
582: return !hierarchy.isLeaf(node);
583: }
584:
585: public boolean isSummary(Node node) {
586: return hierarchy.isSummary(node);
587: }
588:
589: /**
590: * @param key
591: * @param c
592: * @return
593: */
594: public Node search(Object key, Comparator c) {
595: return hierarchy.search(key, c);
596: }
597:
598: private static ImplComparator implComparatorInstance = null;
599:
600: public static ImplComparator getImplComparatorInstance() {
601: if (implComparatorInstance == null)
602: implComparatorInstance = new ImplComparator();
603: return implComparatorInstance;
604: }
605:
606: public static class ImplComparator implements Comparator {
607: ImplComparator() {
608: }
609:
610: public int compare(Object node, Object impl) {
611: if (((Node) node).getImpl() == impl)
612: return 0;
613: else
614: return 1;
615: }
616: }
617:
618: public Node search(Object key) {
619: //TODO consider using a hashtable instead of searching like this
620: return search(key, getImplComparatorInstance());
621: }
622:
623: // Below is for tree model
624: /**
625: * @param arg0
626: */
627: public void addTreeModelListener(TreeModelListener arg0) {
628: hierarchy.addTreeModelListener(arg0);
629: }
630:
631: /**
632: * @param arg0
633: * @param arg1
634: * @return
635: */
636: public Object getChild(Object arg0, int arg1) {
637: return hierarchy.getChild(arg0, arg1);
638: }
639:
640: /**
641: * @param arg0
642: * @return
643: */
644: public int getChildCount(Object arg0) {
645: return hierarchy.getChildCount(arg0);
646: }
647:
648: /**
649: * @param arg0
650: * @param arg1
651: * @return
652: */
653: public int getIndexOfChild(Object arg0, Object arg1) {
654: return hierarchy.getIndexOfChild(arg0, arg1);
655: }
656:
657: /**
658: * @return
659: */
660: public Object getRoot() {
661: return hierarchy.getRoot();
662: }
663:
664: /**
665: * @param arg0
666: * @return
667: */
668: public boolean isLeaf(Object arg0) {
669: return hierarchy.isLeaf(arg0);
670: }
671:
672: /**
673: * @param arg0
674: */
675: public void removeTreeModelListener(TreeModelListener arg0) {
676: hierarchy.removeTreeModelListener(arg0);
677: }
678:
679: /**
680: * @param arg0
681: * @param arg1
682: */
683: public void valueForPathChanged(TreePath arg0, Object arg1) {
684: hierarchy.valueForPathChanged(arg0, arg1);
685: }
686:
687: /* (non-Javadoc)
688: * @see com.projity.grouping.core.NodeModel#setFieldValue(com.projity.field.Field, com.projity.grouping.core.Node, java.lang.Object, java.lang.Object, com.projity.field.FieldContext)
689: */
690: public void setFieldValue(Field field, Node node,
691: Object eventSource, Object value, FieldContext context,
692: int actionType) throws FieldParseException {
693: Object oldValue = field.getValue(node, this , context);
694:
695: // // this prevents the field from sending an update message. However, ideally the field will send the message and the hiearchy event wont
696: // if (context != null)
697: // context.setUserObject(FieldContext.getNoUpdateInstance());
698:
699: field.setValue(node, this , eventSource, value, context);
700:
701: // No longer sending update event
702: // if (isEvent(actionType)) hierarchy.fireUpdate(new Node[]{node});
703: //TODO treat the ObjectEvent instead
704:
705: //Undo
706: if (isUndo(actionType))
707: postEdit(new ModelFieldEdit(this , field, node, eventSource,
708: value, oldValue, context));
709:
710: }
711:
712: public Node replaceImplAndSetFieldValue(Node node,
713: LinkedList previous, Field field, Object eventSource,
714: Object value, FieldContext context, int actionType)
715: throws FieldParseException {
716: //the line following a subproject is connected to the main project
717: if (previous != null && previous.size() > 0) {
718: Node p = (Node) previous.getFirst();
719: if (p != null
720: && p.isInSubproject()
721: && node.getSubprojectLevel() < p
722: .getSubprojectLevel()) {
723: while (node.getSubprojectLevel() < p
724: .getSubprojectLevel())
725: p = (Node) p.getParent();
726: LinkedList newPrevious = new LinkedList();
727: newPrevious.add(p);
728: Node vn, pvn;
729: for (Iterator i = previous.iterator(); i.hasNext();) {
730: vn = (Node) i.next();
731: pvn = (Node) vn.getParent();
732: while (pvn != null && pvn != p)
733: pvn = (Node) pvn.getParent();
734: if (pvn != p)
735: newPrevious.add(vn);
736: }
737: Object parentImpl = p.getImpl();
738: NodeModelDataFactory factory = getFactory(parentImpl);
739: return replaceImplAndSetFieldValue(node, newPrevious,
740: factory.createUnvalidatedObject(this ,
741: parentImpl), field, eventSource, value,
742: context, actionType);
743:
744: }
745: // if (p!=null&&p.getImpl() instanceof NormalTask){
746: // Task task=(Task)p.getImpl();
747: // boolean subprojectParent=false;
748: // while (task.getOwningProject()!=task.getProject()){
749: // Node pParent=(Node)p.getParent();
750: // if (pParent.getIndex(p)==pParent.getChildCount()-1){
751: // p=pParent;
752: // subprojectParent=true;
753: // }else{
754: // subprojectParent=false;
755: // break;
756: // }
757: // }
758: // if (subprojectParent){
759: // LinkedList newPrevious=(LinkedList)previous.clone();
760: // newPrevious.set(0, p);
761: // Object parentImpl = p.getImpl();
762: // NodeModelDataFactory factory = getFactory(parentImpl);
763: // return replaceImplAndSetFieldValue(node,newPrevious,factory.createUnvalidatedObject(this, parentImpl),field,eventSource,value,context,actionType);
764: //
765: // }
766: // }
767:
768: }
769:
770: Node parent = (Node) node.getParent();
771: Object parentImpl = (parent == getHierarchy().getRoot()) ? null
772: : parent.getImpl();
773: NodeModelDataFactory factory = getFactory(parentImpl);
774: return replaceImplAndSetFieldValue(node, previous, factory
775: .createUnvalidatedObject(this , parentImpl), field,
776: eventSource, value, context, actionType);
777: }
778:
779: private NodeModelDataFactory getFactory(Object parentImpl) {
780: if (parentImpl == null)
781: return dataFactory;
782: else
783: return dataFactory
784: .getFactoryToUseForChildOfParent(parentImpl);
785: }
786:
787: public Node replaceImplAndSetFieldValue(Node node,
788: LinkedList previous, Object newImpl, Field field,
789: Object eventSource, Object value, FieldContext context,
790: int actionType) throws FieldParseException {
791: List previousPosition = null;
792: //move in hierarchy
793: if (previous != null) {
794: LinkedList p = (LinkedList) previous.clone();
795: Node sibling = (Node) p.removeFirst();
796: Node parent = (Node) sibling.getParent();
797: p.add(node);
798: if (getUndoableEditSupport() != null & isUndo(actionType)) {
799: previousPosition = new ArrayList(p.size());
800: for (Iterator i = p.iterator(); i.hasNext();) {
801: Node n = (Node) i.next();
802: previousPosition
803: .add(new NodeImplChangeAndValueSetEdit.Position(
804: (Node) n.getParent(), n, n
805: .getParent().getIndex(n)));
806: }
807: }
808: remove(p, NodeModel.SILENT);
809: add(parent, p, parent.getIndex(sibling) + 1,
810: NodeModel.SILENT);
811: //TODO need undo here
812: }
813:
814: Node parent = (Node) node.getParent();
815:
816: Object parentImpl = (parent == getHierarchy().getRoot()) ? null
817: : parent.getImpl();
818: NodeModelDataFactory factory = getFactory(parentImpl);
819: factory.addUnvalidatedObject(newImpl, this , parentImpl);
820: Object oldImpl = node.getImpl();
821: node.setImpl(newImpl);
822: try {
823: field.setValue(node, this , null, value, context); // will throw if error
824: } catch (FieldParseException e) {
825: factory.rollbackUnvalidated(this , newImpl); // in some cases, such as ValueObjectForInterval, some cleanup is needed
826: throw e;
827: }
828: // if no exception was thrown, then validate the object and hook it into model
829: factory.validateObject(newImpl, this , eventSource, null, true);
830:
831: hierarchy.renumber();
832:
833: // dataFactory.fireCreated(newImpl);
834: hierarchy.checkEndVoidNodes(actionType ^ NodeModel.EVENT);
835: getHierarchy().fireInsertion(new Node[] { node }); //TODO Cause critical path to run twice
836:
837: //Undo
838: if (isUndo(actionType))
839: postEdit(new NodeImplChangeAndValueSetEdit(this , node,
840: previous, previousPosition, oldImpl, field, value,
841: context, eventSource));
842: return node;
843: }
844:
845: public Node replaceImpl(Node node, Object newImpl,
846: Object eventSource, int actionType) {
847: Node parent = getParent(node);
848: Object parentImpl = (parent == getHierarchy().getRoot()) ? null
849: : parent.getImpl();
850: NodeModelDataFactory factory = getFactory(parentImpl);
851:
852: factory.addUnvalidatedObject(newImpl, this , parentImpl);
853: Object oldImpl = node.getImpl();
854: node.setImpl(newImpl);
855: factory.validateObject(newImpl, this , eventSource, null, false);
856:
857: hierarchy.renumber();
858:
859: // dataFactory.fireCreated(newImpl);
860: getHierarchy().fireRemoval(new Node[] { node }); //TODO Cause critical path to run twice
861:
862: hierarchy.checkEndVoidNodes(actionType);
863: //Undo
864: if (isUndo(actionType))
865: postEdit(new NodeImplChangeEdit(this , node, oldImpl,
866: eventSource));
867: return node;
868: }
869:
870: public NodeModelDataFactory getDataFactory() {
871: return dataFactory;
872: }
873:
874: /**
875: * @param dataFactory The dataFactory to set.
876: */
877: public void setDataFactory(NodeModelDataFactory dataFactory) {
878: this .dataFactory = dataFactory;
879: }
880:
881: public List getChildren(Node parent) {
882: return getHierarchy().getChildren(parent);
883: }
884:
885: public Node getParent(Node child) {
886: return getHierarchy().getParent(child);
887: }
888:
889: public Document getDocument() {
890: return null;
891: }
892:
893: public static boolean isEvent(int actionType) {
894: return (actionType & NodeModel.EVENT) == NodeModel.EVENT;
895: }
896:
897: public static boolean isUndo(int actionType) {
898: return (actionType & NodeModel.UNDO) == NodeModel.UNDO;
899: }
900:
901: protected UndoController undoController;
902:
903: public UndoController getUndoController() {
904: return undoController;
905: }
906:
907: public void setUndoController(UndoController undoController) {
908: this .undoController = undoController;
909: }
910:
911: public UndoableEditSupport getUndoableEditSupport() {
912: if (undoController == null)
913: return null;
914: return undoController.getEditSupport();
915: }
916:
917: // public void setUndoableEditSupport(UndoableEditSupport undoableEditSupport) {
918: // this.undoableEditSupport = undoableEditSupport;
919: // }
920:
921: public void postEdit(UndoableEdit edit) {
922: if (getUndoableEditSupport() != null) {
923: getUndoableEditSupport().postEdit(edit);
924: }
925:
926: }
927:
928: public boolean confirmRemove(List nodes) {
929: return true;
930: }
931:
932: protected boolean local, master = true;
933:
934: public boolean isLocal() {
935: return local;
936: }
937:
938: public void setLocal(boolean local) {
939: this .local = local;
940: }
941:
942: public boolean isMaster() {
943: return master;
944: }
945:
946: public void setMaster(boolean master) {
947: this .master = master;
948: }
949:
950: // protected int updateLevel=0;
951: // protected synchronized void beginUpdate(){
952: // updateLevel++;
953: // }
954: // protected synchronized void endUpdate(){
955: // updateLevel--;
956: // }
957: // protected synchronized int getUpdateLevel(){
958: // return updateLevel;
959: // }
960:
961: }
|