/* * 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.data.constraint; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.zkoss.ganttz.util.WeakReferencedListeners; import org.zkoss.ganttz.util.WeakReferencedListeners.IListenerNotification; import org.zkoss.zk.ui.Executions; /** * @author Óscar González Fernández <ogonzalez@igalia.com> * */ public abstract class Constraint<T> { public static <T> Constraint<T> coalesce( Collection<? extends Constraint<T>> constraints) { if (constraints.size() == 1) { return constraints.iterator().next(); } return new CompoundConstraint<T>(constraints); } private static class CompoundConstraint<T> extends Constraint<T> { private final List<Constraint<T>> constraints; CompoundConstraint(Collection<? extends Constraint<T>> constraints) { this.constraints = new ArrayList<Constraint<T>>(constraints); } @Override protected T applyConstraintTo(T value) { T result = value; for (Constraint<T> each : constraints) { result = each.applyTo(result); } return result; } @Override public boolean isSatisfiedBy(T value) { for (Constraint<T> each : constraints) { if (!each.isSatisfiedBy(value)) { return false; } } return true; } } public static <T> Constraint<T> emptyConstraint() { return new Constraint<T>() { @Override protected T applyConstraintTo(T currentValue) { return currentValue; } @Override public boolean isSatisfiedBy(T value) { return true; } }; } public interface IConstraintViolationListener<T> { public void constraintViolated(Constraint<T> constraint, T value); public void constraintSatisfied(Constraint<T> constraint, T value); } public static <T> IConstraintViolationListener<T> onlyOnZKExecution( final IConstraintViolationListener<T> original) { return new IConstraintViolationListener<T>() { @Override public void constraintViolated(Constraint<T> constraint, T value) { if (Executions.getCurrent() != null) { original.constraintViolated(constraint, value); } } @Override public void constraintSatisfied(Constraint<T> constraint, T value) { if (Executions.getCurrent() != null) { original.constraintSatisfied(constraint, value); } } }; } public static class ConstraintBuilder<T> { private final T value; private final List<Constraint<T>> constraints = new ArrayList<Constraint<T>>(); public ConstraintBuilder(T value) { this.value = value; } public ConstraintBuilder<T> withConstraints( Constraint<T>... constraints) { return withConstraints(Arrays.asList(constraints)); } public ConstraintBuilder<T> withConstraints( List<Constraint<T>> constraints) { this.constraints.addAll(constraints); return this; } public T apply() { return Constraint.apply(value, constraints); } public T applyWithoutFinalCheck() { return Constraint.apply(value, constraints, false); } } public static <T> ConstraintBuilder<T> initialValue(T value) { return new ConstraintBuilder<T>(value); } public static <T> T apply(T initialValue, Constraint<T>... constraints) { return apply(initialValue, Arrays.asList(constraints)); } public static <T> T apply(T initialValue, Collection<Constraint<T>> constraints) { return apply(initialValue, constraints, true); } public static <T> T apply(T initialValue, Collection<Constraint<T>> constraints, boolean doFinalCheck) { T result = initialValue; for (Constraint<T> each : constraints) { result = each.applyTo(result); } if (doFinalCheck) { checkSatisfyResult(constraints, result); } return result; } public static <T> void checkSatisfyResult( Collection<? extends Constraint<T>> all, T result) { for (Constraint<T> each : all) { each.checkSatisfiesResult(result); } } private static final Constraint<Object> VOID_CONSTRAINT = new Constraint<Object>() { @Override protected Object applyConstraintTo(Object currentValue) { return currentValue; } @Override public boolean isSatisfiedBy(Object value) { return true; } }; @SuppressWarnings("unchecked") public static <T> Constraint<T> voidConstraint() { return (Constraint<T>) VOID_CONSTRAINT; } private WeakReferencedListeners<IConstraintViolationListener<T>> weakListeners = WeakReferencedListeners .create(); public final T applyTo(T value) { T result = applyConstraintTo(value); if (!isSatisfiedBy(result)) { throw new IllegalStateException(result + " doesn't fulfill this constraint: " + this); } return result; } protected abstract T applyConstraintTo(T value); public abstract boolean isSatisfiedBy(T value); public void checkSatisfiesResult(T finalResult) { fireSatisfaction(finalResult, isSatisfiedBy(finalResult)); } private void fireSatisfaction(final T value, final boolean satisfied) { weakListeners .fireEvent(new IListenerNotification<IConstraintViolationListener<T>>() { @Override public void doNotify( IConstraintViolationListener<T> listener) { if (satisfied) { listener.constraintSatisfied(Constraint.this, value); } else { listener.constraintViolated(Constraint.this, value); } } }); } public void addConstraintViolationListener(IConstraintViolationListener<T> listener) { weakListeners.addListener(listener); } }