org.eclipse.xtext.idea.build.XtextAutoBuilderComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.idea.build.XtextAutoBuilderComponent.java

Source

/**
 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) 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
 */
package org.eclipse.xtext.idea.build;

import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.intellij.ProjectTopics;
import com.intellij.compiler.ModuleCompilerUtil;
import com.intellij.facet.Facet;
import com.intellij.facet.FacetType;
import com.intellij.facet.ProjectWideFacetAdapter;
import com.intellij.facet.ProjectWideFacetListenersRegistry;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.components.StorageScheme;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.EditorEventMulticaster;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.ModuleAdapter;
import com.intellij.openapi.project.ModuleListener;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModuleRootAdapter;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.roots.ModuleRootListener;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileAdapter;
import com.intellij.openapi.vfs.VirtualFileEvent;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileMoveEvent;
import com.intellij.openapi.vfs.VirtualFilePropertyEvent;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.PsiModificationTrackerImpl;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.Alarm;
import com.intellij.util.Function;
import com.intellij.util.graph.Graph;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.MessageBusConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Consumer;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor;
import org.eclipse.xtext.build.BuildRequest;
import org.eclipse.xtext.build.IncrementalBuilder;
import org.eclipse.xtext.build.IndexState;
import org.eclipse.xtext.build.Source2GeneratedMapping;
import org.eclipse.xtext.common.types.descriptions.TypeResourceDescription;
import org.eclipse.xtext.idea.build.BuildEvent;
import org.eclipse.xtext.idea.build.BuildProgressReporter;
import org.eclipse.xtext.idea.build.XtextAutoBuilderComponentState;
import org.eclipse.xtext.idea.facet.AbstractFacetConfiguration;
import org.eclipse.xtext.idea.facet.AbstractFacetType;
import org.eclipse.xtext.idea.facet.FacetProvider;
import org.eclipse.xtext.idea.resource.IdeaResourceSetProvider;
import org.eclipse.xtext.idea.resource.VirtualFileURIUtil;
import org.eclipse.xtext.idea.shared.IdeaSharedInjectorProvider;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.eclipse.xtext.resource.impl.ChunkedResourceDescriptions;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData;
import org.eclipse.xtext.service.OperationCanceledManager;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.util.internal.Log;
import org.eclipse.xtext.xbase.lib.CollectionExtensions;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Conversions;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.ListExtensions;
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure0;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;

/**
 * @author Jan Koehnlein - Initial contribution and API
 */
@State(name = "XtextAutoBuilderState", storages = { @Storage(id = "other", file = StoragePathMacros.WORKSPACE_FILE),
        @Storage(id = "dir", file = (StoragePathMacros.PROJECT_CONFIG_DIR
                + "/xtextAutoBuilderState.xml"), scheme = StorageScheme.DIRECTORY_BASED) })
