PcmRepositorySeffExtractor.java

package de.uka.ipd.sdq.beagle.core.pcmconnection;

import de.uka.ipd.sdq.beagle.core.Blackboard;
import de.uka.ipd.sdq.beagle.core.CodeSection;
import de.uka.ipd.sdq.beagle.core.ExternalCallParameter;
import de.uka.ipd.sdq.beagle.core.ResourceDemandType;
import de.uka.ipd.sdq.beagle.core.ResourceDemandingInternalAction;
import de.uka.ipd.sdq.beagle.core.SeffBranch;
import de.uka.ipd.sdq.beagle.core.SeffLoop;
import de.uka.ipd.sdq.beagle.core.facade.SourceCodeFileProvider;
import de.uka.ipd.sdq.beagle.core.failurehandling.FailureHandler;
import de.uka.ipd.sdq.beagle.core.failurehandling.FailureReport;
import de.uka.ipd.sdq.beagle.core.pcmsourcestatementlink.PcmSourceStatementLinkRepository;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.palladiosimulator.pcm.seff.AbstractAction;
import org.palladiosimulator.pcm.seff.AbstractBranchTransition;
import org.palladiosimulator.pcm.seff.LoopAction;
import org.palladiosimulator.pcm.seff.ResourceDemandingBehaviour;
import org.palladiosimulator.pcm.seff.impl.BranchActionImpl;
import org.palladiosimulator.pcm.seff.impl.ExternalCallActionImpl;
import org.palladiosimulator.pcm.seff.impl.InternalActionImpl;
import org.palladiosimulator.pcm.seff.impl.LoopActionImpl;
import org.palladiosimulator.pcm.seff.impl.ResourceDemandingBehaviourImpl;

import java.io.FileNotFoundException;
import java.util.HashSet;
import java.util.Set;

/**
 * Extractor class for SEFFElements. Adding all EObjects that are SeffLoops, SeffBranches,
 * ResourceDemandingInternalActions or ExternalCallParameters to given sets.
 *
 * @author Ansgar Spiegler
 */
public class PcmRepositorySeffExtractor {

	/**
	 * The {@link FailureHandler} that is called by FileNotFoundException, giving this
	 * information to the Fail API.
	 */
	private static final FailureHandler FAILURE_HANDLER = FailureHandler.getHandler("Beagle FileNotFound Handler");

	/**
	 * If Somox' generates no more output with multiple identifiers relating to the same
	 * seffElements, this can be set to true.
	 */
	private static final boolean SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS = false;
	
	/**
	 * Temporary storage for all extracted {@link SeffLoop SeffLoops} that should be
	 * written on the {@link Blackboard}.
	 */
	private final Set<SeffLoop> seffLoopSet;

	/**
	 * Temporary storage for all extracted {@link SeffBranch SeffBranch} that should be
	 * written on the {@link Blackboard}.
	 */
	private final Set<SeffBranch> seffBranchSet;

	/**
	 * Temporary storage for all extracted {@link SeffBranch SeffBranches} that should be
	 * written on the {@link Blackboard}.
	 */
	// private final Set<SeffBranch> seffBranchSet;

	/**
	 * Temporary storage for all extracted {@link ResourceDemandingInternalAction rdSeffs}
	 * that should be written on the {@link Blackboard}.
	 */
	private final Set<ResourceDemandingInternalAction> rdiaSet;

	/**
	 * Temporary storage for all extracted {@link ExternalCallParameter} that should be
	 * written on the {@link Blackboard}.
	 */
	private final Set<ExternalCallParameter> externalCallParameterSet;

	/**
	 * Integer with value 0.
	 */
	private final int zero = 0;

	/**
	 * The {@link PcmNameParser} that is needed for parsing the EntityNames created by
	 * SoMoX.
	 */
	// private final PcmNameParser nameParser;

	/**
	 * The object in which all created SeffElements are mapped to their original ID.
	 */
	private final PcmBeagleMappings pcmMapper;

	/**
	 * The {@link PcmCodeSectionGenerator} that creates {@link CodeSection} for given ID.
	 */
	private final PcmCodeSectionGenerator codeSectionGenerator;


