blob: 3109e8cb2edd0668aa61f1388f0237f6f8cb1cb0 [file] [log] [blame]
/*
* Copyright 2000-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 com.intellij.util.xml.model.impl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.ModelMerger;
import com.intellij.util.xml.model.DomModel;
import com.intellij.util.xml.model.DomModelCache;
import com.intellij.util.xml.model.MultipleDomModelFactory;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* User: Sergey.Vasiliev
*/
public abstract class CachedMultipleDomModelFactory<Scope extends UserDataHolder, T extends DomElement, M extends DomModel<T>, C extends PsiElement>
extends DomModelFactoryHelper<T>
implements CachedDomModelFactory<T,M,Scope>, MultipleDomModelFactory<Scope,T,M> {
private final DomModelCache<M, Scope> myCombinedModelCache;
private final DomModelCache<List<M>, Scope> myAllModelsCache;
protected CachedMultipleDomModelFactory(@NotNull Class<T> aClass,
@NotNull ModelMerger modelMerger,
final Project project,
@NonNls String name) {
super(aClass,modelMerger);
myCombinedModelCache = new DomModelCache<M, Scope>(project, name + " combined model") {
@Override
@NotNull
protected CachedValueProvider.Result<M> computeValue(@NotNull final Scope scope) {
final M combinedModel = computeCombinedModel(scope);
return new CachedValueProvider.Result<M>(combinedModel, computeDependencies(combinedModel, scope));
}
};
myAllModelsCache = new DomModelCache<List<M>, Scope>(project, name + " models list") {
@Override
@NotNull
protected CachedValueProvider.Result<List<M>> computeValue(@NotNull final Scope scope) {
final List<M> models = computeAllModels(scope);
return new CachedValueProvider.Result<List<M>>(models, computeDependencies(null, scope));
}
};
}
@Nullable
public abstract M getModel(@NotNull C context);
@Override
@NotNull
public List<M> getAllModels(@NotNull Scope scope) {
final List<M> models = myAllModelsCache.getCachedValue(scope);
if (models == null) {
return Collections.emptyList();
}
else {
return models;
}
}
@Nullable
protected abstract List<M> computeAllModels(@NotNull Scope scope);
@Override
@Nullable
public M getCombinedModel(@Nullable Scope scope) {
if (scope == null) {
return null;
}
return myCombinedModelCache.getCachedValue(scope);
}
@Nullable
protected M computeCombinedModel(@NotNull Scope scope) {
final List<M> models = getAllModels(scope);
switch (models.size()) {
case 0:
return null;
case 1:
return models.get(0);
}
final Set<XmlFile> configFiles = new LinkedHashSet<XmlFile>();
final LinkedHashSet<DomFileElement<T>> list = new LinkedHashSet<DomFileElement<T>>(models.size());
for (M model: models) {
final Set<XmlFile> files = model.getConfigFiles();
for (XmlFile file: files) {
ContainerUtil.addIfNotNull(getDomRoot(file), list);
}
configFiles.addAll(files);
}
final DomFileElement<T> mergedModel = getModelMerger().mergeModels(DomFileElement.class, list);
final M firstModel = models.get(0);
return createCombinedModel(configFiles, mergedModel, firstModel, scope);
}
/**
* Factory method to create combined model for given module.
* Used by {@link #computeCombinedModel(com.intellij.openapi.module.Module)}.
*
* @param configFiles file set including all files for all models returned by {@link #getAllModels(com.intellij.openapi.module.Module)}.
* @param mergedModel merged model for all models returned by {@link #getAllModels(com.intellij.openapi.module.Module)}.
* @param firstModel the first model returned by {@link #getAllModels(com.intellij.openapi.module.Module)}.
* @param scope
* @return combined model.
*/
protected abstract M createCombinedModel(Set<XmlFile> configFiles, DomFileElement<T> mergedModel, M firstModel, final Scope scope);
@NotNull
public Set<XmlFile> getConfigFiles(@Nullable C context) {
if (context == null) {
return Collections.emptySet();
}
final M model = getModel(context);
if (model == null) {
return Collections.emptySet();
}
else {
return model.getConfigFiles();
}
}
@Override
@NotNull
public Set<XmlFile> getAllConfigFiles(@NotNull Scope scope) {
final HashSet<XmlFile> xmlFiles = new HashSet<XmlFile>();
for (M model: getAllModels(scope)) {
xmlFiles.addAll(model.getConfigFiles());
}
return xmlFiles;
}
public List<DomFileElement<T>> getFileElements(M model) {
final ArrayList<DomFileElement<T>> list = new ArrayList<DomFileElement<T>>(model.getConfigFiles().size());
for (XmlFile configFile: model.getConfigFiles()) {
final DomFileElement<T> element = DomManager.getDomManager(configFile.getProject()).getFileElement(configFile, myClass);
if (element != null) {
list.add(element);
}
}
return list;
}
}