/** * Copyright (c) 2008 Borland Software Corp. * * 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: * Alexander Shatalin (Borland) - initial API and implementation */ package org.eclipse.gmf.internal.xpand.migration.ui; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.gmf.internal.xpand.RootManager; import org.eclipse.gmf.internal.xpand.build.OawBuilder; import org.eclipse.gmf.internal.xpand.migration.ExpressionMigrationFacade; import org.eclipse.gmf.internal.xpand.util.OawMarkerManager; import org.eclipse.ui.actions.WorkspaceModifyOperation; public class MigrateXpandProject extends WorkspaceModifyOperation { private static final String DEFAULT_TEMPLATES_FOLDER = "templates"; public static final String MIGRATED_ROOT_EXTENSION = "migrated"; private static final String PLUGIN_CLOSING_TAG = "</plugin>"; private static final String PLUGIN_OPENNING_TAG = "<plugin>"; private static final String LF = ExpressionMigrationFacade.LF; private static final String PLUGIN_XML_CHARSET = "UTF8"; private static final String PLUGIN_XML_FILE_NAME = "plugin.xml"; private static final int BIG_NUMBER = 100; private static final String NATIVE_EXTENSIONS_SRC_FOLDER = ".qvtlib"; private static final String NEW_BUILDER_ID = "org.eclipse.gmf.xpand.xpandBuilder"; private static final String QVT_BUILDER_ID = "org.eclipse.m2m.qvt.oml.project.QVTOBuilder"; private static final String QVT_BUIDLER_SRC_CONTAINER_ARG = "src_container"; private static final String TRANSFORMATION_NATURE_ID = "org.eclipse.m2m.qvt.oml.project.QVTONature"; private RootManager rootManager; private IProject selectedProject; private BuildPropertiesManager buildPropertiesManager; private static IProgressMonitor createSubProgressMonitor(IProgressMonitor monitor, String taskName, int numberOfTicks) throws InterruptedException { if (monitor.isCanceled()) { throw new InterruptedException("Process was canceled"); } SubProgressMonitor spm = new SubProgressMonitor(monitor, numberOfTicks); if (taskName != null) { spm.setTaskName(taskName); } return spm; } public MigrateXpandProject(IProject project) { selectedProject = project; } @Override protected void execute(IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { monitor.setTaskName("Migrating Xpand project"); List<? extends IContainer> xpandRoots = new ArrayList<IFolder>(getRootManager().getXpandRootFolders()); if (xpandRoots.isEmpty()) { xpandRoots = Collections.singletonList(getSelectedProject()); } monitor.beginTask("Migrating Xpand project", xpandRoots.size() + BIG_NUMBER * xpandRoots.size() + 4); int totalNumberOfSteps = 0; for (IContainer rootContainer : xpandRoots) { // each root migration requires two additional steps totalNumberOfSteps += 2 + getNumberOfSteps(rootContainer, createSubProgressMonitor(monitor, "Counting xpand resources in: " + rootContainer.getName(), 1)); } IProgressMonitor subMonitor = createSubProgressMonitor(monitor, "Migrating all available xpand root folders", BIG_NUMBER * xpandRoots.size()); subMonitor.beginTask("Migrating all available xpand root folders", totalNumberOfSteps); List<CharSequence> nativeLibraryDeclarations = new ArrayList<CharSequence>(); List<List<IPath>> newXpandRootEntries = new ArrayList<List<IPath>>(); for (IContainer rootContainer : xpandRoots) { newXpandRootEntries.add(migrateXpandRoot(rootContainer, nativeLibraryDeclarations, subMonitor)); } registerNativeLibraries(nativeLibraryDeclarations, createSubProgressMonitor(monitor, "Registering native libraries", 1)); switchToNewXpandBuilder(newXpandRootEntries, createSubProgressMonitor(monitor, "Registering new Xpand builder for the project", 1)); updateXpandRootFile(newXpandRootEntries, createSubProgressMonitor(monitor, "Saving modified Xpand roots information", 1)); getBuildPropertiesManager().save(createSubProgressMonitor(monitor, "Saving build.properties", 1)); OawMarkerManager.deleteMarkers(getSelectedProject()); buildPropertiesManager = null; } private void updateXpandRootFile(List<List<IPath>> newXpandRootEntries, IProgressMonitor monitor) throws InvocationTargetException, CoreException { monitor.beginTask("Saving modified Xpand roots information", 2); StringBuilder sb = new StringBuilder(); for (List<IPath> rootEntry : newXpandRootEntries) { for (int i = 0; i < rootEntry.size(); i++) { if (i > 0) { sb.append(","); } sb.append(rootEntry.get(i).toString()); } sb.append(ExpressionMigrationFacade.LF); } monitor.worked(1); SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1); subMonitor.setTaskName("Saving Xpand root file"); IFile xpandRootFile = getSelectedProject().getFile(RootManager.PROJECT_RELATIVE_PATH_TO_CONFIG_FILE); try { if (xpandRootFile.exists()) { xpandRootFile.setContents(new ByteArrayInputStream(sb.toString().getBytes(xpandRootFile.getCharset())), IFile.FORCE | IFile.KEEP_HISTORY, subMonitor); } else { xpandRootFile.create(new ByteArrayInputStream(sb.toString().getBytes(xpandRootFile.getParent().getDefaultCharset())), true, subMonitor); } } catch (UnsupportedEncodingException e) { throw new InvocationTargetException(e); } } private void switchToNewXpandBuilder(List<List<IPath>> newXpandRootEntries, IProgressMonitor monitor) throws CoreException, InterruptedException { monitor.beginTask("Registering new Xpand builder for the project", 2); IProjectDescription pd = getSelectedProject().getDescription(); ArrayList<ICommand> newBuildCommands = new ArrayList<ICommand>(); ICommand[] buildCommands = pd.getBuildSpec(); boolean addNewXpandBuilder = true; boolean addQVTBuilder = true; for (int i = 0; i < buildCommands.length; i++) { String builderName = buildCommands[i].getBuilderName(); if (OawBuilder.getBUILDER_ID().equals(builderName)) { continue; } if (NEW_BUILDER_ID.equals(builderName)) { addNewXpandBuilder = false; } if (QVT_BUILDER_ID.equals(builderName)) { addQVTBuilder = false; } newBuildCommands.add(buildCommands[i]); } if (addQVTBuilder) { ICommand newCommand = pd.newCommand(); newCommand.setBuilderName(QVT_BUILDER_ID); if (newXpandRootEntries.size() > 0) { List<IPath> firstEntry = newXpandRootEntries.get(0); assert firstEntry.size() > 0; IPath mainIPath = firstEntry.get(0); if (!mainIPath.isAbsolute()) { Map arguments = newCommand.getArguments(); if (arguments == null) { arguments = new HashMap(); } arguments.put(QVT_BUIDLER_SRC_CONTAINER_ARG, mainIPath.toString()); newCommand.setArguments(arguments); } } newBuildCommands.add(newCommand); } if (addNewXpandBuilder) { ICommand newCommand = pd.newCommand(); newCommand.setBuilderName(NEW_BUILDER_ID); newBuildCommands.add(newCommand); } pd.setBuildSpec(newBuildCommands.toArray(new ICommand[newBuildCommands.size()])); ArrayList<String> newNatureIDs = new ArrayList<String>(Arrays.asList(pd.getNatureIds())); if (!newNatureIDs.contains(TRANSFORMATION_NATURE_ID)) { newNatureIDs.add(TRANSFORMATION_NATURE_ID); pd.setNatureIds(newNatureIDs.toArray(new String[newNatureIDs.size()])); } OawMarkerManager.deleteMarkers(getSelectedProject()); monitor.worked(1); getSelectedProject().setDescription(pd, createSubProgressMonitor(monitor, "Saving modified project description", 1)); } private void registerNativeLibraries(List<CharSequence> nativeLibraryDeclarations, IProgressMonitor progressMonitor) throws CoreException, InvocationTargetException, InterruptedException { if (nativeLibraryDeclarations.size() == 0) { progressMonitor.done(); return; } progressMonitor.beginTask("Registering native libraries", 2); IFile pluginXml = getSelectedProject().getFile(PLUGIN_XML_FILE_NAME); StringBuilder pluginXmlContent = new StringBuilder(); int insertPosition; if (pluginXml.exists()) { InputStream is = new BufferedInputStream(pluginXml.getContents()); try { for (int ch = is.read(); ch != -1; ch = is.read()) { if (progressMonitor.isCanceled()) { throw new InterruptedException(); } pluginXmlContent.append((char) ch); } } catch (IOException e) { throw new InvocationTargetException(e); } insertPosition = pluginXmlContent.lastIndexOf(PLUGIN_CLOSING_TAG); if (insertPosition < 0) { throw new InvocationTargetException(new Exception("Incorrect " + PLUGIN_XML_FILE_NAME + " file - " + PLUGIN_CLOSING_TAG + " tag was not found")); } } else { pluginXmlContent.append(PLUGIN_OPENNING_TAG); pluginXmlContent.append(LF); insertPosition = pluginXmlContent.length(); pluginXmlContent.append(PLUGIN_CLOSING_TAG); } progressMonitor.worked(1); pluginXmlContent.insert(insertPosition, getNativeLibraryExtensionPoint(nativeLibraryDeclarations)); try { ByteArrayInputStream inputStream = new ByteArrayInputStream(pluginXmlContent.toString().getBytes(PLUGIN_XML_CHARSET)); IProgressMonitor subProgressMonitor = createSubProgressMonitor(progressMonitor, "Saving new " + PLUGIN_XML_FILE_NAME + "content", 1); if (pluginXml.exists()) { pluginXml.setContents(inputStream, IFile.FORCE | IFile.KEEP_HISTORY, subProgressMonitor); } else { pluginXml.create(inputStream, true, subProgressMonitor); getBuildPropertiesManager().addBinInclude(pluginXml); } } catch (UnsupportedEncodingException e) { throw new InvocationTargetException(e); } } private StringBuilder getNativeLibraryExtensionPoint(List<CharSequence> nativeLibraryDeclarations) { StringBuilder result = new StringBuilder("\t<extension point=\"org.eclipse.m2m.qvt.oml.javaBlackboxUnits\">"); result.append(LF); for (CharSequence declaration : nativeLibraryDeclarations) { result.append("\t\t"); result.append(declaration); } result.append("\t</extension>"); return result.append(LF); } private List<IPath> migrateXpandRoot(IContainer rootContainer, List<CharSequence> nativeLibraryDeclarations, IProgressMonitor progressMonitor) throws InterruptedException, CoreException, InvocationTargetException { IFolder templatesOutputFolder = getTemplatesOutputFolder(rootContainer, createSubProgressMonitor(progressMonitor, "Calculating new templates root folder name", 1)); IFolder nativeExtensionsRoot = getNativeExtensionsSourceRoot(rootContainer, createSubProgressMonitor(progressMonitor, "Creating new source rolot for native extensions", 1)); MigrationVisitor visitor = new MigrationVisitor(rootContainer, templatesOutputFolder, nativeExtensionsRoot, getSelectedProject(), getRootManager(), getBuildPropertiesManager(), progressMonitor); acceptVisitor(rootContainer, visitor); visitor.done(); nativeLibraryDeclarations.addAll(visitor.getNativeLibraryDeclarations()); getBuildPropertiesManager().addBinInclude(templatesOutputFolder); return getRootManager().getMigratedXpandRootEntry(rootContainer, templatesOutputFolder); } private int getNumberOfSteps(IContainer rootContainer, IProgressMonitor progressMonitor) throws CoreException, InterruptedException, InvocationTargetException { progressMonitor.beginTask("Counting xpand resources in: " + rootContainer.getName(), 1); ResourceCountingVisitor counter = new ResourceCountingVisitor(progressMonitor); acceptVisitor(rootContainer, counter); progressMonitor.done(); return counter.getNumberOfSteps(); } private void acceptVisitor(IResource resource, AbstractMigrationVisitor visitor) throws InterruptedException, CoreException, InvocationTargetException { try { resource.accept(visitor); } catch (CoreException e) { if (e.getCause() instanceof InterruptedException) { throw (InterruptedException) e.getCause(); } else if (e.getCause() != null) { throw new InvocationTargetException(e.getCause()); } else { throw e; } } } private IFolder getTemplatesOutputFolder(IContainer rootContainer, IProgressMonitor progressMonitor) { assert rootContainer instanceof IFolder || rootContainer instanceof IProject; progressMonitor.beginTask("Calculating new templates root folder name", 1); IContainer parent; IPath relativePathBasis; if (rootContainer instanceof IFolder) { parent = rootContainer.getParent(); relativePathBasis = rootContainer.getProjectRelativePath().removeFirstSegments(parent.getProjectRelativePath().segmentCount()); } else { parent = rootContainer; relativePathBasis = new Path(DEFAULT_TEMPLATES_FOLDER); } IPath relativePath = relativePathBasis.addFileExtension(MIGRATED_ROOT_EXTENSION); for (int i = 1; parent.getFolder(relativePath).exists(); i++) { relativePath = relativePathBasis.addFileExtension(MIGRATED_ROOT_EXTENSION + i); } progressMonitor.done(); return parent.getFolder(relativePath); } private IFolder getNativeExtensionsSourceRoot(IContainer rootContainer, IProgressMonitor progressMonitor) throws CoreException, InterruptedException { assert rootContainer instanceof IFolder || rootContainer instanceof IProject; progressMonitor.beginTask("Calculating source root for native extensions", 1); String baseFolderName; if (rootContainer instanceof IFolder) { baseFolderName = rootContainer.getName(); } else { baseFolderName = DEFAULT_TEMPLATES_FOLDER; } String folderName = baseFolderName + NATIVE_EXTENSIONS_SRC_FOLDER; for (int i = 1; getSelectedProject().getFolder(folderName).exists(); i++) { folderName = baseFolderName + NATIVE_EXTENSIONS_SRC_FOLDER + i; } progressMonitor.done(); return getSelectedProject().getFolder(folderName); } private IProject getSelectedProject() { return selectedProject; } private RootManager getRootManager() { if (rootManager == null) { rootManager = new RootManager(getSelectedProject()); } return rootManager; } private BuildPropertiesManager getBuildPropertiesManager() { if (buildPropertiesManager == null) { buildPropertiesManager = new BuildPropertiesManager(getSelectedProject()); } return buildPropertiesManager; } }