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
;
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") {
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") {
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") {
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
));
87 public Object
[] computeDependencies(@Nullable M model
, @Nullable Module module
) {
89 final ArrayList
<Object
> dependencies
= new ArrayList
<Object
>();
90 final Set
<XmlFile
> files
;
92 files
= model
.getConfigFiles();
93 dependencies
.addAll(files
);
95 dependencies
.add(PsiModificationTracker
.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
);
98 dependencies
.add(ProjectRootManager
.getInstance(module
.getProject()));
100 return dependencies
.toArray(new Object
[dependencies
.size()]);
104 public abstract M
getModel(@NotNull C context
);
107 public List
<M
> getAllModels(@NotNull Module module
) {
109 final List
<M
> models
= myAllModelsCache
.getCachedValue(module
);
110 if (models
== null) {
111 return Collections
.emptyList();
119 protected abstract List
<M
> computeAllModels(@NotNull Module module
);
122 public M
getModelByConfigFile(@Nullable XmlFile psiFile
) {
123 if (psiFile
== null) {
126 return myModelCache
.getCachedValue(psiFile
);
130 protected M
computeModel(@NotNull XmlFile psiFile
, @Nullable Module module
) {
131 if (module
== 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
)) {
145 public M
getCombinedModel(@Nullable Module module
) {
146 if (module
== null) {
149 return myCombinedModelCache
.getCachedValue(module
);
153 protected M
computeCombinedModel(@NotNull Module module
) {
154 final List
<M
> models
= getAllModels(module
);
155 switch (models
.size()) {
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)}.
183 * @return combined model.
185 protected abstract M
createCombinedModel(Set
<XmlFile
> configFiles
, DomFileElement
<T
> mergedModel
, M firstModel
, final Module module
);
188 public Set
<XmlFile
> getConfigFiles(@Nullable C context
) {
189 if (context
== null) {
190 return Collections
.emptySet();
192 final M model
= getModel(context
);
194 return Collections
.emptySet();
197 return model
.getConfigFiles();
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());
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) {
221 public ModelMerger
getModelMerger() {
222 return myModelMerger
;
225 public Class
<T
> getClazz() {