Files
openide/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java
T

448 lines
20 KiB
Java

/*
* Copyright 2000-2009 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.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.JavaTemplateUtil;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.java.parser.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
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.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.NotNull;
import java.util.Map;
import java.util.Properties;
import static com.intellij.openapi.util.text.StringUtil.join;
/**
* @author max
*/
public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJavaParserFacade {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiJavaParserFacadeImpl");
private static final JavaParserUtil.ParserWrapper ANNOTATION = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
DeclarationParser.parseAnnotation(builder);
}
};
private static final JavaParserUtil.ParserWrapper PARAMETER = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
DeclarationParser.parseParameter(builder, true, false);
}
};
private static final JavaParserUtil.ParserWrapper RESOURCE = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
DeclarationParser.parseResource(builder);
}
};
private static final JavaParserUtil.ParserWrapper TYPE = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
ReferenceParser.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) {
ReferenceParser.parseJavaCodeReference(builder, false, true, false, false, false);
}
};
public static final JavaParserUtil.ParserWrapper DIAMOND_REF = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
ReferenceParser.parseJavaCodeReference(builder, false, true, false, false, true);
}
};
public static final JavaParserUtil.ParserWrapper STATIC_IMPORT_REF = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
ReferenceParser.parseImportCodeReference(builder, true);
}
};
private static final JavaParserUtil.ParserWrapper TYPE_PARAMETER = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
ReferenceParser.parseTypeParameter(builder);
}
};
private static final JavaParserUtil.ParserWrapper DECLARATION = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
DeclarationParser.parse(builder, DeclarationParser.Context.CLASS);
}
};
private static final JavaParserUtil.ParserWrapper CODE_BLOCK = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
StatementParser.parseCodeBlockDeep(builder, true);
}
};
private static final JavaParserUtil.ParserWrapper STATEMENT = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
StatementParser.parseStatement(builder);
}
};
private static final JavaParserUtil.ParserWrapper EXPRESSION = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
ExpressionParser.parse(builder);
}
};
private static final JavaParserUtil.ParserWrapper ENUM_CONSTANT = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
DeclarationParser.parseEnumConstant(builder);
}
};
private static final JavaParserUtil.ParserWrapper CATCH_SECTION = new JavaParserUtil.ParserWrapper() {
@Override
public void parse(final PsiBuilder builder) {
StatementParser.parseCatchBlock(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);
}
public PsiJavaParserFacadeImpl(final PsiManagerEx manager) {
super(manager);
}
@NotNull
@Override
public PsiAnnotation createAnnotationFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, ANNOTATION, false), 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(join("/**\n", text, "\n */")).getTags()[0];
}
@NotNull
@Override
public PsiDocTag createDocTagFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
return createDocTagFromText(text);
}
@NotNull
@Override
public PsiDocComment createDocCommentFromText(@NotNull final String text) throws IncorrectOperationException {
final PsiMethod method = createMethodFromText(join(text, "void m();"), null);
final PsiDocComment comment = method.getDocComment();
assert comment != null : text;
return comment;
}
@NotNull
@Override
public PsiDocComment createDocCommentFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
return createDocCommentFromText(text);
}
@NotNull
@Override
public PsiClass createClassFromText(@NotNull final String body, final PsiElement context) throws IncorrectOperationException {
final PsiJavaFile aFile = createDummyJavaFile(join("class _Dummy_ { ", body, " }"));
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, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, DECLARATION, false), 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, final PsiElement context, final LanguageLevel level) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, DECLARATION, false, level), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
if (!(element instanceof PsiMethod)) {
throw new IncorrectOperationException("Incorrect method \"" + text + "\" (" + element + ").");
}
return (PsiMethod)element;
}
@NotNull
@Override
public final PsiMethod createMethodFromText(@NotNull final String text, 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, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, PARAMETER, false), 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, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, RESOURCE, false), 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, final PsiElement context) throws IncorrectOperationException {
return createTypeInner(text, context, false);
}
@NotNull
@Override
public PsiTypeElement createTypeElementFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, TYPE, false), 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, 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) {
((TreeElement)element.getNode()).acceptTree(new GeneratedMarkerVisitor());
}
return element.getType();
}
@NotNull
@Override
public PsiJavaCodeReferenceElement createReferenceFromText(@NotNull final String text, 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, false), 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, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, CODE_BLOCK, 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, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, STATEMENT, false), 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, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, EXPRESSION, false), 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(final String text) {
final String fileName = "_Dummy_." + StdFileTypes.JAVA.getDefaultExtension();
final FileType type = StdFileTypes.JAVA;
return (PsiJavaFile)PsiFileFactory.getInstance(myManager.getProject()).createFileFromText(type, fileName, text, 0, text.length());
}
@NotNull
@Override
public PsiTypeParameter createTypeParameterFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, TYPE_PARAMETER, false), 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, 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, final PsiElement context) throws IncorrectOperationException {
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, ENUM_CONSTANT, false), 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 PsiCatchSection createCatchSection(@NotNull final PsiClassType exceptionType, @NotNull final String exceptionName,
final PsiElement context) throws IncorrectOperationException {
final String text = join("catch (", exceptionType.getCanonicalText(), " ", exceptionName, ") {}");
final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, CATCH_SECTION, false), context);
final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode());
if (!(element instanceof PsiCatchSection)) {
throw new IncorrectOperationException("Incorrect catch section \"" + text + "\".");
}
setupCatchBlock(exceptionName, context, (PsiCatchSection)element);
return (PsiCatchSection)myManager.getCodeStyleManager().reformat(element);
}
private void setupCatchBlock(final String exceptionName, final PsiElement context, final PsiCatchSection psiCatchSection)
throws IncorrectOperationException {
final FileTemplate catchBodyTemplate = FileTemplateManager.getInstance().getCodeTemplate(JavaTemplateUtil.TEMPLATE_CATCH_BODY);
LOG.assertTrue(catchBodyTemplate != null);
final Properties props = new Properties();
props.setProperty(FileTemplate.ATTRIBUTE_EXCEPTION, exceptionName);
if (context != null && context.isPhysical()) {
final PsiDirectory directory = context.getContainingFile().getContainingDirectory();
if (directory != null) {
JavaTemplateUtil.setPackageNameAttribute(props, directory);
}
}
final PsiCodeBlock codeBlockFromText;
try {
codeBlockFromText = createCodeBlockFromText("{\n" + catchBodyTemplate.getText(props) + "\n}", null);
}
catch (ProcessCanceledException ce) {
throw ce;
}
catch (Exception e) {
throw new IncorrectOperationException("Incorrect file template", e);
}
psiCatchSection.getCatchBlock().replace(codeBlockFromText);
}
@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);
}
}