/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.optaplanner.core.impl.domain.variable.inverserelation; import java.util.Collection; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.optaplanner.core.impl.domain.entity.descriptor.EntityDescriptor; import org.optaplanner.core.impl.domain.variable.descriptor.VariableDescriptor; import org.optaplanner.core.impl.domain.variable.listener.StatefulVariableListener; import org.optaplanner.core.impl.score.director.ScoreDirector; /** * Alternative to {@link CollectionInverseVariableListener}. */ public class ExternalizedCollectionInverseVariableSupply implements StatefulVariableListener<Object>, CollectionInverseVariableSupply { protected final VariableDescriptor sourceVariableDescriptor; protected Map<Object, Set<Object>> inverseEntitySetMap = null; public ExternalizedCollectionInverseVariableSupply(VariableDescriptor sourceVariableDescriptor) { this.sourceVariableDescriptor = sourceVariableDescriptor; } @Override public VariableDescriptor getSourceVariableDescriptor() { return sourceVariableDescriptor; } @Override public void resetWorkingSolution(ScoreDirector scoreDirector) { EntityDescriptor entityDescriptor = sourceVariableDescriptor.getEntityDescriptor(); List<Object> entityList = entityDescriptor.extractEntities(scoreDirector.getWorkingSolution()); inverseEntitySetMap = new IdentityHashMap<>(entityList.size()); for (Object entity : entityList) { insert(scoreDirector, entity); } } @Override public void clearWorkingSolution(ScoreDirector scoreDirector) { inverseEntitySetMap = null; } @Override public void beforeEntityAdded(ScoreDirector scoreDirector, Object entity) { // Do nothing } @Override public void afterEntityAdded(ScoreDirector scoreDirector, Object entity) { insert(scoreDirector, entity); } @Override public void beforeVariableChanged(ScoreDirector scoreDirector, Object entity) { retract(scoreDirector, entity); } @Override public void afterVariableChanged(ScoreDirector scoreDirector, Object entity) { insert(scoreDirector, entity); } @Override public void beforeEntityRemoved(ScoreDirector scoreDirector, Object entity) { retract(scoreDirector, entity); } @Override public void afterEntityRemoved(ScoreDirector scoreDirector, Object entity) { // Do nothing } protected void insert(ScoreDirector scoreDirector, Object entity) { Object value = sourceVariableDescriptor.getValue(entity); if (value == null) { return; } Set<Object> inverseEntitySet = inverseEntitySetMap.computeIfAbsent(value, k -> Collections.newSetFromMap(new IdentityHashMap<>())); boolean addSucceeded = inverseEntitySet.add(entity); if (!addSucceeded) { throw new IllegalStateException("The supply (" + this + ") is corrupted," + " because the entity (" + entity + ") for sourceVariable (" + sourceVariableDescriptor.getVariableName() + ") cannot be inserted: it was already inserted."); } } protected void retract(ScoreDirector scoreDirector, Object entity) { Object value = sourceVariableDescriptor.getValue(entity); if (value == null) { return; } Set<Object> inverseEntitySet = inverseEntitySetMap.get(value); boolean removeSucceeded = inverseEntitySet.remove(entity); if (!removeSucceeded) { throw new IllegalStateException("The supply (" + this + ") is corrupted," + " because the entity (" + entity + ") for sourceVariable (" + sourceVariableDescriptor.getVariableName() + ") cannot be retracted: it was never inserted."); } if (inverseEntitySet.isEmpty()) { inverseEntitySetMap.put(value, null); } } @Override public Collection<?> getInverseCollection(Object value) { Set<Object> inverseEntitySet = inverseEntitySetMap.get(value); if (inverseEntitySet == null) { return Collections.emptySet(); } return inverseEntitySet; } @Override public String toString() { return getClass().getSimpleName() + "(" + sourceVariableDescriptor.getVariableName() + ")"; } }