de.ovgu.featureide.migration.impl.DefaultSPLMigrator.java Source code

Java tutorial

Introduction

Here is the source code for de.ovgu.featureide.migration.impl.DefaultSPLMigrator.java

Source

/* FeatureIDE - A Framework for Feature-Oriented Software Development
 * Copyright (C) 2005-2016  FeatureIDE team, University of Magdeburg, Germany
 *
 * This file is part of FeatureIDE.
 * 
 * FeatureIDE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * FeatureIDE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with FeatureIDE.  If not, see <http://www.gnu.org/licenses/>.
 *
 * See http://featureide.cs.ovgu.de/ for further information.
 */
package de.ovgu.featureide.migration.impl;

import static de.ovgu.featureide.fm.core.localization.StringTable.CLASSPATH_OF_PROJECT_TO_MIGRATE_IS_NULL;
import static de.ovgu.featureide.fm.core.localization.StringTable.DESTINATIONFOLDER_NOT_ACCESSIBLE_OR_WRONG_PATH;
import static de.ovgu.featureide.fm.core.localization.StringTable.INTERNAL_ASSERT_MESSAGE_PROJECT_IS_NULL;
import static de.ovgu.featureide.fm.core.localization.StringTable.IS_NOT_OPEN_;
import static de.ovgu.featureide.fm.core.localization.StringTable.JAVA_PROJECTS_COULD_NOT_BE_CREATED;
import static de.ovgu.featureide.fm.core.localization.StringTable.NO_PROJECTS_WERE_SELECTED_FOR_MIGRATION;
import static de.ovgu.featureide.fm.core.localization.StringTable.PROJECT;
import static de.ovgu.featureide.fm.core.localization.StringTable.PROJECT_PROPERTIES_COULD_NOT_BE_COPIED_COMMA__BECAUSE_IT_DOES_NOT_EXIST_;
import static de.ovgu.featureide.fm.core.localization.StringTable.RESTRICTION;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

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.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jface.viewers.IStructuredSelection;

import de.ovgu.featureide.core.CorePlugin;
import de.ovgu.featureide.core.IFeatureProject;
import de.ovgu.featureide.core.builder.IComposerExtensionBase;
import de.ovgu.featureide.fm.core.base.IFeatureModel;
import de.ovgu.featureide.fm.core.base.impl.FMFactoryManager;
import de.ovgu.featureide.fm.ui.handlers.base.SelectionWrapper;
import de.ovgu.featureide.ui.migration.wizard.SPLMigrationDialogSettingsPage;

/**
 * @author Marcus Pinnecke (Feature interface)
 */
@SuppressWarnings(RESTRICTION)
public abstract class DefaultSPLMigrator implements ISPLMigrator {
    public static final String PROJECT_PROPERTIES_FILE_NAME = "project.properties";
    public static final String DEFAULT_PROJECT_NAME = "migratedSPL";
    public static final String ANDROID_NATURE = "com.android.ide.eclipse.adt.AndroidNature";

    /**
     * The new project which contains the software product line, when the
     * migration is done.
     */
    protected IProject newProject;

    /**
     * The (Java) projects which are going to be migrated into a SPL.
     */
    protected Set<IProject> projects;

    protected MigrationConfigurationData configurationData;

    public DefaultSPLMigrator() {
        super();
    }

    @Override
    public void registerProjectsForMigration(Set<IProject> projects) {
        if (projects == null || projects.isEmpty())
            throw new IllegalArgumentException(NO_PROJECTS_WERE_SELECTED_FOR_MIGRATION);

        this.projects = projects;
    }

    @Override
    public void migrate(MigrationConfigurationData configurationData) {
        this.configurationData = configurationData;

        createProject();

        migrateProjects();

        adjustFeatureModel();

        createConfigurationFiles();
    }

    /**
     * The default implementation creates a new java-project in the field
     * {@link #newProject}, opens it, and converts it to a FeatureIDE project.<br>
     * <br>
     * Can be overwritten by extending classes to accomodate
     * {@link IComposerExtensionBase Composers} needs.
     */
    protected void createProject() {
        newProject = SPLMigrationUtils.createProject(configurationData.projectName);

        openProjectHandleExceptions(newProject);

        convertToFeatureProject(configurationData);
    }

