/*
* Copyright 2003-2015 JetBrains s.r.o.
*
* 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.
*/
package jetbrains.mps.project.dependency;
import jetbrains.mps.project.AbstractModule;
import jetbrains.mps.project.dependency.GlobalModuleDependenciesManager.ErrorHandler;
import jetbrains.mps.project.structure.ProjectStructureModule;
import jetbrains.mps.project.structure.modules.Dependency;
import jetbrains.mps.project.structure.modules.DeploymentDescriptor;
import jetbrains.mps.project.structure.modules.ModuleDescriptor;
import jetbrains.mps.smodel.ModuleRepositoryFacade;
import jetbrains.mps.smodel.tempmodel.TempModule;
import jetbrains.mps.util.annotation.Hack;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.language.SLanguage;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.module.SModuleReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Calculates the runtimes of used languages of the given module.
* <p/>
* Behaves differently depending whether the given module has deployed descriptor or it does not.
* <p/>
* Created by apyshkin on 11/12/15.
*/
class RuntimesOfUsedLanguageCalculator {
private static final Logger LOG = LogManager.getLogger(RuntimesOfUsedLanguageCalculator.class);
private final SModule myModule;
private final Strategy myStrategy;
private final Map<SLanguage, Collection<SModule>> myLanguageRuntimesCache;
public RuntimesOfUsedLanguageCalculator(@NotNull SModule module, Map<SLanguage, Collection<SModule>> languageRuntimesCache, ErrorHandler errorHandler) {
myModule = module;
myStrategy = isPackaged() ? new DeploymentStrategy(errorHandler) : new SourceStrategy(errorHandler);
myLanguageRuntimesCache = languageRuntimesCache;
}
private boolean isPackaged() {
return !(myModule instanceof TempModule) && !(myModule instanceof ProjectStructureModule) && myModule.isPackaged();
}
/**
* @return the runtimes of the used languages
*/
public Set<SModule> invoke() {
return myStrategy.findRuntimes();
}
private interface Strategy {
Set<SModule> findRuntimes();
}
/**
* used when we have a deployed module (with a deployed descriptor which has generated runtimes of used languages already)
* NB: we do not have a clear contract which are the dependencies generated in the deployment descriptor.
* As for now it seems to be only the runtimes of used languages of modules, no direct dependencies.
*/
@Hack
private class DeploymentStrategy implements Strategy {
private final ErrorHandler myErrorHandler;
public DeploymentStrategy(ErrorHandler errorHandler) {
myErrorHandler = errorHandler;
}
@Override
public Set<SModule> findRuntimes() {
Set<SModule> result = new HashSet<>();
ModuleDescriptor moduleDescriptor = ((AbstractModule) myModule).getModuleDescriptor();
if (moduleDescriptor == null) {
LOG.warn("Module descriptor could not be found for the module " + myModule + "; falling back to the SourceStrategy.");
return new SourceStrategy(myErrorHandler).findRuntimes();
}
DeploymentDescriptor descriptor = moduleDescriptor.getDeploymentDescriptor();
if (descriptor == null) {
LOG.debug("The deployment descriptor could not be found for the module " + myModule + "; falling back to the SourceStrategy.");
return new SourceStrategy(myErrorHandler).findRuntimes();
}
Collection<Dependency> dependencies = descriptor.getDependencies();
for (Dependency dependency : dependencies) {
SModuleReference runtimeRef = dependency.getModuleRef();
SModule runtime = ModuleRepositoryFacade.getInstance().getModule(runtimeRef);
if (runtime != null) {
result.add(runtime);
} else {
myErrorHandler.runtimeDependencyCannotBeFound(runtimeRef);
}
}
return result;
}
}
/**
* used when we do not have a deployed module; we have to look for the source module of the language to gather its runtimes
*/
private class SourceStrategy implements Strategy {
private final ErrorHandler myErrorHandler;
public SourceStrategy(ErrorHandler errorHandler) {
myErrorHandler = errorHandler;
}
@Override
public Set<SModule> findRuntimes() {
Set<SModule> result = new HashSet<>();
for (SLanguage usedLang : myModule.getUsedLanguages()) {
if (usedLang.getSourceModule() == null) {
if (!(myModule instanceof TempModule)) {
myErrorHandler.langSourceModuleCannotBeResolved(usedLang);
}
continue;
}
if (!myLanguageRuntimesCache.containsKey(usedLang)) {
List<SModule> runtimes = new ArrayList<>();
myLanguageRuntimesCache.put(usedLang, runtimes);
for (SModuleReference runtimeRef : usedLang.getLanguageRuntimes()) {
SModule runtime = ModuleRepositoryFacade.getInstance().getModule(runtimeRef);
if (runtime != null) {
runtimes.add(runtime);
} else {
myErrorHandler.runtimeDependencyCannotBeFound(usedLang, runtimeRef);
}
}
}
result.addAll(myLanguageRuntimesCache.get(usedLang));
}
return result;
}
}
}