	/**
	 * Constructor needs access to the real sets (no copy!), manipulating them by adding
	 * all extracted SeffElements.
	 *
	 * @param seffLoopSet the SeffLoopSet to add SeffLoops
	 * @param seffBranchSet the seffBranchSet to add SeffBranches
	 * @param rdiaSet the rdiaSet to add RDIAS
	 * @param externalCallParameterSet the externalCallParameterSet to add
	 *            externalCallParameters
	 * @param pcmMapper The mapping class that should contain a map of all SeffElements to
	 *            its IDs
	 * @param sourceCodeFileProvider The {@link SourceCodeFileProvider} for the project
	 *            under analysis.
	 * @param sourceStateLinkRepository The {@link PcmSourceStatementLinkRepository} that
	 *            contains a linking between the Pcm elements and SourceCode Sections
	 */
	public PcmRepositorySeffExtractor(final Set<SeffLoop> seffLoopSet, final Set<SeffBranch> seffBranchSet,
		final Set<ResourceDemandingInternalAction> rdiaSet, final Set<ExternalCallParameter> externalCallParameterSet,
		final PcmBeagleMappings pcmMapper, final SourceCodeFileProvider sourceCodeFileProvider,
		final PcmSourceStatementLinkRepository sourceStateLinkRepository) {
		this.seffLoopSet = seffLoopSet;
		this.seffBranchSet = seffBranchSet;
		this.rdiaSet = rdiaSet;
		this.externalCallParameterSet = externalCallParameterSet;
		this.pcmMapper = pcmMapper;

		// this.nameParser = new PcmNameParser(sourceCodeFileProvider);
		this.codeSectionGenerator = new PcmCodeSectionGenerator(sourceStateLinkRepository, sourceCodeFileProvider);
	}

	/**
	 * Extracts the specified SEFF-elements {@link InternalActionImpl},
	 * {@link ExternalCallActionImpl}, {@link BranchActionImpl} and {@link LoopAction}.
	 *
	 * @param eObject Expecting a {@link ResourceDemandingBehaviour} or any eObject that
	 *            has a concrete SEFF-Type.
	 *
	 */
	public void extractBehaviourAndAddToSet(final EObject eObject) {
		if (eObject.getClass() == InternalActionImpl.class) {
			this.addInternalActionToSet((InternalActionImpl) eObject);
		} else if (eObject.getClass() == ExternalCallActionImpl.class) {
			this.addExternalCallActionToSet((ExternalCallActionImpl) eObject);
		} else if (eObject.getClass() == BranchActionImpl.class) {
			this.addBranchActionToSet((BranchActionImpl) eObject);
			this.extractBranchAction((BranchActionImpl) eObject);
		} else if (eObject.getClass() == LoopActionImpl.class) {
			this.addLoopActionToSet((LoopActionImpl) eObject);
			this.extractLoopAction((LoopActionImpl) eObject);
		}
	}

	/**
	 * Extracting the information of a {@link LoopActionImpl}. That means recursively
	 * calling the extraction of its containing {@link ResourceDemandingBehaviour}.
	 *
	 * @param loopAction Action to extract.
	 */
	private void extractLoopAction(final LoopActionImpl loopAction) {
		// MISSING CODE FOR MAPPING ELEMENT
		// final PCMRandomVariable loopVariable =
		// loopAction.getIterationCount_LoopAction();

		final ResourceDemandingBehaviour rdBehave = loopAction.getBodyBehaviour_Loop();
		final EList<AbstractAction> stepBehaviourList = rdBehave.getSteps_Behaviour();
		for (final AbstractAction stepBehaviour : stepBehaviourList) {
			this.extractBehaviourAndAddToSet(stepBehaviour);
		}
	}

	/**
	 * Extracting the information of a {@link BranchActionImpl}. That means recursively
	 * calling the extraction of its containing {@link ResourceDemandingBehaviour}.
	 *
	 * @param branchAction Action to extract.
	 */
	private void extractBranchAction(final BranchActionImpl branchAction) {
		final EList<AbstractBranchTransition> branchActionSpecificBranchList = branchAction.getBranches_Branch();
		for (final EObject branchActionSpecificBranch : branchActionSpecificBranchList) {
			final EList<EObject> specificBranchContentList = branchActionSpecificBranch.eContents();
			for (final EObject specificBranchContent : specificBranchContentList) {
				if (specificBranchContent.getClass() == ResourceDemandingBehaviourImpl.class) {
					for (final EObject stepBehaviour : ((ResourceDemandingBehaviourImpl) specificBranchContent)
						.eContents()) {
						this.extractBehaviourAndAddToSet(stepBehaviour);
					}
				}
			}
		}
	}

