spring "flow" shema supported
[fedora-idea.git] / dom / openapi / src / com / intellij / util / xml / DomFileDescription.java
blob844c7f217543b8c5af66e5c53d541da330057a4b
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.
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.ConcurrentInstanceMap;
25 import com.intellij.util.xml.highlighting.DomElementsAnnotator;
26 import gnu.trove.THashMap;
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 final Map<Class<? extends DomElement>,Class<? extends DomElement>> myImplementations = new HashMap<Class<? extends DomElement>, Class<? extends DomElement>>();
49 private final TypeChooserManager myTypeChooserManager = new TypeChooserManager();
50 private final Map<String, NotNullFunction<XmlTag,List<String>>> myNamespacePolicies = new THashMap<String, NotNullFunction<XmlTag, List<String>>>();
52 public DomFileDescription(final Class<T> rootElementClass, @NonNls final String rootTagName, @NonNls final String... allPossibleRootTagNamespaces) {
53 myRootElementClass = rootElementClass;
54 myRootTagName = rootTagName;
55 myAllPossibleRootTagNamespaces = allPossibleRootTagNamespaces;
58 public String[] getAllPossibleRootTagNamespaces() {
59 return myAllPossibleRootTagNamespaces;
62 /**
63 * Register an implementation class to provide additional functionality for DOM elements.
65 * @param domElementClass interface class.
66 * @param implementationClass abstract implementation class.
68 * @see #initializeFileDescription()
70 protected final <T extends DomElement> void registerImplementation(Class<T> domElementClass, Class<? extends T> implementationClass) {
71 myImplementations.put(domElementClass, implementationClass);
74 /**
75 * @param namespaceKey namespace identifier
76 * @see @com.intellij.util.xml.Namespace()
77 * @param policy function that takes XML file root tag and returns (maybe empty) list of possible namespace URLs or DTD public ids. This
78 * function shouldn't use DOM since it may be not initialized for the file at the moment
80 protected final void registerNamespacePolicy(String namespaceKey, NotNullFunction<XmlTag,List<String>> policy) {
81 myNamespacePolicies.put(namespaceKey, policy);
84 /**
85 * @param namespaceKey namespace identifier
86 * @see @com.intellij.util.xml.Namespace()
87 * @param namespaces XML namespace or DTD public or system id value for the given namespaceKey
89 public final void registerNamespacePolicy(String namespaceKey, final String... namespaces) {
90 registerNamespacePolicy(namespaceKey, new NotNullFunction<XmlTag, List<String>>() {
91 @NotNull
92 public List<String> fun(final XmlTag tag) {
93 return Arrays.asList(namespaces);
95 });
98 @SuppressWarnings({"MethodMayBeStatic"})
99 @NotNull
100 public List<String> getAllowedNamespaces(@NotNull String namespaceKey, @NotNull XmlFile file) {
101 final NotNullFunction<XmlTag, List<String>> function = myNamespacePolicies.get(namespaceKey);
102 if (function != null) {
103 final XmlDocument document = file.getDocument();
104 if (document != null) {
105 final XmlTag tag = document.getRootTag();
106 if (tag != null) {
107 return function.fun(tag);
110 } else {
111 return Collections.singletonList(namespaceKey);
113 return Collections.emptyList();
116 @Deprecated
117 protected final void registerClassChooser(final Type aClass, final TypeChooser typeChooser, Disposable parentDisposable) {
118 registerTypeChooser(aClass, typeChooser);
121 protected final void registerTypeChooser(final Type aClass, final TypeChooser typeChooser) {
122 myTypeChooserManager.registerTypeChooser(aClass, typeChooser);
125 public final TypeChooserManager getTypeChooserManager() {
126 return myTypeChooserManager;
129 public boolean isAutomaticHighlightingEnabled() {
130 return true;
134 * The right place to call {@link #registerImplementation(Class, Class)},
135 * {@link #registerNamespacePolicy(String, com.intellij.util.NotNullFunction)},
136 * and {@link #registerTypeChooser(java.lang.reflect.Type, TypeChooser)}.
138 protected void initializeFileDescription() {}
141 * Create custom DOM annotator that will be used when error-highlighting DOM. The results will be collected to
142 * {@link com.intellij.util.xml.highlighting.DomElementsProblemsHolder}. The highlighting will be most probably done in an
143 * {@link com.intellij.util.xml.highlighting.BasicDomElementsInspection} instance.
144 * @return Annotator or null
146 @Nullable
147 public DomElementsAnnotator createAnnotator() {
148 return null;
151 public final Map<Class<? extends DomElement>,Class<? extends DomElement>> getImplementations() {
152 initializeFileDescription();
153 return myImplementations;
156 @NotNull
157 public final Class<T> getRootElementClass() {
158 return myRootElementClass;
161 public final String getRootTagName() {
162 return myRootTagName;
165 public boolean isMyFile(@NotNull XmlFile file, @Nullable final Module module) {
166 final Namespace namespace = DomReflectionUtil.findAnnotationDFS(myRootElementClass, Namespace.class);
167 if (namespace != null) {
168 final String key = namespace.value();
169 final NotNullFunction<XmlTag, List<String>> function = myNamespacePolicies.get(key);
170 LOG.assertTrue(function != null, "No namespace policy for namespace " + key + " in " + this);
171 final XmlDocument document = file.getDocument();
172 if (document != null) {
173 final XmlTag tag = document.getRootTag();
174 if (tag != null) {
175 final List<String> list = function.fun(tag);
176 if (list.contains(tag.getNamespace())) return true;
178 final XmlProlog prolog = document.getProlog();
179 if (prolog != null) {
180 final XmlDoctype doctype = prolog.getDoctype();
181 if (doctype != null) {
182 final String publicId = doctype.getPublicId();
183 if (publicId != null && list.contains(publicId)) return true;
188 return false;
191 return true;
194 public boolean acceptsOtherRootTagNames() {
195 return false;
199 * Get dependency items (the same, as in {@link com.intellij.psi.util.CachedValue}) for file. On any dependency item change, the
200 * {@link #isMyFile(com.intellij.psi.xml.XmlFile, Module)} method will be invoked once more to ensure that the file description still
201 * accepts this file
202 * @param file XML file to get dependencies of
203 * @return dependency item set
205 @NotNull
206 public Set<? extends Object> getDependencyItems(XmlFile file) {
207 return Collections.emptySet();
211 * @deprecated not used
213 @NotNull
214 public Set<Class<? extends DomElement>> getDomModelDependencyItems() {
215 return Collections.emptySet();
219 * @deprecated not used
221 @NotNull
222 public Set<XmlFile> getDomModelDependentFiles(@NotNull DomFileElement changedRoot) {
223 return Collections.emptySet();
226 protected static Set<Class<? extends DomElement>> convertToSet(Class<? extends DomElement> classes) {
227 return new THashSet<Class<? extends DomElement>>(Arrays.asList(classes));
231 * @param reference DOM reference
232 * @return element, whose all children will be searched for declaration
234 @NotNull
235 public DomElement getResolveScope(GenericDomValue<?> reference) {
236 final DomElement annotation = getScopeFromAnnotation(reference);
237 if (annotation != null) return annotation;
239 return reference.getRoot();
243 * @param element DOM element
244 * @return element, whose direct children names will be compared by name. Basically it's parameter element's parent (see {@link ParentScopeProvider}).
246 @NotNull
247 public DomElement getIdentityScope(DomElement element) {
248 final DomElement annotation = getScopeFromAnnotation(element);
249 if (annotation != null) return annotation;
251 return element.getParent();
254 @Nullable
255 protected final DomElement getScopeFromAnnotation(final DomElement element) {
256 final Scope scope = element.getAnnotation(Scope.class);
257 if (scope != null) {
258 return myScopeProviders.get(scope.value()).getScope(element);
260 return null;