/*****************************************************************************
* Copyright (c) 2010 CEA LIST.
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.common.layout;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.CompartmentEditPart;
/**
* This class is used to represent the hierarchy between selected editparts
*
*
*/
public abstract class EditPartTree extends DefaultMutableTreeNode {
/**
* this UDI can be used to serialize this class
*/
private static final long serialVersionUID = 1557787740787257844L;
/**
* Used to precise if this tree is the first selected element in the branch
*/
private boolean isFirstSelected = false;
/**
* Used to precise if this tree is the reference of the alignment
*/
private boolean isReference = false;
/**
* Indicates if the represented editpart is selected or not
*/
private boolean isSelected = false;
/**
* the request for the editpart
*/
private Request request;
/**
*
* Constructor.
*
* @param editpart
* the editpart represented by this Tree
* @param isSelected
* indicates if the represented editpart is selected or not
*
*/
public EditPartTree(Object editpart, boolean isSelected) {
super(editpart, true);
this.isSelected = isSelected;
}
/**
*
* Constructor.
*
* @param editparts
* the editparts used to build the tree
*/
public EditPartTree(List<EditPart> editparts) {
this(null, false);
buildTree(editparts);
}
/**
* Gets the alignment request for the editpart.
*
* @return the alignment request for the editpart
*/
public Request getRequest() {
return request;
}
/**
* Sets the alignment request for the editpart.
*
* @param request
* the new alignment request for the editpart
*/
public void setRequest(Request request) {
this.request = request;
}
/**
* Sets the used to precise if this tree is the reference of the alignment.
*
* @param isReference
* the new used to precise if this tree is the reference of the
* alignment
*/
public void setIsReference(boolean isReference) {
this.isReference = true;
}
/**
* Gets {@link #isReference()}
*
* @return the {@link #isReference()} value
*/
public boolean isReference() {
return isReference;
}
/**
* Sets the {@link #isSelected} value
*
* @param isSelected
* the new value for {@link #isSelected}
*/
public void setIsSelected(boolean isSelected) {
this.isSelected = isSelected;
}
/**
* Gets {@link #isSelected}
*
* @return the {@link #isSelected} value
*/
public boolean isSelected() {
return isSelected;
}
/**
* Gets the editpart represented by this tree
*
* @return the editpart represented by this tree
*/
public EditPart getEditPart() {
return (EditPart)getUserObject();
}
/**
* @see javax.swing.tree.DefaultMutableTreeNode#toString()
*
* @return the string representing the tree with its children
*/
@Override
public String toString() {
getLevel();
String str = ""; //$NON-NLS-1$
for(int i = 0; i < getLevel(); i++) {
str += " "; //$NON-NLS-1$
}
str += "isSelected =" + isSelected() + " depth =" + getDepth() + " Node =" + userObject + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
if(children != null) {
for(int i = 0; i < children.size(); i++) {
str += children.get(i).toString();
}
} else {
for(int i = 0; i < getLevel() + 2; i++) {
str += " "; //$NON-NLS-1$
}
str += "No child\n"; //$NON-NLS-1$
}
return str;
}
/**
* Gets the subtree containing the editpart.
*
* @param editpart
* the editpart that we look for
* @return the tree, or {@code null} if not found
*/
public EditPartTree getTree(EditPart editpart) {
if(this.getEditPart() == editpart) {
return this;
}
if(children != null) {
for(int i = 0; i < children.size(); i++) {
EditPart ep = ((EditPartTree)children.get(i)).getEditPart();
if(ep == editpart) {
return ((EditPartTree)children.get(i));
} else {
EditPartTree ept = ((EditPartTree)children.get(i)).getTree(editpart);
if(ept != null) {
return ept;
}
}
}
}
return null;
}
/**
* Contains.
*
* @param editpart
* the editpart that we look for
* @return <ul>
* <li> {@code true}</li> if the tree contains the editpart
* <li> {@code false}</li> if not
* </ul>
*/
public boolean contains(EditPart editpart) {
return (getTree(editpart) == null) ? false : true;
}
/**
* Gets the child level.
*
* @param level
* the level
* @return the child level
*/
public List<EditPartTree> getChildLevel(int level) {
List<EditPartTree> epList = new ArrayList<EditPartTree>();
if(level > 0) {
if((level == 1 && this.children != null) && this.children.size() != 0) {
epList.addAll(this.children);
} else if(children != null) {
for(int iter = 0; iter < this.children.size(); iter++) {
epList.addAll(((EditPartTree)this.children.get(iter)).getChildLevel(level - 1));
}
}
}
return epList;
}
/**
* Sets the first selected element.
*
* @param value
* the new first selected element
*/
public void setFirstSelectedElement(boolean value) {
this.isFirstSelected = value;
}
/**
* Checks if is first selected element in branch.
*
* @return true, if is first selected element in branch
*/
public boolean isFirstSelected() {
return this.isFirstSelected;
}
/**
* Gets the level for the first selected element in the full tree. This
* level is calculated from the root of this node.
*
* @param index
* the index
* @return <ul>
* <li>the level for first selected element</li>
* <li> {@code -1}</li> if the first selected element can't be found
* </ul>
*/
public int getLevelForFirstSelectedElement() {
// We search the first level in the tree
TreeNode[] path = this.getPath();
if(path.length > 1) {
EditPartTree result = getSelectedFirstEditPart((EditPartTree)path[1]);
if(result != null) {
return result.getLevel();
}
}
return -1;
}
/**
* Gets the selected first edit part.
*
* @param treeNode
* the tree node (level one of a tree) where we search the first
* selected element
* @return the selected first edit part or {@code null} if not found
*/
protected EditPartTree getSelectedFirstEditPart(EditPartTree treeNode) {
if(treeNode.isFirstSelected) {
return treeNode;
} else if((treeNode.children != null) && (!treeNode.children.isEmpty())) {
for(int iter = 0; iter < treeNode.getChildCount(); iter++) {
EditPartTree result = getSelectedFirstEditPart((EditPartTree)treeNode.children.get(iter));
if(result != null) {
return result;
}
}
}
return null;
}
/**
* Test if this node have child that are not selected
*
* @return <ul>
* <li>{@code true}</li> it exists a child which is not selected
* <li>{@code false}</li> all the children are selected
* </ul>
*/
public boolean existsUnselectedChild() {
return (getDistanceWithTheFirstUnselectedChild() == -1) ? false : true;
}
/**
* Returns the distance between the tree and the first unselected child
*
* @return <ul>
* <li>the distance between this tree and the first unselected child</li>
* <li> {@code -1} when all the children are selected</li>
* </ul>
*/
public int getDistanceWithTheFirstUnselectedChild() {
int depth = this.getDepth();
for(int i = 0; i < depth; i++) {
List<EditPartTree> localChildren = getChildLevel(i);
for(EditPartTree tree : localChildren) {
if(!tree.isSelected()) {
return i;
}
}
}
return -1;
}
/**
* Returns the first unselected child or {@code null} if all the children
* are selected
*
* @return the first unselected child or {@code null} if all the children
* are selected
*/
public EditPartTree getFirstUnselectedChild() {
int depth = this.getDepth();
for(int i = 0; i < depth; i++) {
List<EditPartTree> localChildren = getChildLevel(i);
for(EditPartTree tree : localChildren) {
if(!tree.isSelected()) {
return tree;
}
}
}
return null;
}
/**
* Sorts the editparts in the tree In this tree, we have the selected
* editparts. Moreover, we add the intermediate packages, even if they
* aren't selected. These intermediate packages are used to determine the
* final position of their parents, if the reference is inside on of these
* packages
*
* @param editparts
* the editparts to sort
*/
public void buildTree(List<EditPart> editparts) {
List<EditPart> parentsList;
if(editparts.size() >= 2) {
// we build the tree
for(EditPart currentEP : editparts) {
parentsList = new ArrayList<EditPart>();
EditPart parent = currentEP;
EditPartTree grandFatherTree = this;
int i = 0;
while(parent != null) {
if(this.contains(parent)) {
grandFatherTree = this.getTree(parent);
break; // on sort du while
} else {
// we add all the parent in this list!
/*
* we don't add the parent if it's a comparment , except
* if the parent is the selected element (property,
* enumeration literal...), of course, a property or a
* enumeration literal can't move in there compartment
* (in class diagram) but this choice allows avoid some
* NullPointerException. (see bug 317691)
*/
if(!(parent instanceof CompartmentEditPart) || (parent instanceof CompartmentEditPart && parent == currentEP)) {
if(!(parent instanceof RootEditPart)) {
if(!(parent.getParent() instanceof RootEditPart)) {
parentsList.add(i, parent);
i++;
}
}
}
}
parent = parent.getParent();
}
// We add all the node in the rootTree
EditPartTree childTree = createChildrenTree(editparts, parentsList);
// we add the node to the tree
if(childTree != null) {
grandFatherTree.add(childTree);
}
}
// we precise which element is the reference (the last selected
// element)
this.getTree(editparts.get(editparts.size() - 1)).setIsReference(true);
/*
* we precise for each branch the first selected element it's this
* element (and its brothers) which are really align on the
* reference
*/
Enumeration childrenEnum = this.children();
while(childrenEnum.hasMoreElements()) {
EditPartTree currentTree = (EditPartTree)childrenEnum.nextElement();
for(int i = 0; i < editparts.size(); i++) {
if(currentTree.contains(editparts.get(i))) {
currentTree.getTree(editparts.get(i)).setFirstSelectedElement(true);
break;
}
}
}
postBuildOperations(editparts);
}
}
/**
* This method is used to create an EditPartTree with {@code the List<EditPart> children}
*
* @param editparts
* the selected editparts for this action
* @param children
* intermediate children which could be interesting to add like
* node in the tree
* @return a new EditPartTree
*/
protected abstract EditPartTree createChildrenTree(List<EditPart> editparts, List<EditPart> children);
/**
* Action that can be done to conclude the tree construction
*
* @param editparts
* an editparts list
*/
protected abstract void postBuildOperations(List<EditPart> editparts);
}