CHESSIoT_Dev_old-master/org.polarsys.chess.service/src/org/polarsys/chess/service/core/utils/AnalysisResultUtil.java
/*******************************************************************************
 * Copyright (C) 2018 Fondazione Bruno Kessler.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 * 
 * Contributors:
 *     Luca Cristoforetti - initial API and implementation
 ******************************************************************************/
package org.polarsys.chess.service.core.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.papyrus.uml.tools.model.UmlModel;
import org.eclipse.papyrus.uml.tools.model.UmlUtils;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Component;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PackageableElement;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.UMLPackage;
import org.polarsys.chess.chessmlprofile.Dependability.DependableComponent.Analysis;
import org.polarsys.chess.chessmlprofile.Dependability.DependableComponent.AnalysisContextElement;
import org.polarsys.chess.chessmlprofile.ParameterizedArchitecture.InstantiatedArchitectureConfiguration;
import org.polarsys.chess.contracts.profile.chesscontract.util.EntityUtil;
import org.polarsys.chess.core.util.uml.UMLUtils;
import org.polarsys.chess.service.gui.utils.SelectionUtil;

import eu.fbk.eclipse.standardtools.utils.ui.utils.CommandBuilder;
import eu.fbk.eclipse.standardtools.utils.ui.utils.DialogUtil;
import eu.fbk.eclipse.standardtools.utils.ui.utils.DirectoryUtil;
import eu.fbk.eclipse.standardtools.utils.ui.utils.ErrorsDialogUtil;

/**
 * Utility class to store the results of an analysis.
 * 
 * @author cristofo
 *
 */
public class AnalysisResultUtil {
	private static final String ANALYSIS_CONTEXT_ELEMENT = "CHESS::Dependability::DependableComponent::AnalysisContextElement";
	private static final String ANALYSIS_VIEW = "CHESS::Core::CHESSViews::AnalysisView";
	private static final String DEPENDABILITY_ANALYSIS_VIEW = "CHESS::Core::CHESSViews::DependabilityAnalysisView";
	private static final String RESULTS_FILE_PATH = File.separator + "VerificationResults";
	/*
	 * public static final String FMEA_ANALYSIS = "FMEA"; public static final
	 * String FTA_ANALYSIS = "FTA"; public static final String
	 * CONTRACT_BASED_FTA_ANALYSIS = "CONTRACT_FTA"; public static final String
	 * CONTRACT_REFINEMENT_ANALYSIS = "CONTRACT_REF"; public static final String
	 * CONTRACT_IMPLEMENTATION_ANALYSIS = "CONTRACT_IMPL"; public static final
	 * String PROPERTY_VALIDATION_ANALYSIS = "PROP_VAL"; public static final
	 * String CONTRACT_PROPERTY_VALIDATION_ANALYSIS = "CONT_PROP_VAL"; public
	 * static final String MODEL_CHECKING_ANALYSIS = "MODEL_CHECK";
	 */

	public static final String PROPERTY = "property";
	public static final String CHECK_TYPE = "check_type";
	public static final String SELECTED_PROPERTIES = "selected_properties";
	public static final String SELECTED_COMPONENTS = "selected_components";

	private static AnalysisResultUtil packageUtilInstance;
	private final EntityUtil entityUtil = EntityUtil.getInstance();
	private final ErrorsDialogUtil errorsDialogUtil = ErrorsDialogUtil.getInstance();
	private final DialogUtil dialogUtil = DialogUtil.getInstance();

	public static AnalysisResultUtil getInstance() {
		if (packageUtilInstance == null) {
			packageUtilInstance = new AnalysisResultUtil();
		}
		return packageUtilInstance;
	}

	/**
	 * Converts an Iterator to a Collection
	 */
	private <T> Collection<T> iterator2Collection(final Iterator<T> iter) {
		ArrayList<T> list = new ArrayList<T>();
		for (; iter.hasNext();) {
			T item = iter.next();
			list.add(item);
		}
		return list;
	}

	/**
	 * Returns the requested view, starting from the UML model.
	 * 
	 * @return the requested view
	 */
	public Package getDependabilityView() {
		final UmlModel umlModel = UmlUtils.getUmlModel();

		final TreeIterator<EObject> allElements = umlModel.getResource().getAllContents();
		if (allElements != null) {
			final Collection<Package> packages = EcoreUtil.getObjectsByType(iterator2Collection(allElements),
					UMLPackage.eINSTANCE.getPackage());

			for (Package p : packages) {
				if (p.getAppliedStereotype(DEPENDABILITY_ANALYSIS_VIEW) != null) {
					return p;
				}
			}
		}
		errorsDialogUtil.showMessage_GenericError("Error: DependabilityAnalysisView not found!");
		return null;
	}

	/**
	 * Returns the requested view starting from the given package.
	 * 
	 * @param pkg
	 *            the package from which go up and find the wiew
	 * @return the requested view
	 */
	public Package getDependabilityViewFromPackage(Package pkg) {

		final Model model = pkg.getModel();

		// Get the list of first level packages
		EList<Package> modelPackages = model.getNestedPackages();

		Package analysisView = null;
		for (Package modelPackage : modelPackages) {
			if (modelPackage.getAppliedStereotype(ANALYSIS_VIEW) != null) {
				analysisView = modelPackage;
				break;
			}
		}

		if (analysisView == null) {
			errorsDialogUtil.showMessage_GenericError(
					"No <<AnalysisView>> view found. " + "Cannot read or store analyses results.");
			return null;
		}

		// Get the list of second level packages
		modelPackages = analysisView.getNestedPackages();

		for (Package modelPackage : modelPackages) {
			if (modelPackage.getAppliedStereotype(DEPENDABILITY_ANALYSIS_VIEW) != null) {
				return modelPackage;
			}
		}
		errorsDialogUtil.showMessage_GenericError(
				"No <<DependabilityAnalysisView>> view found. " + "Cannot read or store analyses results.");
		return null;
	}

	/**
	 * Applies the ResultElement stereotype to the given element.
	 * 
	 * @param element
	 *            the element
	 * @return the stereotype applied
	 */
	private Stereotype applyAnalysisContextElementStereotype(Component element) {
		return UMLUtils.applyStereotype(element, ANALYSIS_CONTEXT_ELEMENT);
	}

	/**
	 * Returns the local path of the given absolute path.
	 * 
	 * @param filePath
	 *            the absolute file path
	 * @return the local path
	 */
	private String getLocalPath(String filePath) {
		return filePath.substring(filePath.indexOf(RESULTS_FILE_PATH) + 1, filePath.length());
	}

	/**
	 * Returns the ResultElement application from a given UML element.
	 * 
	 * @param element
	 *            the element containing the appplication, if any
	 * @return the application of the stereotype
	 */
	public AnalysisContextElement getAnalysisContextElementFromUmlElement(Element element) {
		final Stereotype appliedStereotype = element.getAppliedStereotype(ANALYSIS_CONTEXT_ELEMENT);
		return (AnalysisContextElement) element.getStereotypeApplication(appliedStereotype);
	}

	/**
	 * Return a kind of qualified name for the given package.
	 * 
	 * @param pkg
	 *            the package
	 * @return the qualified name, without "model" and "::" replaced by "_"
	 */
	public String getPackageName(Package pkg) {
		String name = pkg.getQualifiedName();
		name = name.substring(name.indexOf("::") + 2);
		return name.replace("::", "_");
	}

	/**
	 * Creates a ResultElement and store the given result.
	 * 
	 * @param type
	 *            the type of the analysis
	 * @param conditions
	 *            the specified conditions
	 * @param filePath
	 *            the path of the result file
	 * @param rootComponent
	 *            the root component of the architecture
	 * @param analysisContext
	 *            the context analysis used to run the check
	 * @return true if succeded
	 */
	public boolean createOrUpdateAnalysisContext(final Analysis type, final EList<String> conditions, final String resultFilePath,
			final boolean storeResultFilePath, final Class rootComponent,
			final InstantiatedArchitectureConfiguration instantiatedArchitecture, final AnalysisContextElement analysisContext) {

		final Package activePackage = rootComponent.getNearestPackage();

		// Select the correct view where to store the result
		final Package dependabilityView = getDependabilityViewFromPackage(activePackage);
		if (dependabilityView == null) {
			return false;
		}

		// Start a transaction to modify the package content
		final TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(dependabilityView);
		domain.getCommandStack().execute(new RecordingCommand(domain) {

			@Override
			protected void doExecute() {

				// Get the correct package, or create it if needed
				final String componentPackageName = getPackageName(rootComponent.getNearestPackage());
				Package dependabilityPackage = dependabilityView.getNestedPackage(componentPackageName, false,
						UMLPackage.eINSTANCE.getPackage(), true);

				// Compute the local path to store in the result
				// final String localFilePath = getLocalPath(filePath);

				// Check if the result is already present in the package
				EList<PackageableElement> packageableElements = dependabilityPackage.getPackagedElements();
				for (PackageableElement packageableElement : packageableElements) {

					if (packageableElement != null && packageableElement instanceof Component
							&& (packageableElement.getAppliedStereotype(ANALYSIS_CONTEXT_ELEMENT) != null)) {

						// There is an element with the same name, check to see
						// if it is possible to reuse it
						final AnalysisContextElement resultElement = getAnalysisContextElementFromUmlElement(
								packageableElement);
						if (resultElement.getType().equals(type) && resultElement.getRoot() == rootComponent) {
							if (isEquals(resultElement.getConditions(), conditions)
									& resultElement.getInstantiatedArchitecture()==(instantiatedArchitecture)) {

								// Same analysis, can be updated with the new
								// values
								resultElement.setDate(new Date().toString());
								resultElement.setValid(true);

								// Remove the result file from disk and set the
								// new one
								/*
								 * File resultFile; try { resultFile = new
								 * File(DirectoryUtil.getInstance().
								 * getCurrentProjectDir() + File.separator +
								 * resultElement.getFile());
								 * resultFile.delete(); } catch (Exception e) {
								 * dialogUtil.showMessage_ExceptionError(e);
								 * e.printStackTrace(); }
								 * resultElement.setFile(localFilePath);
								 */
								// File resultFile = new File(resultFilePath);

								if (resultFilePath != null) {
									if (storeResultFilePath) {
										resultElement.setResult(resultFilePath);

									} else {
										try {
											resultElement.setResult(
													new String(Files.readAllBytes(Paths.get(resultFilePath))));
										} catch (IOException e) {
											e.printStackTrace();
										}
									}
								}

								return;
							}
						}
					}
				}

				int currAnalysisContextElement = countAnalysisContextElements(packageableElements, type) + 1;

				// Create a new result element
				final Component umlComponent = (Component) dependabilityPackage.createPackagedElement(
						type.getName() + "_" + currAnalysisContextElement, UMLPackage.eINSTANCE.getComponent());
				final Stereotype appliedStereotype = applyAnalysisContextElementStereotype(umlComponent);
				if (appliedStereotype == null) {
					errorsDialogUtil.showMessage_GenericError(
							"Cannot apply the "+ANALYSIS_CONTEXT_ELEMENT+" stereotype. " + "Is the CHESS model up to date?");
					return;
				}
				final AnalysisContextElement resultElement = (AnalysisContextElement) umlComponent
						.getStereotypeApplication(appliedStereotype);
				if (resultElement != null) {
					resultElement.setType(type);
					resultElement.setDate(new Date().toString());
					if (conditions != null) {
						resultElement.getConditions().addAll(conditions);
					}
					if (instantiatedArchitecture != null) {
						resultElement.setInstantiatedArchitecture(instantiatedArchitecture);
					}
					resultElement.setValid(true);
					if (resultFilePath != null) {
						if (storeResultFilePath) {
							resultElement.setResult(resultFilePath);

						} else {
							try {
								resultElement.setResult(new String(Files.readAllBytes(Paths.get(resultFilePath))));
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
					}
					resultElement.setRoot(rootComponent);
					// resultElement.setContextAnalysis(contextAnalysis);
				}
			}
		});
		return true;
	}

	protected int countAnalysisContextElements(EList<PackageableElement> packageableElements, Analysis type) {

		int numElements = 0;
		for (PackageableElement packageableElement : packageableElements) {
			if (packageableElement != null && packageableElement instanceof Component
					&& (packageableElement.getAppliedStereotype(ANALYSIS_CONTEXT_ELEMENT) != null)) {

				// There is an element with the same name, check to see
				// if it is possible to reuse it
				final AnalysisContextElement resultElement = getAnalysisContextElementFromUmlElement(
						packageableElement);
				if (resultElement.getType().equals(type)) {
					numElements++;
				}
			}
		}
		return numElements;
	}

	boolean isEquals(EList<String> eList, EList<String> conditions) {
		if ((eList == null) && (conditions == null)) {
			return true;
		}
		if ((eList == null) || (conditions == null)) {
			return false;
		}
		if (eList.size() != conditions.size())
			return false;
		for (String s : conditions) {
			if (!eList.contains(s))
				return false;
		}
		return true;
	}

	/**
	 * Returns the selected package, if any.
	 * 
	 * @param event
	 *            the event
	 * @return the selected package
	 */
	private Package getPackageFromSelectedObject(ExecutionEvent event) {
		final ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event).getActivePage().getSelection();
		final Object selectedUmlElement = SelectionUtil.getInstance().getUmlSelectedObject(selection);

		if (selectedUmlElement instanceof Package) {
			return (Package) selectedUmlElement;
		}
		return null;
	}

	/**
	 * Gets the System component from the selected event, if any.
	 * 
	 * @param event
	 *            the event
	 * @return the System component
	 * @throws ExecutionException
	 */
	public Class getSystemComponentFromEvent(ExecutionEvent event) throws ExecutionException {
		final Package pkg = getPackageFromSelectedObject(event);
		if (pkg == null || !entityUtil.isSystemViewPackage(pkg)) {
			final ExecutionException e = new ExecutionException("Please select a package from <<SystemView>>");
			dialogUtil.showMessage_ExceptionError(e);
			throw e;
		}

		final Class systemComponent = EntityUtil.getInstance().getSystemComponent(pkg);
		if (systemComponent == null) {
			final ExecutionException e = new ExecutionException(
					"The package does not have a <<System>> block or it has more than one. Please check.");
			dialogUtil.showMessage_ExceptionError(e);
			throw e;
		}
		return systemComponent;
	}

	/**
	 * Computes the name for the results directory.
	 * 
	 * @return the name that the directory should have
	 */
	private String computeResultDir() {
		try {
			return DirectoryUtil.getInstance().getCurrentProjectDir() + RESULTS_FILE_PATH;
		} catch (Exception e) {
			dialogUtil.showMessage_ExceptionError(e);
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Returns the directory where validation result files are stored. If the
	 * directory is not present, it will be created.
	 * 
	 * @return the string containing the directory
	 */
	public String getResultDir() {
		final String resultDir = computeResultDir();
		final File directory = new File(resultDir);
		if (!directory.exists()) {
			return createResultDir(resultDir);
		} else {
			return resultDir;
		}
	}

	/**
	 * Creates the directory where to store validation results.
	 * 
	 * @return the string containing the directory
	 */
	private String createResultDir(String dirName) {
		try {
			final File directory = new File(dirName);
			if (!directory.mkdirs()) {
				errorsDialogUtil.showMessage_GenericError("Cannot create results directory: " + dirName);
				return null;
			}
			return dirName;
		} catch (Exception e) {
			dialogUtil.showMessage_ExceptionError(e);
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Shows the result of the given function, reading data from the given file.
	 * 
	 * @param functionName
	 *            the name of the function used to obtain the result
	 * @param fileName
	 *            the file containing the data to visualize
	 */
	public void showResult(String functionName, String fileName) {
		final String commandId = "eu.fbk.tools.adapter.ui.commands.ShowVVResultCommand";
		final String functionNameParam = "function_name";
		final String fileNameParam = "result_file";

		CommandBuilder computeFaultTree;
		try {
			computeFaultTree = CommandBuilder.build(commandId);
			computeFaultTree.setParameter(functionNameParam, functionName);
			computeFaultTree.setParameter(fileNameParam, fileName);
			computeFaultTree.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public List<AnalysisContextElement> getAnalysisContexts(Class element,
			InstantiatedArchitectureConfiguration instantiatedArchitecture, Analysis analysisType, Model model) {
		List<AnalysisContextElement> result = new ArrayList<AnalysisContextElement>();
		for (Element elem : model.allOwnedElements()) {
			Stereotype stereo = elem.getAppliedStereotype(ANALYSIS_CONTEXT_ELEMENT);
			if (stereo != null) {
				AnalysisContextElement contextAnalysis = (AnalysisContextElement) elem.getStereotypeApplication(stereo);
				if (contextAnalysis.getType().equals(analysisType) && contextAnalysis.getRoot().equals(element)
						&& contextAnalysis.getInstantiatedArchitecture()==(instantiatedArchitecture)) {
					result.add(contextAnalysis);
				}
			}
		}
		return result;
	}

}
