| /* |
| * 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; |
| |
| import com.intellij.ide.highlighter.JavaFileType; |
| import com.intellij.lang.PsiBuilder; |
| import com.intellij.lang.java.parser.DeclarationParser; |
| import com.intellij.lang.java.parser.JavaParser; |
| import com.intellij.lang.java.parser.JavaParserUtil; |
| import com.intellij.lang.java.parser.ReferenceParser; |
| import com.intellij.openapi.fileTypes.FileType; |
| import com.intellij.openapi.roots.LanguageLevelProjectExtension; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.DummyHolder; |
| import com.intellij.psi.impl.source.DummyHolderFactory; |
| import com.intellij.psi.impl.source.JavaDummyElement; |
| import com.intellij.psi.impl.source.SourceTreeToPsiMap; |
| import com.intellij.psi.impl.source.tree.FileElement; |
| import com.intellij.psi.impl.source.tree.TreeElement; |
| import com.intellij.psi.javadoc.PsiDocComment; |
| import com.intellij.psi.javadoc.PsiDocTag; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.HashMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.Map; |
| |
| /** |
| * @author max |
| */ |
| public class PsiJavaParserFacadeImpl implements PsiJavaParserFacade { |
| protected final PsiManager myManager; |
| |
| private static final String DUMMY_FILE_NAME = "_Dummy_." + JavaFileType.INSTANCE.getDefaultExtension(); |
| |
| public PsiJavaParserFacadeImpl(PsiManager manager) { |
| myManager = manager; |
| } |
| |
| private static final JavaParserUtil.ParserWrapper ANNOTATION = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getDeclarationParser().parseAnnotation(builder); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper PARAMETER = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getDeclarationParser().parseParameter(builder, true, false); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper RESOURCE = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getDeclarationParser().parseResource(builder); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper TYPE = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getReferenceParser().parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.ELLIPSIS | |
| ReferenceParser.WILDCARD | ReferenceParser.DISJUNCTIONS); |
| } |
| }; |
| |
| public static final JavaParserUtil.ParserWrapper REFERENCE = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getReferenceParser().parseJavaCodeReference(builder, false, true, false, false); |
| } |
| }; |
| |
| public static final JavaParserUtil.ParserWrapper DIAMOND_REF = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getReferenceParser().parseJavaCodeReference(builder, false, true, false, true); |
| } |
| }; |
| |
| public static final JavaParserUtil.ParserWrapper STATIC_IMPORT_REF = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getReferenceParser().parseImportCodeReference(builder, true); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper TYPE_PARAMETER = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getReferenceParser().parseTypeParameter(builder); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper DECLARATION = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getDeclarationParser().parse(builder, DeclarationParser.Context.CLASS); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper CODE_BLOCK = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getStatementParser().parseCodeBlockDeep(builder, true); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper STATEMENT = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getStatementParser().parseStatement(builder); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper EXPRESSION = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getExpressionParser().parse(builder); |
| } |
| }; |
| |
| private static final JavaParserUtil.ParserWrapper ENUM_CONSTANT = new JavaParserUtil.ParserWrapper() { |
| @Override |
| public void parse(final PsiBuilder builder) { |
| JavaParser.INSTANCE.getDeclarationParser().parseEnumConstant(builder); |
| } |
| }; |
| |
| private static final Map<String, PsiPrimitiveType> PRIMITIVE_TYPES; |
| static { |
| PRIMITIVE_TYPES = new HashMap<String, PsiPrimitiveType>(); |
| PRIMITIVE_TYPES.put(PsiType.BYTE.getCanonicalText(), PsiType.BYTE); |
| PRIMITIVE_TYPES.put(PsiType.CHAR.getCanonicalText(), PsiType.CHAR); |
| PRIMITIVE_TYPES.put(PsiType.DOUBLE.getCanonicalText(), PsiType.DOUBLE); |
| PRIMITIVE_TYPES.put(PsiType.FLOAT.getCanonicalText(), PsiType.FLOAT); |
| PRIMITIVE_TYPES.put(PsiType.INT.getCanonicalText(), PsiType.INT); |
| PRIMITIVE_TYPES.put(PsiType.LONG.getCanonicalText(), PsiType.LONG); |
| PRIMITIVE_TYPES.put(PsiType.SHORT.getCanonicalText(), PsiType.SHORT); |
| PRIMITIVE_TYPES.put(PsiType.BOOLEAN.getCanonicalText(), PsiType.BOOLEAN); |
| PRIMITIVE_TYPES.put(PsiType.VOID.getCanonicalText(), PsiType.VOID); |
| PRIMITIVE_TYPES.put(PsiType.NULL.getCanonicalText(), PsiType.NULL); |
| } |
| |
| @NotNull |
| @Override |
| public PsiAnnotation createAnnotationFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, ANNOTATION, level(context)), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiAnnotation)) { |
| throw new IncorrectOperationException("Incorrect annotation '" + text + "'"); |
| } |
| return (PsiAnnotation)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiDocTag createDocTagFromText(@NotNull final String text) throws IncorrectOperationException { |
| return createDocCommentFromText(StringUtil.join("/**\n", text, "\n */")).getTags()[0]; |
| } |
| |
| @NotNull |
| @Override |
| public PsiDocComment createDocCommentFromText(@NotNull final String text) throws IncorrectOperationException { |
| final PsiMethod method = createMethodFromText(StringUtil.join(text, "void m();"), null); |
| final PsiDocComment comment = method.getDocComment(); |
| assert comment != null : text; |
| return comment; |
| } |
| |
| @NotNull |
| @Override |
| public PsiClass createClassFromText(@NotNull final String body, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final PsiJavaFile aFile = createDummyJavaFile(StringUtil.join("class _Dummy_ {\n", body, "\n}")); |
| final PsiClass[] classes = aFile.getClasses(); |
| if (classes.length != 1) { |
| throw new IncorrectOperationException("Incorrect class '" + body + "'"); |
| } |
| return classes[0]; |
| } |
| |
| @NotNull |
| @Override |
| public PsiField createFieldFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, DECLARATION, level(context)), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiField)) { |
| throw new IncorrectOperationException("Incorrect field '" + text + "'"); |
| } |
| return (PsiField)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiMethod createMethodFromText(@NotNull final String text, @Nullable final PsiElement context, final LanguageLevel level) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, DECLARATION, level), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiMethod)) { |
| throw newException("Incorrect method '" + text + "'", holder); |
| } |
| return (PsiMethod)element; |
| } |
| |
| @NotNull |
| @Override |
| public final PsiMethod createMethodFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final LanguageLevel level = LanguageLevelProjectExtension.getInstance(myManager.getProject()).getLanguageLevel(); |
| return createMethodFromText(text, context, level); |
| } |
| |
| @NotNull |
| @Override |
| public PsiParameter createParameterFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, PARAMETER, level(context)), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiParameter)) { |
| throw new IncorrectOperationException("Incorrect parameter '" + text + "'"); |
| } |
| return (PsiParameter)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiResourceVariable createResourceFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, RESOURCE, level(context)), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiResourceVariable)) { |
| throw new IncorrectOperationException("Incorrect resource '" + text + "'"); |
| } |
| return (PsiResourceVariable)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiType createTypeFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| return createTypeInner(text, context, false); |
| } |
| |
| @NotNull |
| @Override |
| public PsiTypeElement createTypeElementFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final LanguageLevel level = level(context); |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, TYPE, level), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiTypeElement)) { |
| throw new IncorrectOperationException("Incorrect type '" + text + "' (" + level + ")"); |
| } |
| return (PsiTypeElement)element; |
| } |
| |
| protected PsiType createTypeInner(final String text, @Nullable final PsiElement context, final boolean markAsCopy) throws IncorrectOperationException { |
| final PsiPrimitiveType primitiveType = PRIMITIVE_TYPES.get(text); |
| if (primitiveType != null) return primitiveType; |
| |
| final PsiTypeElement element = createTypeElementFromText(text, context); |
| if (markAsCopy) { |
| GeneratedMarkerVisitor.markGenerated(element); |
| } |
| return element.getType(); |
| } |
| |
| @NotNull |
| @Override |
| public PsiJavaCodeReferenceElement createReferenceFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final boolean isStaticImport = context instanceof PsiImportStaticStatement && |
| !((PsiImportStaticStatement)context).isOnDemand(); |
| final boolean mayHaveDiamonds = context instanceof PsiNewExpression && |
| PsiUtil.getLanguageLevel(context).isAtLeast(LanguageLevel.JDK_1_7); |
| final JavaParserUtil.ParserWrapper wrapper = isStaticImport ? STATIC_IMPORT_REF : mayHaveDiamonds ? DIAMOND_REF : REFERENCE; |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, wrapper, level(context)), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiJavaCodeReferenceElement)) { |
| throw new IncorrectOperationException("Incorrect reference '" + text + "'"); |
| } |
| return (PsiJavaCodeReferenceElement)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiCodeBlock createCodeBlockFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, CODE_BLOCK, level(context), true), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiCodeBlock)) { |
| throw new IncorrectOperationException("Incorrect code block '" + text + "'"); |
| } |
| return (PsiCodeBlock)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiStatement createStatementFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, STATEMENT, level(context)), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiStatement)) { |
| throw new IncorrectOperationException("Incorrect statement '" + text + "'"); |
| } |
| return (PsiStatement)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiExpression createExpressionFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, EXPRESSION, level(context)), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiExpression)) { |
| throw new IncorrectOperationException("Incorrect expression '" + text + "'"); |
| } |
| return (PsiExpression)element; |
| } |
| |
| protected PsiJavaFile createDummyJavaFile(@NonNls final String text) { |
| final FileType type = JavaFileType.INSTANCE; |
| return (PsiJavaFile)PsiFileFactory.getInstance(myManager.getProject()).createFileFromText(DUMMY_FILE_NAME, type, text); |
| } |
| |
| @NotNull |
| @Override |
| public PsiTypeParameter createTypeParameterFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, TYPE_PARAMETER, level(context)), |
| context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiTypeParameter)) { |
| throw new IncorrectOperationException("Incorrect type parameter '" + text + "'"); |
| } |
| return (PsiTypeParameter)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiComment createCommentFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final PsiJavaFile aFile = createDummyJavaFile(text); |
| for (PsiElement aChildren : aFile.getChildren()) { |
| if (aChildren instanceof PsiComment) { |
| if (!aChildren.getText().equals(text)) { |
| break; |
| } |
| final PsiComment comment = (PsiComment)aChildren; |
| DummyHolderFactory.createHolder(myManager, (TreeElement)SourceTreeToPsiMap.psiElementToTree(comment), context); |
| return comment; |
| } |
| } |
| |
| throw new IncorrectOperationException("Incorrect comment '" + text + "'"); |
| } |
| |
| @NotNull |
| @Override |
| public PsiEnumConstant createEnumConstantFromText(@NotNull final String text, @Nullable final PsiElement context) throws IncorrectOperationException { |
| final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, ENUM_CONSTANT, level(context)), context); |
| final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); |
| if (!(element instanceof PsiEnumConstant)) { |
| throw new IncorrectOperationException("Incorrect enum constant '" + text + "'"); |
| } |
| return (PsiEnumConstant)element; |
| } |
| |
| @NotNull |
| @Override |
| public PsiType createPrimitiveType(@NotNull final String text, @NotNull final PsiAnnotation[] annotations) throws IncorrectOperationException { |
| final PsiPrimitiveType primitiveType = getPrimitiveType(text); |
| if (primitiveType == null) { |
| throw new IncorrectOperationException("Incorrect primitive type '" + text + "'"); |
| } |
| return annotations.length == 0 ? primitiveType : new PsiPrimitiveType(text, annotations); |
| } |
| |
| public static PsiPrimitiveType getPrimitiveType(final String text) { |
| return PRIMITIVE_TYPES.get(text); |
| } |
| |
| protected static LanguageLevel level(@Nullable final PsiElement context) { |
| return context != null && context.isValid() ? PsiUtil.getLanguageLevel(context) : LanguageLevel.HIGHEST; |
| } |
| |
| private static IncorrectOperationException newException(final String msg, final DummyHolder holder) { |
| final FileElement root = holder.getTreeElement(); |
| if (root instanceof JavaDummyElement) { |
| final Throwable cause = ((JavaDummyElement)root).getParserError(); |
| if (cause != null) { |
| return new IncorrectOperationException(msg, cause); |
| } |
| } |
| return new IncorrectOperationException(msg); |
| } |
| } |