com.google.auto.value.processor.JavaScanner Maven / Gradle / Ivy
Show all versions of auto-value Show documentation
/*
* Copyright (C) 2015 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;
/**
* A simplistic Java scanner. This scanner returns a sequence of tokens that can be used to
* reconstruct the source code. Since the source code is coming from a string, the scanner in fact
* just returns token boundaries rather than the tokens themselves.
*
* We are not dealing with arbitrary user code so we can assume there are no exotic things like
* tabs or Unicode escapes that resolve into quotes. The purpose of the scanner here is to return a
* sequence of offsets that split the string up in a way that allows us to work with spaces without
* having to worry whether they are inside strings or comments. The particular properties we use are
* that every string and character literal and every comment is a single token; every newline plus
* all following indentation is a single token; and every other string of consecutive spaces outside
* a comment or literal is a single token. That means that we can safely compress a token that
* starts with a space into a single space, without falsely removing indentation or changing the
* contents of strings.
*
*
In addition to real Java syntax, this scanner recognizes tokens of the form {@code `text`},
* which are used in the templates to wrap fully-qualified type names, so that they can be extracted
* and replaced by imported names if possible.
*
* @author Éamonn McManus
*/
class JavaScanner {
private final String s;
JavaScanner(String s) {
if (!s.endsWith("\n")) {
s += "\n";
// This allows us to avoid checking for the end of the string in most cases.
}
this.s = s;
}
int tokenEnd(int start) {
if (start >= s.length()) {
return s.length();
}
switch (s.charAt(start)) {
case ' ':
case '\n':
return spaceEnd(start);
case '/':
if (s.charAt(start + 1) == '*') {
return blockCommentEnd(start);
} else if (s.charAt(start + 1) == '/') {
return lineCommentEnd(start);
} else {
return start + 1;
}
case '\'':
case '"':
case '`':
return quoteEnd(start);
default:
// Every other character is considered to be its own token.
return start + 1;
}
}
private int spaceEnd(int start) {
assert s.charAt(start) == ' ' || s.charAt(start) == '\n';
int i;
for (i = start + 1; i < s.length() && s.charAt(i) == ' '; i++) {}
return i;
}
private int blockCommentEnd(int start) {
assert s.charAt(start) == '/' && s.charAt(start + 1) == '*';
int i;
for (i = start + 2; s.charAt(i) != '*' || s.charAt(i + 1) != '/'; i++) {}
return i + 2;
}
private int lineCommentEnd(int start) {
assert s.charAt(start) == '/' && s.charAt(start + 1) == '/';
int end = s.indexOf('\n', start + 2);
assert end > 0;
return end;
}
private int quoteEnd(int start) {
char quote = s.charAt(start);
assert quote == '\'' || quote == '"' || quote == '`';
int i;
for (i = start + 1; s.charAt(i) != quote; i++) {
if (s.charAt(i) == '\\') {
i++;
}
}
return i + 1;
}
}