/* * Copyright 2000-2012 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 PsiJavaFile myDummyJavaFile; 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 DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, TYPE, level(context)), context); final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); if (!(element instanceof PsiTypeElement)) { throw new IncorrectOperationException("Incorrect type \"" + text + "\"."); } 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 PsiJavaFile getDummyJavaFile() { if (myDummyJavaFile == null) { myDummyJavaFile = createDummyJavaFile(""); } return myDummyJavaFile; } 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) { @Override public Throwable getCause() { return cause; } }; } } return new IncorrectOperationException(msg); } }