/*******************************************************************************
* 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();
}
}