package ru.khasang.cachoeira.model; import javafx.beans.Observable; import javafx.beans.property.*; import javafx.beans.value.ChangeListener; import javafx.beans.value.WeakChangeListener; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.collections.WeakListChangeListener; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.Comparator; import java.util.concurrent.atomic.AtomicInteger; /** * Класс описывающий задачу. */ public class Task implements ITask { private final ReadOnlyIntegerWrapper id = new ReadOnlyIntegerWrapper(this, "id", taskSequence.incrementAndGet()); private StringProperty name = new SimpleStringProperty(this, "name"); private ObjectProperty<LocalDate> startDate = new SimpleObjectProperty<>(this, "startDate"); private ObjectProperty<LocalDate> finishDate = new SimpleObjectProperty<>(this, "finishDate"); private IntegerProperty duration = new SimpleIntegerProperty(this, "duration"); private IntegerProperty donePercent = new SimpleIntegerProperty(this, "donePercent"); private DoubleProperty cost = new SimpleDoubleProperty(this, "cost"); private StringProperty description = new SimpleStringProperty(this, "description"); private ObservableList<IDependentTask> parentTasks = FXCollections.observableArrayList(this::setObservableParentTaskFields); private ObservableList<IDependentTask> childTasks = FXCollections.observableArrayList(this::setObservableChildTaskFields); private ObjectProperty<ITaskGroup> taskGroup = new SimpleObjectProperty<>(this, "taskGroup"); private ObservableList<IResource> resources = FXCollections.observableArrayList(this::setObservableResourceFields); private static AtomicInteger taskSequence = new AtomicInteger(-1); // -1, потому что первым идет рутовый элемент в таблице задач (rootTask) @SuppressWarnings("FieldCanBeLocal") private ChangeListener<LocalDate> startDateChangeListener; @SuppressWarnings("FieldCanBeLocal") private ChangeListener<LocalDate> finishDateChangeListener; @SuppressWarnings("FieldCanBeLocal") private ListChangeListener<IDependentTask> dependentTaskListChangeListener; /** * Конструктор с дефолтовыми значениями. */ public Task() { this.name.setValue("Task" + " " + id.getValue()); this.startDate.setValue(LocalDate.now()); this.finishDate.setValue(startDate.getValue().plusDays(1)); this.duration.setValue(1); // В случае изменения дат пересчитываем duration startDateChangeListener = (observable, oldValue, newValue) -> { long between = ChronoUnit.DAYS.between(newValue, this.finishDate.getValue()); this.duration.setValue(between); }; finishDateChangeListener = (observable, oldValue, newValue) -> { long between = ChronoUnit.DAYS.between(this.startDate.getValue(), newValue); this.duration.setValue(between); }; this.startDate.addListener(new WeakChangeListener<>(startDateChangeListener)); this.finishDate.addListener(new WeakChangeListener<>(finishDateChangeListener)); dependentTaskListChangeListener = change -> { while (change.next()) { if (change.wasAdded()) { change.getAddedSubList().forEach(this::dependencyConditions); } if (change.wasRemoved()) { change.getList().forEach(this::dependencyConditions); } if (change.wasUpdated()) { change.getList().subList(change.getFrom(), change.getTo()).forEach(this::dependencyConditions); } } }; this.parentTasks.addListener(new WeakListChangeListener<>(dependentTaskListChangeListener)); } private void dependencyConditions(IDependentTask dependentTask) { long between = ChronoUnit.DAYS.between(startDate.getValue(), finishDate.getValue()); if (dependentTask.getDependenceType().equals(TaskDependencyType.FINISHSTART)) { // Финиш-Старт // Находим самую позднюю конечную дату из списка привязанных задач LocalDate latestFinishDate = parentTasks.stream() .map(parentTask -> parentTask.getTask().getFinishDate()) .sorted(Comparator.reverseOrder()) .findFirst() .orElseGet(null); this.startDate.setValue(latestFinishDate); this.finishDate.setValue(startDate.getValue().plusDays(between)); } if (dependentTask.getDependenceType().equals(TaskDependencyType.FINISHFINISH)) { // Финиш-Финиш // TODO: 11.02.2016 протестировать LocalDate latestFinishDate = parentTasks.stream() .map(parentTask -> parentTask.getTask().getFinishDate()) .sorted(Comparator.reverseOrder()) .findFirst() .orElseGet(null); this.finishDate.setValue(latestFinishDate); this.startDate.setValue(finishDate.getValue().minusDays(between)); } if (dependentTask.getDependenceType().equals(TaskDependencyType.STARTFINISH)) { // Старт-Финиш // TODO: 11.02.2016 протестировать LocalDate earliestStartDate = parentTasks.stream() .map(parentTask -> parentTask.getTask().getStartDate()) .sorted() .findFirst() .orElseGet(null); this.finishDate.setValue(earliestStartDate); this.startDate.setValue(finishDate.getValue().minusDays(between)); } if (dependentTask.getDependenceType().equals(TaskDependencyType.STARTSTART)) { // Старт-Старт // TODO: 11.02.2016 протестировать LocalDate earliestStartDate = parentTasks.stream() .map(parentTask -> parentTask.getTask().getStartDate()) .sorted() .findFirst() .orElseGet(null); this.startDate.setValue(earliestStartDate); this.finishDate.setValue(startDate.getValue().plusDays(between)); } } @Override public final int getId() { return id.get(); } @Override public final ReadOnlyIntegerProperty idProperty() { return id.getReadOnlyProperty(); } @Override public final void setId(int id) { this.id.set(id); } @Override public final String getName() { return name.get(); } @Override public final void setName(String name) { this.name.set(name); } @Override public final StringProperty nameProperty() { return name; } @Override public final LocalDate getStartDate() { return startDate.get(); } @Override public final void setStartDateAndVerify(LocalDate startDate) { this.startDate.set(startDate); if (finishDate.getValue() != null) { if (startDate.isEqual(finishDate.getValue()) || startDate.isAfter(finishDate.getValue())) { finishDate.setValue(startDate.plusDays(1)); } } } @Override public final ObjectProperty<LocalDate> startDateProperty() { return startDate; } @Override public final LocalDate getFinishDate() { return finishDate.get(); } @Override public final void setFinishDateAndVerify(LocalDate finishDate) { this.finishDate.set(finishDate); if (startDate.getValue() != null) { if (finishDate.isEqual(startDate.getValue()) || finishDate.isBefore(startDate.getValue())) { startDate.setValue(finishDate.minusDays(1)); } } } @Override public final ObjectProperty<LocalDate> finishDateProperty() { return finishDate; } @Override public final int getDonePercent() { return donePercent.get(); } @Override public final void setDonePercent(int donePercent) { this.donePercent.set(donePercent); } @Override public final IntegerProperty donePercentProperty() { return donePercent; } @Override public final double getCost() { return cost.get(); } @Override public final void setCost(double cost) { this.cost.set(cost); } @Override public final DoubleProperty costProperty() { return cost; } @Override public final String getDescription() { return description.get(); } @Override public final void setDescription(String description) { this.description.set(description); } @Override public final StringProperty descriptionProperty() { return description; } @Override public void addParentTask(IDependentTask parentTask) { this.parentTasks.add(parentTask); } @Override public void removeParentTask(IDependentTask parentTask) { this.parentTasks.remove(parentTask); } public final ObservableList<IDependentTask> getParentTasks() { return parentTasks; } @Override public void setParentTasks(ObservableList<IDependentTask> parentTasks) { this.parentTasks.addAll(parentTasks); } @Override public void addChildTask(IDependentTask childTask) { this.childTasks.add(childTask); } @Override public void removeChildTask(IDependentTask childTask) { this.childTasks.remove(childTask); } @Override public ObservableList<IDependentTask> getChildTasks() { return childTasks; } @Override public void setChildTasks(ObservableList<IDependentTask> childTasks) { this.childTasks.addAll(childTasks); } @Override public final ITaskGroup getGroup() { return taskGroup.get(); } @Override public final void setGroup(ITaskGroup group) { taskGroup.set(group); } @Override public final ObjectProperty<ITaskGroup> groupProperty() { return taskGroup; } @Override public final void addResource(IResource resource) { resources.add(resource); } @Override public final void removeResource(IResource resource) { resources.remove(resource); } @Override public final ObservableList<IResource> getResourceList() { return resources; } @Override public final void setResourceList(ObservableList<IResource> resources) { this.resources = resources; } @Override public int getDuration() { return duration.get(); } @Override public IntegerProperty durationProperty() { return duration; } @Override public void setDuration(int duration) { this.duration.set(duration); } private Observable[] setObservableParentTaskFields(IDependentTask dependentTask) { return new Observable[]{ dependentTask.getTask().finishDateProperty(), dependentTask.dependenceTypeProperty() }; } private Observable[] setObservableChildTaskFields(IDependentTask dependentTask) { return new Observable[]{ dependentTask.taskProperty(), dependentTask.dependenceTypeProperty() }; } private Observable[] setObservableResourceFields(IResource resource) { return new Observable[]{ resource.nameProperty(), resource.resourceTypeProperty(), resource.emailProperty(), resource.descriptionProperty() }; } }