org.eclipse.imp.wizards.IMPWizard.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.imp.wizards.IMPWizard.java

Source

/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* 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:
*    Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/

package org.eclipse.imp.wizards;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
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.imp.WizardPlugin;
import org.eclipse.imp.core.ErrorHandler;
import org.eclipse.imp.utils.ExtensionPointUtils;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModel;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.pde.core.plugin.IExtensions;
import org.eclipse.pde.core.plugin.IPluginAttribute;
import org.eclipse.pde.core.plugin.IPluginElement;
import org.eclipse.pde.core.plugin.IPluginExtension;
import org.eclipse.pde.core.plugin.IPluginModel;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.IPluginObject;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.PluginModelManager;
import org.eclipse.pde.internal.core.bundle.WorkspaceBundleModel;
import org.eclipse.pde.internal.core.plugin.PluginElement;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;

public abstract class IMPWizard extends Wizard {
    protected IMPWizardPage pages[];
    protected int currentPage;

    // Name of template for generating implementation skeleton;
    // most wizards of various types (extension point and other)
    // use exactly one.  If a wizard needs more than one, it
    // will have to define those itself.
    protected String fTemplateName;

    protected int NPAGES;

    protected IProject fProject;

    protected String fProjectName;

    protected String fLanguageName;

    protected String fPackageName;

    protected String fPackageFolder;

    protected String fParserPackage;

    protected String fClassNamePrefix;

    protected String fFullClassName;

    protected static final String START_HERE = "// START_HERE";

    public void init(IWorkbench workbench, IStructuredSelection selection) {
    }

    @Override
    public boolean performFinish() {
        return false;
    }

    public void setPage(int page) {
        currentPage = page;
    }

    public int getPageCount() {
        return NPAGES;
    }

    protected void addPages(IMPWizardPage[] pages) {
        this.pages = pages;
        NPAGES = pages.length;
        for (int n = 0; n < pages.length; n++) {
            addPage(pages[n]);
        }
        List<String> extenRequires = getPluginDependencies();
        for (String requiredPlugin : extenRequires) {
            for (int n = 0; n < pages.length; n++) {
                List<String> pageRequires = pages[n].getRequires();
                pageRequires.add(requiredPlugin);
            }
        }
    }

    public IWizardPage getPreviousPage(IWizardPage page) {
        if (currentPage == 0)
            return null;
        return pages[currentPage];
    }

    public IWizardPage getNextPage(IWizardPage page) {
        if (currentPage == pages.length - 1)
            return null;
        return pages[++currentPage];
    }

    /**
     * @return the list of plugin ID's for the plugin dependencies for this language service.
     */
    protected abstract List<String> getPluginDependencies();

    /**
     * Get the name of the package in which a plugin class is defined
     * for this project, or a default value if there is no such package
     * or if the project is null.  If no default name is provided, then
     * the name of the language is used for a default.
     * 
     * The intention here is to return a the name of the plugin package,
     * if the package exists, or a name that could be used as the name
     * of the plugin package, if the package does not exist.  So this
     * method should not return null and should not be used as a test
     * of whether a given project contains a plugin package or class.
     * 
     * @param project      The project for which the plugin package name is sought;
     *                   may be null
     * @param defaultName   A name to return if the given package lacks a plugin class;
     *                   may be null
     * @return            The name of the package that contains the project's plugin
     *                   class, if there is one, or a name that could be used for the
     *                   plugin package, if there is none.
     */
    public static String getPluginPackageName(IProject project, String defaultName) {
        String result = defaultName;
        if (result == null) {
            result = discoverProjectLanguage(project);
            // SMS 26 Nov 2007 re:  bug #296
            if (result != null)
                result = result.toLowerCase();
        }
        if (project != null) {
            String activator = null;
            IPluginModel pm = ExtensionEnabler.getPluginModelForProject(project);
            if (pm != null) {
                WorkspaceBundleModel wbm = new WorkspaceBundleModel(project.getFile("META-INF/MANIFEST.MF")); //$NON-NLS-1$
                activator = wbm.getBundle().getHeader("Bundle-Activator");
            }

            if (activator != null && !activator.equals("")) {
                if (activator.lastIndexOf(".") >= 0)
                    result = activator.substring(0, activator.lastIndexOf("."));
            }
        }
        return result;
    }

    public static String getProjectSourceLocation(IProject project) {
        try {
            if (project == null)
                return null;
            JavaModel jm = JavaModelManager.getJavaModelManager().getJavaModel();
            IJavaProject jp = jm.getJavaProject(project);
            if (jp == null)
                return null;
            else {
                IPackageFragmentRoot[] roots = jp.getPackageFragmentRoots();
                for (int i = 0; i < roots.length; i++) {
                    if (roots[i].getCorrespondingResource() instanceof IFolder) {
                        IPath lcnPath = roots[i].getPath();
                        lcnPath = lcnPath.removeFirstSegments(1);
                        String lcn = lcnPath.toString();
                        if (lcn.startsWith("/"))
                            lcn = lcn.substring(1);
                        if (!lcn.endsWith("/"))
                            lcn = lcn + "/";
                        return lcn;
                    }
                }
            }
        } catch (JavaModelException e) {

        }
        return null;
    }

    public static String discoverParserPackage(IProject project) {
        if (project == null)
            return null;

        IPluginModelBase pluginModel = getPluginModel(project.getName());

        if (pluginModel != null) {
            try {
                ExtensionEnabler.loadImpExtensionsModel((IPluginModel) pluginModel, project);
            } catch (CoreException e) {
                WizardPlugin.getInstance()
                        .logException("IMPWizard.discoverParserLanguage(): exception loading extensions model", e);
            } catch (ClassCastException e) {
                WizardPlugin.getInstance()
                        .logException("IMPWizard.discoverParserLanguage(): exception loading extensions model", e);
            }

            IPluginExtension[] extensions = pluginModel.getExtensions().getExtensions();

            for (int i = 0; i < extensions.length; i++) {
                if (extensions[i].getPoint().endsWith(".parser")) {
                    IPluginObject[] children = extensions[i].getChildren();

                    for (int j = 0; j < children.length; j++) {
                        if (children[j].getName().indexOf("parser") > -1) {
                            //return (((IPluginElement) children[j]).getAttribute("language").getValue());
                            IPluginAttribute attr = ((IPluginElement) children[j]).getAttribute("class");
                            if (attr == null)
                                return null;
                            String parserClassName = attr.getValue();
                            if (parserClassName == null)
                                return null;
                            if (parserClassName.length() == 0)
                                return null;
                            if (parserClassName.lastIndexOf('.') < 0)
                                return "";
                            return parserClassName.substring(0, parserClassName.lastIndexOf('.'));
                        }
                    }
                }
            }
        }
        return null;
    }

    public static IPluginModelBase getPluginModel(String projectName) {
        try {
            if (projectName == null)
                return null;
            PluginModelManager pmm = PDECore.getDefault().getModelManager();
            // SMS 28 Apr 2008
            IPluginModelBase[] plugins = pmm.getWorkspaceModels(); //getAllPlugins();

            for (int n = 0; n < plugins.length; n++) {
                IPluginModelBase plugin = plugins[n];
                IResource resource = plugin.getUnderlyingResource();
                if (resource != null && projectName.equals(resource.getProject().getName())) {
                    return plugin;
                }
            }
        } catch (Exception e) {
            ErrorHandler.reportError("Could not enable extension point for " + projectName, e);
        }
        return null;
    }

