fix class name completion inside xml tag value
[fedora-idea.git] / dom / openapi / src / com / intellij / util / xml / model / DomModelFactory.java
blob1e5aba508e2a6641755f457992d86988cf22eef3
1 /*
2 * Copyright 2000-2007 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.util.xml.model;
19 import com.intellij.openapi.module.Module;
20 import com.intellij.openapi.module.ModuleUtil;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.roots.ProjectRootManager;
23 import com.intellij.psi.PsiElement;
24 import com.intellij.psi.PsiFile;
25 import com.intellij.psi.util.CachedValueProvider;
26 import com.intellij.psi.util.PsiModificationTracker;
27 import com.intellij.psi.xml.XmlFile;
28 import com.intellij.util.containers.ContainerUtil;
29 import com.intellij.util.xml.DomElement;
30 import com.intellij.util.xml.DomFileElement;
31 import com.intellij.util.xml.DomManager;
32 import com.intellij.util.xml.ModelMerger;
33 import org.jetbrains.annotations.NonNls;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
37 import java.util.*;
39 /**
40 * @author Dmitry Avdeev
42 public abstract class DomModelFactory<T extends DomElement, M extends DomModel<T>, C extends PsiElement> extends SimpleDomModelFactory<T> {
44 private final DomModelCache<M, XmlFile> myModelCache;
45 private final DomModelCache<M, Module> myCombinedModelCache;
46 private final DomModelCache<List<M>, Module> myAllModelsCache;
49 protected DomModelFactory(@NotNull Class<T> aClass,
50 @NotNull ModelMerger modelMerger,
51 final Project project,
52 @NonNls String name) {
53 super(aClass, modelMerger);
55 myModelCache = new DomModelCache<M, XmlFile>(project, name + " model") {
56 @NotNull
57 protected CachedValueProvider.Result<M> computeValue(@NotNull XmlFile file) {
58 final PsiFile originalFile = file.getOriginalFile();
59 if (originalFile != null) {
60 file = (XmlFile)originalFile;
63 final Module module = ModuleUtil.findModuleForPsiElement(file);
64 final M model = computeModel(file, module);
65 return new CachedValueProvider.Result<M>(model, computeDependencies(model, module));
69 myCombinedModelCache = new DomModelCache<M, Module>(project, name + " combined model") {
70 @NotNull
71 protected CachedValueProvider.Result<M> computeValue(@NotNull final Module module) {
72 final M combinedModel = computeCombinedModel(module);
73 return new CachedValueProvider.Result<M>(combinedModel, computeDependencies(combinedModel, module));
77 myAllModelsCache = new DomModelCache<List<M>, Module>(project, name + " models list") {
78 @NotNull
79 protected CachedValueProvider.Result<List<M>> computeValue(@NotNull final Module module) {
80 final List<M> models = computeAllModels(module);
81 return new CachedValueProvider.Result<List<M>>(models, computeDependencies(null, module));
86 @NotNull
87 public Object[] computeDependencies(@Nullable M model, @Nullable Module module) {
89 final ArrayList<Object> dependencies = new ArrayList<Object>();
90 final Set<XmlFile> files;
91 if (model != null) {
92 files = model.getConfigFiles();
93 dependencies.addAll(files);
94 } else {
95 dependencies.add(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
97 if (module != null) {
98 dependencies.add(ProjectRootManager.getInstance(module.getProject()));
100 return dependencies.toArray(new Object[dependencies.size()]);
103 @Nullable
104 public abstract M getModel(@NotNull C context);
106 @NotNull
107 public List<M> getAllModels(@NotNull Module module) {
109 final List<M> models = myAllModelsCache.getCachedValue(module);
110 if (models == null) {
111 return Collections.emptyList();
113 else {
114 return models;
118 @Nullable
119 protected abstract List<M> computeAllModels(@NotNull Module module);
121 @Nullable
122 public M getModelByConfigFile(@Nullable XmlFile psiFile) {
123 if (psiFile == null) {
124 return null;
126 return myModelCache.getCachedValue(psiFile);
129 @Nullable
130 protected M computeModel(@NotNull XmlFile psiFile, @Nullable Module module) {
131 if (module == null) {
132 return null;
134 final List<M> models = getAllModels(module);
135 for (M model: models) {
136 final Set<XmlFile> configFiles = model.getConfigFiles();
137 if (configFiles.contains(psiFile)) {
138 return model;
141 return null;
144 @Nullable
145 public M getCombinedModel(@Nullable Module module) {
146 if (module == null) {
147 return null;
149 return myCombinedModelCache.getCachedValue(module);
152 @Nullable
153 protected M computeCombinedModel(@NotNull Module module) {
154 final List<M> models = getAllModels(module);
155 switch (models.size()) {
156 case 0:
157 return null;
158 case 1:
159 return models.get(0);
161 final Set<XmlFile> configFiles = new LinkedHashSet<XmlFile>();
162 final LinkedHashSet<DomFileElement<T>> list = new LinkedHashSet<DomFileElement<T>>(models.size());
163 for (M model: models) {
164 final Set<XmlFile> files = model.getConfigFiles();
165 for (XmlFile file: files) {
166 ContainerUtil.addIfNotNull(getDomRoot(file), list);
168 configFiles.addAll(files);
170 final DomFileElement<T> mergedModel = getModelMerger().mergeModels(DomFileElement.class, list);
171 final M firstModel = models.get(0);
172 return createCombinedModel(configFiles, mergedModel, firstModel, module);
176 * Factory method to create combined model for given module.
177 * Used by {@link #computeCombinedModel(com.intellij.openapi.module.Module)}.
179 * @param configFiles file set including all files for all models returned by {@link #getAllModels(com.intellij.openapi.module.Module)}.
180 * @param mergedModel merged model for all models returned by {@link #getAllModels(com.intellij.openapi.module.Module)}.
181 * @param firstModel the first model returned by {@link #getAllModels(com.intellij.openapi.module.Module)}.
182 * @param module
183 * @return combined model.
185 protected abstract M createCombinedModel(Set<XmlFile> configFiles, DomFileElement<T> mergedModel, M firstModel, final Module module);
187 @NotNull
188 public Set<XmlFile> getConfigFiles(@Nullable C context) {
189 if (context == null) {
190 return Collections.emptySet();
192 final M model = getModel(context);
193 if (model == null) {
194 return Collections.emptySet();
196 else {
197 return model.getConfigFiles();
201 @NotNull
202 public Set<XmlFile> getAllConfigFiles(@NotNull Module module) {
203 final HashSet<XmlFile> xmlFiles = new HashSet<XmlFile>();
204 for (M model: getAllModels(module)) {
205 xmlFiles.addAll(model.getConfigFiles());
207 return xmlFiles;
210 public List<DomFileElement<T>> getFileElements(M model) {
211 final ArrayList<DomFileElement<T>> list = new ArrayList<DomFileElement<T>>(model.getConfigFiles().size());
212 for (XmlFile configFile: model.getConfigFiles()) {
213 final DomFileElement<T> element = DomManager.getDomManager(configFile.getProject()).getFileElement(configFile, myClass);
214 if (element != null) {
215 list.add(element);
218 return list;
221 public ModelMerger getModelMerger() {
222 return myModelMerger;
225 public Class<T> getClazz() {
226 return myClass;