/******************************************************************************* * MontiCore Language Workbench * Copyright (c) 2015, 2016, MontiCore, All rights reserved. * * This project is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this project. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package de.se_rwth.langeditor.modelstates; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.antlr.v4.runtime.ParserRuleContext; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IStorage; import org.eclipse.core.resources.ResourcesPlugin; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.inject.Inject; import com.google.inject.Singleton; import de.se_rwth.commons.SourcePosition; import de.se_rwth.langeditor.global.LanguageLocator; import de.se_rwth.langeditor.language.Language; import de.se_rwth.langeditor.modelstates.ModelState.ModelStateBuilder; import de.se_rwth.langeditor.util.Misc; import de.se_rwth.langeditor.util.ResourceLocator; @Singleton public class ModelStateAssembler { private final ScheduledExecutorService executor; private final LanguageLocator locator; private final ObservableModelStates observableModelStates; private final ConcurrentHashMap<IStorage, ScheduledFuture<?>> scheduledRebuilds = new ConcurrentHashMap<>(); private final Nodes nodes; @Inject public ModelStateAssembler( ScheduledExecutorService executor, LanguageLocator locator, ObservableModelStates observableModelStates, Nodes nodes) { this.executor = executor; this.locator = locator; this.observableModelStates = observableModelStates; this.nodes = nodes; } public void scheduleRebuild(IStorage storage, IProject project, String content) { ScheduledFuture<?> scheduledRebuild = scheduledRebuilds.get(storage); if (scheduledRebuild != null) { scheduledRebuild.cancel(false); } Runnable runnable = () -> { try { rebuildModelState(storage, project, content); } catch (Exception e) { e.printStackTrace(); } }; scheduledRebuilds.put(storage, executor.schedule(runnable, 1, TimeUnit.SECONDS)); } public void scheduleProjectRebuild(IProject project) { Set<IStorage> storages = Maps.filterValues(ResourceLocator.getStorages(), (projectValue -> projectValue == project)).keySet(); storages.stream() .map(scheduledRebuilds::get) .filter(scheduledRebuild -> scheduledRebuild != null) .forEach(scheduledRebuild -> scheduledRebuild.cancel(false)); executor.execute(() -> { try { rebuildProject(project, storages); } catch (Exception e) { e.printStackTrace(); } }); } public void scheduleFullRebuild() { for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) { scheduleProjectRebuild(project); } } private synchronized void rebuildModelState(IStorage storage, IProject project, String content) { Optional<Language> findLanguage = locator.findLanguage(storage); findLanguage.ifPresent(language -> { ModelState modelState = createNewModelState(storage, project, content, language); if (modelState.isLegal()) { language.buildModel(modelState); } observableModelStates.acceptModelState(modelState); }); } private synchronized void rebuildProject(IProject project, Set<IStorage> storages) { for (Language language : locator.getLanguages()) { Set<ModelState> modelStates = storages.stream() .filter(storage -> locator.findLanguage(storage).map(language::equals).orElse(false)) .map(storage -> createNewModelState(storage, project, Misc.getContents(storage), language)) .collect(Collectors.toSet()); ImmutableSet<ModelState> legalModelStates = ImmutableSet.copyOf(modelStates.stream() .filter(ModelState::isLegal) .collect(Collectors.toSet())); language.buildProject(project, legalModelStates, ResourceLocator.assembleModelPath(project)); modelStates.forEach(observableModelStates::acceptModelState); } } private ModelState createNewModelState( IStorage storage, IProject project, String content, Language language) { ImmutableMultimap.Builder<SourcePosition, String> syntaxErrorBuilder = ImmutableMultimap.builder(); ParserRuleContext rootContext = language.getParserConfig().parse(content, (pos, message) -> syntaxErrorBuilder.put( pos, message)); nodes.addNodes(rootContext); return new ModelStateBuilder() .setStorage(storage) .setProject(project) .setContent(content) .setLanguage(language) .setRootNode(Nodes.getAstNode(rootContext).get()) .setRootContext(rootContext) .setSyntaxErrors(syntaxErrorBuilder.build()) .setLastModelState(observableModelStates.findModelState(storage).orElse(null)) .build(); } }