<commit_before>/*
 * 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.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.joda.time.LocalDate;
import org.zkoss.ganttz.adapters.DomainDependency;
import org.zkoss.ganttz.adapters.IAdapterToTaskFundamentalProperties;
import org.zkoss.ganttz.adapters.IDomainAndBeansMapper;
import org.zkoss.ganttz.adapters.IStructureNavigator;
import org.zkoss.ganttz.adapters.PlannerConfiguration;
import org.zkoss.ganttz.data.Dependency;
import org.zkoss.ganttz.data.DependencyType;
import org.zkoss.ganttz.data.GanttDiagramGraph;
import org.zkoss.ganttz.data.GanttDiagramGraph.GanttZKDiagramGraph;
import org.zkoss.ganttz.data.ITaskFundamentalProperties;
import org.zkoss.ganttz.data.Milestone;
import org.zkoss.ganttz.data.Position;
import org.zkoss.ganttz.data.Task;
import org.zkoss.ganttz.data.TaskContainer;
import org.zkoss.ganttz.data.TaskLeaf;
import org.zkoss.ganttz.data.criticalpath.CriticalPathCalculator;
import org.zkoss.ganttz.extensions.IContext;
import org.zkoss.ganttz.timetracker.TimeTracker;
import org.zkoss.ganttz.timetracker.zoom.IDetailItemModificator;
import org.zkoss.ganttz.timetracker.zoom.TimeTrackerState;
import org.zkoss.ganttz.util.Interval;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Button;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Window;

public class FunctionalityExposedForExtensions<T> implements IContext<T> {

    private static class OneToOneMapper<T> implements IDomainAndBeansMapper<T> {
        private Map<T, Task> fromDomainToTask = new HashMap<T, Task>();

        private Map<Task, T> fromTaskToDomain = new HashMap<Task, T>();

        private Map<Task, TaskContainer> fromTaskToParent = new HashMap<Task, TaskContainer>();

        private List<Task> topLevel = new ArrayList<Task>();

        @Override
        public Task findAssociatedBean(T domainObject)
                throws IllegalArgumentException {
            if (domainObject == null) {
                throw new IllegalArgumentException("domainObject is null");
            }
            if (!fromDomainToTask.containsKey(domainObject)) {
                throw new IllegalArgumentException("not found " + domainObject);
            }
            return fromDomainToTask.get(domainObject);
        }

        public Map<T, Task> getMapDomainToTask() {
            return fromDomainToTask;
        }

        /**
         * Registers the task at the provided position.
         */
        void register(Position position, Task task, T domainObject) {
            fromDomainToTask.put(domainObject, task);
            fromTaskToDomain.put(task, domainObject);

            if (position.isAppendToTop()) {
                topLevel.add(task);
            } else if (position.isAtTop()) {
                topLevel.add(position.getInsertionPosition(), task);
            } else {
                fromTaskToParent.put(task, position.getParent());
            }
        }

        void remove(T domainObject) {
            Task toBeRemoved = findAssociatedBean(domainObject);
            fromDomainToTask.remove(domainObject);
            fromTaskToDomain.remove(toBeRemoved);
            TaskContainer parent = fromTaskToParent.get(toBeRemoved);
            if (parent != null) {
                parent.remove(toBeRemoved);
            }
            fromTaskToParent.remove(toBeRemoved);
            topLevel.remove(toBeRemoved);
        }

        @Override
        public T findAssociatedDomainObject(Task task)
                throws IllegalArgumentException {
            if (task == null) {
                throw new IllegalArgumentException("taskBean is null");
            }
            if (!fromTaskToDomain.containsKey(task)) {
                throw new IllegalArgumentException();
            }
            return fromTaskToDomain.get(task);
        }

        @Override
        public Position findPositionFor(Task task) {
            List<TaskContainer> ancestors = ancestorsOf(task);
            if (ancestors.isEmpty()) {
                return Position.createAtTopPosition(topLevel.indexOf(task));
            }
            TaskContainer parent = ancestors.get(0);
            return Position.createPosition(ancestors, parent.getTasks()
                    .indexOf(task));
        }

        @Override
        public Position findPositionFor(T domainObject) {
            return findPositionFor(findAssociatedBean(domainObject));
        }

        private List<TaskContainer> ancestorsOf(Task task) {
            ArrayList<TaskContainer> result = new ArrayList<TaskContainer>();
            TaskContainer taskContainer = fromTaskToParent.get(task);
            while (taskContainer != null) {
                result.add(taskContainer);
                taskContainer = fromTaskToParent.get(taskContainer);
            }
            return result;
        }

        @Override
        public List<? extends TaskContainer> getParents(Task task) {
            Position position = findPositionFor(task);
            return position.getAncestors();
        }

    }

    private final Planner planner;
    private final IAdapterToTaskFundamentalProperties<T> adapter;
    private final IStructureNavigator<T> navigator;
    private final OneToOneMapper<T> mapper = new OneToOneMapper<T>();
    private final GanttZKDiagramGraph diagramGraph;
    private TimeTracker timeTracker;
    private final PlannerConfiguration<T> configuration;

    public FunctionalityExposedForExtensions(Planner planner,
            PlannerConfiguration<T> configuration,
            GanttZKDiagramGraph diagramGraph) {
        this.planner = planner;
        this.configuration = configuration;
        this.adapter = configuration.getAdapter();
        this.navigator = configuration.getNavigator();
        this.diagramGraph = diagramGraph;

        final IDetailItemModificator firstLevelModificators = configuration
                .getFirstLevelModificators();
        final IDetailItemModificator secondLevelModificators = configuration
                .getSecondLevelModificators();

        Calendar calendarRightNow = Calendar.getInstance();
        LocalDate localDateRightNow = LocalDate.fromCalendarFields(calendarRightNow);
        LocalDate initDate = localDateRightNow.minusYears(1);
        LocalDate endDate = localDateRightNow.plusYears(5);

        this.timeTracker = new TimeTracker(new Interval(
                TimeTrackerState.year(initDate.getYear()),
                TimeTrackerState.year(endDate.getYear())),
                planner.getZoomLevel(), firstLevelModificators,
                secondLevelModificators, planner);
    }

    /**
     * @param insertionPosition
     *            the position in which to register the task at top level. It
     *            can be <code>null</code>
     * @param accumulatedDependencies
     * @param data
     * @param parent
     * @return
     */
    private Task buildAndRegister(Position position,
            List<DomainDependency<T>> accumulatedDependencies, T data) {
        accumulatedDependencies.addAll(adapter.getOutcomingDependencies(data));
        accumulatedDependencies.addAll(adapter.getIncomingDependencies(data));

        final Task result = build(data);

        if (!navigator.isLeaf(data)) {
            TaskContainer container = (TaskContainer) result;
            int i = 0;
            for (T child : navigator.getChildren(data)) {
                container.add(buildAndRegister(position.down(container, i),
                        accumulatedDependencies, child));
                i++;
            }
        } else if (navigator.isMilestone(data)) {
            Milestone milestone = (Milestone) result;
            milestone.setOwner(position.getParent());
        }

        result.setShowingReportedHours(planner.showReportedHoursRightNow());
        result.setShowingAdvances(planner.showAdvancesRightNow());

        mapper.register(position, result, data);
        return result;
    }

    private Task build(T data) {
        ITaskFundamentalProperties adapted = adapter.adapt(data);
        if (navigator.isMilestone(data)) {
            return new Milestone(adapted);
        } else if (navigator.isLeaf(data)) {
            return new TaskLeaf(adapted);
        } else {
            return new TaskContainer(adapted,
                    planner.areContainersExpandedByDefault());
        }
    }

    public void add(Position position, Collection<? extends T> domainObjects) {
        List<DomainDependency<T>> totalDependencies = new ArrayList<DomainDependency<T>>();
        List<Task> tasksCreated = new ArrayList<Task>();
        for (T object : domainObjects) {
            Task task = buildAndRegister(position, totalDependencies, object);
            tasksCreated.add(task);
        }
        updateTimeTracker(tasksCreated);
        if (position.isAppendToTop() || position.isAtTop()) {
            this.diagramGraph.addTopLevel(tasksCreated);
        } else {
            this.diagramGraph.addTasks(tasksCreated);
            TaskContainer parent = position.getParent();
            parent.addAll(position.getInsertionPosition(), tasksCreated);
            this.diagramGraph.childrenAddedTo(parent);
        }
        for (Dependency dependency : DomainDependency.toDependencies(mapper,
                totalDependencies)) {
            this.diagramGraph.addWithoutEnforcingConstraints(dependency);
        }
        this.diagramGraph.enforceAllRestrictions();
        this.planner.addTasks(position, tasksCreated);
    }

    private void updateTimeTracker(List<Task> tasksCreated) {
        for (Task task : tasksCreated) {
            timeTracker.trackPosition(task);
            if (task.isContainer()) {
                TaskContainer container = (TaskContainer) task;
                updateTimeTracker(container.getTasks());
            }
        }
    }

    public void add(Collection<? extends T> domainObjects) {
        add(Position.createAppendToTopPosition(), domainObjects);
    }

    @Override
    public void add(T domainObject) {
        add(Position.createAppendToTopPosition(), domainObject);
    }

    @Override
    public void add(Position position, T domainObject) {
        add(position, Collections.singletonList(domainObject));
    }

    public IDomainAndBeansMapper<T> getMapper() {
        return mapper;
    }

    @Override
    public void reload(PlannerConfiguration<?> configuration) {
        planner.setConfiguration(configuration);
    }

    @Override
    public Position remove(T domainObject) {
        Task task = mapper.findAssociatedBean(domainObject);
        Position position = mapper.findPositionFor(task);
        diagramGraph.remove(task);
        task.removed();
        planner.removeTask(task);
        adapter.doRemovalOf(mapper.findAssociatedDomainObject(task));
        mapper.remove(domainObject);
        return position;
    }

    @Override
    public Component getRelativeTo() {
        return planner;
    }

    @Override
    public void replace(T oldDomainObject, T newDomainObject) {
        Position position = remove(oldDomainObject);
        add(position, newDomainObject);
    }

    public GanttZKDiagramGraph getDiagramGraph() {
        return diagramGraph;<commit_msg>Bug #1282, #1376: Prevent exception when deleting a milestone inside a container.<commit_after>/*
 * 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.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.joda.time.LocalDate;
import org.zkoss.ganttz.adapters.DomainDependency;
import org.zkoss.ganttz.adapters.IAdapterToTaskFundamentalProperties;
import org.zkoss.ganttz.adapters.IDomainAndBeansMapper;
import org.zkoss.ganttz.adapters.IStructureNavigator;
import org.zkoss.ganttz.adapters.PlannerConfiguration;
import org.zkoss.ganttz.data.Dependency;
import org.zkoss.ganttz.data.DependencyType;
import org.zkoss.ganttz.data.GanttDiagramGraph;
import org.zkoss.ganttz.data.GanttDiagramGraph.GanttZKDiagramGraph;
import org.zkoss.ganttz.data.ITaskFundamentalProperties;
import org.zkoss.ganttz.data.Milestone;
import org.zkoss.ganttz.data.Position;
import org.zkoss.ganttz.data.Task;
import org.zkoss.ganttz.data.TaskContainer;
import org.zkoss.ganttz.data.TaskLeaf;
import org.zkoss.ganttz.data.criticalpath.CriticalPathCalculator;
import org.zkoss.ganttz.extensions.IContext;
import org.zkoss.ganttz.timetracker.TimeTracker;
import org.zkoss.ganttz.timetracker.zoom.IDetailItemModificator;
import org.zkoss.ganttz.timetracker.zoom.TimeTrackerState;
import org.zkoss.ganttz.util.Interval;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Button;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Window;

public class FunctionalityExposedForExtensions<T> implements IContext<T> {

    private static class OneToOneMapper<T> implements IDomainAndBeansMapper<T> {
        private Map<T, Task> fromDomainToTask = new HashMap<T, Task>();

        private Map<Task, T> fromTaskToDomain = new HashMap<Task, T>();

        private Map<Task, TaskContainer> fromTaskToParent = new HashMap<Task, TaskContainer>();

        private List<Task> topLevel = new ArrayList<Task>();

        @Override
        public Task findAssociatedBean(T domainObject)
                throws IllegalArgumentException {
            if (domainObject == null) {
                throw new IllegalArgumentException("domainObject is null");
            }
            if (!fromDomainToTask.containsKey(domainObject)) {
                throw new IllegalArgumentException("not found " + domainObject);
            }
            return fromDomainToTask.get(domainObject);
        }

        public Map<T, Task> getMapDomainToTask() {
            return fromDomainToTask;
        }

        /**
         * Registers the task at the provided position.
         */
        void register(Position position, Task task, T domainObject) {
            fromDomainToTask.put(domainObject, task);
            fromTaskToDomain.put(task, domainObject);

            if (position.isAppendToTop()) {
                topLevel.add(task);
            } else if (position.isAtTop()) {
                topLevel.add(position.getInsertionPosition(), task);
            } else {
                fromTaskToParent.put(task, position.getParent());
            }
        }

        void remove(T domainObject) {
            Task toBeRemoved = findAssociatedBean(domainObject);
            fromDomainToTask.remove(domainObject);
            fromTaskToDomain.remove(toBeRemoved);
            TaskContainer parent = fromTaskToParent.get(toBeRemoved);
            if (parent != null) {
                parent.remove(toBeRemoved);
            }
            fromTaskToParent.remove(toBeRemoved);
            topLevel.remove(toBeRemoved);
        }

        @Override
        public T findAssociatedDomainObject(Task task)
                throws IllegalArgumentException {
            if (task == null) {
                throw new IllegalArgumentException("taskBean is null");
            }
            if (!fromTaskToDomain.containsKey(task)) {
                throw new IllegalArgumentException();
            }
            return fromTaskToDomain.get(task);
        }

        @Override
        public Position findPositionFor(Task task) {
            List<TaskContainer> ancestors = ancestorsOf(task);
            if (ancestors.isEmpty()) {
                return Position.createAtTopPosition(topLevel.indexOf(task));
            }
            TaskContainer parent = ancestors.get(0);
            return Position.createPosition(ancestors, parent.getTasks()
                    .indexOf(task));
        }

        @Override
        public Position findPositionFor(T domainObject) {
            return findPositionFor(findAssociatedBean(domainObject));
        }

        private List<TaskContainer> ancestorsOf(Task task) {
            ArrayList<TaskContainer> result = new ArrayList<TaskContainer>();
            TaskContainer taskContainer = fromTaskToParent.get(task);
            while (taskContainer != null) {
                result.add(taskContainer);
                taskContainer = fromTaskToParent.get(taskContainer);
            }
            return result;
        }

        @Override
        public List<? extends TaskContainer> getParents(Task task) {
            Position position = findPositionFor(task);
            return position.getAncestors();
        }

    }

    private final Planner planner;
    private final IAdapterToTaskFundamentalProperties<T> adapter;
    private final IStructureNavigator<T> navigator;
    private final OneToOneMapper<T> mapper = new OneToOneMapper<T>();
    private final GanttZKDiagramGraph diagramGraph;
    private TimeTracker timeTracker;
    private final PlannerConfiguration<T> configuration;

    public FunctionalityExposedForExtensions(Planner planner,
            PlannerConfiguration<T> configuration,
            GanttZKDiagramGraph diagramGraph) {
        this.planner = planner;
        this.configuration = configuration;
        this.adapter = configuration.getAdapter();
        this.navigator = configuration.getNavigator();
        this.diagramGraph = diagramGraph;

        final IDetailItemModificator firstLevelModificators = configuration
                .getFirstLevelModificators();
        final IDetailItemModificator secondLevelModificators = configuration
                .getSecondLevelModificators();

        Calendar calendarRightNow = Calendar.getInstance();
        LocalDate localDateRightNow = LocalDate.fromCalendarFields(calendarRightNow);
        LocalDate initDate = localDateRightNow.minusYears(1);
        LocalDate endDate = localDateRightNow.plusYears(5);

        this.timeTracker = new TimeTracker(new Interval(
                TimeTrackerState.year(initDate.getYear()),
                TimeTrackerState.year(endDate.getYear())),
                planner.getZoomLevel(), firstLevelModificators,
                secondLevelModificators, planner);
    }

    /**
     * @param insertionPosition
     *            the position in which to register the task at top level. It
     *            can be <code>null</code>
     * @param accumulatedDependencies
     * @param data
     * @param parent
     * @return
     */
    private Task buildAndRegister(Position position,
            List<DomainDependency<T>> accumulatedDependencies, T data) {
        accumulatedDependencies.addAll(adapter.getOutcomingDependencies(data));
        accumulatedDependencies.addAll(adapter.getIncomingDependencies(data));

        final Task result = build(data);

        if (!navigator.isLeaf(data)) {
            TaskContainer container = (TaskContainer) result;
            int i = 0;
            for (T child : navigator.getChildren(data)) {
                container.add(buildAndRegister(position.down(container, i),
                        accumulatedDependencies, child));
                i++;
            }
        } else if (navigator.isMilestone(data)) {
            Milestone milestone = (Milestone) result;
            milestone.setOwner(position.getParent());
        }

        result.setShowingReportedHours(planner.showReportedHoursRightNow());
        result.setShowingAdvances(planner.showAdvancesRightNow());

        mapper.register(position, result, data);
        return result;
    }

    private Task build(T data) {
        ITaskFundamentalProperties adapted = adapter.adapt(data);
        if (navigator.isMilestone(data)) {
            return new Milestone(adapted);
        } else if (navigator.isLeaf(data)) {
            return new TaskLeaf(adapted);
        } else {
            return new TaskContainer(adapted,
                    planner.areContainersExpandedByDefault());
        }
    }

    public void add(Position position, Collection<? extends T> domainObjects) {
        List<DomainDependency<T>> totalDependencies = new ArrayList<DomainDependency<T>>();
        List<Task> tasksCreated = new ArrayList<Task>();
        for (T object : domainObjects) {
            Task task = buildAndRegister(position, totalDependencies, object);
            tasksCreated.add(task);
        }
        updateTimeTracker(tasksCreated);
        if (position.isAppendToTop() || position.isAtTop()) {
            this.diagramGraph.addTopLevel(tasksCreated);
        } else {
            this.diagramGraph.addTasks(tasksCreated);
            TaskContainer parent = position.getParent();
            parent.addAll(position.getInsertionPosition(), tasksCreated);
            this.diagramGraph.childrenAddedTo(parent);
        }
        for (Dependency dependency : DomainDependency.toDependencies(mapper,
                totalDependencies)) {
            this.diagramGraph.addWithoutEnforcingConstraints(dependency);
        }
        this.diagramGraph.enforceAllRestrictions();
        this.planner.addTasks(position, tasksCreated);
    }

    private void updateTimeTracker(List<Task> tasksCreated) {
        for (Task task : tasksCreated) {
            timeTracker.trackPosition(task);
            if (task.isContainer()) {
                TaskContainer container = (TaskContainer) task;
                updateTimeTracker(container.getTasks());
            }
        }
    }

    public void add(Collection<? extends T> domainObjects) {
        add(Position.createAppendToTopPosition(), domainObjects);
    }

    @Override
    public void add(T domainObject) {
        add(Position.createAppendToTopPosition(), domainObject);
    }

    @Override
    public void add(Position position, T domainObject) {
        add(position, Collections.singletonList(domainObject));
    }

    public IDomainAndBeansMapper<T> getMapper() {
        return mapper;
    }

    @Override
    public void reload(PlannerConfiguration<?> configuration) {
        planner.setConfiguration(configuration);
    }

    @Override
    public Position remove(T domainObject) {
        Task task = mapper.findAssociatedBean(domainObject);
        Position position = mapper.findPositionFor(task);
        adapter.doRemovalOf(mapper.findAssociatedDomainObject(task));
        mapper.remove(domainObject);
        diagramGraph.remove(task);
        task.removed();
        planner.removeTask(task);
        return position;
    }

    @Override
    public Component getRelativeTo() {
        return planner;
    }

    @Override
    public void replace(T oldDomainObject, T newDomainObject) {
        Position position = remove(oldDomainObject);
        add(position, newDomainObject);
    }

    public GanttZKDiagramGraph getDiagramGraph() {
        return diagramGraph;
{'fraction_non_alphanumeric': 0.08832239474947975, 'fraction_numerical': 0.00200096046102129, 'mean_word_length': 2.7081169312954443, 'pattern_counts': {'":': 0, '<': 83, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6723160', 'n_tokens_mistral': 6748, 'n_tokens_neox': 6492, 'n_words': 1436}