| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * 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 com.intellij.psi.impl.source.tree; |
| |
| import com.intellij.lang.ASTNode; |
| import com.intellij.lang.Language; |
| import com.intellij.lang.StdLanguages; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Key; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl; |
| import com.intellij.psi.impl.source.SourceTreeToPsiMap; |
| import com.intellij.psi.templateLanguages.OuterLanguageElement; |
| import com.intellij.util.IncorrectOperationException; |
| |
| import java.util.Map; |
| |
| public class JavaTreeCopyHandler implements TreeCopyHandler { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.JavaTreeCopyHandler"); |
| |
| @Override |
| public TreeElement decodeInformation(TreeElement element, final Map<Object, Object> decodingState) { |
| boolean shallDecodeEscapedTexts = shallEncodeEscapedTexts(element, decodingState); |
| if (element instanceof CompositeElement) { |
| if (element.getElementType() == JavaElementType.JAVA_CODE_REFERENCE || |
| element.getElementType() == JavaElementType.REFERENCE_EXPRESSION) { |
| PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)SourceTreeToPsiMap.treeElementToPsi(element); |
| final PsiClass refClass = element.getCopyableUserData(JavaTreeGenerator.REFERENCED_CLASS_KEY); |
| if (refClass != null) { |
| element.putCopyableUserData(JavaTreeGenerator.REFERENCED_CLASS_KEY, null); |
| |
| PsiManager manager = refClass.getManager(); |
| JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(refClass.getProject()); |
| PsiElement refElement1 = ref.resolve(); |
| try { |
| if (refClass != refElement1 && !manager.areElementsEquivalent(refClass, refElement1)) { |
| if (((CompositeElement)element).findChildByRole(ChildRole.QUALIFIER) == null) { |
| // can restore only if short (otherwise qualifier should be already restored) |
| ref = (PsiJavaCodeReferenceElement)ref.bindToElement(refClass); |
| } |
| } |
| else { |
| // shorten references to the same package and to inner classes that can be accessed by short name |
| ref = (PsiJavaCodeReferenceElement)codeStyleManager.shortenClassReferences(ref, JavaCodeStyleManager.DO_NOT_ADD_IMPORTS); |
| } |
| return (TreeElement)SourceTreeToPsiMap.psiElementToTree(ref); |
| } |
| catch (IncorrectOperationException e) { |
| ((PsiImportHolder) ref.getContainingFile()).importClass(refClass); |
| } |
| } |
| else { |
| final PsiMember refMember = element.getCopyableUserData(JavaTreeGenerator.REFERENCED_MEMBER_KEY); |
| if (refMember != null) { |
| LOG.assertTrue(ref instanceof PsiReferenceExpression); |
| element.putCopyableUserData(JavaTreeGenerator.REFERENCED_MEMBER_KEY, null); |
| PsiElement refElement1 = ref.resolve(); |
| if (refMember != refElement1 && !refMember.getManager().areElementsEquivalent(refMember, refElement1)) { |
| try { |
| ref = (PsiJavaCodeReferenceElement) ((PsiReferenceExpression)ref).bindToElementViaStaticImport(refMember.getContainingClass()); |
| } |
| catch (IncorrectOperationException e) { |
| // TODO[yole] ignore? |
| } |
| return (TreeElement)SourceTreeToPsiMap.psiElementToTree(ref); |
| } |
| } |
| } |
| } |
| else if (element.getElementType() == JavaElementType.MODIFIER_LIST) { |
| if (element.getUserData(INTERFACE_MODIFIERS_FLAG_KEY) != null) { |
| element.putUserData(INTERFACE_MODIFIERS_FLAG_KEY, null); |
| try { |
| PsiModifierList modifierList = (PsiModifierList)SourceTreeToPsiMap.treeElementToPsi(element); |
| if (element.getTreeParent().getElementType() == JavaElementType.FIELD) { |
| modifierList.setModifierProperty(PsiModifier.PUBLIC, true); |
| modifierList.setModifierProperty(PsiModifier.STATIC, true); |
| modifierList.setModifierProperty(PsiModifier.FINAL, true); |
| } |
| else if (element.getTreeParent().getElementType() == JavaElementType.METHOD || |
| element.getTreeParent().getElementType() == JavaElementType.ANNOTATION_METHOD) { |
| modifierList.setModifierProperty(PsiModifier.PUBLIC, true); |
| modifierList.setModifierProperty(PsiModifier.ABSTRACT, true); |
| } |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| } |
| } |
| else if (shallDecodeEscapedTexts && element instanceof LeafElement && !(element instanceof OuterLanguageElement)) { |
| if (!isInCData(element)) { |
| final String original = element.getText(); |
| final String escaped = StringUtil.escapeXml(original); |
| if (!Comparing.equal(original, escaped) && element.getCopyableUserData(ALREADY_ESCAPED) == null) { |
| LeafElement copy = ((LeafElement)element).replaceWithText(escaped); |
| copy.putCopyableUserData(ALREADY_ESCAPED, Boolean.TRUE); |
| return copy; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| private static final Key<Boolean> ALREADY_ESCAPED = new Key<Boolean>("ALREADY_ESCAPED"); |
| private static final Key<Boolean> ESCAPEMENT_ENGAGED = new Key<Boolean>("ESCAPEMENT_ENGAGED"); |
| private static boolean conversionMayApply(ASTNode element) { |
| PsiElement psi = element.getPsi(); |
| if (psi == null || !psi.isValid()) return false; |
| |
| final PsiFile file = psi.getContainingFile(); |
| final Language baseLanguage = file.getViewProvider().getBaseLanguage(); |
| return baseLanguage == StdLanguages.JSPX && file.getLanguage() != baseLanguage; |
| } |
| |
| |
| @Override |
| public void encodeInformation(final TreeElement element, final ASTNode original, final Map<Object, Object> encodingState) { |
| boolean shallEncodeEscapedTexts = shallEncodeEscapedTexts(original, encodingState); |
| |
| if (original instanceof CompositeElement) { |
| if (original.getElementType() == JavaElementType.JAVA_CODE_REFERENCE || original.getElementType() == JavaElementType.REFERENCE_EXPRESSION) { |
| encodeInformationInRef(element, original); |
| } |
| else if (original.getElementType() == JavaElementType.MODIFIER_LIST |
| && (original.getTreeParent().getElementType() == JavaElementType.FIELD || original.getTreeParent().getElementType() == JavaElementType.METHOD || original.getTreeParent().getElementType() == JavaElementType.ANNOTATION_METHOD) |
| && original.getTreeParent().getTreeParent().getElementType() == JavaElementType.CLASS |
| && (((PsiClass)SourceTreeToPsiMap.treeElementToPsi(original.getTreeParent().getTreeParent())).isInterface() |
| || ((PsiClass)SourceTreeToPsiMap.treeElementToPsi(original.getTreeParent().getTreeParent())).isAnnotationType())) { |
| element.putUserData(INTERFACE_MODIFIERS_FLAG_KEY, Boolean.TRUE); |
| } |
| } |
| else if (shallEncodeEscapedTexts && original instanceof LeafElement && !(original instanceof OuterLanguageElement)) { |
| if (!isInCData(original)) { |
| final String originalText = element.getText(); |
| final String unescapedText = StringUtil.unescapeXml(originalText); |
| if (!Comparing.equal(originalText, unescapedText)) { |
| LeafElement replaced = ((LeafElement)element).rawReplaceWithText(unescapedText); |
| element.putCopyableUserData(ALREADY_ESCAPED, null); |
| replaced.putCopyableUserData(ALREADY_ESCAPED, null); |
| } |
| } |
| } |
| } |
| |
| private static Boolean shallEncodeEscapedTexts(final ASTNode original, final Map<Object, Object> encodingState) { |
| Boolean shallEncodeEscapedTexts = (Boolean)encodingState.get(ESCAPEMENT_ENGAGED); |
| if (shallEncodeEscapedTexts == null) { |
| shallEncodeEscapedTexts = conversionMayApply(original); |
| encodingState.put(ESCAPEMENT_ENGAGED, shallEncodeEscapedTexts); |
| } |
| return shallEncodeEscapedTexts; |
| } |
| |
| private static boolean isInCData(ASTNode element) { |
| ASTNode leaf = element; |
| while (leaf != null) { |
| if (leaf instanceof OuterLanguageElement) { |
| return leaf.getText().indexOf("<![CDATA[") >= 0; |
| } |
| |
| leaf = TreeUtil.prevLeaf(leaf); |
| } |
| |
| return false; |
| } |
| |
| private static void encodeInformationInRef(TreeElement ref, ASTNode original) { |
| if (original.getElementType() == JavaElementType.REFERENCE_EXPRESSION) { |
| final PsiJavaCodeReferenceElement javaRefElement = (PsiJavaCodeReferenceElement)SourceTreeToPsiMap.treeElementToPsi(original); |
| assert javaRefElement != null; |
| final JavaResolveResult resolveResult = javaRefElement.advancedResolve(false); |
| final PsiElement target = resolveResult.getElement(); |
| if (target instanceof PsiClass && |
| original.getTreeParent().getElementType() == JavaElementType.REFERENCE_EXPRESSION) { |
| ref.putCopyableUserData(JavaTreeGenerator.REFERENCED_CLASS_KEY, (PsiClass)target); |
| } |
| else if ((target instanceof PsiMethod || target instanceof PsiField) && |
| ((PsiMember) target).hasModifierProperty(PsiModifier.STATIC) && |
| resolveResult.getCurrentFileResolveScope() instanceof PsiImportStaticStatement) { |
| ref.putCopyableUserData(JavaTreeGenerator.REFERENCED_MEMBER_KEY, (PsiMember) target); |
| } |
| } |
| else if (original.getElementType() == JavaElementType.JAVA_CODE_REFERENCE) { |
| switch (((PsiJavaCodeReferenceElementImpl)original).getKind(((PsiJavaCodeReferenceElementImpl)original).getContainingFile())) { |
| case PsiJavaCodeReferenceElementImpl.CLASS_NAME_KIND: |
| case PsiJavaCodeReferenceElementImpl.CLASS_OR_PACKAGE_NAME_KIND: |
| case PsiJavaCodeReferenceElementImpl.CLASS_IN_QUALIFIED_NEW_KIND: |
| final PsiElement target = SourceTreeToPsiMap.<PsiJavaCodeReferenceElement>treeToPsiNotNull(original).resolve(); |
| if (target instanceof PsiClass) { |
| ref.putCopyableUserData(JavaTreeGenerator.REFERENCED_CLASS_KEY, (PsiClass)target); |
| } |
| break; |
| |
| case PsiJavaCodeReferenceElementImpl.PACKAGE_NAME_KIND: |
| case PsiJavaCodeReferenceElementImpl.CLASS_FQ_NAME_KIND: |
| case PsiJavaCodeReferenceElementImpl.CLASS_FQ_OR_PACKAGE_NAME_KIND: |
| break; |
| |
| default: |
| LOG.assertTrue(false); |
| } |
| } |
| else { |
| LOG.error("Wrong element type: " + original.getElementType()); |
| } |
| } |
| |
| private static final Key<Boolean> INTERFACE_MODIFIERS_FLAG_KEY = Key.create("INTERFACE_MODIFIERS_FLAG_KEY"); |
| } |