update copyright
[fedora-idea.git] / xml / dom-openapi / src / com / intellij / util / xml / DomFileDescription.java
blob5a9e82307d05e47fcbac039c1092fb1dcee4fdcd
1 /*
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;
33 import java.util.*;
35 /**
36 * @author peter
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;
64 /**
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);
76 /**
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);
86 /**
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>>() {
93 @NotNull
94 public List<String> fun(final XmlTag tag) {
95 return Arrays.asList(namespaces);
97 });
100 @SuppressWarnings({"MethodMayBeStatic"})
101 @NotNull
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();
108 if (tag != null) {
109 return function.fun(tag);
112 } else {
113 return Collections.singletonList(namespaceKey);
115 return Collections.emptyList();
118 @Deprecated
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() {
136 return myInjectors;
139 public boolean isAutomaticHighlightingEnabled() {
140 return true;
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
156 @Nullable
157 public DomElementsAnnotator createAnnotator() {
158 return null;
161 public final Map<Class<? extends DomElement>,Class<? extends DomElement>> getImplementations() {
162 if (!myInitialized) {
163 initializeFileDescription();
164 myInitialized = true;
166 return myImplementations;
169 @NotNull
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();
187 if (tag != null) {
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;
201 return false;
204 return true;
207 public boolean acceptsOtherRootTagNames() {
208 return false;
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
214 * accepts this file
215 * @param file XML file to get dependencies of
216 * @return dependency item set
218 @NotNull
219 public Set<? extends Object> getDependencyItems(XmlFile file) {
220 return Collections.emptySet();
224 * @deprecated not used
226 @NotNull
227 public Set<Class<? extends DomElement>> getDomModelDependencyItems() {
228 return Collections.emptySet();
232 * @deprecated not used
234 @NotNull
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
247 @NotNull
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}).
259 @NotNull
260 public DomElement getIdentityScope(DomElement element) {
261 final DomElement annotation = getScopeFromAnnotation(element);
262 if (annotation != null) return annotation;
264 return element.getParent();
267 @Nullable
268 protected final DomElement getScopeFromAnnotation(final DomElement element) {
269 final Scope scope = element.getAnnotation(Scope.class);
270 if (scope != null) {
271 return myScopeProviders.get(scope.value()).getScope(element);
273 return null;