    private void openProjectHandleExceptions(IProject project) {
        assert (project != null) : INTERNAL_ASSERT_MESSAGE_PROJECT_IS_NULL;
        try {
            project.open(null);
        } catch (CoreException e) {
            e.printStackTrace();
            CorePlugin.getDefault().logError(e);
        }
    }

    private void convertToFeatureProject(MigrationConfigurationData configurationData) {
        CorePlugin.setupFeatureProject(newProject, configurationData.composer.getId(), configurationData.sourcePath,
                configurationData.configPath, configurationData.buildPath, false);

        CorePlugin.getDefault().addProject(newProject);
    }

    /**
     * The default implementation recursively traverses the folders of the
     * imported projects and copies all contents into a feature folder with the
     * imported projects name in {@code newProject}. Can be overwritten by
     * extending classes to accomodate {@link IComposerExtensionBase Composers}
     * needs.
     */
    protected void migrateProjects() {
        for (IProject project : projects) {
            IPath destinationPath = new Path(configurationData.sourcePath);

            assert newProject.getFolder(destinationPath)
                    .isAccessible() : DESTINATIONFOLDER_NOT_ACCESSIBLE_OR_WRONG_PATH;
            assert project.isOpen() : PROJECT + project.getName() + IS_NOT_OPEN_;

            IPath featureFolderPath = SPLMigrationUtils
                    .setupFolder(newProject.getFolder(destinationPath).getFolder(project.getName()));

            try {
                migrateClassPathDependentContent(project, featureFolderPath);
            } catch (JavaModelException e) {
                e.printStackTrace();
            }

            //         try
            //         {
            //            if(project.hasNature(ANDROID_NATURE))
            //               copyProjectProperties(project, featureFolderPath);
            //         } catch (CoreException e)
            //         {
            //            e.printStackTrace();
            //         }
        }
    }

    @SuppressWarnings("unused")
    private void copyProjectProperties(IProject project, IPath destinationPath) {
        final IFile source = project.getFile(PROJECT_PROPERTIES_FILE_NAME);
        if (!source.exists()) {
            assert false : PROJECT_PROPERTIES_COULD_NOT_BE_COPIED_COMMA__BECAUSE_IT_DOES_NOT_EXIST_;
            return;
        }

        final IFile destination = newProject.getFile(
                destinationPath.makeRelativeTo(newProject.getFullPath()).append(PROJECT_PROPERTIES_FILE_NAME));
        if (!destination.exists())
            try {
                System.out.println("source: " + source + " ; target: " + destination);
                source.copy(destination.getFullPath(), true, null);
            } catch (CoreException e) {
                e.printStackTrace();
            }
    }

    /**
     * 
     * @param project
     * @param destinationPath
     * @throws JavaModelException
     */
    private void migrateClassPathDependentContent(IProject project, IPath destinationPath)
            throws JavaModelException {
        JavaProject javaProjectToMigrate = new JavaProject(project, null);
        JavaProject newJavaProject = new JavaProject(newProject, null);

        assert (javaProjectToMigrate != null && newJavaProject != null) : JAVA_PROJECTS_COULD_NOT_BE_CREATED;

        IClasspathEntry[] classpathToMigrate = javaProjectToMigrate.getRawClasspath();
        List<IClasspathEntry> newClassPath = new ArrayList<IClasspathEntry>();

        newClassPath.addAll(Arrays.asList(newJavaProject.getRawClasspath()));

        assert classpathToMigrate != null : CLASSPATH_OF_PROJECT_TO_MIGRATE_IS_NULL;

        migrateLibraryAndContainerEntries(newJavaProject, classpathToMigrate, newClassPath);
        migrateSourceFiles(project, destinationPath, classpathToMigrate);
        migrateProjectNatures(project);
    }

