/* * 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.ASTFactory; import com.intellij.lang.ASTNode; import com.intellij.lang.LighterAST; import com.intellij.lang.LighterASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.source.DummyHolder; import com.intellij.psi.impl.source.SourceJavaCodeReference; import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.CharTable; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; public class JavaSourceUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.JavaSourceUtil"); private static final TokenSet REF_FILTER = TokenSet.orSet( ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET, TokenSet.create(JavaElementType.ANNOTATION)); private JavaSourceUtil() { } public static void fullyQualifyReference(@NotNull CompositeElement reference, @NotNull PsiClass targetClass) { if (((SourceJavaCodeReference)reference).isQualified()) { // qualified reference final PsiClass parentClass = targetClass.getContainingClass(); if (parentClass == null) return; final ASTNode qualifier = reference.findChildByRole(ChildRole.QUALIFIER); if (qualifier instanceof SourceJavaCodeReference) { ((SourceJavaCodeReference)qualifier).fullyQualify(parentClass); } } else { // unqualified reference, need to qualify with package name final String qName = targetClass.getQualifiedName(); if (qName == null) { return; // todo: local classes? } final int i = qName.lastIndexOf('.'); if (i > 0) { final String prefix = qName.substring(0, i); final PsiManager manager = reference.getManager(); final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(manager.getProject()).getParserFacade(); final TreeElement qualifier; if (reference instanceof PsiReferenceExpression) { qualifier = (TreeElement)parserFacade.createExpressionFromText(prefix, null).getNode(); } else { qualifier = (TreeElement)parserFacade.createReferenceFromText(prefix, null).getNode(); } if (qualifier != null) { final CharTable systemCharTab = SharedImplUtil.findCharTableByTree(qualifier); final LeafElement dot = Factory.createSingleLeafElement(JavaTokenType.DOT, ".", 0, 1, systemCharTab, manager); qualifier.rawInsertAfterMe(dot); reference.addInternal(qualifier, dot, null, Boolean.FALSE); } } } } @NotNull public static String getReferenceText(@NotNull PsiJavaCodeReferenceElement ref) { final StringBuilder buffer = new StringBuilder(); ((TreeElement)ref.getNode()).acceptTree(new RecursiveTreeElementWalkingVisitor() { @Override public void visitLeaf(LeafElement leaf) { if (!REF_FILTER.contains(leaf.getElementType())) { String leafText = leaf.getText(); if (buffer.length() > 0 && !leafText.isEmpty() && Character.isJavaIdentifierPart(leafText.charAt(0))) { char lastInBuffer = buffer.charAt(buffer.length() - 1); if (lastInBuffer == '?' || Character.isJavaIdentifierPart(lastInBuffer)) { buffer.append(" "); } } buffer.append(leafText); } } @Override public void visitComposite(CompositeElement composite) { if (!REF_FILTER.contains(composite.getElementType())) { super.visitComposite(composite); } } }); return buffer.toString(); } @NotNull public static String getReferenceText(@NotNull LighterAST tree, @NotNull LighterASTNode node) { return LightTreeUtil.toFilteredString(tree, node, REF_FILTER); } public static TreeElement addParenthToReplacedChild(@NotNull IElementType parenthType, @NotNull TreeElement newChild, @NotNull PsiManager manager) { CompositeElement parenthExpr = ASTFactory.composite(parenthType); TreeElement dummyExpr = (TreeElement)newChild.clone(); final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(newChild); new DummyHolder(manager, parenthExpr, null, charTableByTree); parenthExpr.putUserData(CharTable.CHAR_TABLE_KEY, charTableByTree); parenthExpr.rawAddChildren(ASTFactory.leaf(JavaTokenType.LPARENTH, "(")); parenthExpr.rawAddChildren(dummyExpr); parenthExpr.rawAddChildren(ASTFactory.leaf(JavaTokenType.RPARENTH, ")")); try { CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(manager.getProject()); PsiElement formatted = codeStyleManager.reformat(SourceTreeToPsiMap.treeToPsiNotNull(parenthExpr)); parenthExpr = (CompositeElement)SourceTreeToPsiMap.psiToTreeNotNull(formatted); } catch (IncorrectOperationException e) { LOG.error(e); // should not happen } newChild.putUserData(CharTable.CHAR_TABLE_KEY, SharedImplUtil.findCharTableByTree(newChild)); dummyExpr.getTreeParent().replaceChild(dummyExpr, newChild); // TODO remove explicit caches drop since this should be ok if we will use ChangeUtil for the modification TreeUtil.clearCaches(TreeUtil.getFileElement(parenthExpr)); return parenthExpr; } public static void deleteSeparatingComma(@NotNull CompositeElement element, @NotNull ASTNode child) { assert child.getElementType() != JavaTokenType.COMMA : child; ASTNode next = PsiImplUtil.skipWhitespaceAndComments(child.getTreeNext()); if (next != null && next.getElementType() == JavaTokenType.COMMA) { element.deleteChildInternal(next); } else { ASTNode prev = PsiImplUtil.skipWhitespaceAndCommentsBack(child.getTreePrev()); if (prev != null && prev.getElementType() == JavaTokenType.COMMA) { element.deleteChildInternal(prev); } } } public static void addSeparatingComma(@NotNull CompositeElement element, @NotNull ASTNode child, @NotNull TokenSet listTypes) { assert child.getElementType() != JavaTokenType.COMMA : child; scanChildren(element, child, listTypes, true); scanChildren(element, child, listTypes, false); } private static void scanChildren(CompositeElement element, ASTNode node, TokenSet listTypes, boolean forward) { ASTNode child = node; while (true) { child = (forward ? child.getTreeNext() : child.getTreePrev()); if (child == null || child.getElementType() == JavaTokenType.COMMA) break; if (listTypes.contains(child.getElementType())) { CharTable charTable = SharedImplUtil.findCharTableByTree(element); PsiManager manager = element.getPsi().getManager(); TreeElement comma = Factory.createSingleLeafElement(JavaTokenType.COMMA, ",", 0, 1, charTable, manager); element.addInternal(comma, comma, (forward ? node : child), Boolean.FALSE); break; } } } }