randori.plugin.components.RandoriProjectComponent.java Source code

Java tutorial

Introduction

Here is the source code for randori.plugin.components.RandoriProjectComponent.java

Source

/***
 * Copyright 2013 Teoti Graphix, LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 * @author Michael Schmalle <mschmalle@teotigraphix.com>
 */

package randori.plugin.components;

import com.intellij.compiler.CompilerWorkspaceConfiguration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompileScope;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ui.configuration.ClasspathEditor;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileListener;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import org.apache.flex.compiler.internal.workspaces.Workspace;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import randori.compiler.clients.CompilerArguments;
import randori.compiler.plugin.IPreProcessPlugin;
import randori.plugin.builder.FileChangeListener;
import randori.plugin.compiler.RandoriProjectCompiler;
import randori.plugin.forms.RandoriProjectConfigurationForm;
import randori.plugin.module.RandoriModuleType;
import randori.plugin.roots.RandoriSdk;
import randori.plugin.runner.RandoriRunConfiguration;
import randori.plugin.runner.RandoriServerComponent;
import randori.plugin.service.ProblemsService;
import randori.plugin.ui.ProblemsToolWindowFactory;
import randori.plugin.util.NotificationUtils;
import randori.plugin.util.ProjectUtils;
import randori.plugin.util.VFileUtils;
import randori.plugin.workspace.IRandoriWorkspace;

import javax.swing.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

@State(name = RandoriProjectComponent.COMPONENT_NAME, storages = {
        @Storage(id = "randoriProject", file = "$PROJECT_FILE$") })
/**
 * The project component manages the global state of the current project and wrap the compiler.
 *
 * @author Frdric THOMAS
 */
