package com.plectix.simulator.simulationclasses.solution;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.plectix.simulator.interfaces.ConnectedComponentInterface;
import com.plectix.simulator.interfaces.SolutionInterface;
import com.plectix.simulator.simulationclasses.injections.Injection;
import com.plectix.simulator.simulator.initialization.InjectionsBuilder;
/**
* <p>This one is the SuperStorage. This kind of storage keeps substances
* as connected components with counters (SuperSubstances). This type of storage
* was intended specially for operation modes feature.</p>
* @see SuperSubstance
* @see OperationMode
*/
public final class SuperStorage implements StorageInterface {
private final Map<String, SuperSubstance> data = new LinkedHashMap<String, SuperSubstance>();
private final SolutionInterface solution;
private int agentsLimit = Integer.MAX_VALUE;
/**
* This one keeps current maximum length for the connected component stored here.
* We use it to optimize the work of tryIncrement() method.
*/
private int maxComponentLength = 0;
SuperStorage(SolutionInterface solution) {
this.solution = solution;
}
/**
* This method searches for the super substance where this component could be
* added to. And if it finds it, then it increments substance's counter and returns <tt>true</tt>
* @param component component to be added
* @return <tt>true</tt> if there is super substance and we successfully incremented it's counter,
* otherwise <tt>false</tt>
*/
public final boolean tryIncrement(ConnectedComponentInterface component) {
if (component.getAgents().size() <= maxComponentLength) {
String hash = component.getSmilesString();
SuperSubstance previousEntry = data.get(hash);
if (previousEntry != null) {
previousEntry.add();
// there could be some injections here even after the negative update!
component.deleteIncomingInjections();
previousEntry.getComponent().incrementIncomingInjections();
return true;
}
}
return false;
}
private final void refreshMaxLength(ConnectedComponentInterface component) {
maxComponentLength = Math.max(maxComponentLength, component.getAgents().size());
}
/**
* This method should be used only on the initialization stage, when adding new components
* to solution. It
* @param quant
* @param component
*/
final void addOrEvenIncrement(long quant, ConnectedComponentInterface component) {
String hash = component.getSmilesString();
SuperSubstance substanceToAdd = new SuperSubstance(quant, component);
SuperSubstance previousEntry = data.get(hash);
if (previousEntry == null) {
data.put(hash, substanceToAdd);
refreshMaxLength(component);
} else {
previousEntry.add(substanceToAdd.getQuantity());
}
}
@Override
public final void addConnectedComponent(ConnectedComponentInterface component) {
if (!this.tryIncrement(component)) {
SuperSubstance s = new SuperSubstance(1, component);
refreshMaxLength(component);
data.put(s.getHash(), s);
component.deleteIncomingInjections();
setInjectionsForSuperSubstance(s);
}
}
@Override
public final void clear() {
data.clear();
}
@Override
public final Collection<ConnectedComponentInterface> split() {
List<ConnectedComponentInterface> list = new ArrayList<ConnectedComponentInterface>();
for (SuperSubstance substance : data.values()) {
for (int i = 0; i < substance.getQuantity(); i++) {
list.add(substance.getComponent());
}
}
return list;
}
@Override
// TODO add a chance that we can take the same component twice?
public final ConnectedComponentInterface extractComponent(Injection injection) {
SuperSubstance image = injection.getSuperSubstance();
if (image != null && !image.isEmpty()) {
ConnectedComponentInterface component = this.extract(image);
component.burnIncomingInjections();
return component;
} else {
return null;
}
}
private final ConnectedComponentInterface extract(SuperSubstance image) {
if (!image.isEmpty()) {
ConnectedComponentInterface component = image.extract();
image.setComponent(solution.cloneConnectedComponent(component));
// we don't want to choose an injection built to empty SS
if (!image.isEmpty()) {
setInjectionsForSuperSubstance(image);
} else {
// TODO do we really want to remove this component from collection?
data.remove(image.getHash());
image.getComponent().deleteIncomingInjections();
}
return component;
} else {
// not reachable
return null;
}
}
private final void setInjectionsForSuperSubstance(SuperSubstance substance) {
(new InjectionsBuilder(solution.getKappaSystem())).build(substance);
}
/**
* This method returns collection of SuperSubstances contained in this storage.
* Method specified for this type of storage only.
* @return collection of super substances
*/
public final Collection<SuperSubstance> getComponents() {
return data.values();
}
/**
* This method retrieves the limit for the length of connected component allowed
* to be stored in super substance. Default value is Integer.MAX_VALUE
* @return limit for the agents' quantity in connected component
*/
public final int getAgentsLimit() {
return agentsLimit;
}
/**
* This method sets the limit for the length of connected component allowed
* to be stored in super substance
* @param limit limit for the agents' quantity in connected component
*/
public final void setAgentsLimit(int limit) {
agentsLimit = limit;
}
}