    /**
     * @param project
     */
    private void migrateProjectNatures(IProject project) {
        try {
            List<String> natureIds = new ArrayList<String>();
            natureIds.addAll(Arrays.asList(newProject.getDescription().getNatureIds()));
            for (String natureId : project.getDescription().getNatureIds()) {
                if (!natureIds.contains(natureId))
                    natureIds.add(natureId);
            }
            IProjectDescription description = newProject.getDescription();
            description.setNatureIds(natureIds.toArray(new String[natureIds.size()]));
            newProject.setDescription(description, null);
        } catch (CoreException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param project
     * @param destinationPath
     * @param classpathToMigrate
     */
    private void migrateSourceFiles(IProject project, IPath destinationPath, IClasspathEntry[] classpathToMigrate) {
        IPath relativeDestinationPath = ((IPath) destinationPath.clone()).makeRelativeTo(newProject.getFullPath());
        System.out.println(
                "migrate source destination: " + destinationPath + " relative: " + relativeDestinationPath);
        IFolder destination = newProject.getFolder(relativeDestinationPath);

        for (IClasspathEntry entry : classpathToMigrate) {
            if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                try {
                    IPath relativeSourcePath = entry.getPath().makeRelativeTo(project.getFullPath());
                    IFolder source = project.getFolder(relativeSourcePath);

                    SPLMigrationUtils.recursiveCopyFiles(source, destination);

                    newProject.refreshLocal(IProject.DEPTH_INFINITE, null);

                } catch (CoreException e) {
                    CorePlugin.getDefault().logError(e);
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * @param newJavaProject
     * @param classpathToMigrate
     * @param newClassPath
     * @throws JavaModelException
     */
    private void migrateLibraryAndContainerEntries(JavaProject newJavaProject, IClasspathEntry[] classpathToMigrate,
            List<IClasspathEntry> newClassPath) throws JavaModelException {
        for (IClasspathEntry entry : classpathToMigrate) {
            if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER
                    || entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY)
                if (!newClassPath.contains(entry))
                    newClassPath.add(entry);
        }
        newJavaProject.setRawClasspath(newClassPath.toArray(new IClasspathEntry[newClassPath.size()]), null);
    }

    /**
     * The default implementation resets the featuremodel and creates a new one
     * with an abstract "Base" feature that lists the migrated projects as
     * alternative child features.<br>
     * <br>
     * The result is written to {@code /model.xml}. <br>
     * <br>
     * Can be overwritten by extending classes to accomodate
     * {@link IComposerExtensionBase Composers} needs.
     */
    protected void adjustFeatureModel() {
        final IFeatureModel featureModelOfVariants = generateFeatureModelOfVariants();

        SPLMigrationUtils.writeFeatureModelToDefaultFile(newProject, featureModelOfVariants);
    }

    private IFeatureModel generateFeatureModelOfVariants() {
        final IFeatureProject featureProject = CorePlugin.getFeatureProject(newProject);
        final IFeatureModel featureModel = featureProject.getFeatureModel();

        featureModel.reset();

        featureModel.getStructure().setRoot(
                FMFactoryManager.getFactory(featureModel).createFeature(featureModel, "Base").getStructure());
        featureModel.getStructure().getRoot().changeToAlternative();
        featureModel.getStructure().getRoot().setAbstract(true);

        for (IProject project : projects)
            featureModel.getStructure().getRoot().addChild(FMFactoryManager.getFactory(featureModel)
                    .createFeature(featureModel, project.getName()).getStructure());

        return featureModel;
    }

    /**
     * The default implementation creates a .config file for each product
     * variant and saves it to the user defined configpath. Can be overwritten
     * by extending classes to accomodate {@link IComposerExtensionBase
     * Composers} needs.
     * 
     * @see SPLMigrationDialogSettingsPage#getConfigPath()
     */
    protected void createConfigurationFiles() {
        for (IProject project : projects)
            try {
                SPLMigrationUtils.createConfigFile(newProject, configurationData.configPath, project.getName());
            } catch (UnsupportedEncodingException e) {
                CorePlugin.getDefault().logError(e);
                e.printStackTrace();
            } catch (CoreException e) {
                CorePlugin.getDefault().logError(e);
                e.printStackTrace();
            }
    }

    /**
     * This default implementation gets instances and adapters for
     * {@link IProject}s in the selection and adds them to the field
     * {@code projects}. <br>
     * <br>
     * 
     * @param selection
     *            a selection that should contain {@link IProject}s
     */
    protected void registerProjectsFromSelection(IStructuredSelection selection) {
        final SelectionWrapper<IProject> selectionWrapper = SelectionWrapper.init(selection, IProject.class);
        final Set<IProject> projects = new HashSet<IProject>();
        IProject curProject;
        while ((curProject = selectionWrapper.getNext()) != null) {
            projects.add(curProject);
        }
        registerProjectsForMigration(projects);
    }

}