blob: 6dbdcee283081a4b96b7c0bbefaadcd13d190d00 [file] [log] [blame]
/*
* Copyright 2005 Sascha Weinreuter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.intellij.lang.xpath.xslt.context;
import com.intellij.codeInsight.daemon.impl.analysis.CreateNSDeclarationIntentionFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.XmlElementFactory;
import com.intellij.psi.impl.source.xml.SchemaPrefix;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.IncorrectOperationException;
import org.intellij.lang.xpath.context.NamespaceContext;
import org.intellij.lang.xpath.xslt.XsltSupport;
import org.intellij.lang.xpath.xslt.impl.XsltChecker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class XsltNamespaceContext implements NamespaceContext {
public static final XsltNamespaceContext NAMESPACE_CONTEXT = new XsltNamespaceContext();
@Override
public String getNamespaceURI(String prefix, XmlElement context) {
return getNamespaceUriStatic(prefix, context);
}
@Nullable
public static String getNamespaceUriStatic(String prefix, XmlElement context) {
final XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class);
return tag != null ? tag.getNamespaceByPrefix(prefix) : null;
}
@Override
@Nullable
public String getPrefixForURI(String uri, XmlElement context) {
final XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class);
return tag != null ? tag.getPrefixByNamespace(uri) : null;
}
@Override
@NotNull
public Collection<String> getKnownPrefixes(XmlElement context) {
return getPrefixes(context);
}
public static Collection<String> getPrefixes(XmlElement context) {
final XmlTag tag = PsiTreeUtil.getParentOfType(context, XmlTag.class);
if (tag != null) {
final HashSet<String> allPrefixes = new HashSet<String>();
final String[] uris = tag.knownNamespaces();
for (String uri : uris) {
for (XmlTag p = tag; p != null; p = p.getParentTag()) {
final Set<Map.Entry<String,String>> localPrefixes = p.getLocalNamespaceDeclarations().entrySet();
for (Map.Entry<String,String> entry : localPrefixes) {
final String prefix = entry.getKey();
if (!prefix.isEmpty() && entry.getValue().equals(uri)) {
if (!allPrefixes.contains(prefix)) {
allPrefixes.add(prefix);
}
}
}
}
}
return allPrefixes;
} else {
return Collections.emptySet();
}
}
@Override
@Nullable
public PsiElement resolve(String prefix, XmlElement context) {
return resolvePrefix(prefix, context);
}
@Nullable
public static PsiElement resolvePrefix(final String prefix, XmlElement context) {
final String name = "xmlns:" + prefix;
XmlTag parent = PsiTreeUtil.getParentOfType(context, XmlTag.class);
while (parent != null) {
final XmlAttribute attribute = parent.getAttribute(name, null);
if (attribute != null) {
final TextRange textRange = TextRange.from("xmlns:".length(), prefix.length());
return new SchemaPrefix(attribute, textRange, prefix) {
@Override
public boolean equals(Object obj) {
if (obj instanceof SchemaPrefix) {
final SchemaPrefix p = (SchemaPrefix)obj;
return prefix.equals(p.getName()) && p.getParent() == attribute;
}
return super.equals(obj);
}
};
}
parent = PsiTreeUtil.getParentOfType(parent, XmlTag.class);
}
return null;
}
@Override
public IntentionAction[] getUnresolvedNamespaceFixes(PsiReference reference, String localName) {
return getUnresolvedNamespaceFixesStatic(reference, localName);
}
@Override
public String getDefaultNamespace(XmlElement context) {
if (context != null) {
if (XsltSupport.getXsltLanguageLevel(context.getContainingFile()) == XsltChecker.LanguageLevel.V2) {
context = PsiTreeUtil.getParentOfType(context, XmlTag.class, false);
if (context != null) {
do {
final XmlTag tag = (XmlTag)context;
String uri;
if ((uri = tag.getAttributeValue("xpath-default-namespace", null)) != null ||
(uri = tag.getAttributeValue("xpath-default-namespace", XsltSupport.XSLT_NS)) != null) {
return !uri.isEmpty() ? uri : null;
}
context = PsiTreeUtil.getParentOfType(context, XmlTag.class, true);
}
while (context != null);
}
}
}
return null;
}
public static IntentionAction[] getUnresolvedNamespaceFixesStatic(PsiReference reference, String localName) {
final XmlElementFactory factory = XmlElementFactory.getInstance(reference.getElement().getProject());
final XmlTag tag = factory.createTagFromText("<" + reference.getCanonicalText() + ":" + localName + " />", XMLLanguage.INSTANCE);
final XmlFile xmlFile = PsiTreeUtil.getContextOfType(reference.getElement(), XmlFile.class, true);
return new IntentionAction[]{
new MyCreateNSDeclarationAction(tag, reference.getCanonicalText(), xmlFile)
};
}
static class MyCreateNSDeclarationAction extends CreateNSDeclarationIntentionFix {
private final XmlFile myXmlFile;
// TODO: verify API
public MyCreateNSDeclarationAction(XmlElement xmlElement, String prefix, XmlFile xmlFile) {
super(xmlElement, prefix);
myXmlFile = xmlFile;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile psiFile) throws IncorrectOperationException {
super.invoke(project, editor, myXmlFile);
}
@Override
public boolean showHint(@NotNull Editor editor) {
return false; // doesn't work properly yet
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile psiFile) {
return super.isAvailable(project, editor, myXmlFile);
}
}
}