/* * Copyright 2000-2013 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.lang.java.parser; import com.intellij.AbstractBundle; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.lang.PsiBuilder; import com.intellij.openapi.util.Pair; import com.intellij.psi.JavaTokenType; import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.intellij.lang.PsiBuilderUtil.expect; import static com.intellij.lang.java.parser.JavaParserUtil.*; public class FileParser { private static final TokenSet IMPORT_LIST_STOPPER_SET = TokenSet.orSet( ElementType.MODIFIER_BIT_SET, TokenSet.create(JavaTokenType.CLASS_KEYWORD, JavaTokenType.INTERFACE_KEYWORD, JavaTokenType.ENUM_KEYWORD, JavaTokenType.AT)); private final JavaParser myParser; public FileParser(@NotNull final JavaParser javaParser) { myParser = javaParser; } public void parse(final PsiBuilder builder) { parseFile(builder, IMPORT_LIST_STOPPER_SET, JavaErrorMessages.INSTANCE, "expected.class.or.interface"); } private static String error(@NotNull AbstractBundle bundle, @NotNull String errorMessageKey) { return bundle.getMessage(errorMessageKey); } public void parseFile(@NotNull final PsiBuilder builder, @NotNull final TokenSet importListStoppers, @NotNull final AbstractBundle bundle, @NotNull final String errorMessageKey) { parsePackageStatement(builder); Pair<PsiBuilder.Marker, Boolean> impListInfo = parseImportList(builder, importListStoppers); Boolean firstDeclarationOk = null; PsiBuilder.Marker firstDeclaration = null; PsiBuilder.Marker invalidElements = null; while (!builder.eof()) { if (builder.getTokenType() == JavaTokenType.SEMICOLON) { builder.advanceLexer(); continue; } final PsiBuilder.Marker declaration = parseInitial(builder); if (declaration != null) { if (invalidElements != null) { invalidElements.errorBefore(error(bundle, errorMessageKey), declaration); invalidElements = null; } if (firstDeclarationOk == null) { firstDeclarationOk = exprType(declaration) != JavaElementType.MODIFIER_LIST; if (firstDeclarationOk) { firstDeclaration = declaration; } } continue; } if (invalidElements == null) { invalidElements = builder.mark(); } builder.advanceLexer(); if (firstDeclarationOk == null) firstDeclarationOk = false; } if (invalidElements != null) { invalidElements.error(error(bundle, errorMessageKey)); } if (impListInfo.second && firstDeclarationOk == Boolean.TRUE) { impListInfo.first.setCustomEdgeTokenBinders(PRECEDING_COMMENT_BINDER, null); // pass comments behind fake import list firstDeclaration.setCustomEdgeTokenBinders(SPECIAL_PRECEDING_COMMENT_BINDER, null); } } @Nullable protected PsiBuilder.Marker parseInitial(PsiBuilder builder) { return myParser.getDeclarationParser().parse(builder, DeclarationParser.Context.FILE); } @Nullable public PsiBuilder.Marker parsePackageStatement(final PsiBuilder builder) { final PsiBuilder.Marker statement = builder.mark(); if (!expect(builder, JavaTokenType.PACKAGE_KEYWORD)) { final PsiBuilder.Marker modList = builder.mark(); myParser.getDeclarationParser().parseAnnotations(builder); done(modList, JavaElementType.MODIFIER_LIST); if (!expect(builder, JavaTokenType.PACKAGE_KEYWORD)) { statement.rollbackTo(); return null; } } final PsiBuilder.Marker ref = myParser.getReferenceParser().parseJavaCodeReference(builder, true, false, false, false); if (ref == null) { statement.error(JavaErrorMessages.message("expected.class.or.interface")); return null; } semicolon(builder); done(statement, JavaElementType.PACKAGE_STATEMENT); return statement; } @NotNull public Pair<PsiBuilder.Marker, Boolean> parseImportList(final PsiBuilder builder, final TokenSet stoppers) { PsiBuilder.Marker list = builder.mark(); IElementType tokenType = builder.getTokenType(); boolean isEmpty = tokenType != JavaTokenType.IMPORT_KEYWORD && tokenType != JavaTokenType.SEMICOLON; if (!isEmpty) { PsiBuilder.Marker invalidElements = null; while (!builder.eof()) { tokenType = builder.getTokenType(); if (stoppers.contains(tokenType)) { break; } else if (tokenType == JavaTokenType.SEMICOLON) { builder.advanceLexer(); continue; } final PsiBuilder.Marker statement = parseImportStatement(builder); if (statement != null) { if (invalidElements != null) { invalidElements.errorBefore(JavaErrorMessages.message("unexpected.token"), statement); invalidElements = null; } continue; } if (invalidElements == null) { invalidElements = builder.mark(); } builder.advanceLexer(); } if (invalidElements != null) { invalidElements.error(JavaErrorMessages.message("unexpected.token")); } } done(list, JavaElementType.IMPORT_LIST); return Pair.create(list, isEmpty); } @Nullable private PsiBuilder.Marker parseImportStatement(final PsiBuilder builder) { if (builder.getTokenType() != JavaTokenType.IMPORT_KEYWORD) return null; final PsiBuilder.Marker statement = builder.mark(); builder.advanceLexer(); final boolean isStatic = expect(builder, JavaTokenType.STATIC_KEYWORD); final IElementType type = isStatic ? JavaElementType.IMPORT_STATIC_STATEMENT : JavaElementType.IMPORT_STATEMENT; final boolean isOk = myParser.getReferenceParser().parseImportCodeReference(builder, isStatic); if (isOk) { semicolon(builder); } done(statement, type); return statement; } }