    public static String discoverProjectLanguage(IProject project) {
        if (project == null)
            return null;

        IPluginModelBase pluginModel = getPluginModel(project.getName());

        if (pluginModel != null) {
            try {
                ExtensionEnabler.loadImpExtensionsModel((IPluginModel) pluginModel, project);
            } catch (CoreException e) {
                WizardPlugin.getInstance()
                        .logException("IMPWizard.discoverProjectLanguage(): exception loading extensions model", e);
            } catch (ClassCastException e) {
                WizardPlugin.getInstance()
                        .logException("IMPWizard.discoverProjectLanguage(): exception loading extensions model", e);
            }

            IPluginExtension[] extensions = pluginModel.getExtensions().getExtensions();

            for (int i = 0; i < extensions.length; i++) {
                if (extensions[i].getPoint().endsWith(".languageDescription")) {
                    IPluginObject[] children = extensions[i].getChildren();

                    for (int j = 0; j < children.length; j++) {
                        if (children[j].getName().equals("language")) {
                            //return (((IPluginElement) children[j]).getAttribute("language").getValue());
                            IPluginAttribute attr = ((IPluginElement) children[j]).getAttribute("language");
                            if (attr == null)
                                return null;
                            return (((IPluginElement) children[j]).getAttribute("language").getValue());
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Get the name of the plugin class for this project, or a default
     * name if there is no plugin class or if the given project is null.
     * If no default name is provided, then a name based on the name of
     * the language is used for a default.
     * 
     * The intention here is to return a the name of the plugin class,
     * if it exists, or a name that could be used as the name of the
     * plugin class, if it does not exist.  So this method should not
     * return null and should not be used as a test of whether a given
     * project contains a plugin class.
     * 
     * SMS 27 Mar 2007
     * 
     * @param project      The project for which the plugin class name is sought;
     *                   may be null
     * @param defaultName   A name to return if the given package lacks a plugin class;
     *                   may be null
     * @return            The name of the project's plugin class, if there is one,
     *                   or a name that could be used for the plugin class, if there
     *                   is none.
     */
    public String getPluginClassName(IProject project, String defaultName) {
        String result = defaultName;
        if (result == null)
            result = fClassNamePrefix + "Plugin";
        if (project != null) {
            String activator = null;
            IPluginModel pm = ExtensionEnabler.getPluginModelForProject(project);
            if (pm != null) {
                WorkspaceBundleModel wbm = new WorkspaceBundleModel(project.getFile("META-INF/MANIFEST.MF")); //$NON-NLS-1$
                activator = wbm.getBundle().getHeader("Bundle-Activator");
            }

            if (activator != null) {
                result = activator.substring(activator.lastIndexOf(".") + 1);
            }
        }
        return result;
    }

    /**
     * Get the plugin id defined for this project, or a default value if
     * there is no plugin id or if the given project is null.   If no default
     * id is provided, then an id based on the name of the project is used
     * for a default.
     * 
     * The intention here is to return a plugin id, if it exists, or a
     * value that could be used as the id of the plugin, if it does not
     * exist.  So this method should not return null and should not be
     * used as a test of whether a given project has a plugin id.
     * 
     * SMS 27 Mar 2007
     * 
     * @param project      The project for which the plugin id name is sought;
     *                   may be null
     * @param defaultID      A value to return if the given package lacks a plugin id;
     *                   may be null
     * @return            The plugin id of the project, if there is one, or a value
     *                   that could be used as the plugin id, if there is none.
     */
    public String getPluginID(IProject project, String defaultID) {
        String result = defaultID;
        if (result == null)
            getPluginPackageName(project, null);
        if (project != null) {
            result = ExtensionEnabler.getPluginIDForProject(project);
        }
        return result;
    }

    public Map<String, String> getStandardSubstitutions(IProject project) {
        Map<String, String> result = getStandardSubstitutions();
        result.put("$PLUGIN_PACKAGE$", getPluginPackageName(project, null));
        result.put("$PLUGIN_CLASS$", getPluginClassName(project, null));
        result.put("$PLUGIN_ID$", getPluginID(project, null));
        return result;
    }

    public Map<String, String> getStandardSubstitutions() {
        Map<String, String> result = new HashMap<String, String>();

        result = ExtensionPointUtils.getASTInformation((IPluginModel) pages[0].getPluginModel(), fProject);

        result.put("$LANG_NAME$", fLanguageName);
        result.put("$CLASS_NAME_PREFIX$", fClassNamePrefix);
        result.put("$PACKAGE_NAME$", fPackageName);
        result.put("$PROJECT_NAME$", fProjectName);

        // NOTE:  These are default values for plug-in substitutions;
        // they should typically be overridden by real values obtained
        // from getStandardSubstitutions(IProject).  That means that
        // that method should be called after this one (or that one
        // should call this one before setting those values).
        result.put("$PLUGIN_PACKAGE$", getPluginPackageName(null, null));
        result.put("$PLUGIN_CLASS$", getPluginClassName(null, null));
        result.put("$PLUGIN_ID$", getPluginID(null, null));

        return result;
    }

    /**
     * Return a Map containing the the names of the AST package and class
     * bound to "well-known" symbols, "$AST_PACKAGE$" and "$AST_CLASS$", 
     * respectively.
     * 
     * WARNING:  The names returned are currently the DEFAULT names (which
     * should be the most commonly occurring but which may not be appropriate
     * in general).
     * 
     * The actual values for the AST package and class are generated in the
     * NewParser wizard but are not (yet) stored anywhere for reference by
     * other wizards.  There is at least one other wizard, the NewFoldingUpdater
     * wizard, which does need these names to complete a template.  In order
     * to make available some reasonable values for these names, this method
     * recomputes the names using the same assumptions as are used for the
     * default case in the NewParser wizard.
     * 
     * TODO:  Provide a means for (more) persistently maintaining the names
     * of the AST package and class in such a way that they can become part
     * of the "standard substitutions."  (ALTERNATIVELY:  the class could just
     * be obtained in wizards where needed, in which case it need not be part
     * of the standard substitutions.)
     * 
     * @return   A Map that contains two values:  the name of the package that
     *          contains the AST class, and the name of the AST class.
     *          These values keyed, respectively, by the symbols "$AST_PACKAGE$"
     *          and "$AST_NODE$".
     * 
     * Updates: Stan Sutton, 9 Aug 2006
     *          Changed return from $AST_CLASS$ to $AST_NODE$ since the latter
     *          is the symbol more commonly used (and the one on which I will
     *          try to standardize)
     * 
     * @author   Stan Sutton
     * @since   17 May 2006
     */
    public static Map<String, String> getASTInformation(IPluginModel pluginModel, IProject project) {
        Map<String, String> result = new HashMap<String, String>();

        // Get the extension that represents the parser

        // SMS 26 Jul 2007
        // Load the extensions model in detail, using the adapted IMP representation,
        // to assure that the children of model elements are represented
        try {
            ExtensionEnabler.loadImpExtensionsModel((IPluginModel) pluginModel, project);
        } catch (CoreException e) {
            WizardPlugin.getInstance()
                    .logException("IMPWizard.discoverProjectLanguage(): exception loading extensions model", e);
        } catch (ClassCastException e) {
            WizardPlugin.getInstance()
                    .logException("IMPWizard.discoverProjectLanguage(): exception loading extensions model", e);
        }

        IExtensions extensionsThing = pluginModel.getExtensions();
        IPluginExtension[] extensions = extensionsThing.getExtensions();
        IPluginExtension parserExtension = null;
        for (int i = 0; i < extensions.length; i++) {
            if (extensions[i].getPoint().equals("org.eclipse.imp.runtime.parser")) {
                parserExtension = extensions[i];
                break;
            }
        }

        // Get the plugin element that represents the class of the parser
        PluginElement parserPluginElement = null;
        if (parserExtension != null) {
            IPluginObject[] children = parserExtension.getChildren();
            for (int i = 0; i < children.length; i++) {
                String name = children[i].getName();
                if (name.equals("parser") || name.equals("parserWrapper")) {
                    parserPluginElement = (PluginElement) children[i];
                    break;
                }
            }
        }
        if (parserPluginElement == null)
            return result;

        // Get the names of the parser package, AST package, and AST (node) class name

        IPluginAttribute parserClassAttribute = parserPluginElement.getAttribute("class");
        String parserPackageName = parserClassAttribute.getValue();
        parserPackageName = parserPackageName.substring(0, parserPackageName.lastIndexOf('.'));
        // ASSUME that the AST package name is the parser package name extended 
        // with ".Ast" (this is the default when auto-generated)
        String astPackageName = parserPackageName + ".Ast";
        // Just assume this is true
        // TBD:  check whether this exists (or put the info somewhere from
        // where it can be retrieved here)
        String astClassName = "ASTNode";

        // Save these values in the substitutions map
        result.put("$PROJ_NAME$", project.getName());
        result.put("$PARSER_PACKAGE$", parserPackageName);
        result.put("$AST_PACKAGE$", astPackageName);
        result.put("$AST_NODE$", astClassName);

        return result;
    }

    /**
     * Collects basic information from wizard-page fields and computes
     * additional common values for use by wizards in generating code.
     *    
     * Can be extended by subclasses for specific wizards in order to
     * gather wizard-specific values.
     */
    protected void collectCodeParms() {
        fProject = pages[0].getProjectOfRecord();
        fProjectName = pages[0].fProjectText.getText();
        fLanguageName = pages[0].fLanguageText.getText();

        if (pages[0].fTemplateText != null)
            fTemplateName = pages[0].fTemplateText.getText();

        fClassNamePrefix = Character.toUpperCase(fLanguageName.charAt(0)) + fLanguageName.substring(1);

        WizardPageField classField = pages[0].getField("class");

        if (classField != null) {
            String qualifiedClassName = classField.fValue;
            fFullClassName = qualifiedClassName.substring(qualifiedClassName.lastIndexOf('.') + 1);
            fPackageName = qualifiedClassName.substring(0, qualifiedClassName.lastIndexOf('.'));
        } else {
            WizardPageField pkgField = pages[0].getField("package");
            if (pkgField != null) {
                fPackageName = pkgField.getText();
            } else {
                fPackageName = "";
            }
        }
        fPackageFolder = fPackageName.replace('.', File.separatorChar);
        fParserPackage = discoverParserPackage(fProject);
    }

    /**
     * Returns (in an array of Strings) the names of files that will be
     * generated by the SAFARI wizard and that thus may clobber existing
     * files.
     * 
     * The basic implementation provided here simply returns an array with
     * the name of the one class that will provide the core implementation
     * of the service.  (It seems that this is all that is necessary for
     * most wizards.)  If the wizard actually generates no implementation
     * class, then an emtpy array is returned.
     * 
     * Subclasses for specific wizards should override this method if the
     * wizard will generate more than one class.
     * 
     * @return   An array of names files that will be generated by the wizard
     */
    protected String[] getFilesThatCouldBeClobbered() {

        // In case there's not any implementation class ...
        if (fFullClassName == null) {
            return new String[0];
        }

        // In the usual case that there is ...

        String prefix = fProject.getLocation().toString() + '/' + getProjectSourceLocation(fProject);
        // getProjectSourceLocation should return a "/"-terminated string
        String prefixTail = (fPackageName == null ? "/" : fPackageName.replace('.', '/') + "/");
        prefix = prefix + prefixTail;

        return new String[] { prefix + fFullClassName + ".java" };
    }

    /**
     * Check whether it's okay for the files to be generated to clobber
     * any existing files.
     * 
     * Current implementation expects that the file names provided will
     * be the full absolute path names in the file system.
     * 
     * @param files      The names of files that would be clobbered by
     *                files to be generated
     * @return         True if there are no files that would be clobbered
     *                or if the users presses OK; false if there are
     *                files and the user presses CANCEL
     */
    protected boolean okToClobberFiles(String[] files) {
        if (files.length == 0)
            return true;
        String message = "File(s) with the following name(s) already exist; do you want to overwrite?\n";
        boolean askUser = false;
        for (int i = 0; i < files.length; i++) {
            File file = new File(files[i]);
            if (file.exists()) {
                askUser = true;
                message = message + "\n" + files[i];
            }
        }
        if (!askUser)
            return true;
        Shell parent = this.getShell();
        MessageBox messageBox = new MessageBox(parent, (SWT.CANCEL | SWT.OK));
        messageBox.setMessage(message);
        int result = messageBox.open();
        if (result == SWT.CANCEL)
            return false;
        return true;
    }

    /**
     * Generate any necessary code for this extension from template files in the
     * templates directory.<br>
     * Implementations can use <code>getTemplateFile(String)</code> to access the
     * necessary template files.<br>
     * Implementations must be careful not to access the fields of the wizard page,
     * as this code will probably be called from a thread other than the UI thread.
     * I.e., don't write something like:<br>
     * <code>pages[0].languageText.getText()</code><br>
     * Instead, in the wizard class, override <code>collectCodeParams()</code>,
     * which gets called earlier from the UI thread, and save any necessary data
     * in fields in the wizard class.
     * @param monitor
     * @throws CoreException
     */
    protected abstract void generateCodeStubs(IProgressMonitor mon) throws CoreException;

    /**
     * Opens the given file in the appropriate editor for editing.<br>
     * If the file contains a comment "// START_HERE", the cursor will
     * be positioned just after that.
     * @param monitor
     * @param file
     */
    protected void editFile(IProgressMonitor monitor, final IFile file) {
        WizardUtilities.editFile(monitor, file, getShell());
    }

    public boolean canFinish() {
        return super.canFinish();// pages[currentPage].isPageComplete() && (currentPage >= pages.length - 1);
    }

    public IFile createFileFromTemplate(String fileName, String templateName, String folder, Map replacements,
            IProject project, IProgressMonitor monitor) throws CoreException {
        return createFileFromTemplate(fileName, "org.eclipse.imp.metatooling", templateName, folder, replacements,
                project, monitor);
    }

    protected IFile createFileFromTemplate(String fileName, String templateBundleID, String templateName,
            String folder, Map replacements, IProject project, IProgressMonitor monitor) throws CoreException {
        return WizardUtilities.createFileFromTemplate(fileName, templateBundleID, templateName, folder,
                getProjectSourceLocation(project), replacements, project, monitor);
    }

    protected static IFile createFile(String fileName, String folder, IProject project, IProgressMonitor monitor)
            throws CoreException {
        monitor.setTaskName("Creating " + fileName);

        final IFile file = project.getFile(new Path(getProjectSourceLocation(project) + folder + "/" + fileName));

        if (!file.exists()) {
            WizardUtilities.createSubFolders(getProjectSourceLocation(project) + folder, project, monitor);
            file.create(new ByteArrayInputStream("".getBytes()), true, monitor);
        }
        //   monitor.worked(1);
        return file;
    }

    protected IFile getFile(String fileName, String folder, IProject project) throws CoreException {
        IFile file = project.getFile(new Path(getProjectSourceLocation(fProject) + folder + "/" + fileName));

        return file;
    }
}