/*
* Copyright 2003-2014 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;
import jetbrains.mps.smodel.DefaultScope;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.smodel.MPSModuleOwner;
import jetbrains.mps.util.annotation.ToRemove;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.module.ModelAccess;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.module.SRepository;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* MPS Project abstraction. Project may rely on the idea Project or it may not.
* It has a scope and a corresponding project repository to store modules in it.
*/
public abstract class Project implements MPSModuleOwner, IProject {
private static final Logger LOG = LogManager.getLogger(Project.class);
private final String myName;
private final ProjectScope myScope = new ProjectScope();
private final ProjectRepository myRepository;
private boolean myDisposed;
protected Project(String name) {
myName = name;
myRepository = new ProjectRepository(this);
myRepository.init();
}
@NotNull
@Override
public final ProjectScope getScope() {
return myScope;
}
@Override
@NotNull
public final SRepository getRepository() {
return myRepository;
}
/**
* Shorthand for <code>getRepository().getModelAccess()</code>
*
* @return access facility to models coming from a {@link #getRepository() repository} associated with this project.
*/
@Override
@NotNull
public final ModelAccess getModelAccess() {
return myRepository.getModelAccess();
}
@NotNull
@ToRemove(version = 3.4)
public abstract String getName();
@ToRemove(version = 3.3)
public abstract <T> T getComponent(Class<T> t);
/**
* @deprecated the project is not necessarily backed up by file. Left for compatibility
* @see FileBasedProject
*/
@Deprecated
@ToRemove(version = 3.3)
public File getProjectFile() {
if (this instanceof FileBasedProject) {
FileBasedProject fileBasedProject = (FileBasedProject) this;
return fileBasedProject.getProjectFile();
}
return null;
}
/**
* @deprecated use {@link #getProjectModules)} instead
* AP fixme : why to return Iterable<? extends>? isn't it easier to give out a collection, e.g. a list?
*/
@NotNull
@Deprecated
public final Iterable<? extends SModule> getModules() {
return getProjectModules();
}
@NotNull
public final List<SModule> getProjectModulesWithGenerators() {
final ArrayList<SModule> result = new ArrayList<>();
// although getProjectModules likely to access cached value, Language.getGenerators needs MA.
// Since we are interested in a consistent repository state, and add/remove of a module from a repository
// is guarded by repository's MA, it doesn't hurt to ensure proper access here.
getModelAccess().runReadAction(() -> {
for (SModule m : getProjectModules()) {
result.add(m);
if (m instanceof Language) {
result.addAll(((Language) m).getGenerators());
}
}
});
return result;
}
/**
* @deprecated use {@link #getProjectModulesWithGenerators()} instead
*/
@Deprecated
@NotNull
public final Iterable<? extends SModule> getModulesWithGenerators() {
return getProjectModulesWithGenerators();
}
// AP todo remove from Project
public final boolean isProjectModule(@NotNull SModule module) {
return getProjectModules().contains(module);
}
// AP todo transfer from Project to ProjectBase; helping method -- no need to be here
@NotNull
public final <T extends SModule> List<T> getProjectModules(Class<T> moduleClass) {
List<T> result = new ArrayList<T>();
for (SModule module : getProjectModules()) {
if (moduleClass.isInstance(module)) {
result.add(moduleClass.cast(module));
}
}
return result;
}
// AP todo transfer from Project to ProjectBase
public final Iterable<SModel> getProjectModels() {
List<SModel> result = new ArrayList<SModel>();
for (SModule module : getProjectModules()) {
for (SModel model : module.getModels()) {
result.add(model);
}
}
return result;
}
@Override
public final boolean isHidden() {
return false;
}
@NotNull
public String toString() {
return "MPS Project [" + myName + "] " + (myDisposed ? ", disposed]" : "]");
}
/**
* closes and disposes the project
*/
public void dispose() {
myRepository.dispose();
myDisposed = true;
}
final void checkNotDisposed() {
if (isDisposed()) {
throw new IllegalStateException("Cannot proceed with disposed project " + this);
}
}
public boolean isDisposed() {
return myDisposed;
}
public final class ProjectScope extends DefaultScope {
@Override
protected Set<SModule> getInitialModules() {
List<Project> openProjects = ProjectManager.getInstance().getOpenedProjects();
assert openProjects.contains(Project.this) : "trying to get scope on a not-yet-loaded project";
Set<SModule> result = new HashSet<SModule>();
result.addAll(getProjectModules(SModule.class));
for (Language l : getProjectModules(Language.class)) {
result.addAll(l.getGenerators());
}
return result;
}
}
}