	/**
	 * Adding a new {@link ResourceDemandingInternalAction} based on a
	 * {@link InternalActionImpl} to the {@link rdiaSet}. Fails silently if file from
	 * EntityName not found!
	 *
	 * @param internalAction SEFF-Action to add.
	 *
	 */
	private void addInternalActionToSet(final InternalActionImpl internalAction) {

		try {
			final CodeSection codeSection = this.codeSectionGenerator.getCodeSectionForID(internalAction.getId());
			final ResourceDemandingInternalAction rdiaCpu =
				new ResourceDemandingInternalAction(ResourceDemandType.RESOURCE_TYPE_CPU, codeSection);

			if (this.shouldBeIgnoredDueToSomoxCollision(rdiaCpu, internalAction.getId())) {
				return;
				// The other rdiaTypes do not need to be analysed, because if one of them
				// would be present in the mapping,
				// rdiaCPU would be present, too.
			}

			this.rdiaSet.add(rdiaCpu);
			this.pcmMapper.addPcmIdOf(rdiaCpu, internalAction.getId());

			final ResourceDemandingInternalAction rdiaCpuNs =
				new ResourceDemandingInternalAction(ResourceDemandType.RESOURCE_TYPE_CPU_NS, codeSection);
			this.rdiaSet.add(rdiaCpuNs);
			this.pcmMapper.addPcmIdOf(rdiaCpuNs, internalAction.getId());

			final ResourceDemandingInternalAction rdiaHdd =
				new ResourceDemandingInternalAction(ResourceDemandType.RESOURCE_TYPE_HDD, codeSection);
			this.rdiaSet.add(rdiaHdd);
			this.pcmMapper.addPcmIdOf(rdiaHdd, internalAction.getId());

			final ResourceDemandingInternalAction rdiaHddNs =
				new ResourceDemandingInternalAction(ResourceDemandType.RESOURCE_TYPE_HDD_NS, codeSection);
			this.rdiaSet.add(rdiaHddNs);
			this.pcmMapper.addPcmIdOf(rdiaHddNs, internalAction.getId());

			final ResourceDemandingInternalAction rdiaLan =
				new ResourceDemandingInternalAction(ResourceDemandType.RESOURCE_TYPE_NETWORK, codeSection);
			this.rdiaSet.add(rdiaLan);
			this.pcmMapper.addPcmIdOf(rdiaLan, internalAction.getId());

			final ResourceDemandingInternalAction rdiaLanNs =
				new ResourceDemandingInternalAction(ResourceDemandType.RESOURCE_TYPE_NETWORK_NS, codeSection);
			this.rdiaSet.add(rdiaLanNs);
			this.pcmMapper.addPcmIdOf(rdiaLanNs, internalAction.getId());

		} catch (final FileNotFoundException exception) {
			this.handleFailureFor(internalAction, exception);
		}

	}

	/**
	 * Adding a new {@link ExternalCallParameter} based on a
	 * {@link ExternalCallActionImpl} to the {@link externalCallParameterSet}.
	 *
	 * @param externalAction SEFF-Action to add.
	 *
	 */
	private void addExternalCallActionToSet(final ExternalCallActionImpl externalAction) {

		try {
			final CodeSection codeSection = this.codeSectionGenerator.getCodeSectionForID(externalAction.getId());
			final ExternalCallParameter exCallParam = new ExternalCallParameter(codeSection, this.zero);

			if (this.shouldBeIgnoredDueToSomoxCollision(exCallParam, externalAction.getId())) {
				return;
			}

			this.externalCallParameterSet.add(exCallParam);
			this.pcmMapper.addPcmIdOf(exCallParam, externalAction.getId());
		} catch (final FileNotFoundException exception) {
			this.handleFailureFor(externalAction, exception);
		}

		/*
		 * try { final CodeSection codeSection =
		 * this.nameParser.parse(externalAction.getEntityName()); if (codeSection != null)
		 * { final ExternalCallParameter exCallParam = new
		 * ExternalCallParameter(codeSection, this.zero);
		 * this.externalCallParameterSet.add(exCallParam);
		 * this.pcmMapper.addPcmIdOf(exCallParam, externalAction.getId()); } } catch
		 * (final FileNotFoundException fileNotFoundE) {
		 * this.handleFailureFor(externalAction, fileNotFoundE); }
		 */

	}