public class RandoriProjectComponent implements ProjectComponent, Configurable,
        PersistentStateComponent<RandoriProjectModel>, IRandoriWorkspace {

    public static final String COMPONENT_NAME = "RandoriProject";
    private List<VirtualFile> modifiedFiles;
    private Workspace workspace;
    private RandoriProjectConfigurationForm form;
    private Project project;
    private RandoriProjectModel state;
    private VirtualFileListener fileChangeListener;
    private RandoriProjectCompiler compiler;

    public RandoriProjectComponent(Project project) {
        this.project = project;
    }

    @Override
    public void initComponent() {
    }

    @Override
    public void projectOpened() {

        if (!ProjectUtils.hasRandoriModuleType(project))
            return;

        CompilerWorkspaceConfiguration workspaceConfiguration = CompilerWorkspaceConfiguration.getInstance(project)
                .getState();
        workspaceConfiguration.USE_COMPILE_SERVER = false;
        CompilerWorkspaceConfiguration.getInstance(project).loadState(workspaceConfiguration);

        workspace = new Workspace();
        compiler = addProjectCompiler(this.project);
        state = new RandoriProjectModel();
        modifiedFiles = new ArrayList<VirtualFile>();

        fileChangeListener = new FileChangeListener(project);
        VirtualFileManager.getInstance().addVirtualFileListener(fileChangeListener);

        parse(false);
    }

    @Override
    public RandoriProjectCompiler addProjectCompiler(Project project) {

        if (!ProjectUtils.hasRandoriModuleType(project))
            return null;

        RandoriProjectCompiler compiler = new RandoriProjectCompiler(new Workspace());

        compiler.getPluginFactory().registerPlugin(IPreProcessPlugin.class, DummyPreProcessPlugin.class);

        return compiler;
    }

    @Override
    public void projectClosed() {

        if (!ProjectUtils.hasRandoriModuleType(project))
            return;

        ToolWindow toolWindow = ToolWindowManager.getInstance(project)
                .getToolWindow(ProblemsToolWindowFactory.WINDOW_ID);
        if (toolWindow != null) {
            toolWindow.hide(new Runnable() {
                @Override
                public void run() {
                }
            });
        }
    }

    @Override
    public void disposeComponent() {

        if (!ProjectUtils.hasRandoriModuleType(project))
            return;

        VirtualFileManager.getInstance().removeVirtualFileListener(fileChangeListener);

        compiler = null;
        workspace = null;
        state = null;
    }

    @NotNull
    @Override
    public String getComponentName() {
        return COMPONENT_NAME;
    }

    @Nls
    @Override
    public String getDisplayName() {
        return "Randori Project";
    }

    @Override
    public String getHelpTopic() {
        return null;
    }

    @Override
    public RandoriProjectModel getState() {
        return state;
    }

    @Override
    public void loadState(RandoriProjectModel state) {
    }

    @Override
    public JComponent createComponent() {
        if (form == null) {
            form = new RandoriProjectConfigurationForm();
        }
        return form.getComponent();
    }

    @Override
    public boolean isModified() {
        return form.isModified(getState());
    }

    @Override
    public void apply() throws ConfigurationException {
        if (form != null) {
            form.getData(getState());
        }
    }

    @Override
    public void reset() {
        if (form != null) {
            form.setData(getState());
        }
    }

    @Override
    public void disposeUIResources() {
    }

    /**
     * Returns the {@link Project} instance open.
     */
    public Project getProject() {
        return project;
    }

    @Override
    public Workspace getWorkspace() {
        return workspace;
    }

    /**
     * Returns the {@link ProblemsService} that manages {@link ICompilerProblem} s from the compiler.
     */
    public ProblemsService getProblemsService() {
        return ProblemsService.getInstance(project);
    }

    public List<VirtualFile> getModifiedFiles() {
        return modifiedFiles;
    }

    /**
     * Opens a ICompilerProblem in a new editor, or opens the editor and places the caret a the specific problem.
     * 
     * @param problem The ICompilerProblem to focus.
     */
    public void openFileForProblem(ICompilerProblem problem) {
        VirtualFile virtualFile = VFileUtils.getFile(problem.getSourcePath());
        if (virtualFile != null) {
            OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile);
            if (descriptor != null) {
                Editor editor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
                if (editor != null) {
                    LogicalPosition position = new LogicalPosition(problem.getLine(), problem.getColumn());
                    editor.getCaretModel().moveToLogicalPosition(position);
                }
            }
        }
    }

    public void build(boolean doClean, boolean sync) {
        compiler = addProjectCompiler(project);
        CompilerArguments arguments = new CompilerArguments();
        configureDependencies(arguments);

        if (sync) {
            buildSync(doClean, arguments);
        } else {
            build(doClean, arguments);
        }
    }

    public void parse(boolean sync) {
        compiler = addProjectCompiler(project);
        CompilerArguments arguments = new CompilerArguments();
        configureDependencies(arguments);

        if (sync) {
            parseSync(arguments);
        } else {
            parse(arguments);
        }
    }

    public void run(RandoriRunConfiguration configuration) {
        RandoriServerComponent component = project.getComponent(RandoriServerComponent.class);
        String explicitWebRoot = (configuration.useExplicitWebroot) ? configuration.explicitWebroot : "";
        component.openURL(configuration.indexRoot, explicitWebRoot);
    }

    //--------------------------------------------------------------------------

    void parseSync(final CompilerArguments arguments) {
        clearProblems();

        compiler.configure(arguments.toArguments());
        boolean success = compiler.compile(false);

        ApplicationManager.getApplication().invokeLater(new ProblemRunnable(success));
    }

    void parse(final CompilerArguments arguments) {
        clearProblems();

        ProgressManager.getInstance()
                .run(new Task.Backgroundable(project, "Randori compiler building project", true, null) {
                    @Override
                    public void run(@NotNull ProgressIndicator indicator) {
                        compiler.configure(arguments.toArguments());
                        boolean success = compiler.compile(false);

                        ApplicationManager.getApplication().invokeLater(new ProblemRunnable(success));
                    }
                });
    }

    //--------------------------------------------------------------------------

    void buildSync(boolean doClean, final CompilerArguments arguments) {
        if (doClean) {
            clean();
        }

        compiler.configure(arguments.toArguments());
        boolean success = compiler.compile(true);

        ApplicationManager.getApplication().invokeLater(new ProblemUpdateRunnable());

        // only run if success, the compile will return false
        // with any errors or mis configurations
        if (success) {
            buildComplete();
        }

        // this will take care of success and failure notices
        ApplicationManager.getApplication().invokeLater(new ProblemBuildRunnable());
    }

    void build(boolean doClean, final CompilerArguments arguments) {
        clearProblems();

        if (doClean) {
            clean();
        }

        ProgressManager.getInstance()
                .run(new Task.Backgroundable(project, "Randori compiler building project", true, null) {
                    @Override
                    public void run(@NotNull ProgressIndicator indicator) {
                        compiler.configure(arguments.toArguments());
                        boolean success = compiler.compile(true);

                        // only run if success, the compile will return false
                        // with any errors or mis configurations
                        if (success) {
                            buildComplete();
                        }

                        // this will take care of success and failure notices
                        ApplicationManager.getApplication().invokeLater(new ProblemBuildRunnable());
                    }
                });
    }

    //--------------------------------------------------------------------------

    void configureDependencies(CompilerArguments arguments) {

        arguments.clear();

        configure(getState(), arguments);

        for (String library : ProjectUtils.getAllProjectSWCs(project)) {
            arguments.addLibraryPath(library);
        }

        for (String library : ProjectUtils.getAllProjectSourcePaths(project)) {
            arguments.addSourcepath(library);
        }

        Module[] modules = ModuleManager.getInstance(project).getModules();
        for (Module module : modules) {
            // RandoriFlash/src
            for (VirtualFile sourceRoot : ModuleRootManager.getInstance(module).getSourceRoots()) {
                arguments.addSourcepath(sourceRoot.getPath());
            }
            if (modifiedFiles != null) {
                for (VirtualFile virtualFile : modifiedFiles) {
                    arguments.addIncludedSources(virtualFile.getPath());
                }
            }
        }
    }

    void configure(RandoriProjectModel model, CompilerArguments arguments) {
        arguments.setAppName(project.getName());
        arguments.setJsBasePath(model.getBasePath());
        arguments.setJsLibraryPath(model.getLibraryPath());
        arguments.setJsOutputAsFiles(model.isClassesAsFile());
        arguments.setOutput(project.getBasePath());
    }

    public boolean validateConfiguration(CompileScope scope) {
        if (!ProjectUtils.isSDKInstalled(project))
            return false;

        // TODO Implement the Randori facet for modules.
        for (final Module module : scope.getAffectedModules()) {
            if (ModuleType.get(module) != RandoriModuleType.getInstance()) {
                Messages.showErrorDialog(module.getProject(), "This module is not a Randori module",
                        "Can not compile");
                ModulesConfigurator.showDialog(module.getProject(), module.getName(), ClasspathEditor.NAME);
                return false;
            }
        }

        return true;
    }

    private void clean() {
        final VirtualFile baseDir = project.getBaseDir();

        // wipe the generated directory
        VirtualFile virtualFile = baseDir.findFileByRelativePath(this.getState().getBasePath());
        if (virtualFile != null && virtualFile.exists()) {
            File fsFile = new File(virtualFile.getPath());
            FileUtil.asyncDelete(fsFile);
        }
    }

    protected void buildComplete() {
        RandoriSdk.copySdkLibraries(project);
    }

    protected void clearProblems() {
        ApplicationManager.getApplication().invokeLater(new ProblemClearRunnable());
    }

    @SuppressWarnings("unused")
    private String toErrorCode(int code) {
        switch (code) {
        case 1:
            return "Unknown";
        case 2:
            return "Compiler problems";
        case 3:
            return "Compiler Exceptions";
        case 4:
            return "Configuration Problems";
        }

        return "Unknown error code";
    }

    public RandoriProjectCompiler getCompiler() {
        return compiler;
    }

    class ProblemClearRunnable implements Runnable {
        @Override
        public void run() {
            ProblemsService service = ProblemsService.getInstance(project);
            service.clearProblems();
        }
    }

    class ProblemUpdateRunnable implements Runnable {

        ProblemUpdateRunnable() {
        }

        @Override
        public void run() {
            ProblemsService service = ProblemsService.getInstance(project);
            service.clearProblems();
            service.addAll(compiler.getProblemQuery().getProblems());
        }
    }

    class ProblemRunnable implements Runnable {
        private final boolean success;

        ProblemRunnable(boolean success) {
            this.success = success;
        }

        @Override
        public void run() {
            ProblemsService service = ProblemsService.getInstance(project);

            service.addAll(compiler.getProblemQuery().getProblems());

            if (success) {
                service.clearProblems();
                NotificationUtils.sendRandoriInformation("Success", "Successfully compiled project", project);
            } else {
                if (service.hasErrors()) {
                    NotificationUtils.sendRandoriError("Error",
                            "Error(s) in project, Check the <a href='" + ProblemsToolWindowFactory.WINDOW_ID + "'>"
                                    + ProblemsToolWindowFactory.WINDOW_ID + "</a> for more information",
                            project);
                }
            }
        }
    }

    class ProblemBuildRunnable implements Runnable {
        ProblemBuildRunnable() {
        }

        @Override
        public void run() {
            ProblemsService service = ProblemsService.getInstance(project);

            service.addAll(compiler.getProblemQuery().getProblems());

            if (service.hasErrors()) {
                NotificationUtils.sendRandoriError(
                        "Error", "Error(s) in project, Check the <a href='" + ProblemsToolWindowFactory.WINDOW_ID
                                + "'>" + ProblemsToolWindowFactory.WINDOW_ID + "</a> for more information",
                        project);
            } else {
                NotificationUtils.sendRandoriInformation("Success", "Successfully compiled and built project",
                        project);
            }

        }

    }

}