package lombok;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Assert;
import lombok.core.LombokImmutableList;
import lombok.core.configuration.BubblingConfigurationResolver;
import lombok.core.configuration.ConfigurationProblemReporter;
import lombok.core.configuration.ConfigurationResolver;
import lombok.core.configuration.StringConfigurationSource;
public class LombokTestSource {
private final File file;
private final String content;
private final LombokImmutableList<CompilerMessageMatcher> messages;
private final Map<String, String> formatPreferences;
private final boolean ignore;
private final boolean skipCompareContent;
private final boolean unchanged;
private final int versionLowerLimit, versionUpperLimit;
private final ConfigurationResolver configuration;
private final String specifiedEncoding;
private final List<String> platforms;
public boolean runOnPlatform(String platform) {
if (platforms == null || platforms.isEmpty()) return true;
for (String pl : platforms) if (pl.equalsIgnoreCase(platform)) return true;
return false;
}
public boolean versionWithinLimit(int version) {
return version >= versionLowerLimit && version <= versionUpperLimit;
}
public File getFile() {
return file;
}
public String getContent() {
return content;
}
public LombokImmutableList<CompilerMessageMatcher> getMessages() {
return messages;
}
public boolean isIgnore() {
return ignore;
}
public boolean forceUnchanged() {
return unchanged;
}
public boolean isSkipCompareContent() {
return skipCompareContent;
}
public String getSpecifiedEncoding() {
return specifiedEncoding;
}
public ConfigurationResolver getConfiguration() {
return configuration;
}
public Map<String, String> getFormatPreferences() {
return formatPreferences;
}
private static final Pattern VERSION_STYLE_1 = Pattern.compile("^(\\d+)$");
private static final Pattern VERSION_STYLE_2 = Pattern.compile("^\\:(\\d+)$");
private static final Pattern VERSION_STYLE_3 = Pattern.compile("^(\\d+):$");
private static final Pattern VERSION_STYLE_4 = Pattern.compile("^(\\d+):(\\d+)$");
private int[] parseVersionLimit(String spec) {
{
Matcher m = VERSION_STYLE_1.matcher(spec);
if (m.matches()) {
int v = Integer.parseInt(m.group(1));
return new int[] {v, v};
}
}
{
Matcher m = VERSION_STYLE_2.matcher(spec);
if (m.matches()) return new int[] {0, Integer.parseInt(m.group(1))};
}
{
Matcher m = VERSION_STYLE_3.matcher(spec);
if (m.matches()) return new int[] {Integer.parseInt(m.group(1)), Integer.MAX_VALUE};
}
{
Matcher m = VERSION_STYLE_4.matcher(spec);
if (m.matches()) return new int[] {Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2))};
}
return null;
}
private static final Pattern IGNORE_PATTERN = Pattern.compile("^\\s*ignore\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE);
private static final Pattern UNCHANGED_PATTERN = Pattern.compile("^\\s*unchanged\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE);
private static final Pattern SKIP_COMPARE_CONTENT_PATTERN = Pattern.compile("^\\s*skip[- ]?compare[- ]?contents?\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE);
private LombokTestSource(File file, String content, List<CompilerMessageMatcher> messages, List<String> directives) {
this.file = file;
this.content = content;
this.messages = messages == null ? LombokImmutableList.<CompilerMessageMatcher>of() : LombokImmutableList.copyOf(messages);
StringBuilder conf = new StringBuilder();
int versionLower = 0;
int versionUpper = Integer.MAX_VALUE;
boolean ignore = false;
boolean skipCompareContent = false;
boolean unchanged = false;
String encoding = null;
Map<String, String> formats = new HashMap<String, String>();
String[] platformLimit = null;
for (String directive : directives) {
directive = directive.trim();
String lc = directive.toLowerCase();
if (IGNORE_PATTERN.matcher(directive).matches()) {
ignore = true;
continue;
}
if (UNCHANGED_PATTERN.matcher(directive).matches()) {
unchanged = true;
continue;
}
if (SKIP_COMPARE_CONTENT_PATTERN.matcher(directive).matches()) {
skipCompareContent = true;
continue;
}
if (lc.startsWith("platform ")) {
String platformDesc = lc.substring("platform ".length());
int idx = platformDesc.indexOf(':');
if (idx != -1) platformDesc = platformDesc.substring(0, idx).trim();
platformLimit = platformDesc.split("\\s*,\\s*");
continue;
}
if (lc.startsWith("version ")) {
int[] limits = parseVersionLimit(lc.substring(7).trim());
if (limits == null) {
Assert.fail("Directive line \"" + directive + "\" in '" + file.getAbsolutePath() + "' invalid: version must be followed by a single integer.");
throw new RuntimeException();
}
versionLower = limits[0];
versionUpper = limits[1];
continue;
}
if (lc.startsWith("conf:")) {
String confLine = directive.substring(5).trim();
conf.append(confLine).append("\n");
continue;
}
if (lc.startsWith("encoding:")) {
encoding = directive.substring(9).trim();
continue;
}
if (lc.startsWith("format:")) {
String formatLine = directive.substring(7).trim();
int idx = formatLine.indexOf('=');
if (idx == -1) throw new IllegalArgumentException("To add a format directive, use: \"//FORMAT: javaLangAsFQN = skip\"");
String key = formatLine.substring(0, idx).trim();
String value = formatLine.substring(idx + 1).trim();
formats.put(key.toLowerCase(), value);
continue;
}
if (lc.startsWith("issue ")) continue;
Assert.fail("Directive line \"" + directive + "\" in '" + file.getAbsolutePath() + "' invalid: unrecognized directive.");
throw new RuntimeException();
}
this.specifiedEncoding = encoding;
this.versionLowerLimit = versionLower;
this.versionUpperLimit = versionUpper;
this.ignore = ignore;
this.skipCompareContent = skipCompareContent;
this.unchanged = unchanged;
this.platforms = platformLimit == null ? null : Arrays.asList(platformLimit);
ConfigurationProblemReporter reporter = new ConfigurationProblemReporter() {
@Override public void report(String sourceDescription, String problem, int lineNumber, CharSequence line) {
Assert.fail("Problem on directive line: " + problem + " at conf line #" + lineNumber + " (" + line + ")");
}
};
this.configuration = new BubblingConfigurationResolver(Collections.singleton(StringConfigurationSource.forString(conf, reporter, file.getAbsolutePath())));
this.formatPreferences = Collections.unmodifiableMap(formats);
}
public static LombokTestSource readDirectives(File file) throws IOException {
List<String> directives = new ArrayList<String>();
{
InputStream rawIn = new FileInputStream(file);
try {
BufferedReader in = new BufferedReader(new InputStreamReader(rawIn, "UTF-8"));
try {
for (String i = in.readLine(); i != null; i = in.readLine()) {
if (i.isEmpty()) continue;
if (i.startsWith("//")) {
directives.add(i.substring(2));
} else {
break;
}
}
}
finally {
in.close();
}
}
finally {
rawIn.close();
}
}
return new LombokTestSource(file, "", null, directives);
}
public static LombokTestSource read(File sourceFolder, File messagesFolder, String fileName) throws IOException {
return read0(sourceFolder, messagesFolder, fileName, "UTF-8");
}
private static LombokTestSource read0(File sourceFolder, File messagesFolder, String fileName, String encoding) throws IOException {
StringBuilder content = null;
List<String> directives = new ArrayList<String>();
File sourceFile = new File(sourceFolder, fileName);
if (sourceFile.exists()) {
InputStream rawIn = new FileInputStream(sourceFile);
try {
BufferedReader in = new BufferedReader(new InputStreamReader(rawIn, encoding));
try {
for (String i = in.readLine(); i != null; i = in.readLine()) {
if (content != null) {
content.append(i).append("\n");
continue;
}
if (i.isEmpty()) continue;
if (i.startsWith("//")) {
directives.add(i.substring(2));
} else {
content = new StringBuilder();
content.append(i).append("\n");
}
}
}
finally {
in.close();
}
}
finally {
rawIn.close();
}
}
if (content == null) content = new StringBuilder();
List<CompilerMessageMatcher> messages = null;
if (messagesFolder != null) {
File messagesFile = new File(messagesFolder, fileName + ".messages");
try {
InputStream rawIn = new FileInputStream(messagesFile);
try {
messages = CompilerMessageMatcher.readAll(rawIn);
}
finally {
rawIn.close();
}
} catch (FileNotFoundException e) {
messages = null;
}
}
LombokTestSource source = new LombokTestSource(sourceFile, content.toString(), messages, directives);
String specifiedEncoding = source.getSpecifiedEncoding();
if (specifiedEncoding == null || specifiedEncoding.equalsIgnoreCase(encoding)) return source;
return read0(sourceFolder, messagesFolder, fileName, specifiedEncoding);
}
}