Skip to content

Commit

Permalink
PLANNER-731 Move<Solution_>:Add generic parameter to Move
Browse files Browse the repository at this point in the history
  • Loading branch information
ge0ffrey committed Jan 19, 2017
1 parent 1ef4b46 commit e7533ef
Show file tree
Hide file tree
Showing 60 changed files with 573 additions and 508 deletions.
Expand Up @@ -26,7 +26,7 @@ public class PickedMoveTypeBestScoreDiffStatisticPoint extends StatisticPoint {

private final long timeMillisSpent;
/**
* Not a {@link Class}{@code <}{@link Move}{@code >} because {@link CompositeMove}'s need to be atomized
* Not a {@link Class}{@code <}{@link Move}{@code >} because {@link CompositeMove}s need to be atomized
* and because that {@link Class} might no longer exist when {@link BenchmarkAggregator} aggregates.
*/
private final String moveType;
Expand Down
Expand Up @@ -26,7 +26,7 @@ public class PickedMoveTypeStepScoreDiffStatisticPoint extends StatisticPoint {

private final long timeMillisSpent;
/**
* Not a {@link Class}{@code <}{@link Move}{@code >} because {@link CompositeMove}'s need to be atomized
* Not a {@link Class}{@code <}{@link Move}{@code >} because {@link CompositeMove}s need to be atomized
* and because that {@link Class} might no longer exist when {@link BenchmarkAggregator} aggregates.
*/
private final String moveType;
Expand Down
Expand Up @@ -16,21 +16,23 @@

package org.optaplanner.core.impl.heuristic.move;

import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.impl.score.director.ScoreDirector;

/**
* Abstract superclass for {@link Move}.
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
* @see Move
*/
public abstract class AbstractMove implements Move {
public abstract class AbstractMove<Solution_> implements Move<Solution_> {

@Override
public String getSimpleMoveTypeDescription() {
return getClass().getSimpleName();
}

@Override
public final void doMove(ScoreDirector scoreDirector) {
public final void doMove(ScoreDirector<Solution_> scoreDirector) {
doMoveOnGenuineVariables(scoreDirector);
scoreDirector.triggerVariableListeners();
}
Expand All @@ -40,6 +42,6 @@ public final void doMove(ScoreDirector scoreDirector) {
* (because {@link #doMove(ScoreDirector)} already does that).
* @param scoreDirector never null
*/
protected abstract void doMoveOnGenuineVariables(ScoreDirector scoreDirector);
protected abstract void doMoveOnGenuineVariables(ScoreDirector<Solution_> scoreDirector);

}
Expand Up @@ -23,67 +23,71 @@
import java.util.Set;
import java.util.TreeSet;

import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.impl.score.director.ScoreDirector;

/**
* A CompositeMove is composed out of multiple other moves.
* <p>
* Warning: each of moves in the moveList must not rely on the effect of a previous move in the moveList
* to create its undoMove correctly.
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
* @see Move
*/
public class CompositeMove implements Move {
public class CompositeMove<Solution_> implements Move<Solution_> {

/**
* @param moves never null, sometimes empty. Do not modify this argument afterwards or the CompositeMove corrupts.
* @return never null
*/
public static Move buildMove(Move... moves) {
@SafeVarargs
public static <Solution_, Move_ extends Move<Solution_>> Move<Solution_> buildMove(Move_... moves) {
int size = moves.length;
if (size > 1) {
return new CompositeMove(moves);
return new CompositeMove<>(moves);
} else if (size == 1) {
return moves[0];
} else {
return new NoChangeMove();
return new NoChangeMove<>();
}
}

/**
* @param moveList never null, sometimes empty
* @return never null
*/
public static Move buildMove(List<Move> moveList) {
public static <Solution_, Move_ extends Move<Solution_>> Move<Solution_> buildMove(List<Move_> moveList) {
int size = moveList.size();
if (size > 1) {
return new CompositeMove(moveList.toArray(new Move[0]));
return new CompositeMove<>(moveList.toArray(new Move[0]));
} else if (size == 1) {
return moveList.get(0);
} else {
return new NoChangeMove();
return new NoChangeMove<>();
}
}

// ************************************************************************
// Non-static members
// ************************************************************************

protected final Move[] moves;
protected final Move<Solution_>[] moves;

/**
* @param moves never null, never empty. Do not modify this argument afterwards or this CompositeMove corrupts.
*/
public CompositeMove(Move... moves) {
@SafeVarargs
public CompositeMove(Move<Solution_>... moves) {
this.moves = moves;
}

public Move[] getMoves() {
public Move<Solution_>[] getMoves() {
return moves;
}

@Override
public boolean isMoveDoable(ScoreDirector scoreDirector) {
for (Move move : moves) {
public boolean isMoveDoable(ScoreDirector<Solution_> scoreDirector) {
for (Move<Solution_> move : moves) {
if (!move.isMoveDoable(scoreDirector)) {
return false;
}
Expand All @@ -92,19 +96,19 @@ public boolean isMoveDoable(ScoreDirector scoreDirector) {
}

@Override
public CompositeMove createUndoMove(ScoreDirector scoreDirector) {
Move[] undoMoves = new Move[moves.length];
public CompositeMove<Solution_> createUndoMove(ScoreDirector<Solution_> scoreDirector) {
Move<Solution_>[] undoMoves = new Move[moves.length];
for (int i = 0; i < moves.length; i++) {
// Note: this undoMove creation doesn't have the effect yet of a previous move in the moveList
Move undoMove = moves[i].createUndoMove(scoreDirector);
Move<Solution_> undoMove = moves[i].createUndoMove(scoreDirector);
undoMoves[moves.length - 1 - i] = undoMove;
}
return new CompositeMove(undoMoves);
return new CompositeMove<>(undoMoves);
}

@Override
public void doMove(ScoreDirector scoreDirector) {
for (Move move : moves) {
public void doMove(ScoreDirector<Solution_> scoreDirector) {
for (Move<Solution_> move : moves) {
// Calls scoreDirector.triggerVariableListeners() between moves
// because a later move can depend on the shadow variables changed by an earlier move
move.doMove(scoreDirector);
Expand All @@ -119,7 +123,7 @@ public void doMove(ScoreDirector scoreDirector) {
@Override
public String getSimpleMoveTypeDescription() {
Set<String> childMoveTypeDescriptionSet = new TreeSet<>();
for (Move move : moves) {
for (Move<Solution_> move : moves) {
childMoveTypeDescriptionSet.add(move.getSimpleMoveTypeDescription());
}
StringBuilder moveTypeDescription = new StringBuilder(20 * (moves.length + 1));
Expand All @@ -136,7 +140,7 @@ public String getSimpleMoveTypeDescription() {
@Override
public Collection<? extends Object> getPlanningEntities() {
Set<Object> entities = new LinkedHashSet<>(moves.length * 2);
for (Move move : moves) {
for (Move<Solution_> move : moves) {
entities.addAll(move.getPlanningEntities());
}
return entities;
Expand All @@ -145,7 +149,7 @@ public Collection<? extends Object> getPlanningEntities() {
@Override
public Collection<? extends Object> getPlanningValues() {
Set<Object> values = new LinkedHashSet<>(moves.length * 2);
for (Move move : moves) {
for (Move<Solution_> move : moves) {
values.addAll(move.getPlanningValues());
}
return values;
Expand Down
Expand Up @@ -43,9 +43,10 @@
* A Move should implement {@link Object#equals(Object)} and {@link Object#hashCode()} for {@link MoveTabuAcceptor}.
* <p>
* An implementation must extend {@link AbstractMove} to ensure backwards compatibility in future versions.
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
* @see AbstractMove
*/
public interface Move {
public interface Move<Solution_> {

/**
* Called before a move is evaluated to decide whether the move can be done and evaluated.
Expand All @@ -63,15 +64,15 @@ public interface Move {
* @param scoreDirector the {@link ScoreDirector} not yet modified by the move.
* @return true if the move achieves a change in the solution and the move is possible to do on the solution.
*/
boolean isMoveDoable(ScoreDirector scoreDirector);
boolean isMoveDoable(ScoreDirector<Solution_> scoreDirector);

/**
* Called before the move is done, so the move can be evaluated and then be undone
* without resulting into a permanent change in the solution.
* @param scoreDirector the {@link ScoreDirector} not yet modified by the move.
* @return an undoMove which does the exact opposite of this move.
*/
Move createUndoMove(ScoreDirector scoreDirector);
Move<Solution_> createUndoMove(ScoreDirector<Solution_> scoreDirector);

/**
* Does the move (which indirectly affects the {@link ScoreDirector#getWorkingSolution()}).
Expand All @@ -83,7 +84,7 @@ public interface Move {
* This method must end with calling {@link ScoreDirector#triggerVariableListeners()} to ensure all shadow variables are updated.
* @param scoreDirector never null, the {@link ScoreDirector} that needs to get notified of the changes.
*/
void doMove(ScoreDirector scoreDirector);
void doMove(ScoreDirector<Solution_> scoreDirector);

// ************************************************************************
// Introspection methods
Expand Down
Expand Up @@ -19,25 +19,27 @@
import java.util.Collection;
import java.util.Collections;

import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.impl.score.director.ScoreDirector;

/**
* Makes no changes.
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
*/
public class NoChangeMove extends AbstractMove {
public class NoChangeMove<Solution_> extends AbstractMove<Solution_> {

@Override
public boolean isMoveDoable(ScoreDirector scoreDirector) {
public boolean isMoveDoable(ScoreDirector<Solution_> scoreDirector) {
return true;
}

@Override
public Move createUndoMove(ScoreDirector scoreDirector) {
public NoChangeMove<Solution_> createUndoMove(ScoreDirector<Solution_> scoreDirector) {
return new NoChangeMove();
}

@Override
protected void doMoveOnGenuineVariables(ScoreDirector scoreDirector) {
protected void doMoveOnGenuineVariables(ScoreDirector<Solution_> scoreDirector) {
// do nothing
}

Expand Down
Expand Up @@ -22,18 +22,22 @@

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import org.optaplanner.core.impl.heuristic.move.AbstractMove;
import org.optaplanner.core.impl.heuristic.move.Move;
import org.optaplanner.core.impl.score.director.ScoreDirector;

public class ChangeMove extends AbstractMove {
/**
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
*/
public class ChangeMove<Solution_> extends AbstractMove<Solution_> {

protected final Object entity;
protected final GenuineVariableDescriptor variableDescriptor;
protected final GenuineVariableDescriptor<Solution_> variableDescriptor;
protected final Object toPlanningValue;

public ChangeMove(Object entity, GenuineVariableDescriptor variableDescriptor,
public ChangeMove(Object entity, GenuineVariableDescriptor<Solution_> variableDescriptor,
Object toPlanningValue) {
this.entity = entity;
this.variableDescriptor = variableDescriptor;
Expand All @@ -57,19 +61,19 @@ public Object getToPlanningValue() {
// ************************************************************************

@Override
public boolean isMoveDoable(ScoreDirector scoreDirector) {
public boolean isMoveDoable(ScoreDirector<Solution_> scoreDirector) {
Object oldValue = variableDescriptor.getValue(entity);
return !Objects.equals(oldValue, toPlanningValue);
}

@Override
public Move createUndoMove(ScoreDirector scoreDirector) {
public ChangeMove<Solution_> createUndoMove(ScoreDirector<Solution_> scoreDirector) {
Object oldValue = variableDescriptor.getValue(entity);
return new ChangeMove(entity, variableDescriptor, oldValue);
return new ChangeMove<>(entity, variableDescriptor, oldValue);
}

@Override
protected void doMoveOnGenuineVariables(ScoreDirector scoreDirector) {
protected void doMoveOnGenuineVariables(ScoreDirector<Solution_> scoreDirector) {
scoreDirector.beforeVariableChanged(variableDescriptor, entity);
variableDescriptor.setValue(entity, toPlanningValue);
scoreDirector.afterVariableChanged(variableDescriptor, entity);
Expand Down
Expand Up @@ -35,15 +35,15 @@
* This {@link Move} is not cacheable.
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
*/
public class PillarChangeMove<Solution_> extends AbstractMove {
public class PillarChangeMove<Solution_> extends AbstractMove<Solution_> {

protected final GenuineVariableDescriptor<Solution_> variableDescriptor;

protected final List<Object> pillar;
protected final Object toPlanningValue;

public PillarChangeMove(List<Object> pillar, GenuineVariableDescriptor<Solution_> variableDescriptor,
Object toPlanningValue) {
Object toPlanningValue) {
this.pillar = pillar;
this.variableDescriptor = variableDescriptor;
this.toPlanningValue = toPlanningValue;
Expand All @@ -66,15 +66,15 @@ public Object getToPlanningValue() {
// ************************************************************************

@Override
public boolean isMoveDoable(ScoreDirector scoreDirector) {
public boolean isMoveDoable(ScoreDirector<Solution_> scoreDirector) {
Object oldValue = variableDescriptor.getValue(pillar.get(0));
if (Objects.equals(oldValue, toPlanningValue)) {
return false;
}
if (!variableDescriptor.isValueRangeEntityIndependent()) {
ValueRangeDescriptor<Solution_> valueRangeDescriptor = variableDescriptor.getValueRangeDescriptor();
// type cast in order to avoid having to make Move and all its sub-types generic
Solution_ workingSolution = (Solution_) scoreDirector.getWorkingSolution();
Solution_ workingSolution = scoreDirector.getWorkingSolution();
for (Object entity : pillar) {
ValueRange rightValueRange = valueRangeDescriptor.extractValueRange(workingSolution, entity);
if (!rightValueRange.contains(toPlanningValue)) {
Expand All @@ -86,13 +86,13 @@ public boolean isMoveDoable(ScoreDirector scoreDirector) {
}

@Override
public Move createUndoMove(ScoreDirector scoreDirector) {
public PillarChangeMove<Solution_> createUndoMove(ScoreDirector<Solution_> scoreDirector) {
Object oldValue = variableDescriptor.getValue(pillar.get(0));
return new PillarChangeMove<>(pillar, variableDescriptor, oldValue);
}

@Override
protected void doMoveOnGenuineVariables(ScoreDirector scoreDirector) {
protected void doMoveOnGenuineVariables(ScoreDirector<Solution_> scoreDirector) {
for (Object entity : pillar) {
scoreDirector.beforeVariableChanged(variableDescriptor, entity);
variableDescriptor.setValue(entity, toPlanningValue);
Expand Down

0 comments on commit e7533ef

Please sign in to comment.