blob: 1959e08095a8d97cb2cc804ffb7547f56e804254 [file] [log] [blame]
/*
* Copyright 2007 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.plugins.relaxNG.compact.psi.impl;
import com.intellij.codeInsight.CodeInsightUtilCore;
import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.template.*;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.LocalQuickFixProvider;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiReference;
import com.intellij.psi.ResolveState;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.scope.BaseScopeProcessor;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.intellij.plugins.relaxNG.compact.RncElementTypes;
import org.intellij.plugins.relaxNG.compact.RncFileType;
import org.intellij.plugins.relaxNG.compact.RncTokenTypes;
import org.intellij.plugins.relaxNG.compact.psi.*;
import org.intellij.plugins.relaxNG.compact.psi.util.EscapeUtil;
import org.intellij.plugins.relaxNG.compact.psi.util.RenameUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
* User: sweinreuter
* Date: 14.08.2007
*/
public class RncNameImpl extends RncElementImpl implements RncName, PsiReference,
EmptyResolveMessageProvider, LocalQuickFixProvider {
private enum Kind {
NAMESPACE, DATATYPES
}
public RncNameImpl(ASTNode node) {
super(node);
}
@Override
@Nullable
public String getPrefix() {
final String[] parts = EscapeUtil.unescapeText(getNode()).split(":", 2);
return parts.length == 2 ? parts[0] : null;
}
@Override
@NotNull
public String getLocalPart() {
final String[] parts = EscapeUtil.unescapeText(getNode()).split(":", 2);
return parts.length == 1 ? parts[0] : parts[1];
}
@Override
public void accept(@NotNull RncElementVisitor visitor) {
visitor.visitName(this);
}
@Override
public PsiReference getReference() {
return getPrefix() == null ? null : this;
}
@Override
public PsiElement getElement() {
return this;
}
@Override
public TextRange getRangeInElement() {
return TextRange.from(0, getText().indexOf(':'));
}
@Override
@Nullable
public PsiElement resolve() {
final MyResolver resolver = new MyResolver(getPrefix(), getKind());
getContainingFile().processDeclarations(resolver, ResolveState.initial(), this, this);
return resolver.getResult();
}
private Kind getKind() {
final IElementType parent = getNode().getTreeParent().getElementType();
if (parent == RncElementTypes.DATATYPE_PATTERN) {
return Kind.DATATYPES;
} else {
return Kind.NAMESPACE;
}
}
@Override
@NotNull
public String getCanonicalText() {
return getRangeInElement().substring(getText());
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
final ASTNode node = getNode();
final ASTNode child = RenameUtil.createPrefixedNode(getManager(), newElementName, getLocalPart());
node.getTreeParent().replaceChild(node, child);
return child.getPsi();
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
throw new UnsupportedOperationException();
}
@Override
public boolean isReferenceTo(PsiElement element) {
return element instanceof RncElement && Comparing.equal(resolve(), element);
}
@Override
@NotNull
public Object[] getVariants() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
@Override
public boolean isSoft() {
final String prefix = getPrefix();
return "xsd".equals(prefix) || "xml".equals(prefix);
}
@Override
@NotNull
public String getUnresolvedMessagePattern() {
return "Unresolved namespace prefix ''{0}''";
}
@Nullable
@Override
public LocalQuickFix[] getQuickFixes() {
if (getPrefix() != null) {
return new LocalQuickFix[] { new CreateDeclFix(this) };
}
return LocalQuickFix.EMPTY_ARRAY;
}
private static class MyResolver extends BaseScopeProcessor {
private final String myPrefix;
private final Kind myKind;
private PsiElement myResult;
public MyResolver(String prefix, Kind kind) {
myPrefix = prefix;
myKind = kind;
}
@Override
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState substitutor) {
final ASTNode node = element.getNode();
if (node == null) return true;
if (!(element instanceof RncDecl)) {
return false;
}
final IElementType type = node.getElementType();
if (myKind == Kind.NAMESPACE && type == RncElementTypes.NS_DECL) {
if (checkDecl(element)) return false;
} else if (myKind == Kind.DATATYPES && type == RncElementTypes.DATATYPES_DECL) {
if (checkDecl(element)) return false;
}
return true;
}
private boolean checkDecl(PsiElement element) {
if (myPrefix.equals(((RncDecl)element).getPrefix())) {
myResult = element;
return true;
}
return false;
}
public PsiElement getResult() {
return myResult;
}
}
public static class CreateDeclFix implements LocalQuickFix {
private final RncNameImpl myReference;
public CreateDeclFix(RncNameImpl reference) {
myReference = reference;
}
@Override
@NotNull
public String getName() {
return getFamilyName() + " '" + myReference.getPrefix() + "'";
}
@Override
@NotNull
public String getFamilyName() {
return "Create " + myReference.getKind().name().toLowerCase() + " declaration";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final String prefix = myReference.getPrefix();
final PsiFileFactory factory = PsiFileFactory.getInstance(myReference.getProject());
final RncFile psiFile = (RncFile)factory.createFileFromText("dummy.rnc",
RncFileType.getInstance(),
myReference.getKind().name().toLowerCase() + " " + prefix + " = \"###\"");
final RncFile rncFile = (RncFile)myReference.getContainingFile();
final RncDecl[] declarations = rncFile.getDeclarations();
final RncDecl decl = psiFile.getDeclarations()[0];
final RncDecl e;
if (declarations.length > 0) {
e = (RncDecl)rncFile.addAfter(decl, declarations[declarations.length - 1]);
} else {
final RncGrammar rncGrammar = rncFile.getGrammar();
if (rncGrammar != null) {
e = (RncDecl)rncFile.addBefore(decl, rncGrammar);
} else {
e = (RncDecl)rncFile.add(decl);
}
}
final ASTNode blockNode = e.getParent().getNode();
assert blockNode != null;
final ASTNode newNode = e.getNode();
assert newNode != null;
CodeStyleManager.getInstance(e.getManager().getProject()).reformatNewlyAddedElement(blockNode, newNode);
final PsiElement literal = e.getLastChild();
assert literal != null;
final ASTNode literalNode = literal.getNode();
assert literalNode != null;
assert literalNode.getElementType() == RncTokenTypes.LITERAL;
final int offset = literal.getTextRange().getStartOffset();
literal.delete();
VirtualFile virtualFile = myReference.getElement().getContainingFile().getVirtualFile();
if (virtualFile != null) {
Editor editor = FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, virtualFile, offset), true);
if (editor != null) {
RncDecl rncDecl = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(e);
final TemplateManager manager = TemplateManager.getInstance(project);
final Template t = manager.createTemplate("", "");
t.addTextSegment(" \"");
final Expression expression = new Expression() {
@Override
public Result calculateResult(ExpressionContext context) {
return new TextResult("");
}
@Override
public Result calculateQuickResult(ExpressionContext context) {
return calculateResult(context);
}
@Override
public LookupItem[] calculateLookupItems(ExpressionContext context) {
return LookupItem.EMPTY_ARRAY;
}
};
t.addVariable("uri", expression, expression, true);
t.addTextSegment("\"");
t.addEndVariable();
editor.getCaretModel().moveToOffset(rncDecl.getTextRange().getEndOffset());
manager.startTemplate(editor, t);
}
}
}
}
}