Blackboard.java
package de.uka.ipd.sdq.beagle.core;
import de.uka.ipd.sdq.beagle.core.evaluableexpressions.EvaluableExpression;
import de.uka.ipd.sdq.beagle.core.judge.EvaluableExpressionFitnessFunction;
import de.uka.ipd.sdq.beagle.core.measurement.BranchDecisionMeasurementResult;
import de.uka.ipd.sdq.beagle.core.measurement.LoopRepetitionCountMeasurementResult;
import de.uka.ipd.sdq.beagle.core.measurement.ParameterChangeMeasurementResult;
import de.uka.ipd.sdq.beagle.core.measurement.ResourceDemandMeasurementResult;
import org.apache.commons.lang3.Validate;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Central and only storage of all knowledge gained by Beagle. Implements, together with
* {@link AnalysisController}, the Blackboard pattern from POSA I. The Blackboard’s
* vocabularies are: {@link ResourceDemandingInternalAction}, {@link SeffBranch},
* {@link SeffLoop}, {@link ResourceDemandMeasurementResult},
* {@link BranchDecisionMeasurementResult} , {@link LoopRepetitionCountMeasurementResult}
* and {@link EvaluableExpression}. It further allows classes to store custom data.
*
* <p>The Blackboard is typically not accessed directly by its using classes, but through
* <em>blackboard views</em> (recognisable by having the {@code BlackboardView} suffix).
* These are surrogates for the blackboard. They don’t modify its contents but only
* restrict access to it.
*
* @author Christoph Michelbach
* @author Joshua Gleitze
* @author Roman Langrehr
* @author Michael Vogt
* @see AnalysisController
*/
public class Blackboard implements Serializable {
/**
* Serialisation version UID, see {@link java.io.Serializable}.
*/
private static final long serialVersionUID = 6382577321150787599L;
/**
* All measurable SEFF Elements.
*/
private final Set<MeasurableSeffElement> allSeffElements = new HashSet<>();
/**
* All Resource Demanding Internal Actions.
*/
private final Set<ResourceDemandingInternalAction> rdias = new HashSet<>();
/**
* All SEFF Branches.
*/
private final Set<SeffBranch> branches = new HashSet<>();
/**
* All SEFF Loops.
*/
private final Set<SeffLoop> loops = new HashSet<>();
/**
* All External Call Parameter.
*/
private final Set<ExternalCallParameter> externalCallParameters = new HashSet<>();
/**
* All resource demanding internal actions which are to be measured.
*/
private final Set<ResourceDemandingInternalAction> rdiasToBeMeasured = new HashSet<>();
/**
* All SEFF branches which are to be measured.
*/
private final Set<SeffBranch> branchesToBeMeasured = new HashSet<>();
/**
* All SEFF loops which are to be count.
*/
private final Set<SeffLoop> loopsToBeMeasured = new HashSet<>();
/**
* All external call parameter which are to be measured.
*/
private final Set<ExternalCallParameter> externalCallParameterToBeMeasured = new HashSet<>();
/**
* All resource demanding internal results.
*/
private final Map<ResourceDemandingInternalAction, Set<ResourceDemandMeasurementResult>> rdiasMeasurementResults =
new HashMap<>();
/**
* All SEFF branches results.
*/
private final Map<SeffBranch, Set<BranchDecisionMeasurementResult>> branchDecisionMeasurementResults =
new HashMap<>();
/**
* All SEFF loop count results.
*/
private final Map<SeffLoop, Set<LoopRepetitionCountMeasurementResult>> loopRepititionCountMeasurementResults =
new HashMap<>();
/**
* All parameter change results.
*/
private final Map<ExternalCallParameter, Set<ParameterChangeMeasurementResult>> parameterChangeMeasurementResults =
new HashMap<>();
/**
* All evaluable expressions.
*/
private final Map<MeasurableSeffElement, Set<EvaluableExpression>> proposedExpressions = new HashMap<>();
/**
* Is the final expression.
*/
private final Map<MeasurableSeffElement, EvaluableExpression> finalExpressions = new HashMap<>();
/**
* Is the function to get a better evaluable expression result.
*/
private final EvaluableExpressionFitnessFunction fitnessFunction;
/**
* Private data of tools, written through {@link #writeFor(Class, Serializable)}.
*/
private final Map<Class<? extends BlackboardStorer<? extends Serializable>>, Object> privateWrittenData =
new HashMap<>();
/**
* Information about the project belonging to this blackboard.
*/
private final ProjectInformation projectInformation;
/**
* Creates a new blackboard that can be used to analyse the given elements.
*
* @param rdias All resource demanding internal action to be known to analysers.
* @param branches All SEFF branches to be known to analysers.
* @param loops All SEFF loops to be known to analysers.
* @param externalCalls All external call parameter to be known to analysers.
* @param fitnessFunction The function to get better evaluable expression results.
* @param projectInformation Information about the project belonging to this
* blackboard.
*/
public Blackboard(final Set<ResourceDemandingInternalAction> rdias, final Set<SeffBranch> branches,
final Set<SeffLoop> loops, final Set<ExternalCallParameter> externalCalls,
final EvaluableExpressionFitnessFunction fitnessFunction, final ProjectInformation projectInformation) {
Validate.noNullElements(rdias);
Validate.noNullElements(branches);
Validate.noNullElements(loops);
Validate.noNullElements(externalCalls);
Validate.notNull(fitnessFunction);
this.rdias.addAll(rdias);
this.branches.addAll(branches);
this.loops.addAll(loops);
this.externalCallParameters.addAll(externalCalls);
this.fitnessFunction = fitnessFunction;
this.projectInformation = projectInformation;
this.allSeffElements.addAll(rdias);
this.allSeffElements.addAll(branches);
this.allSeffElements.addAll(loops);
this.allSeffElements.addAll(externalCalls);
for (final SeffBranch branch : this.branches) {
this.branchDecisionMeasurementResults.put(branch, new HashSet<>());
}
for (final SeffLoop loop : this.loops) {
this.loopRepititionCountMeasurementResults.put(loop, new HashSet<>());
}
for (final ExternalCallParameter parameter : this.externalCallParameters) {
this.parameterChangeMeasurementResults.put(parameter, new HashSet<>());
}
for (final ResourceDemandingInternalAction rdia : this.rdias) {
this.rdiasMeasurementResults.put(rdia, new HashSet<>());
}
for (final MeasurableSeffElement element : this.allSeffElements) {
this.proposedExpressions.put(element, new HashSet<>());
}
}
/**
* All {@linkplain ResourceDemandingInternalAction resource demanding internal
* actions} known to Beagle.
*
* @return all {@linkplain ResourceDemandingInternalAction resource demanding internal
* actions} known to Beagle. Changes to the returned set will not modify the
* blackboard content. Is never {@code null}.
*/
public Set<ResourceDemandingInternalAction> getAllRdias() {
return new HashSet<>(this.rdias);
}
/**
* All {@linkplain SeffBranch SEFF branches} known to Beagle.
*
* @return all {@linkplain SeffBranch SEFF branches} known to Beagle. Changes to the
* returned set will not modify the blackboard content. Is never {@code null}.
*/
public Set<SeffBranch> getAllSeffBranches() {
return new HashSet<>(this.branches);
}
/**
* All {@linkplain SeffLoop SEFF loops} known to Beagle.
*
* @return all {@linkplain SeffLoop SEFF loops} known to Beagle. Changes to the
* returned set will not modify the blackboard content. Is never {@code null}.
*/
public Set<SeffLoop> getAllSeffLoops() {
return new HashSet<>(this.loops);
}
/**
* Returns all {@linkplain ExternalCallParameter external call parameters} known to
* Beagle.
*
* @return All {@linkplain ExternalCallParameter external call parameters} known to
* Beagle. Is never {@code null}.
*/
public Set<ExternalCallParameter> getAllExternalCallParameters() {
return new HashSet<>(this.externalCallParameters);
}
/**
* {@linkplain ResourceDemandingInternalAction RDIAs} that shall be measured for their
* resource demands.
*
* @return All {@linkplain ResourceDemandingInternalAction resource demanding internal
* actions} to be measured. Changes to the returned set will not modify the
* blackboard content. Is never {@code null}.
*/
public Set<ResourceDemandingInternalAction> getRdiasToBeMeasured() {
return new HashSet<>(this.rdiasToBeMeasured);
}
/**
* {@linkplain SeffBranch SEFF branches} that shall be measured for their branch
* decisions.
*
* @return All {@linkplain SeffBranch SEFF branches} to be measured. Changes to the
* returned set will not modify the blackboard content. Is never {@code null}.
*/
public Set<SeffBranch> getSeffBranchesToBeMeasured() {
return new HashSet<>(this.branchesToBeMeasured);
}
/**
* {@linkplain SeffLoop SEFF loops} that shall be measured for their repetitions.
*
* @return All {@linkplain SeffLoop SEFF loops} to be measured. Changes to the
* returned set will not modify the blackboard content. Is never {@code null}.
*/
public Set<SeffLoop> getSeffLoopsToBeMeasured() {
return new HashSet<>(this.loopsToBeMeasured);
}
/**
* Returns all {@linkplain ExternalCallParameter external call parameters} which shall
* be measured.
*
* @return All {@linkplain ExternalCallParameter external call parameters} which shall
* be measured. Is never {@code null}.
*/
public Set<ExternalCallParameter> getExternalCallParametersToBeMeasured() {
return new HashSet<>(this.externalCallParameterToBeMeasured);
}
/**
* Reports that {@code rdias} shall be measured for its resource demands.
*
* @param toMeasureRdias Resource demanding internal actions that shall be measured.
* Must not be {@code null} and must be known to this blackboard.
* @see #addToBeMeasuredRdias(Collection)
*/
public void addToBeMeasuredRdias(final ResourceDemandingInternalAction... toMeasureRdias) {
Validate.noNullElements(toMeasureRdias);
Validate.isTrue(this.rdias.containsAll(Arrays.asList(toMeasureRdias)),
"toMeasureRdias may only contain elements that are already on the blackboard");
this.rdiasToBeMeasured.addAll(Arrays.asList(toMeasureRdias));
}
/**
* Reports that {@code rdias} shall be measured for its resource demands.
*
* @param toMeasureRdias Resource demanding internal actions that shall be measured.
* Must not be {@code null} and must be known to this blackboard.
* @see #addToBeMeasuredRdias(ResourceDemandingInternalAction...)
*/
public void addToBeMeasuredRdias(final Collection<ResourceDemandingInternalAction> toMeasureRdias) {
Validate.noNullElements(toMeasureRdias);
Validate.isTrue(this.rdias.containsAll(toMeasureRdias),
"toMeasureRdias may only contain elements that are already on the blackboard");
this.rdiasToBeMeasured.addAll(toMeasureRdias);
}
/**
* Reports that {@code branches} shall be measured for its branch decisions.
*
* @param toMeasureBranches SEFF branches that shall be measured. Must not be
* {@code null} and must be known to this blackboard.
* @see #addToBeMeasuredSeffBranches(Collection)
*/
public void addToBeMeasuredSeffBranches(final SeffBranch... toMeasureBranches) {
Validate.noNullElements(toMeasureBranches);
Validate.isTrue(this.branches.containsAll(Arrays.asList(toMeasureBranches)),
"toMeasureBranches may only contain elements that are already on the blackboard");
this.branchesToBeMeasured.addAll(Arrays.asList(toMeasureBranches));
}
/**
* Reports that {@code branches} shall be measured for its branch decisions.
*
* @param toMeasureBranches SEFF branches that shall be measured. Must not be
* {@code null} and must be known to this blackboard.
* @see #addToBeMeasuredSeffBranches(SeffBranch...)
*/
public void addToBeMeasuredSeffBranches(final Collection<SeffBranch> toMeasureBranches) {
Validate.noNullElements(toMeasureBranches);
Validate.isTrue(this.branches.containsAll(toMeasureBranches),
"toMeasureBranches may only contain elements that are already on the blackboard");
this.branchesToBeMeasured.addAll(toMeasureBranches);
}
/**
* Reports that {@code loops} shall be measured for its repetitions.
*
* @param toMeasureLoops SEFF Loops that shall be measured. Must not be {@code null}
* and must be known to this blackboard.
* @see #addToBeMeasuredSeffLoops(Collection)
*/
public void addToBeMeasuredSeffLoops(final SeffLoop... toMeasureLoops) {
Validate.noNullElements(toMeasureLoops);
Validate.isTrue(this.loops.containsAll(Arrays.asList(toMeasureLoops)),
"toMeasureLoops may only contain elements that are already on the blackboard");
this.loopsToBeMeasured.addAll(Arrays.asList(toMeasureLoops));
}
/**
* Reports that {@code loops} shall be measured for its repetitions.
*
* @param toMeasureLoops SEFF Loops that shall be measured. Must not be {@code null}
* and must be known to this blackboard.
* @see #addToBeMeasuredSeffLoops(SeffLoop...)
*/
public void addToBeMeasuredSeffLoops(final Collection<SeffLoop> toMeasureLoops) {
Validate.noNullElements(toMeasureLoops);
Validate.isTrue(this.loops.containsAll(toMeasureLoops),
"toMeasureLoops may only contain elements that are already on the blackboard");
this.loopsToBeMeasured.addAll(toMeasureLoops);
}
/**
* Reports that {@code parameters} shall be measured.
*
* @param parameters external call parameters that shall be measured. Must not be
* {@code null} and must be known to this blackboard.
* @see #addToBeMeasuredExternalCallParameters(Collection)
*/
public void addToBeMeasuredExternalCallParameters(final ExternalCallParameter... parameters) {
Validate.noNullElements(parameters);
Validate.isTrue(this.externalCallParameters.containsAll(Arrays.asList(parameters)),
"parameters may only contain elements that are already on the blackboard");
this.externalCallParameterToBeMeasured.addAll(Arrays.asList(parameters));
}
/**
* Reports that {@code parameters} shall be measured.
*
* @param parameters external call parameters that shall be measured. Must not be
* {@code null} and must be known to this blackboard.
* @see #addToBeMeasuredExternalCallParameters(ExternalCallParameter...)
*/
public void addToBeMeasuredExternalCallParameters(final Collection<ExternalCallParameter> parameters) {
Validate.noNullElements(parameters);
Validate.isTrue(this.externalCallParameters.containsAll(parameters),
"parameters may only contain elements that are already on the blackboard");
this.externalCallParameterToBeMeasured.addAll(parameters);
}
/**
* Clears the list of {@code rdiasToBeMeasured}.
*
*/
public void clearToBeMeasuredRdias() {
this.rdiasToBeMeasured.clear();
}
/**
* Clears the list of {@code branchesToBeMeasured}.
*
*/
public void clearToBeMeasuredBranches() {
this.branchesToBeMeasured.clear();
}
/**
* Clears the list of {@code loopsToBeMeasured}.
*
*/
public void clearToBeMeasuredLoops() {
this.loopsToBeMeasured.clear();
}
/**
* Clears the list of {@code externalCallParameterToBeMeasured}.
*
*/
public void clearToBeMeasuredExternalCalls() {
this.externalCallParameterToBeMeasured.clear();
}
/**
* Gets all results yet measured for the resource demands of {@code rdia}.
*
* @param rdia An resource demanding internal action to get the measurement results
* of. Must not be {@code null}.
* @return All measurement results reported for {@code rdia}. Changes to the returned
* set will not modify the blackboard content. Is never {@code null}.
*/
public Set<ResourceDemandMeasurementResult> getMeasurementResultsFor(final ResourceDemandingInternalAction rdia) {
Validate.notNull(rdia);
Validate.isTrue(this.rdias.contains(rdia));
return new HashSet<>(this.rdiasMeasurementResults.get(rdia));
}
/**
* Gets all results yet measured for branch decisions of {@code branch}.
*
* @param branch A SEFF Branch to get the measurement results of. Must not be
* {@code null}.
* @return All measurement results reported for {@code branch}. Changes to the
* returned set will not modify the blackboard content. Is never {@code null}.
*/
public Set<BranchDecisionMeasurementResult> getMeasurementResultsFor(final SeffBranch branch) {
Validate.notNull(branch);
Validate.isTrue(this.branches.contains(branch));
return new HashSet<>(this.branchDecisionMeasurementResults.get(branch));
}
/**
* Gets all results yet measured for the loop repetitions of {@code loop}.
*
* @param loop A SEFF Loop to get the measurement results of. Must not be {@code null}
* .
* @return All measurement results reported for {@code loop}. Changes to the returned
* set will not modify the blackboard content. Is never {@code null}.
*/
public Set<LoopRepetitionCountMeasurementResult> getMeasurementResultsFor(final SeffLoop loop) {
Validate.notNull(loop);
Validate.isTrue(this.loops.contains(loop));
return new HashSet<>(this.loopRepititionCountMeasurementResults.get(loop));
}
/**
* Gets all results yet measured for the external parameter
* {@code externalCallParameter}.
*
* @param externalCallParameter An external parameter to get the measurement results
* of. Must not be {@code null}.
* @return All measurement results reported for {@code externalCallParameter}. Changes
* to the returned set will not modify the blackboard content. Is never
* {@code null}.
*/
public Set<ParameterChangeMeasurementResult> getMeasurementResultsFor(
final ExternalCallParameter externalCallParameter) {
Validate.notNull(externalCallParameter);
Validate.isTrue(this.externalCallParameters.contains(externalCallParameter));
return new HashSet<>(this.parameterChangeMeasurementResults.get(externalCallParameter));
}
/**
* Adds a measurement result for the provided {@code rdia}.
*
* @param rdia A resource demanding internal action that was measured. Must not be
* {@code null} .
* @param results The result of that measurement. Must not be {@code null}.
*/
public void addMeasurementResultFor(final ResourceDemandingInternalAction rdia,
final ResourceDemandMeasurementResult results) {
Validate.notNull(rdia);
Validate.notNull(results);
Validate.isTrue(this.rdias.contains(rdia), "rdia must already be on the blackboard");
this.rdiasMeasurementResults.get(rdia).add(results);
}
/**
* Adds a measurement result for the provided {@code branch}.
*
* @param branch A SEFF Branch which was measured. Must not be {@code null}.
* @param results The result of that measurement. Must not be {@code null}.
*/
public void addMeasurementResultFor(final SeffBranch branch, final BranchDecisionMeasurementResult results) {
Validate.notNull(branch);
Validate.notNull(results);
Validate.isTrue(this.branches.contains(branch), "branch must already be on the blackboard");
this.branchDecisionMeasurementResults.get(branch).add(results);
}
/**
* Adds a measurement result for the provided {@code loop}.
*
* @param loop A SEFF Loop which was measured. Must not be {@code null}.
* @param results The result of that measurement. Must not be {@code null}.
*/
public void addMeasurementResultFor(final SeffLoop loop, final LoopRepetitionCountMeasurementResult results) {
Validate.notNull(loop);
Validate.notNull(results);
Validate.isTrue(this.loops.contains(loop), "loop must already be on the blackboard");
this.loopRepititionCountMeasurementResults.get(loop).add(results);
}
/**
* Adds a measurement result for the provided {@code loop}.
*
* @param parameter An external call parameter which was measured. Must not be
* {@code null}.
* @param results The result of that measurement. Must not be {@code null}.
*/
public void addMeasurementResultFor(final ExternalCallParameter parameter,
final ParameterChangeMeasurementResult results) {
Validate.notNull(parameter);
Validate.notNull(results);
Validate.isTrue(this.externalCallParameters.contains(parameter), "parameter must already be on the blackboard");
this.parameterChangeMeasurementResults.get(parameter).add(results);
}
/**
* Returns a set of all {@linkplain EvaluableExpression evaluable expressions}
* proposed for {@code element}.
*
* @param element A SEFF element. Must not be {@code null}.
* @return A set of all {@linkplain EvaluableExpression evaluable expressions}
* proposed for {@code element}.
*/
public Set<EvaluableExpression> getProposedExpressionFor(final MeasurableSeffElement element) {
Validate.notNull(element);
Validate.isTrue(this.allSeffElements.contains(element), "element must already be on the blackboard");
return new HashSet<>(this.proposedExpressions.get(element));
}
/**
* Adds {@code expression} as a proposal.
*
* @param element A SEFF element. Must not be {@code null}.
* @param expression An evaluable expression proposed to describe {@code element}’s
* measurement results. Must not be {@code null}.
*/
public void addProposedExpressionFor(final MeasurableSeffElement element, final EvaluableExpression expression) {
Validate.notNull(element);
Validate.notNull(expression);
Validate.isTrue(this.allSeffElements.contains(element), "element must already be on the blackboard");
this.proposedExpressions.get(element).add(expression);
}
/**
* Returns the final expression set for {@code element}. The return value of this
* method may change if
* {@link #setFinalExpressionFor(MeasurableSeffElement, EvaluableExpression)} is
* called with the same {@code element} between calls to
* {@code addProposedExpressionFor} with this element as parameter.
*
* @param element A SEFF element. Must not be {@code null}.
* @return The expression momentarily marked to be the final for {@code element}.
* {@code null} if no expression has been marked yet.
*/
public EvaluableExpression getFinalExpressionFor(final MeasurableSeffElement element) {
Validate.notNull(element);
Validate.isTrue(this.allSeffElements.contains(element), "element must already be on the blackboard");
return this.finalExpressions.get(element);
}
/**
* Sets {@code expression} as the final description of {@code element}’s measurement
* results. Consecutive calls to this method with the same {@code element} will
* override previous settings.
*
* @param element A SEFF element Must not be {@code null}.
* @param expression An evaluable expression describing {@code element}’s measurement
* results. May be {@code null} to describe that no suitable expression was
* found.
*/
public void setFinalExpressionFor(final MeasurableSeffElement element, final EvaluableExpression expression) {
Validate.notNull(element);
Validate.isTrue(this.allSeffElements.contains(element), "element must already be on the blackboard");
this.finalExpressions.put(element, expression);
}
/**
* Returns an object which holds and is responsible allows access to the fitness
* function grading {@linkplain EvaluableExpression evaluable expressions} regarding
* their fitness.
*
* @return An object which holds and is responsible allows access to the fitness
* function grading {@linkplain EvaluableExpression evaluable expressions}
* regarding their fitness.
*/
public EvaluableExpressionFitnessFunction getFitnessFunction() {
return this.fitnessFunction;
}
/**
* Writes data for {@code writer} to the blackboard. This method serves as a type safe
* mean for tools to store data that is not part of their results. Values stored here
* will never contribute to Beagle’s results. Any class calling this method should
* always pass its own {@linkplain Class} instance as {@code writer}. {@code writer}
* has to implement {@link BlackboardStorer} and thereby declare the type of values
* they write. Calling this method will override any data potentially stored
* previously for the given {@code writer}.
*
* @param writer The class the data should be written for. Must not be {@code null}.
* @param written The data to write.
* @param <WRITTEN_TYPE> {@code written}’s type.
* @see #readFor(Class)
*/
public <WRITTEN_TYPE extends Serializable> void writeFor(
final Class<? extends BlackboardStorer<WRITTEN_TYPE>> writer, final WRITTEN_TYPE written) {
Validate.notNull(writer);
this.privateWrittenData.put(writer, written);
}
/**
* Reads data previously written for {@code writer} through
* {@link #writeFor(Class, Serializable)}.
*
* @param writer The class the desired data was written for. Must not be {@code null}.
* @param <WRITTEN_TYPE> The type of the data to be read.
* @return The data written in the last call to {@linkplain #writeFor} for
* {@code writer}. {@code null} if no data has been written for {@code writer}
* yet.
* @see #writeFor(Class, Serializable)
*/
@SuppressWarnings("unchecked")
public <WRITTEN_TYPE extends Serializable> WRITTEN_TYPE readFor(
final Class<? extends BlackboardStorer<WRITTEN_TYPE>> writer) {
Validate.notNull(writer);
/*
* This performs a cast based on generics. While the cast can not be checked by
* the JVM at runtime, type safety is assured by the signature of {@link
* #writeFor}, which is the only method writing data to {@link
* #privateWrittenData}.
*/
return (WRITTEN_TYPE) this.privateWrittenData.get(writer);
}
/**
* Information about the project belonging to this blackboard.
*
* @return Information about the project belonging to this blackboard.
*/
public ProjectInformation getProjectInformation() {
return this.projectInformation;
}
}