@Log
@SuppressWarnings("all")
public class XtextAutoBuilderComponent extends AbstractProjectComponent
        implements Disposable, PersistentStateComponent<XtextAutoBuilderComponentState> {
    @FinalFieldsConstructor
    public static class MutableCancelIndicator implements CancelIndicator {
        private final ProgressIndicator indicator;

        private volatile boolean canceled;

        @Override
        public boolean isCanceled() {
            boolean _or = false;
            if (this.canceled) {
                _or = true;
            } else {
                boolean _isCanceled = false;
                if (this.indicator != null) {
                    _isCanceled = this.indicator.isCanceled();
                }
                _or = _isCanceled;
            }
            return _or;
        }

        public boolean setCanceled(final boolean canceled) {
            return this.canceled = canceled;
        }

        public MutableCancelIndicator(final ProgressIndicator indicator) {
            super();
            this.indicator = indicator;
        }
    }

    private volatile boolean disposed;

    private final LinkedBlockingQueue<BuildEvent> queue = new LinkedBlockingQueue<BuildEvent>();

    private final LinkedBlockingQueue<XtextAutoBuilderComponent.MutableCancelIndicator> cancelIndicators = new LinkedBlockingQueue<XtextAutoBuilderComponent.MutableCancelIndicator>();

    private Alarm alarm;

    private Project project;

    @Inject
    private Provider<IncrementalBuilder> builderProvider;

    @Inject
    private Provider<BuildProgressReporter> buildProgressReporterProvider;

    @Inject
    private IdeaResourceSetProvider resourceSetProvider;

    @Inject
    private IResourceServiceProvider.Registry resourceServiceProviderRegistry;

    @Inject
    private IQualifiedNameConverter qualifiedNameConverter;

    @Inject
    private Provider<ChunkedResourceDescriptions> chunkedResourceDescriptionsProvider;

    @Inject
    private OperationCanceledManager operationCanceledManager;

    private ChunkedResourceDescriptions chunkedResourceDescriptions;

    private Map<String, Source2GeneratedMapping> moduleName2GeneratedMapping = CollectionLiterals
            .<String, Source2GeneratedMapping>newHashMap();

    public Iterable<URI> getGeneratedSources(final URI source) {
        Collection<Source2GeneratedMapping> _values = this.moduleName2GeneratedMapping.values();
        final Function1<Source2GeneratedMapping, List<URI>> _function = (Source2GeneratedMapping it) -> {
            return it.getGenerated(source);
        };
        Iterable<List<URI>> _map = IterableExtensions.<Source2GeneratedMapping, List<URI>>map(_values, _function);
        Iterable<URI> _flatten = Iterables.<URI>concat(_map);
        return IterableExtensions.<URI>toList(_flatten);
    }

    public Iterable<URI> getSource4GeneratedSource(final URI generated) {
        Collection<Source2GeneratedMapping> _values = this.moduleName2GeneratedMapping.values();
        final Function1<Source2GeneratedMapping, List<URI>> _function = (Source2GeneratedMapping it) -> {
            return it.getSource(generated);
        };
        Iterable<List<URI>> _map = IterableExtensions.<Source2GeneratedMapping, List<URI>>map(_values, _function);
        Iterable<URI> _flatten = Iterables.<URI>concat(_map);
        return IterableExtensions.<URI>toList(_flatten);
    }

    public XtextAutoBuilderComponent(final Project project) {
        super(project);
        Application _application = ApplicationManager.getApplication();
        boolean _isUnitTestMode = _application.isUnitTestMode();
        XtextAutoBuilderComponent.TEST_MODE = _isUnitTestMode;
        Injector _injector = IdeaSharedInjectorProvider.getInjector();
        _injector.injectMembers(this);
        ChunkedResourceDescriptions _get = this.chunkedResourceDescriptionsProvider.get();
        this.chunkedResourceDescriptions = _get;
        this.project = project;
        Alarm _alarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this);
        this.alarm = _alarm;
        this.disposed = false;
        Disposer.register(project, this);
        EditorFactory _instance = EditorFactory.getInstance();
        EditorEventMulticaster _eventMulticaster = _instance.getEventMulticaster();
        _eventMulticaster.addDocumentListener(new DocumentAdapter() {
            @Override
            public void documentChanged(final DocumentEvent event) {
                FileDocumentManager _instance = FileDocumentManager.getInstance();
                Document _document = event.getDocument();
                VirtualFile file = _instance.getFile(_document);
                boolean _notEquals = (!Objects.equal(file, null));
                if (_notEquals) {
                    XtextAutoBuilderComponent.this.fileModified(file);
                } else {
                    Document _document_1 = event.getDocument();
                    String _plus = ("No virtual file for document. Contents was " + _document_1);
                    XtextAutoBuilderComponent.LOG.info(_plus);
                }
            }
        }, project);
        VirtualFileManager _instance_1 = VirtualFileManager.getInstance();
        _instance_1.addVirtualFileListener(new VirtualFileAdapter() {
            @Override
            public void beforePropertyChange(final VirtualFilePropertyEvent event) {
                String _propertyName = event.getPropertyName();
                boolean _equals = Objects.equal(_propertyName, VirtualFile.PROP_NAME);
                if (_equals) {
                    VirtualFile _file = event.getFile();
                    XtextAutoBuilderComponent.this.scheduleDeletion(_file);
                }
            }

            @Override
            public void propertyChanged(final VirtualFilePropertyEvent event) {
                String _propertyName = event.getPropertyName();
                boolean _equals = Objects.equal(_propertyName, VirtualFile.PROP_NAME);
                if (_equals) {
                    XtextAutoBuilderComponent.this.commitScheduledDeletion();
                    VirtualFile _file = event.getFile();
                    XtextAutoBuilderComponent.this.fileAdded(_file);
                }
            }

            @Override
            public void contentsChanged(final VirtualFileEvent event) {
                VirtualFile _file = event.getFile();
                XtextAutoBuilderComponent.this.fileModified(_file);
            }

            @Override
            public void fileCreated(final VirtualFileEvent event) {
                VirtualFile _file = event.getFile();
                XtextAutoBuilderComponent.this.fileAdded(_file);
            }

            @Override
            public void fileDeleted(final VirtualFileEvent event) {
                XtextAutoBuilderComponent.this.commitScheduledDeletion();
            }

            @Override
            public void beforeFileDeletion(final VirtualFileEvent event) {
                VirtualFile _file = event.getFile();
                XtextAutoBuilderComponent.this.scheduleDeletion(_file);
            }

            @Override
            public void beforeFileMovement(final VirtualFileMoveEvent event) {
                VirtualFile _file = event.getFile();
                XtextAutoBuilderComponent.this.scheduleDeletion(_file);
            }

            @Override
            public void fileMoved(final VirtualFileMoveEvent event) {
                XtextAutoBuilderComponent.this.commitScheduledDeletion();
                VirtualFile _file = event.getFile();
                XtextAutoBuilderComponent.this.fileAdded(_file);
            }
        }, project);
        MessageBus _messageBus = project.getMessageBus();
        final MessageBusConnection connection = _messageBus.connect(project);
        connection.<ModuleRootListener>subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
            @Override
            public void rootsChanged(final ModuleRootEvent event) {
                XtextAutoBuilderComponent.this.doCleanBuild();
            }
        });
        connection.<ModuleListener>subscribe(ProjectTopics.MODULES, new ModuleAdapter() {
            @Override
            public void moduleAdded(final Project project, final Module module) {
                boolean _isInitialized = project.isInitialized();
                if (_isInitialized) {
                    XtextAutoBuilderComponent.this.doCleanBuild(module);
                }
            }

            @Override
            public void moduleRemoved(final Project project, final Module module) {
                String _name = module.getName();
                XtextAutoBuilderComponent.this.chunkedResourceDescriptions.removeContainer(_name);
                String _name_1 = module.getName();
                XtextAutoBuilderComponent.this.moduleName2GeneratedMapping.remove(_name_1);
            }

            @Override
            public void modulesRenamed(final Project project, final List<Module> modules,
                    final Function<Module, String> oldNameProvider) {
                for (final Module module : modules) {
                    {
                        String _fun = oldNameProvider.fun(module);
                        XtextAutoBuilderComponent.this.chunkedResourceDescriptions.removeContainer(_fun);
                        String _name = module.getName();
                        XtextAutoBuilderComponent.this.moduleName2GeneratedMapping.remove(_name);
                        XtextAutoBuilderComponent.this.doCleanBuild(module);
                    }
                }
            }
        });
        ProjectWideFacetListenersRegistry _instance_2 = ProjectWideFacetListenersRegistry.getInstance(project);
        _instance_2.registerListener(new ProjectWideFacetAdapter<Facet>() {
            @Override
            public void facetAdded(final Facet facet) {
                if (((!XtextAutoBuilderComponent.this.isXtextFacet(facet)) || (!project.isInitialized()))) {
                    return;
                }
                Module _module = facet.getModule();
                XtextAutoBuilderComponent.this.doCleanBuild(_module);
            }

            @Override
            public void facetRemoved(final Facet facet) {
                boolean _isXtextFacet = XtextAutoBuilderComponent.this.isXtextFacet(facet);
                boolean _not = (!_isXtextFacet);
                if (_not) {
                    return;
                }
                Module _module = facet.getModule();
                XtextAutoBuilderComponent.this.doCleanBuild(_module);
            }

            @Override
            public void facetConfigurationChanged(final Facet facet) {
                boolean _isXtextFacet = XtextAutoBuilderComponent.this.isXtextFacet(facet);
                boolean _not = (!_isXtextFacet);
                if (_not) {
                    return;
                }
                Module _module = facet.getModule();
                XtextAutoBuilderComponent.this.doCleanBuild(_module);
            }
        }, this);
    }

    protected boolean isXtextFacet(final Facet<?> facet) {
        final FacetType facetType = facet.getType();
        return (facetType instanceof AbstractFacetType<?>);
    }

    @Override
    public void dispose() {
        this.disposed = true;
        this.alarm.cancelAllRequests();
        this.queue.clear();
        ChunkedResourceDescriptions _get = this.chunkedResourceDescriptionsProvider.get();
        this.chunkedResourceDescriptions = _get;
    }

    protected Project getProject() {
        return this.myProject;
    }

    public void fileModified(final VirtualFile file) {
        this.enqueue(BuildEvent.Type.MODIFIED, file);
    }

    public void scheduleDeletion(final VirtualFile root) {
        ProjectFileIndex _instance = ProjectFileIndex.SERVICE.getInstance(this.project);
        Module _findModule = this.findModule(root, _instance);
        boolean _tripleEquals = (_findModule == null);
        if (_tripleEquals) {
            return;
        }
        final ArrayList<VirtualFile> files = CollectionLiterals.<VirtualFile>newArrayList();
        VfsUtilCore.visitChildrenRecursively(root, new VirtualFileVisitor() {
            @Override
            public boolean visitFile(final VirtualFile file) {
                boolean _xblockexpression = false;
                {
                    boolean _isDirectory = file.isDirectory();
                    boolean _not = (!_isDirectory);
                    if (_not) {
                        files.add(file);
                    }
                    _xblockexpression = true;
                }
                return _xblockexpression;
            }
        });
        boolean _isEmpty = files.isEmpty();
        boolean _not = (!_isEmpty);
        if (_not) {
            BuildEvent _buildEvent = new BuildEvent(BuildEvent.Type.DELETED,
                    ((VirtualFile[]) Conversions.unwrapArray(files, VirtualFile.class)));
            CollectionExtensions.<BuildEvent>addAll(this.scheduledDeletions, _buildEvent);
        }
    }

    private final LinkedBlockingQueue<BuildEvent> scheduledDeletions = new LinkedBlockingQueue<BuildEvent>();

    public void commitScheduledDeletion() {
        final ArrayList<BuildEvent> scheduledEvents = CollectionLiterals.<BuildEvent>newArrayList();
        this.scheduledDeletions.drainTo(scheduledEvents);
        for (final BuildEvent event : scheduledEvents) {
            this.enqueue(event);
        }
    }

    public void fileAdded(final VirtualFile file) {
        if (((!file.isDirectory()) && (file.getLength() > 0))) {
            this.enqueue(BuildEvent.Type.ADDED, file);
        } else {
            boolean _isInfoEnabled = XtextAutoBuilderComponent.LOG.isInfoEnabled();
            if (_isInfoEnabled) {
                String _path = file.getPath();
                String _plus = ("Ignoring new empty file " + _path);
                String _plus_1 = (_plus + ". Waiting for content.");
                XtextAutoBuilderComponent.LOG.info(_plus_1);
            }
        }
    }

    /**
     * For testing purposes! When set to <code>true</code>, the builds are not running asynchronously and delayed, but directly when the event comes in
     */
    public static boolean TEST_MODE = false;

    protected void enqueue(final BuildEvent.Type type, final VirtualFile... files) {
        final Function1<VirtualFile, Boolean> _function = (VirtualFile it) -> {
            boolean _isExcluded = this.isExcluded(it);
            return Boolean.valueOf((!_isExcluded));
        };
        final Iterable<VirtualFile> filteredFiles = IterableExtensions
                .<VirtualFile>filter(((Iterable<VirtualFile>) Conversions.doWrapArray(files)), _function);
        boolean _isEmpty = IterableExtensions.isEmpty(filteredFiles);
        if (_isEmpty) {
            return;
        }
        BuildEvent _buildEvent = new BuildEvent(type,
                ((VirtualFile[]) Conversions.unwrapArray(filteredFiles, VirtualFile.class)));
        this.enqueue(_buildEvent);
    }

    protected void enqueue(final BuildEvent buildEvent) {
        try {
            if (((!this.disposed) && (!this.isLoaded()))) {
                this.queueAllResources();
            }
            boolean _isInfoEnabled = XtextAutoBuilderComponent.LOG.isInfoEnabled();
            if (_isInfoEnabled) {
                Map<URI, VirtualFile> _filesByURI = buildEvent.getFilesByURI();
                Set<URI> _keySet = _filesByURI.keySet();
                for (final URI uri : _keySet) {
                    BuildEvent.Type _type = buildEvent.getType();
                    String _plus = ("Queuing " + _type);
                    String _plus_1 = (_plus + " - ");
                    String _plus_2 = (_plus_1 + uri);
                    String _plus_3 = (_plus_2 + ".");
                    XtextAutoBuilderComponent.LOG.info(_plus_3);
                }
            }
            if ((!this.disposed)) {
                this.queue.put(buildEvent);
                this.doRunBuild();
            }
        } catch (Throwable _e) {
            throw Exceptions.sneakyThrow(_e);
        }
    }

    protected void doCleanBuild() {
        if (this.ignoreIncomingEvents) {
            return;
        }
        this.alarm.cancelAllRequests();
        ChunkedResourceDescriptions _get = this.chunkedResourceDescriptionsProvider.get();
        this.chunkedResourceDescriptions = _get;
        Collection<Source2GeneratedMapping> _values = this.moduleName2GeneratedMapping.values();
        final Function1<Source2GeneratedMapping, List<URI>> _function = (Source2GeneratedMapping it) -> {
            return it.getAllGenerated();
        };
        Iterable<List<URI>> _map = IterableExtensions.<Source2GeneratedMapping, List<URI>>map(_values, _function);
        Iterable<URI> _flatten = Iterables.<URI>concat(_map);
        List<URI> _list = IterableExtensions.<URI>toList(_flatten);
        this.safeDeleteUris(_list);
        this.moduleName2GeneratedMapping.clear();
        this.queueAllResources();
        this.doRunBuild();
    }

    protected void doCleanBuild(final Module module) {
        if (this.ignoreIncomingEvents) {
            return;
        }
        this.alarm.cancelAllRequests();
        String _name = module.getName();
        this.chunkedResourceDescriptions.removeContainer(_name);
        String _name_1 = module.getName();
        final Source2GeneratedMapping before = this.moduleName2GeneratedMapping.remove(_name_1);
        boolean _notEquals = (!Objects.equal(before, null));
        if (_notEquals) {
            List<URI> _allGenerated = before.getAllGenerated();
            this.safeDeleteUris(_allGenerated);
        }
        this.queueAllResources(module);
        this.doRunBuild();
    }

    protected void safeDeleteUris(final List<URI> uris) {
        boolean _isEmpty = uris.isEmpty();
        boolean _not = (!_isEmpty);
        if (_not) {
            final Application app = ApplicationManager.getApplication();
            final Runnable _function = () -> {
                try {
                    try {
                        this.ignoreIncomingEvents = true;
                        for (final URI uri : uris) {
                            {
                                final VirtualFile file = VirtualFileURIUtil.getVirtualFile(uri);
                                if (((file != null) && file.exists())) {
                                    file.delete(this);
                                }
                            }
                        }
                    } finally {
                        this.ignoreIncomingEvents = false;
                    }
                } catch (Throwable _e) {
                    throw Exceptions.sneakyThrow(_e);
                }
            };
            final Runnable runnable = _function;
            boolean _isDispatchThread = app.isDispatchThread();
            if (_isDispatchThread) {
                app.runWriteAction(runnable);
            } else {
                final Runnable _function_1 = () -> {
                    app.runWriteAction(runnable);
                };
                ModalityState _defaultModalityState = app.getDefaultModalityState();
                app.invokeLater(_function_1, _defaultModalityState);
            }
        }
    }

    private boolean avoidBuildInTestMode = false;

    /**
     * <p>
     * Should be used in the test mode only.
     * </p>
     * <p>
     * Collects all events produced by the operation and builds them at once.
     * </p>
     */
    public void runOperation(final Procedure0 operation) {
        if ((!XtextAutoBuilderComponent.TEST_MODE)) {
            throw new IllegalStateException("Should be used only for testing");
        }
        this.avoidBuildInTestMode = true;
        try {
            operation.apply();
        } finally {
            this.avoidBuildInTestMode = false;
            this.doRunBuild();
        }
    }

    protected void doRunBuild() {
        if (XtextAutoBuilderComponent.TEST_MODE) {
            if ((!this.avoidBuildInTestMode)) {
                Project _project = this.getProject();
                PsiManager _instance = PsiManager.getInstance(_project);
                PsiModificationTracker _modificationTracker = _instance.getModificationTracker();
                ((PsiModificationTrackerImpl) _modificationTracker).incCounter();
                this.build();
            }
        } else {
            this.alarm.cancelAllRequests();
            final Consumer<XtextAutoBuilderComponent.MutableCancelIndicator> _function = (
                    XtextAutoBuilderComponent.MutableCancelIndicator it) -> {
                it.canceled = true;
            };
            this.cancelIndicators.forEach(_function);
            final Runnable _function_1 = () -> {
                this.build();
            };
            Application _application = ApplicationManager.getApplication();
            ModalityState _defaultModalityState = _application.getDefaultModalityState();
            this.alarm.addRequest(_function_1, 200, _defaultModalityState);
        }
    }

    protected boolean isExcluded(final VirtualFile file) {
        if (this.ignoreIncomingEvents) {
            boolean _isDebugEnabled = XtextAutoBuilderComponent.LOG.isDebugEnabled();
            if (_isDebugEnabled) {
                String _path = file.getPath();
                String _plus = ("Ignoring transitive file change " + _path);
                XtextAutoBuilderComponent.LOG.debug(_plus);
            }
            return true;
        }
        return (Objects.equal(file, null) || file.isDirectory());
    }

    protected boolean isLoaded() {
        return ((!this.chunkedResourceDescriptions.isEmpty()) || (!this.queue.isEmpty()));
    }

    protected void queueAllResources(final Module module) {
        ModuleRootManager _instance = ModuleRootManager.getInstance(module);
        final ContentEntry[] entries = _instance.getContentEntries();
        final Function1<ContentEntry, Set<VirtualFile>> _function = (ContentEntry it) -> {
            VirtualFile[] _sourceFolderFiles = it.getSourceFolderFiles();
            return IterableExtensions
                    .<VirtualFile>toSet(((Iterable<VirtualFile>) Conversions.doWrapArray(_sourceFolderFiles)));
        };
        List<Set<VirtualFile>> _map = ListExtensions.<ContentEntry, Set<VirtualFile>>map(
                ((List<ContentEntry>) Conversions.doWrapArray(entries)), _function);
        Iterable<VirtualFile> _flatten = Iterables.<VirtualFile>concat(_map);
        for (final VirtualFile root : _flatten) {
            final Procedure1<VirtualFile> _function_1 = (VirtualFile file) -> {
                try {
                    if (((!file.isDirectory()) && file.exists())) {
                        BuildEvent _buildEvent = new BuildEvent(BuildEvent.Type.ADDED, file);
                        this.queue.put(_buildEvent);
                    }
                } catch (Throwable _e) {
                    throw Exceptions.sneakyThrow(_e);
                }
            };
            this.visitFileTree(root, _function_1);
        }
    }

    protected void queueAllResources() {
        final ModuleManager moduleManager = ModuleManager.getInstance(this.project);
        Application _application = ApplicationManager.getApplication();
        final Computable<Module[]> _function = () -> {
            return moduleManager.getModules();
        };
        Module[] _runReadAction = _application.<Module[]>runReadAction(_function);
        for (final Module module : _runReadAction) {
            this.queueAllResources(module);
        }
    }

    public void visitFileTree(final VirtualFile file, final Procedure1<? super VirtualFile> handler) {
        boolean _isDirectory = file.isDirectory();
        if (_isDirectory) {
            VirtualFile[] _children = file.getChildren();
            for (final VirtualFile child : _children) {
                this.visitFileTree(child, handler);
            }
        }
        handler.apply(file);
    }

    private volatile boolean ignoreIncomingEvents = false;

    private final static Object BUILD_MONITOR = new Object();

    protected void build() {
        if (this.disposed) {
            return;
        }
        boolean _isReadyToBeBuilt = this.isReadyToBeBuilt();
        boolean _not = (!_isReadyToBeBuilt);
        if (_not) {
            XtextAutoBuilderComponent.LOG.info("Project not yet initialized, wait some more");
            final Runnable _function = () -> {
                this.build();
            };
            this.alarm.addRequest(_function, 500);
        } else {
            if (XtextAutoBuilderComponent.TEST_MODE) {
                this.internalBuild(null);
            } else {
                ProgressManager _instance = ProgressManager.getInstance();
                _instance.run(new Task.Backgroundable(this.project, "Code Generation...") {
                    @Override
                    public void run(final ProgressIndicator indicator) {
                        indicator.setIndeterminate(true);
                        synchronized (XtextAutoBuilderComponent.BUILD_MONITOR) {
                            XtextAutoBuilderComponent.this.internalBuild(indicator);
                        }
                    }
                });
            }
        }
    }

    private boolean isReadyToBeBuilt() {
        return (XtextAutoBuilderComponent.TEST_MODE
                || (this.project.isInitialized() && (!DumbService.getInstance(this.project).isDumb())));
    }

    private List<BuildEvent> unProcessedEvents = CollectionLiterals.<BuildEvent>newArrayList();

    protected void internalBuild(final ProgressIndicator indicator) {
        this.queue.drainTo(this.unProcessedEvents);
        boolean _isEmpty = this.unProcessedEvents.isEmpty();
        if (_isEmpty) {
            return;
        }
        final Application app = ApplicationManager.getApplication();
        final XtextAutoBuilderComponent.MutableCancelIndicator cancelIndicator = new XtextAutoBuilderComponent.MutableCancelIndicator(
                indicator);
        this.cancelIndicators.add(cancelIndicator);
        Project _project = this.getProject();
        final ModuleManager moduleManager = ModuleManager.getInstance(_project);
        final BuildProgressReporter buildProgressReporter = this.buildProgressReporterProvider.get();
        buildProgressReporter.setProject(this.project);
        ArrayList<BuildEvent> _arrayList = new ArrayList<BuildEvent>(this.unProcessedEvents);
        buildProgressReporter.setEvents(_arrayList);
        try {
            final Computable<Graph<Module>> _function = () -> {
                return moduleManager.moduleGraph();
            };
            final Graph<Module> moduleGraph = app.<Graph<Module>>runReadAction(_function);
            final ArrayList<IResourceDescription.Delta> deltas = CollectionLiterals.<IResourceDescription.Delta>newArrayList();
            Collection<Module> _nodes = moduleGraph.getNodes();
            final ArrayList<Module> sortedModules = new ArrayList<Module>(_nodes);
            ModuleCompilerUtil.sortModules(this.project, sortedModules);
            for (final Module module : sortedModules) {
                {
                    Source2GeneratedMapping _elvis = null;
                    String _name = module.getName();
                    Source2GeneratedMapping _get = this.moduleName2GeneratedMapping.get(_name);
                    if (_get != null) {
                        _elvis = _get;
                    } else {
                        Source2GeneratedMapping _source2GeneratedMapping = new Source2GeneratedMapping();
                        _elvis = _source2GeneratedMapping;
                    }
                    final Source2GeneratedMapping fileMappings = _elvis;
                    ResourceDescriptionsData _elvis_1 = null;
                    String _name_1 = module.getName();
                    ResourceDescriptionsData _container = this.chunkedResourceDescriptions.getContainer(_name_1);
                    if (_container != null) {
                        _elvis_1 = _container;
                    } else {
                        List<IResourceDescription> _emptyList = CollectionLiterals
                                .<IResourceDescription>emptyList();
                        ResourceDescriptionsData _resourceDescriptionsData = new ResourceDescriptionsData(
                                _emptyList);
                        _elvis_1 = _resourceDescriptionsData;
                    }
                    final ResourceDescriptionsData moduleDescriptions = _elvis_1;
                    final HashSet<URI> changedUris = CollectionLiterals.<URI>newHashSet();
                    final HashSet<URI> deletedUris = CollectionLiterals.<URI>newHashSet();
                    ModuleRootManager _instance = ModuleRootManager.getInstance(module);
                    final VirtualFile[] contentRoots = _instance.getContentRoots();
                    final LinkedHashSet<BuildEvent> events = this.getEventsForModule(this.unProcessedEvents,
                            module);
                    if ((((List<VirtualFile>) Conversions.doWrapArray(contentRoots)).isEmpty()
                            || (events.isEmpty() && deltas.isEmpty()))) {
                        String _name_2 = module.getName();
                        String _plus = ("Skipping module \'" + _name_2);
                        String _plus_1 = (_plus + "\'. Nothing to do here.");
                        XtextAutoBuilderComponent.LOG.info(_plus_1);
                    } else {
                        this.collectChanges(events, module, changedUris, deletedUris, deltas);
                        final ResourceDescriptionsData newIndex = moduleDescriptions.copy();
                        BuildRequest _buildRequest = new BuildRequest();
                        final Procedure1<BuildRequest> _function_1 = (BuildRequest it) -> {
                            XtextResourceSet _createResourceSet = this.createResourceSet(module, newIndex);
                            it.setResourceSet(_createResourceSet);
                            List<URI> _dirtyFiles = it.getDirtyFiles();
                            Iterables.<URI>addAll(_dirtyFiles, changedUris);
                            List<URI> _deletedFiles = it.getDeletedFiles();
                            Iterables.<URI>addAll(_deletedFiles, deletedUris);
                            List<IResourceDescription.Delta> _externalDeltas = it.getExternalDeltas();
                            Iterables.<IResourceDescription.Delta>addAll(_externalDeltas, deltas);
                            VirtualFile _head = IterableExtensions.<VirtualFile>head(
                                    ((Iterable<VirtualFile>) Conversions.doWrapArray(contentRoots)));
                            URI _uRI = VirtualFileURIUtil.getURI(_head);
                            it.setBaseDir(_uRI);
                            Source2GeneratedMapping _copy = fileMappings.copy();
                            IndexState _indexState = new IndexState(newIndex, _copy);
                            it.setState(_indexState);
                            it.setAfterValidate(buildProgressReporter);
                            final Procedure1<URI> _function_2 = (URI it_1) -> {
                                buildProgressReporter.markAsAffected(it_1);
                            };
                            it.setAfterDeleteFile(_function_2);
                            it.setCancelIndicator(cancelIndicator);
                        };
                        final BuildRequest request = ObjectExtensions
                                .<BuildRequest>operator_doubleArrow(_buildRequest, _function_1);
                        IncrementalBuilder _get_1 = this.builderProvider.get();
                        Function1<URI, IResourceServiceProvider> _serviceProviderProvider = this
                                .getServiceProviderProvider(module);
                        final IncrementalBuilder.Result result = _get_1.build(request, _serviceProviderProvider);
                        final Runnable _function_2 = () -> {
                            try {
                                this.ignoreIncomingEvents = true;
                                XtextResourceSet _resourceSet = request.getResourceSet();
                                final IdeaResourceSetProvider.VirtualFileBasedUriHandler handler = IdeaResourceSetProvider.VirtualFileBasedUriHandler
                                        .find(_resourceSet);
                                handler.flushToDisk();
                            } finally {
                                this.ignoreIncomingEvents = false;
                                buildProgressReporter.rehighlight();
                            }
                        };
                        ModalityState _defaultModalityState = app.getDefaultModalityState();
                        app.invokeAndWait(_function_2, _defaultModalityState);
                        String _name_3 = module.getName();
                        IndexState _indexState = result.getIndexState();
                        ResourceDescriptionsData _resourceDescriptions = _indexState.getResourceDescriptions();
                        this.chunkedResourceDescriptions.setContainer(_name_3, _resourceDescriptions);
                        String _name_4 = module.getName();
                        IndexState _indexState_1 = result.getIndexState();
                        Source2GeneratedMapping _fileMappings = _indexState_1.getFileMappings();
                        this.moduleName2GeneratedMapping.put(_name_4, _fileMappings);
                        List<IResourceDescription.Delta> _affectedResources = result.getAffectedResources();
                        deltas.addAll(_affectedResources);
                        Iterables.removeAll(this.unProcessedEvents, events);
                    }
                }
            }
            this.unProcessedEvents.clear();
        } catch (final Throwable _t) {
            if (_t instanceof Throwable) {
                final Throwable exc = (Throwable) _t;
                boolean _isOperationCanceledException = this.operationCanceledManager
                        .isOperationCanceledException(exc);
                if (_isOperationCanceledException) {
                    boolean _isInfoEnabled = XtextAutoBuilderComponent.LOG.isInfoEnabled();
                    if (_isInfoEnabled) {
                        XtextAutoBuilderComponent.LOG.info("Build canceled.");
                    }
                } else {
                    XtextAutoBuilderComponent.LOG.error("Error during auto build.", exc);
                }
            } else {
                throw Exceptions.sneakyThrow(_t);
            }
        } finally {
            buildProgressReporter.clearProgress();
            this.cancelIndicators.remove(cancelIndicator);
        }
    }

    protected LinkedHashSet<BuildEvent> getEventsForModule(final List<BuildEvent> events, final Module module) {
        final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
        final String[] excludeRootUrls = moduleRootManager.getExcludeRootUrls();
        final String[] sourceRootUrls = moduleRootManager.getSourceRootUrls();
        final LinkedHashSet<BuildEvent> result = new LinkedHashSet<BuildEvent>();
        for (final BuildEvent event : events) {
            Set<URI> _uRIs = event.getURIs();
            boolean _isEmpty = _uRIs.isEmpty();
            boolean _not = (!_isEmpty);
            if (_not) {
                Set<URI> _uRIs_1 = event.getURIs();
                URI _head = IterableExtensions.<URI>head(_uRIs_1);
                final String url = _head.toString();
                if ((IterableExtensions.<String>forall(
                        ((Iterable<String>) Conversions.doWrapArray(excludeRootUrls)),
                        ((Function1<String, Boolean>) (String it) -> {
                            boolean _isUrlUnderRoot = this.isUrlUnderRoot(url, it);
                            return Boolean.valueOf((!_isUrlUnderRoot));
                        })) && IterableExtensions.<String>exists(
                                ((Iterable<String>) Conversions.doWrapArray(sourceRootUrls)),
                                ((Function1<String, Boolean>) (String it) -> {
                                    return Boolean.valueOf(this.isUrlUnderRoot(url, it));
                                })))) {
                    result.add(event);
                }
            }
        }
        return result;
    }

    private final static char SEGMENT_SEPARATOR = '/';

    protected boolean isUrlUnderRoot(final String url, final String rootUrl) {
        return (((url.length() > rootUrl.length())
                && (url.charAt(rootUrl.length()) == XtextAutoBuilderComponent.SEGMENT_SEPARATOR))
                && FileUtil.startsWith(url, rootUrl));
    }

    public Function1<URI, IResourceServiceProvider> getServiceProviderProvider(final Module module) {
        final Function1<URI, IResourceServiceProvider> _function = (URI it) -> {
            final IResourceServiceProvider serviceProvider = this.resourceServiceProviderRegistry
                    .getResourceServiceProvider(it);
            boolean _notEquals = (!Objects.equal(serviceProvider, null));
            if (_notEquals) {
                final FacetProvider facetProvider = serviceProvider.<FacetProvider>get(FacetProvider.class);
                boolean _notEquals_1 = (!Objects.equal(facetProvider, null));
                if (_notEquals_1) {
                    final Facet<? extends AbstractFacetConfiguration> facet = facetProvider.getFacet(module);
                    boolean _notEquals_2 = (!Objects.equal(facet, null));
                    if (_notEquals_2) {
                        return serviceProvider;
                    }
                }
            }
            return null;
        };
        return _function;
    }

    public XtextResourceSet createResourceSet(final Module module, final ResourceDescriptionsData newData) {
        final XtextResourceSet result = this.resourceSetProvider.get(module);
        final ChunkedResourceDescriptions fullIndex = ChunkedResourceDescriptions.findInEmfObject(result);
        String _name = module.getName();
        fullIndex.setContainer(_name, newData);
        return result;
    }

    public String getContainerHandle(final Module module) {
        return module.getName();
    }

    protected void collectChanges(final Set<BuildEvent> events, final Module module, final HashSet<URI> changedUris,
            final HashSet<URI> deletedUris, final ArrayList<IResourceDescription.Delta> deltas) {
        final Application app = ApplicationManager.getApplication();
        String _name = module.getName();
        final Source2GeneratedMapping fileMappings = this.moduleName2GeneratedMapping.get(_name);
        for (final BuildEvent event : events) {
            BuildEvent.Type _type = event.getType();
            if (_type != null) {
                switch (_type) {
                case MODIFIED:
                case ADDED:
                    Set<URI> _uRIs = event.getURIs();
                    for (final URI uri : _uRIs) {
                        {
                            List<URI> _source = null;
                            if (fileMappings != null) {
                                _source = fileMappings.getSource(uri);
                            }
                            final List<URI> sourceUris = _source;
                            if (((!Objects.equal(sourceUris, null)) && (!sourceUris.isEmpty()))) {
                                for (final URI sourceUri : sourceUris) {
                                    this.consistentAdd(sourceUri, changedUris, deletedUris);
                                }
                            } else {
                                boolean _isJavaFile = this.isJavaFile(uri);
                                if (_isJavaFile) {
                                    final Computable<Set<IResourceDescription.Delta>> _function = () -> {
                                        VirtualFile _file = event.getFile(uri);
                                        return this.getJavaDeltas(_file, module);
                                    };
                                    Set<IResourceDescription.Delta> _runReadAction = app.<Set<IResourceDescription.Delta>>runReadAction(
                                            _function);
                                    Iterables.<IResourceDescription.Delta>addAll(deltas, _runReadAction);
                                } else {
                                    this.consistentAdd(uri, changedUris, deletedUris);
                                }
                            }
                        }
                    }
                    break;
                case DELETED:
                    Set<URI> _uRIs_1 = event.getURIs();
                    for (final URI uri_1 : _uRIs_1) {
                        {
                            List<URI> _source = null;
                            if (fileMappings != null) {
                                _source = fileMappings.getSource(uri_1);
                            }
                            final List<URI> sourceUris = _source;
                            if (((!Objects.equal(sourceUris, null)) && (!sourceUris.isEmpty()))) {
                                for (final URI sourceUri : sourceUris) {
                                    boolean _contains = deletedUris.contains(sourceUri);
                                    boolean _not = (!_contains);
                                    if (_not) {
                                        changedUris.add(sourceUri);
                                    }
                                }
                            } else {
                                boolean _isJavaFile = this.isJavaFile(uri_1);
                                if (_isJavaFile) {
                                    final Computable<Set<IResourceDescription.Delta>> _function = () -> {
                                        VirtualFile _file = event.getFile(uri_1);
                                        return this.getJavaDeltas(_file, module);
                                    };
                                    Set<IResourceDescription.Delta> _runReadAction = app.<Set<IResourceDescription.Delta>>runReadAction(
                                            _function);
                                    Iterables.<IResourceDescription.Delta>addAll(deltas, _runReadAction);
                                } else {
                                    this.consistentAdd(uri_1, deletedUris, changedUris);
                                }
                            }
                        }
                    }
                    break;
                default:
                    break;
                }
            }
        }
    }

    protected void consistentAdd(final URI uri, final Set<URI> toBeAdded, final Set<URI> toBeRemoved) {
        toBeAdded.add(uri);
        toBeRemoved.remove(uri);
    }

    public boolean isJavaFile(final URI file) {
        String _fileExtension = file.fileExtension();
        return Objects.equal(_fileExtension, "java");
    }

    public Set<IResourceDescription.Delta> getJavaDeltas(final VirtualFile file, final Module module) {
        boolean _isValid = file.isValid();
        boolean _not = (!_isValid);
        if (_not) {
            return CollectionLiterals.<IResourceDescription.Delta>emptySet();
        }
        Project _project = module.getProject();
        PsiManager _instance = PsiManager.getInstance(_project);
        final PsiFile psiFile = _instance.findFile(file);
        final LinkedHashSet<IResourceDescription.Delta> result = CollectionLiterals.<IResourceDescription.Delta>newLinkedHashSet();
        if ((psiFile instanceof PsiJavaFile)) {
            PsiClass[] _classes = ((PsiJavaFile) psiFile).getClasses();
            for (final PsiClass clazz : _classes) {
                String _qualifiedName = clazz.getQualifiedName();
                QualifiedName _qualifiedName_1 = this.qualifiedNameConverter.toQualifiedName(_qualifiedName);
                TypeResourceDescription.ChangedDelta _changedDelta = new TypeResourceDescription.ChangedDelta(
                        _qualifiedName_1);
                result.add(_changedDelta);
            }
        }
        return result;
    }

    public ChunkedResourceDescriptions installCopyOfResourceDescriptions(final ResourceSet resourceSet) {
        return this.chunkedResourceDescriptions.createShallowCopyWith(resourceSet);
    }

    protected Module findModule(final BuildEvent it, final ProjectFileIndex fileIndex) {
        Module _xblockexpression = null;
        {
            Map<URI, VirtualFile> _filesByURI = it.getFilesByURI();
            Collection<VirtualFile> _values = _filesByURI.values();
            final VirtualFile file = IterableExtensions.<VirtualFile>head(_values);
            Module _xifexpression = null;
            BuildEvent.Type _type = it.getType();
            boolean _equals = Objects.equal(_type, BuildEvent.Type.DELETED);
            if (_equals) {
                _xifexpression = this.findModule(file, fileIndex);
            } else {
                _xifexpression = fileIndex.getModuleForFile(file, true);
            }
            _xblockexpression = _xifexpression;
        }
        return _xblockexpression;
    }

    protected Module findModule(final VirtualFile file, final ProjectFileIndex fileIndex) {
        boolean _equals = Objects.equal(file, null);
        if (_equals) {
            return null;
        }
        final Module module = fileIndex.getModuleForFile(file, true);
        boolean _notEquals = (!Objects.equal(module, null));
        if (_notEquals) {
            return module;
        }
        VirtualFile _parent = file.getParent();
        return this.findModule(_parent, fileIndex);
    }

    @Override
    public String getComponentName() {
        return "Xtext Compiler Component";
    }

    public ChunkedResourceDescriptions getIndexState() {
        return this.chunkedResourceDescriptions;
    }

    @Inject
    private XtextAutoBuilderComponentState.Codec codec;

    @Override
    public XtextAutoBuilderComponentState getState() {
        return this.codec.encode(this.resourceServiceProviderRegistry, this.chunkedResourceDescriptions,
                this.moduleName2GeneratedMapping);
    }

    @Override
    public void loadState(final XtextAutoBuilderComponentState state) {
        try {
            Map<String, Object> _extensionToFactoryMap = this.resourceServiceProviderRegistry
                    .getExtensionToFactoryMap();
            final Set<String> installedNow = _extensionToFactoryMap.keySet();
            final Set<String> installedLastTime = this.codec.decodeInstalledLanguages(state);
            boolean _equals = installedNow.equals(installedLastTime);
            boolean _not = (!_equals);
            if (_not) {
                XtextAutoBuilderComponent.LOG.info("Different Xtext plugins than last time. Reindexing project.");
                ChunkedResourceDescriptions _get = this.chunkedResourceDescriptionsProvider.get();
                this.chunkedResourceDescriptions = _get;
                this.moduleName2GeneratedMapping.clear();
                this.doCleanBuild();
            } else {
                boolean _isDebugEnabled = XtextAutoBuilderComponent.LOG.isDebugEnabled();
                if (_isDebugEnabled) {
                    XtextAutoBuilderComponent.LOG.debug("Loading persisted index state.");
                }
                ChunkedResourceDescriptions _decodeIndex = this.codec.decodeIndex(state);
                this.chunkedResourceDescriptions = _decodeIndex;
                Map<String, Source2GeneratedMapping> _decodeModuleToGenerated = this.codec
                        .decodeModuleToGenerated(state);
                this.moduleName2GeneratedMapping = _decodeModuleToGenerated;
            }
        } catch (final Throwable _t) {
            if (_t instanceof Exception) {
                final Exception exc = (Exception) _t;
                XtextAutoBuilderComponent.LOG.error("Error loading XtextAutoBuildComponentState ", exc);
                ChunkedResourceDescriptions _get_1 = this.chunkedResourceDescriptionsProvider.get();
                this.chunkedResourceDescriptions = _get_1;
                this.moduleName2GeneratedMapping.clear();
                this.doCleanBuild();
            } else {
                throw Exceptions.sneakyThrow(_t);
            }
        }
    }

    private final static Logger LOG = Logger.getLogger(XtextAutoBuilderComponent.class);
}