| /* |
| * 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); |
| } |
| } |
| } |
| } |
| } |