deprecation removed
[fedora-idea.git] / plugins / xpath / xpath-lang / src / org / intellij / lang / xpath / xslt / context / XsltContextProvider.java
blob979dee5ddfd9e71c908ec5e3bdd95a35685a59f5
1 /*
2 * Copyright 2005 Sascha Weinreuter
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 org.intellij.lang.xpath.xslt.context;
18 import com.intellij.lang.LanguageRefactoringSupport;
19 import com.intellij.lang.refactoring.RefactoringSupportProvider;
20 import com.intellij.lang.xml.XMLLanguage;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.util.SimpleFieldCache;
24 import com.intellij.psi.*;
25 import com.intellij.psi.util.CachedValue;
26 import com.intellij.psi.util.CachedValueProvider;
27 import com.intellij.psi.util.CachedValuesManager;
28 import com.intellij.psi.util.PsiTreeUtil;
29 import com.intellij.psi.xml.*;
30 import com.intellij.util.IncorrectOperationException;
31 import com.intellij.xml.XmlAttributeDescriptor;
32 import com.intellij.xml.XmlElementDescriptor;
33 import com.intellij.xml.XmlNSDescriptor;
34 import com.intellij.xml.impl.schema.XmlElementDescriptorImpl;
35 import com.intellij.xml.impl.schema.XmlNSDescriptorImpl;
36 import com.intellij.xml.util.XmlUtil;
37 import gnu.trove.THashSet;
38 import org.intellij.lang.xpath.XPathFile;
39 import org.intellij.lang.xpath.context.ContextProvider;
40 import org.intellij.lang.xpath.context.ContextType;
41 import org.intellij.lang.xpath.context.NamespaceContext;
42 import org.intellij.lang.xpath.context.VariableContext;
43 import org.intellij.lang.xpath.context.functions.FunctionContext;
44 import org.intellij.lang.xpath.psi.XPathExpression;
45 import org.intellij.lang.xpath.psi.XPathType;
46 import org.intellij.lang.xpath.validation.inspections.quickfix.XPathQuickFixFactory;
47 import org.intellij.lang.xpath.xslt.XsltSupport;
48 import org.intellij.lang.xpath.xslt.associations.FileAssociationsManager;
49 import org.intellij.lang.xpath.xslt.psi.XsltElement;
50 import org.intellij.lang.xpath.xslt.psi.XsltElementFactory;
51 import org.intellij.lang.xpath.xslt.psi.XsltVariable;
52 import org.intellij.lang.xpath.xslt.psi.XsltWithParam;
53 import org.intellij.lang.xpath.xslt.psi.impl.XsltLanguage;
54 import org.intellij.lang.xpath.xslt.util.NSDeclTracker;
55 import org.intellij.lang.xpath.xslt.util.QNameUtil;
56 import org.jetbrains.annotations.NotNull;
57 import org.jetbrains.annotations.Nullable;
59 import javax.xml.namespace.QName;
60 import java.util.*;
62 public class XsltContextProvider extends ContextProvider {
63 public static final ContextType TYPE = ContextType.lookupOrCreate("XSLT");
65 private static final Set<String> IGNORED_URIS = new THashSet<String>();
66 static {
67 IGNORED_URIS.add(XsltSupport.XSLT_NS);
68 IGNORED_URIS.addAll(XmlUtil.ourSchemaUrisList);
71 private CachedValue<ElementNames> myNames;
73 private static final SimpleFieldCache<CachedValue<ElementNames>, XsltContextProvider> myNamesCache = new SimpleFieldCache<CachedValue<ElementNames>, XsltContextProvider>() {
74 protected CachedValue<ElementNames> compute(final XsltContextProvider xsltContextProvider) {
75 return xsltContextProvider.createCachedValue(xsltContextProvider.getFile());
78 protected CachedValue<ElementNames> getValue(final XsltContextProvider xsltContextProvider) {
79 return xsltContextProvider.myNames;
82 protected void putValue(final CachedValue<ElementNames> elementNamesCachedValue, final XsltContextProvider xsltContextProvider) {
83 xsltContextProvider.myNames = elementNamesCachedValue;
87 private final SmartPsiElementPointer<XmlElement> myContextElement;
88 private final FileAssociationsManager myFileAssociationsManager;
90 protected XsltContextProvider(@NotNull XmlElement contextElement) {
91 final Project project = contextElement.getProject();
92 myFileAssociationsManager = FileAssociationsManager.getInstance(project);
93 myContextElement = SmartPointerManager.getInstance(project).createLazyPointer(contextElement);
94 attachTo(contextElement);
97 @NotNull
98 public ContextType getContextType() {
99 return TYPE;
102 public PsiFile[] getRelatedFiles(final XPathFile file) {
104 final XmlAttribute attribute = PsiTreeUtil.getContextOfType(file, XmlAttribute.class, false);
105 assert attribute != null;
107 final PsiFile psiFile = attribute.getContainingFile();
108 assert psiFile != null;
110 final List<PsiFile> files = new ArrayList<PsiFile>();
112 psiFile.accept(new XmlRecursiveElementVisitor() {
113 @Override
114 public void visitXmlAttribute(XmlAttribute attribute) {
115 final PsiFile[] _files = XsltSupport.getFiles(attribute);
116 for (PsiFile _file : _files) {
117 if (_file != file) files.add(_file);
122 return files.toArray(new PsiFile[files.size()]);
125 @Nullable
126 public XmlElement getContextElement() {
127 return myContextElement.getElement();
130 @NotNull
131 public XPathType getExpectedType(XPathExpression expr) {
132 final XmlTag tag = PsiTreeUtil.getContextOfType(expr, XmlTag.class, true);
133 if (tag != null && XsltSupport.isXsltTag(tag)) {
134 final XsltElement element = XsltElementFactory.getInstance().wrapElement(tag, XsltElement.class);
135 if (element instanceof XsltVariable) {
136 return ((XsltVariable)element).getType();
137 } else {
138 final XmlAttribute attr = PsiTreeUtil.getContextOfType(expr, XmlAttribute.class, true);
139 if (attr != null) {
140 if (element instanceof XsltWithParam) {
141 final XmlAttribute nameAttr = tag.getAttribute("name", null);
142 if (nameAttr != null) {
143 final XmlAttributeValue valueElement = nameAttr.getValueElement();
144 if (valueElement != null) {
145 final PsiReference[] references = valueElement.getReferences();
146 for (PsiReference reference : references) {
147 final PsiElement psiElement = reference.resolve();
148 if (psiElement instanceof XsltVariable) {
149 return ((XsltVariable)psiElement).getType();
154 } else {
155 final String name = attr.getName();
156 final String tagName = tag.getLocalName();
157 if ("select".equals(name)) {
158 if ("copy-of".equals(tagName) || "for-each".equals(tagName) || "apply-templates".equals(tagName)) {
159 return XPathType.NODESET;
160 } else if ("value-of".equals(tagName) || "sort".equals(tagName)) {
161 return XPathType.STRING;
162 } else {
163 return XPathType.ANY;
165 } else if ("test".equals(name)) {
166 if ("if".equals(tagName) || "when".equals(tagName)) {
167 return XPathType.BOOLEAN;
169 } else if ("number".equals(name)) {
170 if ("value".equals(tagName)) {
171 return XPathType.NUMBER;
178 return XPathType.UNKNOWN;
181 @NotNull
182 public NamespaceContext getNamespaceContext() {
183 return XsltNamespaceContext.NAMESPACE_CONTEXT;
186 @NotNull
187 public VariableContext getVariableContext() {
188 return XsltVariableContext.INSTANCE;
191 @NotNull
192 public FunctionContext getFunctionContext() {
193 return XsltFunctionContext.INSTANCE;
196 static class ElementNames {
197 boolean validateNames;
199 Set<QName> elementNames = new HashSet<QName>();
200 Set<QName> attributeNames = new HashSet<QName>();
202 @SuppressWarnings({ "RawUseOfParameterizedType" })
203 Set dependencies = new HashSet();
206 @Nullable
207 public Set<QName> getAttributes(boolean forValidation) {
208 final ElementNames names = getNames(getFile());
209 if (names != null) {
210 return !forValidation || names.validateNames ? names.attributeNames : null;
212 return null;
215 @Nullable
216 public Set<QName> getElements(boolean forValidation) {
217 final ElementNames names = getNames(getFile());
218 if (names != null) {
219 return !forValidation || names.validateNames ? names.elementNames : null;
221 return null;
224 @Nullable
225 private ElementNames getNames(@Nullable PsiFile file) {
226 if (file == null) return null;
228 return myNamesCache.get(this).getValue();
231 private CachedValue<ElementNames> createCachedValue(final PsiFile file) {
232 return CachedValuesManager.getManager(file.getProject()).createCachedValue(new CachedValueProvider<ElementNames>() {
233 public Result<ElementNames> compute() {
234 final ElementNames names = new ElementNames();
235 final PsiFile[] associations = myFileAssociationsManager.getAssociationsFor(file, FileAssociationsManager.XML_FILES);
237 if (associations.length == 0) {
238 fillFromSchema(file, names);
239 } else {
240 names.validateNames = true;
241 //noinspection unchecked
242 names.dependencies.addAll(Arrays.asList(associations));
244 //noinspection unchecked
245 names.dependencies.add(myFileAssociationsManager);
247 for (PsiFile file : associations) {
248 if (!(file instanceof XmlFile)) continue;
249 file.accept(new XmlRecursiveElementVisitor() {
250 @Override
251 public void visitXmlTag(XmlTag tag) {
252 names.elementNames.add(QNameUtil.createQName(tag));
253 super.visitXmlTag(tag);
256 @Override
257 public void visitXmlAttribute(XmlAttribute attribute) {
258 if (!attribute.isNamespaceDeclaration()) {
259 names.attributeNames.add(QNameUtil.createQName(attribute));
261 super.visitXmlAttribute(attribute);
266 //noinspection unchecked
267 return new Result<ElementNames>(names, names.dependencies.toArray(new Object[names.dependencies.size()]));
269 }, false);
272 private static void fillFromSchema(PsiFile file, ElementNames names) {
273 if (!(file instanceof XmlFile)) return;
274 final XmlFile f = (XmlFile)file;
275 final XmlDocument d = f.getDocument();
276 if (d == null) return;
277 final XmlTag rootTag = d.getRootTag();
278 if (rootTag == null) return;
280 //noinspection unchecked
281 names.dependencies.add(new NSDeclTracker(rootTag));
283 try {
284 final Map<String, String> namespaceDeclarations = rootTag.getLocalNamespaceDeclarations();
285 final Collection<String> prefixes = namespaceDeclarations.keySet();
287 //noinspection unchecked
288 final Set<XmlElementDescriptor> history = new THashSet<XmlElementDescriptor>();
290 final XmlElementFactory ef = XmlElementFactory.getInstance(file.getProject());
291 int noSchemaNamespaces = 0;
292 for (String prefix : prefixes) {
293 final String namespace = namespaceDeclarations.get(prefix);
294 if (isIgnoredNamespace(prefix, namespace)) continue;
296 final XmlTag tag = ef.createTagFromText("<dummy-tag xmlns='" + namespace + "' />", XMLLanguage.INSTANCE);
297 final XmlDocument document = PsiTreeUtil.getParentOfType(tag, XmlDocument.class);
298 final XmlNSDescriptor rootDescriptor = tag.getNSDescriptor(tag.getNamespace(), true);
299 if (rootDescriptor == null ||
300 (rootDescriptor instanceof XmlNSDescriptorImpl && ((XmlNSDescriptorImpl)rootDescriptor).getTag() == null) ||
301 !rootDescriptor.getDeclaration().isPhysical())
303 final QName any = QNameUtil.createAnyLocalName(namespace);
304 names.elementNames.add(any);
305 names.attributeNames.add(any);
306 noSchemaNamespaces++;
307 continue;
310 //noinspection unchecked
311 names.dependencies.add(rootDescriptor.getDescriptorFile());
313 final XmlElementDescriptor[] e = rootDescriptor.getRootElementsDescriptors(document);
314 for (XmlElementDescriptor descriptor : e) {
315 processElementDescriptors(descriptor, tag, names, history);
319 names.validateNames = names.elementNames.size() > noSchemaNamespaces;
321 // final QName any = QNameUtil.createAnyLocalName("");
322 // names.elementNames.add(any);
323 // names.attributeNames.add(any);
324 } catch (IncorrectOperationException e) {
325 Logger.getInstance(XsltContextProvider.class.getName()).error(e);
329 private static boolean isIgnoredNamespace(String prefix, String namespace) {
330 return IGNORED_URIS.contains(namespace) || prefix.length() == 0 || "xmlns".equals(prefix);
333 private static void processElementDescriptors(XmlElementDescriptor descriptor, XmlTag tag, ElementNames names, Set<XmlElementDescriptor> history) {
334 if (!history.add(descriptor)) {
335 return;
337 final String namespace = descriptor instanceof XmlElementDescriptorImpl
338 ? ((XmlElementDescriptorImpl)descriptor).getNamespace()
339 : tag.getNamespace();
340 names.elementNames.add(new QName(namespace, descriptor.getName()));
342 final XmlAttributeDescriptor[] attributesDescriptors = descriptor.getAttributesDescriptors(null);
343 for (XmlAttributeDescriptor attributesDescriptor : attributesDescriptors) {
344 final String localPart = attributesDescriptor.getName();
345 if (!"xmlns".equals(localPart)) names.attributeNames.add(new QName(localPart));
348 final XmlElementDescriptor[] descriptors = descriptor.getElementsDescriptors(tag);
349 for (XmlElementDescriptor elem : descriptors) {
350 processElementDescriptors(elem, tag, names, history);
354 @Nullable
355 private PsiFile getFile() {
356 final XmlElement element = getContextElement();
357 if (element == null) {
358 return null;
360 return element.getContainingFile().getOriginalFile();
364 @NotNull
365 public XPathQuickFixFactory getQuickFixFactory() {
366 return XsltQuickFixFactory.INSTANCE;
369 @Override
370 @Nullable
371 public RefactoringSupportProvider getRefactoringSupportProvider() {
372 return LanguageRefactoringSupport.INSTANCE.forLanguage(XsltLanguage.INSTANCE);