2 * Copyright 2000-2009 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.
16 package com
.intellij
.util
.xml
;
18 import com
.intellij
.openapi
.Disposable
;
19 import com
.intellij
.openapi
.diagnostic
.Logger
;
20 import com
.intellij
.openapi
.extensions
.ExtensionPointName
;
21 import com
.intellij
.openapi
.module
.Module
;
22 import com
.intellij
.psi
.xml
.*;
23 import com
.intellij
.util
.NotNullFunction
;
24 import com
.intellij
.util
.containers
.ConcurrentHashMap
;
25 import com
.intellij
.util
.containers
.ConcurrentInstanceMap
;
26 import com
.intellij
.util
.xml
.highlighting
.DomElementsAnnotator
;
27 import gnu
.trove
.THashSet
;
28 import org
.jetbrains
.annotations
.NonNls
;
29 import org
.jetbrains
.annotations
.NotNull
;
30 import org
.jetbrains
.annotations
.Nullable
;
32 import java
.lang
.reflect
.Type
;
38 * @see com.intellij.util.xml.MergingFileDescription
40 public class DomFileDescription
<T
> {
41 public static final ExtensionPointName
<DomFileDescription
> EP_NAME
= ExtensionPointName
.create("com.intellij.dom.fileDescription");
43 private static final Logger LOG
= Logger
.getInstance("#com.intellij.util.xml.DomFileDescription");
44 private final ConcurrentInstanceMap
<ScopeProvider
> myScopeProviders
= new ConcurrentInstanceMap
<ScopeProvider
>();
45 protected final Class
<T
> myRootElementClass
;
46 protected final String myRootTagName
;
47 private final String
[] myAllPossibleRootTagNamespaces
;
48 private volatile boolean myInitialized
;
49 private final Map
<Class
<?
extends DomElement
>,Class
<?
extends DomElement
>> myImplementations
= new HashMap
<Class
<?
extends DomElement
>, Class
<?
extends DomElement
>>();
50 private final TypeChooserManager myTypeChooserManager
= new TypeChooserManager();
51 private final List
<DomReferenceInjector
> myInjectors
= new ArrayList
<DomReferenceInjector
>();
52 private final Map
<String
, NotNullFunction
<XmlTag
,List
<String
>>> myNamespacePolicies
= new ConcurrentHashMap
<String
, NotNullFunction
<XmlTag
, List
<String
>>>();
54 public DomFileDescription(final Class
<T
> rootElementClass
, @NonNls final String rootTagName
, @NonNls final String
... allPossibleRootTagNamespaces
) {
55 myRootElementClass
= rootElementClass
;
56 myRootTagName
= rootTagName
;
57 myAllPossibleRootTagNamespaces
= allPossibleRootTagNamespaces
;
60 public String
[] getAllPossibleRootTagNamespaces() {
61 return myAllPossibleRootTagNamespaces
;
65 * Register an implementation class to provide additional functionality for DOM elements.
67 * @param domElementClass interface class.
68 * @param implementationClass abstract implementation class.
70 * @see #initializeFileDescription()
72 public final <T
extends DomElement
> void registerImplementation(Class
<T
> domElementClass
, Class
<?
extends T
> implementationClass
) {
73 myImplementations
.put(domElementClass
, implementationClass
);
77 * @param namespaceKey namespace identifier
78 * @see com.intellij.util.xml.Namespace
79 * @param policy function that takes XML file root tag and returns (maybe empty) list of possible namespace URLs or DTD public ids. This
80 * function shouldn't use DOM since it may be not initialized for the file at the moment
82 protected final void registerNamespacePolicy(String namespaceKey
, NotNullFunction
<XmlTag
,List
<String
>> policy
) {
83 myNamespacePolicies
.put(namespaceKey
, policy
);
87 * @param namespaceKey namespace identifier
88 * @see com.intellij.util.xml.Namespace
89 * @param namespaces XML namespace or DTD public or system id value for the given namespaceKey
91 public final void registerNamespacePolicy(String namespaceKey
, final String
... namespaces
) {
92 registerNamespacePolicy(namespaceKey
, new NotNullFunction
<XmlTag
, List
<String
>>() {
94 public List
<String
> fun(final XmlTag tag
) {
95 return Arrays
.asList(namespaces
);
100 @SuppressWarnings({"MethodMayBeStatic"})
102 public List
<String
> getAllowedNamespaces(@NotNull String namespaceKey
, @NotNull XmlFile file
) {
103 final NotNullFunction
<XmlTag
, List
<String
>> function
= myNamespacePolicies
.get(namespaceKey
);
104 if (function
!= null) {
105 final XmlDocument document
= file
.getDocument();
106 if (document
!= null) {
107 final XmlTag tag
= document
.getRootTag();
109 return function
.fun(tag
);
113 return Collections
.singletonList(namespaceKey
);
115 return Collections
.emptyList();
119 protected final void registerClassChooser(final Type aClass
, final TypeChooser typeChooser
, Disposable parentDisposable
) {
120 registerTypeChooser(aClass
, typeChooser
);
123 protected final void registerTypeChooser(final Type aClass
, final TypeChooser typeChooser
) {
124 myTypeChooserManager
.registerTypeChooser(aClass
, typeChooser
);
127 public final TypeChooserManager
getTypeChooserManager() {
128 return myTypeChooserManager
;
131 protected final void registerReferenceInjector(DomReferenceInjector injector
) {
132 myInjectors
.add(injector
);
135 public List
<DomReferenceInjector
> getReferenceInjectors() {
139 public boolean isAutomaticHighlightingEnabled() {
144 * The right place to call {@link #registerImplementation(Class, Class)},
145 * {@link #registerNamespacePolicy(String, com.intellij.util.NotNullFunction)},
146 * and {@link #registerTypeChooser(java.lang.reflect.Type, TypeChooser)}.
148 protected void initializeFileDescription() {}
151 * Create custom DOM annotator that will be used when error-highlighting DOM. The results will be collected to
152 * {@link com.intellij.util.xml.highlighting.DomElementsProblemsHolder}. The highlighting will be most probably done in an
153 * {@link com.intellij.util.xml.highlighting.BasicDomElementsInspection} instance.
154 * @return Annotator or null
157 public DomElementsAnnotator
createAnnotator() {
161 public final Map
<Class
<?
extends DomElement
>,Class
<?
extends DomElement
>> getImplementations() {
162 if (!myInitialized
) {
163 initializeFileDescription();
164 myInitialized
= true;
166 return myImplementations
;
170 public final Class
<T
> getRootElementClass() {
171 return myRootElementClass
;
174 public final String
getRootTagName() {
175 return myRootTagName
;
178 public boolean isMyFile(@NotNull XmlFile file
, @Nullable final Module module
) {
179 final Namespace namespace
= DomReflectionUtil
.findAnnotationDFS(myRootElementClass
, Namespace
.class);
180 if (namespace
!= null) {
181 final String key
= namespace
.value();
182 final NotNullFunction
<XmlTag
, List
<String
>> function
= myNamespacePolicies
.get(key
);
183 LOG
.assertTrue(function
!= null, "No namespace policy for namespace " + key
+ " in " + this);
184 final XmlDocument document
= file
.getDocument();
185 if (document
!= null) {
186 final XmlTag tag
= document
.getRootTag();
188 final List
<String
> list
= function
.fun(tag
);
189 if (list
.contains(tag
.getNamespace())) return true;
191 final XmlProlog prolog
= document
.getProlog();
192 if (prolog
!= null) {
193 final XmlDoctype doctype
= prolog
.getDoctype();
194 if (doctype
!= null) {
195 final String publicId
= doctype
.getPublicId();
196 if (publicId
!= null && list
.contains(publicId
)) return true;
207 public boolean acceptsOtherRootTagNames() {
212 * Get dependency items (the same, as in {@link com.intellij.psi.util.CachedValue}) for file. On any dependency item change, the
213 * {@link #isMyFile(com.intellij.psi.xml.XmlFile, Module)} method will be invoked once more to ensure that the file description still
215 * @param file XML file to get dependencies of
216 * @return dependency item set
219 public Set
<?
extends Object
> getDependencyItems(XmlFile file
) {
220 return Collections
.emptySet();
224 * @deprecated not used
227 public Set
<Class
<?
extends DomElement
>> getDomModelDependencyItems() {
228 return Collections
.emptySet();
232 * @deprecated not used
235 public Set
<XmlFile
> getDomModelDependentFiles(@NotNull DomFileElement changedRoot
) {
236 return Collections
.emptySet();
239 protected static Set
<Class
<?
extends DomElement
>> convertToSet(Class
<?
extends DomElement
> classes
) {
240 return new THashSet
<Class
<?
extends DomElement
>>(Arrays
.asList(classes
));
244 * @param reference DOM reference
245 * @return element, whose all children will be searched for declaration
248 public DomElement
getResolveScope(GenericDomValue
<?
> reference
) {
249 final DomElement annotation
= getScopeFromAnnotation(reference
);
250 if (annotation
!= null) return annotation
;
252 return DomUtil
.getRoot(reference
);
256 * @param element DOM element
257 * @return element, whose direct children names will be compared by name. Basically it's parameter element's parent (see {@link ParentScopeProvider}).
260 public DomElement
getIdentityScope(DomElement element
) {
261 final DomElement annotation
= getScopeFromAnnotation(element
);
262 if (annotation
!= null) return annotation
;
264 return element
.getParent();
268 protected final DomElement
getScopeFromAnnotation(final DomElement element
) {
269 final Scope scope
= element
.getAnnotation(Scope
.class);
271 return myScopeProviders
.get(scope
.value()).getScope(element
);