/******************************************************************************* * Copyright (c) 2014 École Polytechnique de Montréal * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Mathieu Rail - Initial API and implementation * Guilliano Molaire - Initial API and implementation *******************************************************************************/ package fr.inria.linuxtools.tmf.core.analysis; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.jdt.annotation.NonNull; import fr.inria.linuxtools.tmf.core.trace.ITmfTrace; import fr.inria.linuxtools.tmf.core.trace.ITmfTraceWithPreDefinedEvents; import fr.inria.linuxtools.tmf.core.trace.TmfEventTypeCollectionHelper; /** * Class that contains all the values associated with a type needed by an * analysis in order to execute. Each value is peered with a level that * determines the importance of that specific value for the requirement. * * The type gives an indication about the kind of value the requirement * contains. The value should depend on the type. For instance, a requirement * type could be "event" and all the values that would be added in the * requirement object could indicate the possible events handled by the * analysis. * * For these values, a level will be assigned indicating how important the value * is based on two possibilities: Mandatory or optional. * * Moreover, useful information that can not be leveled with a priority but are * important for the proper execution of an analysis can be added. * * @author Guilliano Molaire * @author Mathieu Rail * @since 3.0 */ public class TmfAnalysisRequirement { /** * String for requirement type 'event', that can be used by analysis */ public static final String TYPE_EVENT = "event"; //$NON-NLS-1$ private final String fType; private final Map<String, ValuePriorityLevel> fValues = new HashMap<>(); private final Set<String> fInformation = new HashSet<>(); /** * The possible level for each value. They must be listed in ascending order * of priority. */ public enum ValuePriorityLevel { /** The value could be absent and the analysis would still work */ OPTIONAL, /** The value must be present at runtime (for the analysis) */ MANDATORY } /** * Constructor * * @param type * The type of the requirement */ public TmfAnalysisRequirement(String type) { fType = type; } /** * Constructor. Instantiate a requirement object with a list of values of * the same level * * @param type * The type of the requirement * @param values * All the values associated with that type * @param level * A level associated with all the values */ public TmfAnalysisRequirement(String type, Iterable<String> values, ValuePriorityLevel level) { fType = type; addValues(values, level); } /** * Merges the values of the specified requirement with those of this * requirement. All new values will retain their priority value level. If a * value was already inside the current requirement, the current priority * level will be overridden if the new priority level is higher. * * @param subRequirement * The requirement to be merged into the current one * @param maxSubRequirementValueLevel * The level associated with all the new values or currently * lower priority ones * @return True if the merge was successful */ public Boolean merge(TmfAnalysisRequirement subRequirement, ValuePriorityLevel maxSubRequirementValueLevel) { /* Two requirements can't be merged if their types are different */ if (!isSameType(subRequirement)) { return false; } Set<String> values = subRequirement.getValues(); for (String value : values) { /* * Sub-requirement value levels are limited to * maxSubRequirementValueLevel, so the level associated with the * values in the merge is the minimum value between * maxSubRequirementValueLevel and its true level. */ int minLevel = Math.min(subRequirement.getValueLevel(value).ordinal(), maxSubRequirementValueLevel.ordinal()); ValuePriorityLevel subRequirementValueLevel = ValuePriorityLevel.values()[minLevel]; if (fValues.containsKey(value)) { /* * If a value is already in a requirement, we update the level * by the highest value between the current level in the * requirement and the level of the value in the * sub-requirement. */ ValuePriorityLevel requirementValueLevel = getValueLevel(value); int newValueLevel = Math.max(requirementValueLevel.ordinal(), subRequirementValueLevel.ordinal()); ValuePriorityLevel highestLevel = ValuePriorityLevel.values()[newValueLevel]; addValue(value, highestLevel); } else { addValue(value, subRequirementValueLevel); } } /* Merge the information */ fInformation.addAll(subRequirement.getInformation()); return true; } /** * Adds a list of value inside the requirement with the same level. * * @param values * A list of value * @param level * The level associated with all the values */ public void addValues(Iterable<String> values, ValuePriorityLevel level) { for (String value : values) { addValue(value, level); } } /** * Adds a value with his associated level into the requirement. If the value * is already contained in the requirement the method modifies its existing * value level. * * @param value * The value * @param level * The level */ public void addValue(String value, ValuePriorityLevel level) { synchronized (fValues) { fValues.put(value, level); } } /** * Adds an information about the requirement. * * @param information * The information to be added */ public void addInformation(String information) { fInformation.add(information); } /** * Determines if the analysis requirement has the same type of another * requirement. * * @param requirement * Requirement whose type is to be compared to this requirement's * type. * @return True if the two requirements have the same type; otherwise false */ public Boolean isSameType(TmfAnalysisRequirement requirement) { return fType.equals(requirement.getType()); } /** * Gets the requirement type. The type is read only. * * @return The type of this requirement */ public String getType() { return fType; } /** * Gets all the values associated with the requirement. * * @return Set containing the values */ public Set<String> getValues() { synchronized (fValues) { return fValues.keySet(); } } /** * Gets all the values associated with the requirement with a given priority * level. * * @param level * The desired level * @return Set containing the values with the given priority level */ public Set<String> getValues(ValuePriorityLevel level) { synchronized (fValues) { Set<String> values = new HashSet<>(); for (Entry<String, ValuePriorityLevel> entry : fValues.entrySet()) { if (entry.getValue() == level) { values.add(entry.getKey()); } } return values; } } /** * Gets information about the requirement. * * @return The set of all the information */ public Set<String> getInformation() { return fInformation; } /** * Gets the level associated with a particular type * * @param value * The value * @return The level or null if the value does not exist */ public ValuePriorityLevel getValueLevel(String value) { synchronized (fValues) { return fValues.get(value); } } /** * Verifies whether a trace fulfills this requirement * * @param trace * The trace on which to check for this requirement * @return True if the trace has all mandatory values of this requirement */ public boolean isFulfilled(@NonNull ITmfTrace trace) { switch (fType) { case TYPE_EVENT: if (trace instanceof ITmfTraceWithPreDefinedEvents) { Set<String> traceEvents = TmfEventTypeCollectionHelper.getEventNames(((ITmfTraceWithPreDefinedEvents) trace).getContainedEventTypes()); Set<String> mandatoryValues = getValues(ValuePriorityLevel.MANDATORY); return traceEvents.containsAll(mandatoryValues); } break; default: return true; } return true; } @Override public String toString() { return fType + ": " + fValues; //$NON-NLS-1$ } }