org.inferred.internal.testing.integration.SourceBuilder Maven / Gradle / Ivy
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* 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 org.inferred.internal.testing.integration;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.Throwables;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
/** Simple builder API for an in-memory {@link JavaFileObject}. */
public class SourceBuilder {
private static final Pattern TYPE_NAME_PATTERN =
Pattern.compile("(class|[@]?interface|enum)\\s+(\\w+)");
private static final Pattern PACKAGE_PATTERN =
Pattern.compile("package\\s+(\\w+(\\s*\\.\\s*\\w+)*)\\s*;");
private final StringBuilder code = new StringBuilder();
/**
* Appends a formatted line of code to the source. Formatting is done by {@link String#format},
* except that {@link Class} instances use their entity's name unadorned, rather than the usual
* toString implementation.
*/
public SourceBuilder addLine(String fmt, Object... args) {
Object[] substituteArgs = new Object[args.length];
for (int i = 0; i < args.length; i++) {
substituteArgs[i] = substitute(args[i]);
}
code.append(String.format(fmt, substituteArgs)).append("\n");
return this;
}
/**
* Returns a {@link JavaFileObject} for the source added to the builder.
*/
public JavaFileObject build() {
return new Source(code.toString());
}
/** Substitutes the given object with one that has a better toString() for code generation. */
static Object substitute(Object arg) {
if (arg instanceof Class>) {
return ((Class>) arg).getCanonicalName();
} else {
return arg;
}
}
/** Parses the given source code and returns the name of the type it defines. */
static String getTypeNameFromSource(CharSequence source) {
Matcher packageMatcher = SourceBuilder.PACKAGE_PATTERN.matcher(source);
Matcher typeNameMatcher = SourceBuilder.TYPE_NAME_PATTERN.matcher(source);
checkArgument(packageMatcher.find(), "Source contains no package definition");
checkArgument(typeNameMatcher.find(), "Source contains no type definition");
String typeName = packageMatcher.group(1) + "." + typeNameMatcher.group(2);
typeName = typeName.replaceAll("\\s+", "");
return typeName;
}
/** Returns a dummy URI for the given type name. */
static URI uriForClass(String typeName) {
try {
return new URI("mem:///" + typeName.replaceAll("\\.", "/") + ".java");
} catch (URISyntaxException e) {
throw Throwables.propagate(e);
}
}
/** Simple in-memory implementation of {@link javax.tools.JavaFileObject JavaFileObject}. */
private class Source extends SimpleJavaFileObject {
private final String content;
/**
* Creates a new {@link javax.tools.JavaFileObject JavaFileObject} containing the supplied
* source code. File name is derived from the source code's package and type name.
*/
Source(String source) {
super(uriForClass(getTypeNameFromSource(source)), Kind.SOURCE);
this.content = source;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy