| /***************************************************************************** |
| * Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, and others. |
| * |
| * 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: |
| * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation |
| * Christian W. Damus - bug 496439 |
| * Vincent Lorenzo - bug 496176 |
| *****************************************************************************/ |
| |
| package org.eclipse.papyrus.interoperability.common.transformation; |
| |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.IJobChangeEvent; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.jobs.JobChangeAdapter; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.papyrus.interoperability.common.Activator; |
| import org.eclipse.papyrus.interoperability.common.MigrationParameters.ThreadConfig; |
| import org.eclipse.papyrus.interoperability.common.internal.Schedulable; |
| import org.eclipse.papyrus.interoperability.common.internal.Scheduler; |
| import org.eclipse.papyrus.interoperability.common.internal.TransformationWrapper; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.console.ConsolePlugin; |
| import org.eclipse.ui.console.IConsole; |
| import org.eclipse.ui.console.IConsoleManager; |
| import org.eclipse.ui.console.MessageConsole; |
| import org.eclipse.ui.console.MessageConsoleStream; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| /** |
| * Executes a batch of {@link ImportTransformation}s, then restores the dependencies (References) |
| * between each other |
| * |
| * @author Vincent Lorenzo |
| * |
| */ |
| public abstract class AbstractImportTransformationLauncher implements IImportTransformationLauncher { |
| |
| // Nano to Second |
| protected final static long SECOND = 1000 * 1000 * 1000; |
| |
| // Nano to Milliseconds |
| protected final static long MILLIS = 1000 * 1000; |
| |
| protected final ThreadConfig config; |
| |
| protected final Control baseControl; |
| |
| /** |
| * Own execution time, in nano-seconds. Doesn't take individual transformation's exec |
| * time into account. Also ignores the time when Blocking user Dialogs are opened |
| */ |
| protected long ownExecutionTime; |
| |
| /** |
| * Own loading time (For initial model loading) |
| */ |
| protected long ownLoadingTime; |
| |
| /** |
| * Own cumulated execution time for repairing stereotypes |
| */ |
| protected long ownRepairStereotypesTime; |
| |
| /** |
| * Own cumulated execution time for repairing libraries |
| */ |
| protected long ownRepairLibrariesTime; |
| |
| /** |
| * Own execution time for resolving all matches for broken profiles/libraries |
| */ |
| protected long resolveAllDependencies; |
| |
| /** |
| * The top-level job for this transformation |
| */ |
| protected Job importDependenciesJob; |
| |
| /** |
| * Total time for all invidivual transformations to complete. Since they are executed in parallel, |
| * this may be different from their cumulated execution time (Unless a single thread is used) |
| */ |
| protected long transformationsExecutionTime = 0L; |
| |
| protected IDependencyAnalysisHelper analysisHelper; |
| |
| public AbstractImportTransformationLauncher(ThreadConfig config) { |
| this(config, null); |
| } |
| |
| public AbstractImportTransformationLauncher(ThreadConfig config, Control baseControl) { |
| this.config = config; |
| this.baseControl = baseControl; |
| } |
| |
| /** |
| * Executes the transformation (Asynchronous) |
| * |
| * @param urisToImport |
| */ |
| public void run(List<URI> urisToImport) { |
| List<IImportTransformation> transformations = new LinkedList<IImportTransformation>(); |
| |
| analysisHelper = createDependencyAnalysisHelper(config); |
| |
| for (URI uri : urisToImport) { |
| IImportTransformation transformation = createTransformation(uri, config, analysisHelper); |
| transformations.add(transformation); |
| } |
| |
| // Always use the batch launcher, even if there is only 1 transformation (Bug 455012) |
| importModels(transformations); |
| } |
| |
| protected abstract IDependencyAnalysisHelper createDependencyAnalysisHelper(final ThreadConfig config); |
| |
| protected abstract IImportTransformation createTransformation(URI transformationURI, final ThreadConfig config, final IDependencyAnalysisHelper analysisHelper); |
| |
| public static final String IMPORT_MODELS_JOB_NAME = "Import Models"; // $NON-NLS-0$ |
| |
| /** |
| * Start a Job and delegate to {@link #importModels(IProgressMonitor, List)} |
| * |
| * @param transformations |
| */ |
| protected void importModels(final List<IImportTransformation> transformations) { |
| importDependenciesJob = new Job(IMPORT_MODELS_JOB_NAME) { |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| IStatus result = AbstractImportTransformationLauncher.this.importModels(monitor, transformations); |
| |
| if (monitor.isCanceled()) { |
| return new Status(IStatus.CANCEL, Activator.PLUGIN_ID, "Operation Canceled"); |
| } |
| |
| long cumulatedLoadingTime = 0L; |
| long cumulatedTransformationTime = 0L; |
| long cumulatedHandleDanglingTime = 0L; |
| long cumulatedImportExtensionsTime = 0L; |
| for (IImportTransformation transformation : transformations) { |
| cumulatedLoadingTime += transformation.getLoadingTime(); |
| cumulatedImportExtensionsTime += transformation.getImportExtensionsTime(); |
| cumulatedHandleDanglingTime += transformation.getHandleDanglingRefTime(); |
| |
| cumulatedTransformationTime += transformation.getExecutionTime(); |
| |
| log("Import " + transformation.getModelName()); |
| log("First phase (0-50%):"); |
| log("\tTotal loading time: " + timeFormat(transformation.getLoadingTime())); |
| log("\tTotal Import Extensions time: " + timeFormat(transformation.getImportExtensionsTime())); |
| log("\tTotal Handle Dangling References time: " + timeFormat(transformation.getHandleDanglingRefTime())); |
| log("\tTotal execution time: " + timeFormat(transformation.getExecutionTime())); |
| |
| Long loadingTime = loadingTimeV2.get(transformation); |
| Long repairProxiesTime = proxiesTime.get(transformation); |
| Long repairStereoTime = stereoTime.get(transformation); |
| Long totalPhase2 = totalTimeV2.get(transformation); |
| |
| log("Second phase (50-100%):"); |
| log("\tTotal loading time: " + timeFormat(loadingTime)); |
| log("\tTotal fix proxies time: " + timeFormat(repairProxiesTime)); |
| log("\tTotal fix stereotypes time: " + timeFormat(repairStereoTime)); |
| log("\tTotal execution time: " + timeFormat(totalPhase2)); |
| |
| log("Total"); |
| log("\tTotal execution time: " + timeFormat(transformation.getExecutionTime() + (totalPhase2 == null ? 0 : totalPhase2))); |
| log("\n"); |
| } |
| |
| int nbThreads = Math.max(1, config.getMaxThreads()); |
| log("First phase (0-50%) / " + nbThreads + " Threads"); |
| log("\tCumulated Transformation Time: " + timeFormat(cumulatedTransformationTime)); |
| log("\tCumulated Loading Time: " + timeFormat(cumulatedLoadingTime)); |
| log("\tCumulated Handle Dangling Refs Time: " + timeFormat(cumulatedHandleDanglingTime)); |
| log("\tCumulated Import Extensions Time: " + timeFormat(cumulatedImportExtensionsTime)); |
| log("\tTotal Transformation Time: " + timeFormat(transformationsExecutionTime)); |
| |
| log("Second phase (50-100%) / " + nbThreads + " Threads"); |
| log("\tTotal Handle all Dangling References: " + timeFormat(resolveAllDependencies)); |
| log("\tCumulated Loading Time: " + timeFormat(ownLoadingTime)); |
| log("\tCumulated Fix Libraries Time: " + timeFormat(ownRepairLibrariesTime)); |
| log("\tCumulated Fix Stereotypes Time: " + timeFormat(ownRepairStereotypesTime)); |
| log("\tTotal Fix Dependencies Time: " + timeFormat(ownExecutionTime)); |
| |
| log("Total"); |
| log("\tCumulated Total time: " + timeFormat(ownExecutionTime + cumulatedTransformationTime)); |
| log("\tTotal time: " + timeFormat(ownExecutionTime + transformationsExecutionTime)); |
| |
| log("Import Complete"); |
| log(""); |
| |
| return result; |
| } |
| |
| }; |
| |
| importDependenciesJob.addJobChangeListener(new JobChangeAdapter() { |
| @Override |
| public void done(IJobChangeEvent event) { |
| |
| MultiStatus multiStatus = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, "", null); |
| |
| multiStatus.merge(event.getResult()); |
| |
| for (IImportTransformation transformation : transformations) { |
| multiStatus.merge(transformation.getStatus()); |
| } |
| |
| int severity = multiStatus.getSeverity(); |
| String message; |
| |
| switch (severity) { |
| case IStatus.OK: |
| message = "The selected models have been successfully imported"; |
| break; |
| case IStatus.CANCEL: |
| message = "Operation canceled"; |
| break; |
| case IStatus.WARNING: |
| message = "The selected models have been imported; some warnings have been reported"; |
| break; |
| default: |
| message = "Some errors occurred during model import"; |
| break; |
| } |
| |
| |
| handle(new MultiStatus(Activator.PLUGIN_ID, severity, multiStatus.getChildren(), message, null)); |
| } |
| |
| protected void handle(final IStatus status) { |
| if (baseControl == null) { |
| int severity = status.getSeverity(); |
| if (severity == IStatus.OK || severity == IStatus.CANCEL) { |
| return; |
| } |
| |
| StatusManager.getManager().handle(status, StatusManager.LOG); |
| return; |
| } |
| |
| Display display = baseControl.getDisplay(); |
| |
| if (status.getSeverity() == IStatus.OK) { |
| display.asyncExec(new Runnable() { |
| |
| @Override |
| public void run() { |
| MessageDialog.openInformation(baseControl.getShell(), "Import models", status.getMessage()); |
| } |
| }); |
| |
| } else if (status.getSeverity() == IStatus.CANCEL) { |
| display.asyncExec(new Runnable() { |
| |
| @Override |
| public void run() { |
| MessageDialog.openInformation(baseControl.getShell(), "Import models", status.getMessage()); |
| } |
| }); |
| } else { |
| StatusManager.getManager().handle(status, StatusManager.BLOCK); |
| } |
| } |
| }); |
| |
| importDependenciesJob.setUser(true); |
| importDependenciesJob.schedule(); |
| } |
| |
| protected void log(String message) { |
| System.out.println(message); |
| |
| MessageConsole console = getConsole(); |
| MessageConsoleStream out = console.newMessageStream(); |
| out.println(message); |
| } |
| |
| protected static final String CONSOLE_NAME = "Model Import Results"; // The name is both the ID and the Label |
| |
| protected MessageConsole getConsole() { |
| ConsolePlugin plugin = ConsolePlugin.getDefault(); |
| IConsoleManager consoleManager = plugin.getConsoleManager(); |
| IConsole[] existing = consoleManager.getConsoles(); |
| for (int i = 0; i < existing.length; i++) { |
| if (CONSOLE_NAME.equals(existing[i].getName())) { |
| return (MessageConsole) existing[i]; |
| } |
| } |
| // no console found, so create a new one |
| MessageConsole rsaConsole = new MessageConsole(CONSOLE_NAME, null); |
| consoleManager.addConsoles(new IConsole[] { rsaConsole }); |
| return rsaConsole; |
| } |
| |
| protected String timeFormat(Long nano) { |
| if (nano == null) { |
| return "?"; // FIXME: crash? |
| } |
| long seconds = nano / SECOND; |
| if (seconds < 1) { |
| long millis = nano / MILLIS; |
| return String.format("%s ms", millis); |
| } |
| return String.format("%d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, (seconds % 60)); |
| } |
| |
| /** |
| * Schedules all the individual transformations, wait for completion, then |
| * call {@link #handleModelDependencies(List, IProgressMonitor)} |
| * |
| * @param monitor |
| * @param transformations |
| * @return |
| */ |
| protected IStatus importModels(IProgressMonitor monitor, List<IImportTransformation> transformations) { |
| |
| long begin = System.nanoTime(); |
| |
| monitor.setTaskName("Waiting for import tasks to complete..."); |
| int numTasks = transformations.size() * 2; // For each transformation: wait for completion, then handle dependencies |
| monitor.beginTask("Importing Models...", numTasks); |
| |
| List<Schedulable> tasks = new LinkedList<Schedulable>(); |
| |
| for (IImportTransformation transformation : transformations) { |
| tasks.add(new TransformationWrapper(transformation)); |
| } |
| |
| Scheduler scheduler = new Scheduler(config.getMaxThreads()); |
| scheduler.schedule(monitor, tasks); |
| |
| long end = System.nanoTime(); |
| transformationsExecutionTime = end - begin; |
| |
| if (monitor.isCanceled()) { |
| return new Status(IStatus.CANCEL, Activator.PLUGIN_ID, "Operation canceled"); |
| } |
| |
| handleModelDependencies(transformations, monitor); |
| |
| return Status.OK_STATUS; |
| } |
| |
| protected void handleModelDependencies(List<IImportTransformation> transformations, IProgressMonitor monitor) { |
| // TODO, seem not required for Rpy import |
| } |
| |
| final protected Map<IImportTransformation, Long> loadingTimeV2 = new HashMap<IImportTransformation, Long>(); |
| |
| final protected Map<IImportTransformation, Long> proxiesTime = new HashMap<IImportTransformation, Long>(); |
| |
| final protected Map<IImportTransformation, Long> stereoTime = new HashMap<IImportTransformation, Long>(); |
| |
| final protected Map<IImportTransformation, Long> totalTimeV2 = new HashMap<IImportTransformation, Long>(); |
| |
| /** Mainly for test purpose */ |
| public void waitForCompletion() throws Exception { |
| importDependenciesJob.join(); |
| } |
| |
| /** Mainly for test purpose */ |
| public IStatus getResult() { |
| return importDependenciesJob.getResult(); |
| } |
| } |