All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.auto.value.processor.AbstractMethodExtractor Maven / Gradle / Ivy

/*
 * Copyright (C) 2013 Google, Inc.
 *
 * 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.google.auto.value.processor;

import com.google.common.collect.ImmutableListMultimap;
import java.util.ArrayDeque;
import java.util.Deque;

/**
 * An ultrasimplified Java parser for {@link EclipseHack} that examines classes to extract just
 * the abstract methods. The parsing is very superficial. It assumes that the source text is
 * syntactically correct, which it must be in the context of an annotation processor because the
 * compiler doesn't invoke the processor if there are syntax errors.
 *
 * 

We recognize the text {@code ... class Foo ... { ... } } as a class called Foo, whose * definition extends to the matching right brace. Within a class definition, we recognize the text * {@code abstract ... bar ( ) } as an abstract method called bar. We also recognize {@code ... * interface Foo ... { ... } } so that we can discover {@code @AutoValue} classes that are nested * in an interface. * *

We construct a {@code Map>} that represents the abstract methods found in * each class, in the order they were found. If com.example.Foo contains a nested class Bar, then * there will be an entry for "com.example.Foo.Bar" in this Map. * * @author [email protected] (Éamonn McManus) */ final class AbstractMethodExtractor { AbstractMethodExtractor() { } // Here are the details of the matching. We track the current brace depth, and we artificially // consider that the whole file is at brace depth 1 inside a pseudo-class whose name is the // name of the package. When we see a class definition, we push the fully-qualified name of the // class on a stack so that we can associate abstract methods we find with the possibly-nested // class they belong to. A class definition must occur at brace depth one more than the class // containing it, which is equivalent to saying that the brace depth must be the same as the // class stack depth. This check excludes local class definitions within methods and // initializers. If we meet these constraints and we see the word "class" followed by an // identifier Foo, then we consider that we are entering the definition of class Foo. We determine // the fully-qualified name of Foo, which is container.Foo, where container is the current top of // the class stack (initially, the package name). We push this new fully-qualified name on the // class stack. We have not yet seen the left brace with the class definition so at this point the // class stack depth is one more than the brace depth. When we subsequently see a right brace that // takes us back to this situation then we know we have completed the definition of Foo and we can // pop it from the class stack. // // We check that the token after "class" is indeed an identifier to avoid confusion // with Foo.class. Even though the tokenizer does not distinguish between identifiers and // keywords, it is enough to exclude the single word "instanceof" because that is the only word // that can legally appear after Foo.class (though in a legal program the resultant expression // will always be true). // // Again, we are at the top level of a class when the brace depth is equal to the class stack // depth. If we then see the word "abstract" then that is the start either of an abstract class // definition or of an abstract method. We record that we have seen "abstract" and we cancel // that indication as soon as we see a left brace, to exclude the abstract class case, and also // the case of interfaces or @interfaces redundantly declared abstract. Now, when // we see an identifier that is preceded by an uncanceled "abstract" and followed by a left paren // then we have found an abstract method of the class on the top of the class stack. We record it // in the list of abstract methods of the class on the top of the class stack. We don't bother // checking that the method has no parameters, because an @AutoValue class will cause a compiler // error if there are abstract methods with parameters, since the @AutoValue processor doesn't // know how to implement them in the concrete subclass it generates. ImmutableListMultimap abstractMethods( EclipseHackTokenizer tokenizer, String packageName) { ImmutableListMultimap.Builder abstractMethods = ImmutableListMultimap.builder(); Deque classStack = new ArrayDeque(); classStack.addLast(packageName); int braceDepth = 1; boolean sawAbstract = false; String className = null; for (String previousToken = "", token = tokenizer.nextToken(); token != null; previousToken = token, token = tokenizer.nextToken()) { boolean topLevel = (braceDepth == classStack.size()); if (className != null) { if (Character.isJavaIdentifierStart(className.charAt(0)) && !className.equals("instanceof")) { String container = classStack.getLast(); // container might be empty in the case of a packageless class classStack.add(container.isEmpty() ? className : container + "." + className); } className = null; } if (token.equals("{")) { braceDepth++; sawAbstract = false; } else if (token.equals("}")) { braceDepth--; if (topLevel) { classStack.removeLast(); } } else if (topLevel) { if (token.equals("class") || token.equals("interface")) { className = tokenizer.nextToken(); } else if (token.equals("abstract")) { sawAbstract = true; } else if (token.equals("(")) { if (sawAbstract && Character.isJavaIdentifierStart(previousToken.charAt(0))) { abstractMethods.put(classStack.getLast(), previousToken); } sawAbstract = false; } } } return abstractMethods.build(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy