Package org.zkoss.ganttz

Source Code of org.zkoss.ganttz.LeftTasksTree$DetailsForBeans

/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
*                         Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.zkoss.ganttz;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zkoss.ganttz.LeftTasksTreeRow.ILeftTasksTreeNavigator;
import org.zkoss.ganttz.adapters.IDisabilityConfiguration;
import org.zkoss.ganttz.data.Position;
import org.zkoss.ganttz.data.Task;
import org.zkoss.ganttz.data.TaskContainer;
import org.zkoss.ganttz.data.TaskContainer.IExpandListener;
import org.zkoss.ganttz.util.ComponentsFinder;
import org.zkoss.ganttz.util.MutableTreeModel;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.HtmlMacroComponent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zul.Tree;
import org.zkoss.zul.TreeModel;
import org.zkoss.zul.Treecell;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.TreeitemRenderer;

/**
* Tree element to display tasks structure in the planning Gantt <br />
*
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Manuel Rego Casasnovas <mrego@igalia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
public class LeftTasksTree extends HtmlMacroComponent {

    private final class TaskBeanRenderer implements TreeitemRenderer {
        private Map<TaskContainer, IExpandListener> expandListeners = new HashMap<TaskContainer, IExpandListener>();

        public void render(final Treeitem item, Object data) throws Exception {
            Task task = (Task) data;
            item.setOpen(isOpened(task));
            if (task instanceof TaskContainer) {
                final TaskContainer container = (TaskContainer) task;
                IExpandListener expandListener = new IExpandListener() {

                    @Override
                    public void expandStateChanged(boolean isNowExpanded) {
                        item.setOpen(isNowExpanded);
                    }
                };
                expandListeners.put(container, expandListener);
                container.addExpandListener(expandListener);

            }
            LeftTasksTreeRow leftTasksTreeRow = LeftTasksTreeRow.create(
                    disabilityConfiguration, task, new TreeNavigator(
                            tasksTreeModel, task), planner);
            if (task.isContainer()) {
                expandWhenOpened((TaskContainer) task, item);
            }
            Component row;
            if (disabilityConfiguration.isTreeEditable()) {
                row = Executions.getCurrent().createComponents(
                        "~./ganttz/zul/leftTasksTreeRow.zul", item, null);
            } else {
                row = Executions.getCurrent().createComponents(
                        "~./ganttz/zul/leftTasksTreeRowLabels.zul", item, null);
            }
            leftTasksTreeRow.doAfterCompose(row);
            List<Object> rowChildren = row.getChildren();
            List<Treecell> treeCells = ComponentsFinder.findComponentsOfType(
                    Treecell.class, rowChildren);
            detailsForBeans.put(task, leftTasksTreeRow);
            deferredFiller.isBeingRendered(task, item);
        }

        private void expandWhenOpened(final TaskContainer taskBean,
                Treeitem item) {
            item.addEventListener("onOpen", new EventListener() {
                @Override
                public void onEvent(Event event) {
                    OpenEvent openEvent = (OpenEvent) event;
                    taskBean.setExpanded(openEvent.isOpen());
                }
            });
        }

    }

    public boolean isOpened(Task task) {
        return task.isLeaf() || task.isExpanded();
    }

    private static final class DetailsForBeans {

        private Map<Task, LeftTasksTreeRow> map = new HashMap<Task, LeftTasksTreeRow>();

        private Set<Task> focusRequested = new HashSet<Task>();

        public void put(Task task, LeftTasksTreeRow leftTasksTreeRow) {
            map.put(task, leftTasksTreeRow);
            if (focusRequested.contains(task)) {
                focusRequested.remove(task);
                leftTasksTreeRow.receiveFocus();
            }
        }

        public void requestFocusFor(Task task) {
            focusRequested.add(task);
        }

        public LeftTasksTreeRow get(Task taskbean) {
            return map.get(taskbean);
        }

    }

    private DetailsForBeans detailsForBeans = new DetailsForBeans();

    private final class TreeNavigator implements ILeftTasksTreeNavigator {
        private final int[] pathToNode;
        private final Task task;

        private TreeNavigator(TreeModel treemodel, Task task) {
            this.task = task;
            this.pathToNode = tasksTreeModel.getPath(tasksTreeModel.getRoot(),
                    task);
        }

        @Override
        public LeftTasksTreeRow getAboveRow() {
            Task parent = getParent(pathToNode);
            int lastPosition = pathToNode[pathToNode.length - 1];
            if (lastPosition != 0) {
                return getChild(parent, lastPosition - 1);
            } else if (tasksTreeModel.getRoot() != parent) {
                return getDetailFor(parent);
            }
            return null;
        }

        private LeftTasksTreeRow getChild(Task parent, int position) {
            Task child = tasksTreeModel.getChild(parent, position);
            return getDetailFor(child);
        }

        private LeftTasksTreeRow getDetailFor(Task child) {
            return detailsForBeans.get(child);
        }

        @Override
        public LeftTasksTreeRow getBelowRow() {
            if (isExpanded() && hasChildren()) {
                return getChild(task, 0);
            }
            for (ChildAndParent childAndParent : group(task, tasksTreeModel
                    .getParents(task))) {
                if (childAndParent.childIsNotLast()) {
                    return getDetailFor(childAndParent.getNextToChild());
                }
            }
            // it's the last one, it has none below
            return null;
        }

        public List<ChildAndParent> group(Task origin, List<Task> parents) {
            ArrayList<ChildAndParent> result = new ArrayList<ChildAndParent>();
            Task child = origin;
            Task parent;
            ListIterator<Task> listIterator = parents.listIterator();
            while (listIterator.hasNext()) {
                parent = listIterator.next();
                result.add(new ChildAndParent(child, parent));
                child = parent;
            }
            return result;
        }

        private class ChildAndParent {
            private final Task parent;

            private final Task child;

            private Integer positionOfChildCached;

            private ChildAndParent(Task child, Task parent) {
                this.parent = parent;
                this.child = child;
            }

            public Task getNextToChild() {
                return tasksTreeModel
                        .getChild(parent, getPositionOfChild() + 1);
            }

            public boolean childIsNotLast() {
                return getPositionOfChild() < numberOfChildrenForParent() - 1;
            }

            private int numberOfChildrenForParent() {
                return tasksTreeModel.getChildCount(parent);
            }

            private int getPositionOfChild() {
                if (positionOfChildCached != null) {
                    return positionOfChildCached;
                }
                int[] path = tasksTreeModel.getPath(parent, child);
                return positionOfChildCached = path[path.length - 1];
            }
        }

        private boolean hasChildren() {
            return task.isContainer() && task.getTasks().size() > 0;
        }

        private boolean isExpanded() {
            return task.isContainer() && task.isExpanded();
        }

        private Task getParent(int[] path) {
            Task current = tasksTreeModel.getRoot();
            for (int i = 0; i < path.length - 1; i++) {
                current = tasksTreeModel.getChild(current, path[i]);
            }
            return current;
        }

    }

    /**
     * This class is a workaround for an issue with zk {@link Tree}. Once the
     * tree is created, adding a node with children is troublesome. Only the top
     * element is added to the tree, although the element has children. The Tree
     * discards the adding event for the children because the parent says it's
     * not loaded. This is the condition that is not satisfied:<br />
     * <code>if(parent != null &&
        (!(parent instanceof Treeitem) || ((Treeitem)parent).isLoaded())){</code><br />
     * This problem is present in zk 3.6.1 at least.
     * @author Óscar González Fernández <ogonzalez@igalia.com>
     * @see Tree#onTreeDataChange
     */
    private class DeferredFiller {

        private Set<Task> pendingToAddChildren = new HashSet<Task>();

        public void addParentOfPendingToAdd(Task parent) {
            pendingToAddChildren.add(parent);
        }

        public void isBeingRendered(final Task parent, final Treeitem item) {
            if (!pendingToAddChildren.contains(parent)) {
                return;
            }
            markLoaded(item);
            fillModel(parent, 0, parent.getTasks(), false);
            pendingToAddChildren.remove(parent);
        }

        private void markLoaded(Treeitem item) {
            try {
                Method method = getSetLoadedMethod();
                method.invoke(item, true);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        private Method setLoadedMethod = null;

        private Method getSetLoadedMethod() {
            if (setLoadedMethod != null) {
                return setLoadedMethod;
            }
            try {
                Method method = Treeitem.class.getDeclaredMethod("setLoaded",
                        Boolean.TYPE);
                method.setAccessible(true);
                return setLoadedMethod = method;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static Log LOG = LogFactory.getLog(LeftTasksTree.class);

    private final DeferredFiller deferredFiller = new DeferredFiller();

    private final List<Task> tasks;

    private MutableTreeModel<Task> tasksTreeModel;

    private Tree tasksTree;

    private CommandContextualized<?> goingDownInLastArrowCommand;

    private final IDisabilityConfiguration disabilityConfiguration;

    private FilterAndParentExpandedPredicates predicate;

    private final List<Task> visibleTasks = new ArrayList<Task>();

    private Planner planner;

    public LeftTasksTree(IDisabilityConfiguration disabilityConfiguration,
            Planner planner,
            FilterAndParentExpandedPredicates predicate) {
        this.disabilityConfiguration = disabilityConfiguration;
        this.tasks = planner.getTaskList().getAllTasks();
        this.predicate = predicate;
        this.planner = planner;
    }

    private void fillModel(Collection<? extends Task> tasks, boolean firstTime) {
        fillModel(this.tasksTreeModel.getRoot(), 0, tasks, firstTime);
    }

    private void fillModel(Task parent, Integer insertionPosition,
            Collection<? extends Task> children, final boolean firstTime) {
        if (predicate.isFilterContainers()) {
            parent = this.tasksTreeModel.getRoot();
        }

        if (firstTime) {
            for (Task node : children) {
                if (predicate.accpetsFilterPredicateAndContainers(node)) {
                    if (!visibleTasks.contains(node)) {
                        this.tasksTreeModel.add(parent, node);
                        visibleTasks.add(node);
                    }
                } else {
                    if (visibleTasks.contains(node)) {
                        this.tasksTreeModel.remove(node);
                        visibleTasks.remove(node);
                    }
                }

                if (node.isContainer()) {
                    fillModel(node, 0, node.getTasks(), firstTime);
                }
            }

        } else {
            for (Task node : children) {
                if (node.isContainer()) {
                    if (predicate.accpetsFilterPredicateAndContainers(node)) {
                        if (!visibleTasks.contains(node)) {
                            this.deferredFiller.addParentOfPendingToAdd(node);
                        }
                    }
                }
            }
            // the node must be added after, so the multistepTreeFiller is
            // ready
            for (Task node : children) {
                if (predicate.accpetsFilterPredicateAndContainers(node)) {
                    if (!visibleTasks.contains(node)) {
                        this.tasksTreeModel.add(parent, insertionPosition,
                                Arrays.asList(node));
                        visibleTasks.add(node);
                    }
                } else {
                    if (visibleTasks.contains(node)) {
                        this.tasksTreeModel.remove(node);
                        removeTaskAndAllChildren(visibleTasks, node);
                    }
                }

                if (node.isContainer()) {
                    fillModel(node, 0, node.getTasks(), firstTime);
                }

                if (visibleTasks.contains(node)) {
                    insertionPosition++;
                }
            }
        }
    }

    private void removeTaskAndAllChildren(List<Task> visibleTasks, Task task) {
        visibleTasks.remove(task);

        if (task.isContainer()) {
            for (Task node : task.getTasks()) {
                removeTaskAndAllChildren(visibleTasks, node);
            }
        }
    }

    public void taskRemoved(Task taskRemoved) {
        tasksTreeModel.remove(taskRemoved);
    }

    @Override
    public void afterCompose() {
        setClass("listdetails");
        super.afterCompose();
        tasksTree = (Tree) getFellow("tasksTree");
        tasksTreeModel = MutableTreeModel.create(Task.class);
        fillModel(tasks, true);
        tasksTree.setModel(tasksTreeModel);
        tasksTree.setTreeitemRenderer(new TaskBeanRenderer());
    }

    void addTask(Position position, Task task) {
        if (position.isAppendToTop()) {
            fillModel(Arrays.asList(task), false);
            detailsForBeans.requestFocusFor(task);
        } else {
            List<Task> toAdd = Arrays.asList(task);
            fillModel(position.getParent(), position.getInsertionPosition(),
                    toAdd, false);
        }
    }

    public void addTasks(Position position, Collection<? extends Task> newTasks) {
        Task root = tasksTreeModel.getRoot();
        if (position.isAppendToTop()) {
            fillModel(root, tasksTreeModel.getChildCount(root), newTasks, false);
        } else if (position.isAtTop()) {
            fillModel(root,
                    position.getInsertionPosition(), newTasks, false);
        } else {
            fillModel(position.getParent(), position.getInsertionPosition(),
                    newTasks, false);
        }
    }

    public CommandContextualized<?> getGoingDownInLastArrowCommand() {
        return goingDownInLastArrowCommand;
    }

    public void setGoingDownInLastArrowCommand(
            CommandContextualized<?> goingDownInLastArrowCommand) {
        this.goingDownInLastArrowCommand = goingDownInLastArrowCommand;
    }

    public void setPredicate(FilterAndParentExpandedPredicates predicate) {
        this.predicate = predicate;

        visibleTasks.clear();
        tasksTreeModel = MutableTreeModel.create(Task.class);
        fillModel(tasks, true);
        tasksTree.setModel(tasksTreeModel);
    }

}
TOP

Related Classes of org.zkoss.ganttz.LeftTasksTree$DetailsForBeans

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.