001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 the ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package eu.cqse.check.framework.shallowparser.languages.base;
018
019import java.util.List;
020
021import eu.cqse.check.framework.scanner.ETokenType;
022import eu.cqse.check.framework.scanner.IToken;
023import eu.cqse.check.framework.shallowparser.TokenStreamUtils;
024import eu.cqse.check.framework.shallowparser.framework.ParserState;
025import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
026
027/**
028 * Recognizer that finds anonymous classes as they would appear in java and
029 * performs parsing within this class.
030 */
031public class JavaLikeAnonymousClassRecognizer<STATE extends Enum<STATE>> extends RecognizerBase<STATE> {
032
033        /**
034         * The state that will be used to parse, once the anonymous class has been
035         * verified.
036         */
037        private final STATE subParseState;
038
039        /**
040         * Constructor.
041         *
042         * @param subParseState
043         *            {@link #subParseState}
044         */
045        public JavaLikeAnonymousClassRecognizer(STATE subParseState) {
046                this.subParseState = subParseState;
047        }
048
049        /** {@inheritDoc} */
050        @Override
051        protected int matchesLocally(ParserState<STATE> parserState, List<IToken> tokens, int startOffset) {
052                int offset = matchesAnonymousClass(parserState, tokens, startOffset);
053                // If a simple statement starts with the new keyword the shallow
054                // parser rule skips the keyword thus we have to check whether an
055                // anonymous class declaration starts one token in front of the
056                // start offset
057                if (offset == NO_MATCH && startOffset > 0) {
058                        offset = matchesAnonymousClass(parserState, tokens, startOffset - 1);
059                }
060                return offset;
061        }
062
063        /** Matches an anonymous class declaration. */
064        protected int matchesAnonymousClass(ParserState<STATE> parserState, List<IToken> tokens, int startOffset) {
065
066                int currentOffset = startOffset;
067                if (!TokenStreamUtils.hasTokenTypeSequence(tokens, currentOffset, ETokenType.NEW, ETokenType.IDENTIFIER)) {
068                        return NO_MATCH;
069                }
070                currentOffset += 2;
071
072                // skip fully qualified names
073                while (TokenStreamUtils.hasTokenTypeSequence(tokens, currentOffset, ETokenType.DOT, ETokenType.IDENTIFIER)) {
074                        currentOffset += 2;
075                }
076
077                // skip generics specification
078                if (TokenStreamUtils.hasTokenTypeSequence(tokens, currentOffset, ETokenType.LT)) {
079                        currentOffset = TokenStreamUtils.findMatchingClosingToken(tokens, currentOffset + 1, ETokenType.LT,
080                                        ETokenType.GT);
081                        if (currentOffset == TokenStreamUtils.NOT_FOUND) {
082                                return NO_MATCH;
083                        }
084                        currentOffset += 1;
085                }
086
087                // expect and skip parentheses
088                if (!TokenStreamUtils.hasTokenTypeSequence(tokens, currentOffset, ETokenType.LPAREN)) {
089                        return NO_MATCH;
090                }
091                currentOffset = TokenStreamUtils.findMatchingClosingToken(tokens, currentOffset + 1, ETokenType.LPAREN,
092                                ETokenType.RPAREN);
093                if (currentOffset == TokenStreamUtils.NOT_FOUND) {
094                        return NO_MATCH;
095                }
096                currentOffset += 1;
097
098                if (TokenStreamUtils.hasTokenTypeSequence(tokens, currentOffset, ETokenType.LBRACE)) {
099                        return parserState.parse(subParseState, tokens, startOffset);
100                }
101
102                return NO_MATCH;
103        }
104
105}