/*
* 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.smodel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.language.SConcept;
import org.jetbrains.mps.openapi.language.SLanguage;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeUtil;
import org.jetbrains.mps.openapi.model.SReference;
import java.util.HashSet;
import java.util.Set;
/**
* Facility to collect actual external dependencies, like languages, concepts, and other models for a given model or set of nodes.
* Walks a model and gathers information about nodes according to configuration.
* By default, collects languages in use and references to external models.
*
* @author Artem Tikhomirov
*/
public final class ModelDependencyScanner {
private final Set<SLanguage> myUsedLanguages;
private final Set<org.jetbrains.mps.openapi.model.SModelReference> myCrossModelReferences;
private final Set<SConcept> myConcepts;
private boolean myNeedLanguages = true;
private boolean myNeedCrossModel = true;
private boolean myNeedConcepts = false;
public ModelDependencyScanner() {
myUsedLanguages = new HashSet<SLanguage>();
myCrossModelReferences = new HashSet<org.jetbrains.mps.openapi.model.SModelReference>();
myConcepts = new HashSet<SConcept>();
}
/**
* Default: <code>true</code>
*/
public ModelDependencyScanner usedLanguages(boolean collectUsedLanguages) {
myNeedLanguages = collectUsedLanguages;
return this;
}
/**
* Default: <code>true</code>
*/
public ModelDependencyScanner crossModelReferences(boolean crossModelReferences) {
myNeedCrossModel = crossModelReferences;
return this;
}
/**
* Default: <code>false</code>
*/
public ModelDependencyScanner usedConcepts(boolean collectConcepts) {
myNeedConcepts = collectConcepts;
return this;
}
@NotNull
public Set<SLanguage> getUsedLanguages() {
return myUsedLanguages;
}
@NotNull
public Set<org.jetbrains.mps.openapi.model.SModelReference> getCrossModelReferences() {
return myCrossModelReferences;
}
@NotNull
public Set<SConcept> getConcepts() {
return myConcepts;
}
/**
* Iterate over all nodes of the given model
* @return <code>this</code> for convenience
*/
public ModelDependencyScanner walk(@NotNull SModel model) {
return walk(SNodeUtil.getDescendants(model));
}
/**
* Iterate over given nodes.
* IMPORTANT! This method doesn't look into children of supplied nodes, it takes parameter 'literally' and visits
* only nodes specified. To visit complete tree, consider using {@link SNodeUtil#getDescendants(SNode)}.
*
* @return <code>this</code> for convenience
*/
public ModelDependencyScanner walk(@NotNull Iterable<org.jetbrains.mps.openapi.model.SNode> nodes) {
HashSet<org.jetbrains.mps.openapi.model.SModelReference> allRefTargets = new HashSet<org.jetbrains.mps.openapi.model.SModelReference>();
// collection of input nodes is not restricted to come from a single model,
// hence we track models of nodes we iterate through to exclude them later from the set of cross-model.
HashSet<org.jetbrains.mps.openapi.model.SModelReference> sourceModels = new HashSet<org.jetbrains.mps.openapi.model.SModelReference>();
for (SNode n : nodes) {
if (myNeedConcepts) {
myConcepts.add(n.getConcept());
}
if (myNeedLanguages) {
myUsedLanguages.add(n.getConcept().getLanguage());
}
if (myNeedCrossModel) {
final org.jetbrains.mps.openapi.model.SModel sourceModel = n.getModel();
if (sourceModel != null) {
sourceModels.add(sourceModel.getReference());
}
for (SReference ref : n.getReferences()) {
final org.jetbrains.mps.openapi.model.SModelReference targetModel = ref.getTargetSModelReference();
if (targetModel != null) {
allRefTargets.add(targetModel);
}
}
}
}
if (myNeedCrossModel) {
allRefTargets.removeAll(sourceModels);
myCrossModelReferences.addAll(allRefTargets);
}
return this;
}
}