	/**
	 * Adding a new {@link SeffBranch} based on a {@link BranchActionImpl} to the
	 * {@link seffBranchSet}. Fails silently if file from EntityName not found!
	 *
	 * @param branchAction SEFF-Action to add.
	 *
	 */
	private void addBranchActionToSet(final BranchActionImpl branchAction) {

		try {
			final EList<AbstractBranchTransition> branchTransitionList = branchAction.getBranches_Branch();
			final Set<CodeSection> codeSectionSet = new HashSet<CodeSection>();

			for (AbstractBranchTransition branchTransition : branchTransitionList) {
				codeSectionSet.add(this.codeSectionGenerator
					.getCodeSectionForID(branchTransition.getBranchBehaviour_BranchTransition().getId()));
			}
			final SeffBranch seffBranch = new SeffBranch(codeSectionSet);
			
			if (this.shouldBeIgnoredDueToSomoxCollision(seffBranch, branchAction.getId())) {
				return;
			}
			
			this.seffBranchSet.add(seffBranch);
			this.pcmMapper.addPcmIdOf(seffBranch, branchAction.getId());
		} catch (final FileNotFoundException exception) {
			this.handleFailureFor(branchAction, exception);

		}

		// try {
		// final Set<CodeSection> codeSectionSet = new HashSet<CodeSection>();
		// final CodeSection codeSection =
		// this.nameParser.parse(branchAction.getEntityName());
		// TO DO branches can't be analysed right now because we can't get the code
		// sections
		// if (codeSection != null) {
		// codeSectionSet.add(codeSection);
		// final CodeSection pseudoCodesection = new CodeSection(new
		// File("src/main/resources/pseudoFile.md"), 1,
		// new File("src/main/resources/pseudoFile.md"), 1);
		// codeSectionSet.add(pseudoCodesection);
		// final SeffBranch seffBranch = new SeffBranch(codeSectionSet);
		//
		// this.seffBranchSet.add(seffBranch);
		// this.pcmMapper.addPcmIdOf(seffBranch, branchAction.getId());
		// }
		// } catch (final FileNotFoundException fileNotFoundE) {
		// this.handleFailureFor(branchAction, fileNotFoundE);
		// }
	}

	/**
	 * Adding a new {@link SeffLoop} based on a {@link LoopActionImpl} to the
	 * {@link seffLoopSet}. Fails silently if file from EntityName not found!
	 *
	 * @param loopAction SEFF-Action to add.
	 */
	private void addLoopActionToSet(final LoopActionImpl loopAction) {

		try {
			final CodeSection codeSection =
				this.codeSectionGenerator.getCodeSectionForID(loopAction.getBodyBehaviour_Loop().getId());
			final SeffLoop seffLoop = new SeffLoop(codeSection);
			
			if (this.shouldBeIgnoredDueToSomoxCollision(seffLoop, loopAction.getId())) {
				return;
			}
			
			this.seffLoopSet.add(seffLoop);
			this.pcmMapper.addPcmIdOf(seffLoop, loopAction.getId());
		} catch (final FileNotFoundException exception) {
			this.handleFailureFor(loopAction, exception);
		}

		/*
		 * try {
		 * 
		 * final CodeSection codeSection =
		 * this.nameParser.parse(loopAction.getEntityName()); if (codeSection != null) {
		 * final SeffLoop seffLoop = new SeffLoop(codeSection);
		 * this.seffLoopSet.add(seffLoop); this.pcmMapper.addPcmIdOf(seffLoop,
		 * loopAction.getId()); } } catch (final FileNotFoundException fileNotFoundE) {
		 * this.handleFailureFor(loopAction, fileNotFoundE); }
		 */
	}

	/**
	 * Testing method to prevent Beagle to fail due to Somox producing ambiguous
	 * identifiers.
	 *
	 * @param rdia the seffElement to check
	 * @param identifier the Identifier to check
	 * @return {@code true} if there are no collisions otherwise {@code false}. May be shut down by
	 *         the variable {@link #SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS} and returning always
	 *         {@code false}.
	 */
	private boolean shouldBeIgnoredDueToSomoxCollision(final ResourceDemandingInternalAction rdia,
		final String identifier) {
		if (SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS) {
			return false;
		}

		if (this.pcmMapper.hasPcmIdOf(rdia) && !this.pcmMapper.getPcmIdOf(rdia).equals(identifier)) {
			return true;
		}
		return false;
	}

	/**
	 * Testing method to prevent Beagle to fail due to Somox producing ambiguous
	 * identifiers.
	 *
	 * @param exParam the seffElement to check
	 * @param identifier the Identifier to check
	 * @return {@code true} if there are no collisions otherwise {@code false}. May be shut down by
	 *         the variable {@link #SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS} and returning always
	 *         {@code false}.
	 */
	private boolean shouldBeIgnoredDueToSomoxCollision(final ExternalCallParameter exParam, final String identifier) {
		if (SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS) {
			return false;
		}

		if (this.pcmMapper.hasPcmIdOf(exParam) && !this.pcmMapper.getPcmIdOf(exParam).equals(identifier)) {
			return true;
		}

		return false;
	}
	
	/**
	 * Testing method to prevent Beagle to fail due to Somox producing ambiguous
	 * identifiers.
	 *
	 * @param seffBranch the seffElement to check
	 * @param identifier the Identifier to check
	 * @return {@code true} if there are no collisions otherwise {@code false}. May be shut down by
	 *         the variable {@link #SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS} and returning always
	 *         {@code false}.
	 */
	private boolean shouldBeIgnoredDueToSomoxCollision(final SeffBranch seffBranch, final String identifier) {
		if (SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS) {
			return false;
		}

		if (this.pcmMapper.hasPcmIdOf(seffBranch) && !this.pcmMapper.getPcmIdOf(seffBranch).equals(identifier)) {
			return true;
		}

		return false;
	}
	
	/**
	 * Testing method to prevent Beagle to fail due to Somox producing ambiguous
	 * identifiers.
	 *
	 * @param seffLoop the seffElement to check
	 * @param identifier the Identifier to check
	 * @return {@code true} if there are no collisions otherwise {@code false}. May be shut down by
	 *         the variable {@link #SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS} and returning always
	 *         {@code false}.
	 */
	private boolean shouldBeIgnoredDueToSomoxCollision(final SeffLoop seffLoop, final String identifier) {
		if (SOMOX_IDENTIFIERS_ARE_UNAMBIGUOUS) {
			return false;
		}

		if (this.pcmMapper.hasPcmIdOf(seffLoop) && !this.pcmMapper.getPcmIdOf(seffLoop).equals(identifier)) {
			return true;
		}

		return false;
	}

	/**
	 * Creating a Failure handling action for the specific SeffElement.
	 *
	 * @param loopAction The SeffElement.
	 * @param exception The FileNotFoundException.
	 */
	private void handleFailureFor(final LoopActionImpl loopAction, final FileNotFoundException exception) {
		final FailureReport<Void> failure =
			new FailureReport<Void>().message("The File for ID %s with EntityName %s can not be found!",
				loopAction.getId(), loopAction.getEntityName()).cause(exception).recoverable().retryWith(
					() -> PcmRepositorySeffExtractor.this.addLoopActionToSet(loopAction));
		FAILURE_HANDLER.handle(failure);
	}

	/**
	 * Creating a Failure handling action for the specific SeffElement.
	 *
	 * @param ecAction The SeffElement.
	 * @param exception The FileNotFoundException.
	 */
	private void handleFailureFor(final ExternalCallActionImpl ecAction, final FileNotFoundException exception) {
		final FailureReport<Void> failure =
			new FailureReport<Void>().message("The File for ID %s with EntityName %s can not be found!",
				ecAction.getId(), ecAction.getEntityName()).cause(exception).recoverable().retryWith(
					() -> PcmRepositorySeffExtractor.this.addExternalCallActionToSet(ecAction));
		FAILURE_HANDLER.handle(failure);
	}

	/**
	 * Creating a Failure handling action for the specific SeffElement.
	 *
	 * @param internalAction The SeffElement.
	 * @param exception The FileNotFoundException.
	 */
	private void handleFailureFor(final InternalActionImpl internalAction, final FileNotFoundException exception) {
		final FailureReport<Void> failure =
			new FailureReport<Void>().message("The File for ID %s with EntityName %s can not be found!",
				internalAction.getId(), internalAction.getEntityName()).cause(exception).recoverable().retryWith(
					() -> PcmRepositorySeffExtractor.this.addInternalActionToSet(internalAction));
		FAILURE_HANDLER.handle(failure);
	}

	/**
	 * Creating a Failure handling action for the specific SeffElement.
	 *
	 * @param branchAction The SeffElement.
	 * @param exception The FileNotFoundException.
	 */
	private void handleFailureFor(final BranchActionImpl branchAction, final FileNotFoundException exception) {
		final FailureReport<Void> failure =
			new FailureReport<Void>().message("The File for ID %s with EntityName %s can not be found!",
				branchAction.getId(), branchAction.getEntityName()).cause(exception).recoverable().retryWith(
					() -> PcmRepositorySeffExtractor.this.addBranchActionToSet(branchAction));
		FAILURE_HANDLER.handle(failure);
	}

}