ItEr34S12CUCreacionUnidadesPlanificacionItEr33S14: Adding SchedulingState class and accompanying test.
This commit is contained in:
parent
73006de168
commit
0b9a58bdcc
2 changed files with 539 additions and 0 deletions
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* This file is part of ###PROJECT_NAME###
|
||||
*
|
||||
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
|
||||
* Desenvolvemento Tecnolóxico de Galicia
|
||||
*
|
||||
* 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.navalplanner.business.orders.entities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
/**
|
||||
* @author Óscar González Fernández <ogonzalez@igalia.com>
|
||||
*
|
||||
*/
|
||||
public class SchedulingState {
|
||||
|
||||
public enum Type {
|
||||
SCHEDULING_POINT {
|
||||
@Override
|
||||
public boolean belongsToSchedulingPoint() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompletelyScheduled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartiallyScheduled() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
SCHEDULED_SUBELEMENT {
|
||||
@Override
|
||||
public boolean belongsToSchedulingPoint() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompletelyScheduled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartiallyScheduled() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
PARTIALY_SCHEDULED_SUPERELEMENT {
|
||||
@Override
|
||||
public boolean belongsToSchedulingPoint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompletelyScheduled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartiallyScheduled() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
COMPLETELY_SCHEDULED_SUPERELEMENT {
|
||||
@Override
|
||||
public boolean belongsToSchedulingPoint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompletelyScheduled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartiallyScheduled() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
NO_SCHEDULED {
|
||||
@Override
|
||||
public boolean belongsToSchedulingPoint() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompletelyScheduled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPartiallyScheduled() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
public abstract boolean belongsToSchedulingPoint();
|
||||
|
||||
public abstract boolean isPartiallyScheduled();
|
||||
|
||||
public abstract boolean isCompletelyScheduled();
|
||||
|
||||
public boolean isSomewhatScheduled() {
|
||||
return isPartiallyScheduled() || isCompletelyScheduled();
|
||||
}
|
||||
}
|
||||
|
||||
private Type type = Type.NO_SCHEDULED;
|
||||
private SchedulingState parent;
|
||||
|
||||
private List<SchedulingState> children = new ArrayList<SchedulingState>();
|
||||
|
||||
public SchedulingState() {
|
||||
}
|
||||
|
||||
public SchedulingState(List<SchedulingState> children) {
|
||||
for (SchedulingState each : children) {
|
||||
if (!each.isRoot()) {
|
||||
throw new IllegalArgumentException(each
|
||||
+ " is already child of another "
|
||||
+ SchedulingState.class.getSimpleName());
|
||||
}
|
||||
add(each);
|
||||
}
|
||||
}
|
||||
|
||||
public SchedulingState(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void add(SchedulingState child) {
|
||||
child.parent = this;
|
||||
children.add(child);
|
||||
setType(calculateTypeFromChildren());
|
||||
}
|
||||
|
||||
public SchedulingState getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return parent == null;
|
||||
}
|
||||
|
||||
public boolean canBeScheduled() {
|
||||
return type == Type.NO_SCHEDULED;
|
||||
}
|
||||
|
||||
public void schedule() {
|
||||
if (type.isSomewhatScheduled()) {
|
||||
throw new IllegalStateException("it's already somewhat scheduled");
|
||||
}
|
||||
setType(Type.SCHEDULING_POINT);
|
||||
for (SchedulingState schedulingState : getDescendants()) {
|
||||
schedulingState.setType(Type.SCHEDULED_SUBELEMENT);
|
||||
}
|
||||
}
|
||||
|
||||
private void setType(Type type) {
|
||||
if (this.type == type) {
|
||||
return;
|
||||
}
|
||||
this.type = type;
|
||||
notifyParentOfTypeChange();
|
||||
}
|
||||
|
||||
private void notifyParentOfTypeChange() {
|
||||
if (isRoot()) {
|
||||
return;
|
||||
}
|
||||
parent.typeChangedOnChild(this);
|
||||
}
|
||||
|
||||
private void typeChangedOnChild(SchedulingState child) {
|
||||
if (getType().belongsToSchedulingPoint()) {
|
||||
return;
|
||||
}
|
||||
setType(calculateTypeFromChildren());
|
||||
}
|
||||
|
||||
private Type calculateTypeFromChildren() {
|
||||
Validate.isTrue(!children.isEmpty());
|
||||
boolean allScheduled = true;
|
||||
boolean someScheduled = false;
|
||||
for (SchedulingState each : children) {
|
||||
someScheduled = someScheduled
|
||||
|| each.getType().isSomewhatScheduled();
|
||||
allScheduled = allScheduled
|
||||
&& each.getType().isCompletelyScheduled();
|
||||
}
|
||||
if (allScheduled) {
|
||||
return Type.COMPLETELY_SCHEDULED_SUPERELEMENT;
|
||||
} else if (someScheduled) {
|
||||
return Type.PARTIALY_SCHEDULED_SUPERELEMENT;
|
||||
} else {
|
||||
return Type.NO_SCHEDULED;
|
||||
}
|
||||
}
|
||||
|
||||
private List<SchedulingState> getDescendants() {
|
||||
List<SchedulingState> result = new ArrayList<SchedulingState>();
|
||||
addDescendants(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addDescendants(List<SchedulingState> result) {
|
||||
for (SchedulingState each : children) {
|
||||
result.add(each);
|
||||
each.addDescendants(result);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCompletelyScheduled() {
|
||||
return type.isCompletelyScheduled();
|
||||
}
|
||||
|
||||
public boolean isPartiallyScheduled() {
|
||||
return type.isPartiallyScheduled();
|
||||
}
|
||||
|
||||
public boolean isSomewhatScheduled() {
|
||||
return type.isSomewhatScheduled();
|
||||
}
|
||||
|
||||
public void removeChild(SchedulingState schedulingState) {
|
||||
boolean removed = children.remove(schedulingState);
|
||||
if (removed) {
|
||||
schedulingState.parent = null;
|
||||
}
|
||||
setType(calculateTypeFromChildren());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this).append(type).toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* This file is part of ###PROJECT_NAME###
|
||||
*
|
||||
* Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
|
||||
* Desenvolvemento Tecnolóxico de Galicia
|
||||
*
|
||||
* 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.navalplanner.business.test.orders.entities;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.matchers.JUnitMatchers.each;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.navalplanner.business.orders.entities.SchedulingState;
|
||||
import org.navalplanner.business.orders.entities.SchedulingState.Type;
|
||||
|
||||
/**
|
||||
* @author Óscar González Fernández <ogonzalez@igalia.com>
|
||||
*
|
||||
*/
|
||||
public class SchedulingStateTest {
|
||||
|
||||
private SchedulingState root;
|
||||
|
||||
private SchedulingState childA;
|
||||
|
||||
private SchedulingState childB;
|
||||
|
||||
private SchedulingState grandChildA1;
|
||||
|
||||
private SchedulingState grandChildA2;
|
||||
|
||||
private SchedulingState grandChildB1;
|
||||
|
||||
private SchedulingState grandChildB2;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
root = new SchedulingState();
|
||||
root.add(childA = new SchedulingState());
|
||||
childA.add(grandChildA1 = new SchedulingState());
|
||||
childA.add(grandChildA2 = new SchedulingState());
|
||||
root.add(childB = new SchedulingState());
|
||||
childB.add(grandChildB1 = new SchedulingState());
|
||||
childB.add(grandChildB2 = new SchedulingState());
|
||||
}
|
||||
|
||||
private List<SchedulingState> all(){
|
||||
return Arrays.asList(root,childA, childB, grandChildA1, grandChildA2,
|
||||
grandChildB1, grandChildB2);
|
||||
}
|
||||
|
||||
private List<SchedulingState> allRootDescendants() {
|
||||
return Arrays.asList(childA, childB, grandChildA1, grandChildA2,
|
||||
grandChildB1, grandChildB2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aNewlyCreatedSchedulingStateIsNoScheduled() {
|
||||
SchedulingState schedulingState = new SchedulingState();
|
||||
assertThat(schedulingState.getType(),
|
||||
equalTo(Type.NO_SCHEDULED));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void cannotCreateASchedulingStateWithChildrenAlreadyAssigned() {
|
||||
new SchedulingState(Arrays.asList(childA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anAddedSchedulingStateHasAParent() {
|
||||
assertThat(childA.getParent(), equalTo(root));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theRootOfASchedulingStateTreeHasNoParent() {
|
||||
assertNull(root.getParent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ifHasNoParentItsRoot() {
|
||||
assertTrue(root.isRoot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSchedulingAElementItTursIntoASchedulingPoint() {
|
||||
grandChildA1.schedule();
|
||||
assertThat(grandChildA1.getType(), equalTo(Type.SCHEDULING_POINT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSchedulingAElementItTurnsAllItsDescendantsIntoScheduledSubelements() {
|
||||
root.schedule();
|
||||
assertThat(allRootDescendants(),
|
||||
each(hasType(Type.SCHEDULED_SUBELEMENT)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aScheduledElementIsCompletelyScheduled() {
|
||||
root.schedule();
|
||||
assertThat(all(), each(completelyScheduled()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aSomewhatScheduledElemenetCannotBeScheduled() {
|
||||
grandChildA1.schedule();
|
||||
grandChildB1.schedule();
|
||||
for (SchedulingState schedulingState : all()) {
|
||||
if (schedulingState == grandChildA2
|
||||
|| schedulingState == grandChildB2) {
|
||||
// can be scheduled
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
schedulingState.schedule();
|
||||
fail("must send " + IllegalStateException.class);
|
||||
} catch (IllegalStateException e) {
|
||||
// ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scheduledSubelementsCantBeScheduled() {
|
||||
root.schedule();
|
||||
assertThat(allRootDescendants(), each(not(canBeScheduled())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aNoScheduledElementCanBeScheduled() {
|
||||
assertThat(all(), each(canBeScheduled()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aSchedulingPointCantBeScheduled() {
|
||||
root.schedule();
|
||||
assertFalse(root.canBeScheduled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rootIsPartiallyScheduledWhenSchedulingOneOfTheChildren() {
|
||||
childA.schedule();
|
||||
assertThat(root, hasType(Type.PARTIALY_SCHEDULED_SUPERELEMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rootIsCompletelyScheduledWhenSchedulingAllOfTheChildren() {
|
||||
childA.schedule();
|
||||
childB.schedule();
|
||||
assertThat(root, hasType(Type.COMPLETELY_SCHEDULED_SUPERELEMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSchedulingAGrandChildrenTheRootIsPartiallyScheduled() {
|
||||
grandChildA1.schedule();
|
||||
assertThat(root, hasType(Type.PARTIALY_SCHEDULED_SUPERELEMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ifSchedulingAllGrandChildrenTheRootIsCompletelyScheduled() {
|
||||
grandChildA1.schedule();
|
||||
grandChildA2.schedule();
|
||||
grandChildB1.schedule();
|
||||
grandChildB2.schedule();
|
||||
assertThat(root, hasType(Type.COMPLETELY_SCHEDULED_SUPERELEMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addingANewChildToACompletelyScheduled() {
|
||||
childA.schedule();
|
||||
childB.schedule();
|
||||
root.add(new SchedulingState());
|
||||
assertThat(root, hasType(Type.PARTIALY_SCHEDULED_SUPERELEMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removingTheOnlyNoScheduled() {
|
||||
childA.schedule();
|
||||
root.removeChild(childB);
|
||||
assertThat(root, hasType(Type.COMPLETELY_SCHEDULED_SUPERELEMENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removingAChildMakesItHasNoParent() {
|
||||
childA.schedule();
|
||||
root.removeChild(childB);
|
||||
assertThat(childB.getParent(), nullValue());
|
||||
}
|
||||
|
||||
abstract static class SchedulingStateMatcher extends
|
||||
BaseMatcher<SchedulingState> {
|
||||
@Override
|
||||
public boolean matches(Object object) {
|
||||
if (object instanceof SchedulingState) {
|
||||
return matches((SchedulingState) object);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean matches(SchedulingState schedulingState);
|
||||
}
|
||||
|
||||
private Matcher<SchedulingState> hasType(final Type type) {
|
||||
return new SchedulingStateMatcher() {
|
||||
|
||||
@Override
|
||||
public boolean matches(SchedulingState state) {
|
||||
return state.getType() == type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description
|
||||
.appendText("the type of the SchedulingState must be: "
|
||||
+ type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Matcher<SchedulingState> completelyScheduled() {
|
||||
return new SchedulingStateMatcher() {
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("completely scheduled");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(SchedulingState schedulingState) {
|
||||
return schedulingState.isCompletelyScheduled();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Matcher<SchedulingState> canBeScheduled() {
|
||||
return new SchedulingStateMatcher() {
|
||||
|
||||
@Override
|
||||
public boolean matches(SchedulingState state) {
|
||||
return state.canBeScheduled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("can be scheduled");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue