/*
* Copyright ( c ) 2013 , 2022 , Oracle and / or its affiliates . All rights reserved .
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER .
*
* This code is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 only , as
* published by the Free Software Foundation .
*
* This code is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* version 2 for more details ( a copy is included in the LICENSE file that
* accompanied this code ) .
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Please contact Oracle , 500 Oracle Parkway , Redwood Shores , CA 94065 USA
* or visit www . oracle . com if you need additional information or have any
* questions .
*/
package toolbox;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.FilterWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
/**
* Utility methods and classes for writing jtreg tests for
* javac , javah , and javap . ( For javadoc support , see JavadocTester . )
*
* < p > There is support for common file operations similar to
* shell commands like cat , cp , diff , mv , rm , grep .
*
* < p > There is also support for invoking various tools , like
* javac , javah , javap , jar , java and other JDK tools .
*
* < p > < em > File separators < / em > : for convenience , many operations accept strings
* to represent filenames . On all platforms on which JDK is supported ,
* " / " is a legal filename component separator . In particular , even
* on Windows , where the official file separator is " \ " , " / " is a legal
* alternative . It is therefore recommended that any client code using
* strings to specify filenames should use " / " .
*
* @ author Vicente Romero ( original )
* @ author Jonathan Gibbons ( revised )
*/
public class ToolBox {
/** The platform line separator. */
public static final String lineSeparator = System.getProperty("line.separator" );
/** The platform OS name. */
public static final String osName = System.getProperty("os.name" );
/** The location of the class files for this test, or null if not set. */
public static final String testClasses = System.getProperty("test.classes" );
/** The location of the source files for this test, or null if not set. */
public static final String testSrc = System.getProperty("test.src" );
/** The location of the test JDK for this test, or null if not set. */
public static final String testJDK = System.getProperty("test.jdk" );
/** The timeout factor for slow systems. */
public static final float timeoutFactor;
static {
String ttf = System.getProperty("test.timeout.factor" );
timeoutFactor = (ttf == null ) ? 1 .0 f : Float .parseFloat(ttf);
}
/** The current directory. */
public static final Path currDir = Path.of("." );
/** The stream used for logging output. */
public PrintStream out = System.err;
/**
* Checks if the host OS is some version of Windows .
* @ return true if the host OS is some version of Windows
*/
public static boolean isWindows() {
return osName.toLowerCase(Locale.ENGLISH).startsWith("windows" );
}
/**
* Splits a string around matches of the given regular expression .
* If the string is empty , an empty list will be returned .
*
* @ param text the string to be split
* @ param sep the delimiting regular expression
* @ return the strings between the separators
*/
public List<String> split(String text, String sep) {
if (text.isEmpty())
return Collections.emptyList();
return Arrays.asList(text.split(sep));
}
/**
* Checks if two lists of strings are equal .
*
* @ param l1 the first list of strings to be compared
* @ param l2 the second list of strings to be compared
* @ throws Error if the lists are not equal
*/
public void checkEqual(List<String> l1, List<String> l2) throws Error {
if (!Objects.equals(l1, l2)) {
// l1 and l2 cannot both be null
if (l1 == null )
throw new Error("comparison failed: l1 is null" );
if (l2 == null )
throw new Error("comparison failed: l2 is null" );
// report first difference
for (int i = 0 ; i < Math.min(l1.size(), l2.size()); i++) {
String s1 = l1.get(i);
String s2 = l2.get(i);
if (!Objects.equals(s1, s2)) {
throw new Error("comparison failed, index " + i +
", (" + s1 + ":" + s2 + ")" );
}
}
throw new Error("comparison failed: l1.size=" + l1.size() + ", l2.size=" + l2.size());
}
}
/**
* Filters a list of strings according to the given regular expression ,
* returning the strings that match the regular expression .
*
* @ param regex the regular expression
* @ param lines the strings to be filtered
* @ return the strings matching the regular expression
*/
public List<String> grep(String regex, List<String> lines) {
return grep(Pattern.compile(regex), lines, true );
}
/**
* Filters a list of strings according to the given regular expression ,
* returning the strings that match the regular expression .
*
* @ param pattern the regular expression
* @ param lines the strings to be filtered
* @ return the strings matching the regular expression
*/
public List<String> grep(Pattern pattern, List<String> lines) {
return grep(pattern, lines, true );
}
/**
* Filters a list of strings according to the given regular expression ,
* returning either the strings that match or the strings that do not match .
*
* @ param regex the regular expression
* @ param lines the strings to be filtered
* @ param match if true , return the lines that match ; otherwise if false , return the lines that do not match .
* @ return the strings matching ( or not matching ) the regular expression
*/
public List<String> grep(String regex, List<String> lines, boolean match) {
return grep(Pattern.compile(regex), lines, match);
}
/**
* Filters a list of strings according to the given regular expression ,
* returning either the strings that match or the strings that do not match .
*
* @ param pattern the regular expression
* @ param lines the strings to be filtered
* @ param match if true , return the lines that match ; otherwise if false , return the lines that do not match .
* @ return the strings matching ( or not matching ) the regular expression
*/
public List<String> grep(Pattern pattern, List<String> lines, boolean match) {
return lines.stream()
.filter(s -> pattern.matcher(s).find() == match)
.collect(Collectors.toList());
}
/**
* Copies a file .
* If the given destination exists and is a directory , the copy is created
* in that directory . Otherwise , the copy will be placed at the destination ,
* possibly overwriting any existing file .
* < p > Similar to the shell " cp " command : { @ code cp from to } .
*
* @ param from the file to be copied
* @ param to where to copy the file
* @ throws IOException if any error occurred while copying the file
*/
public void copyFile(String from, String to) throws IOException {
copyFile(Path.of(from), Path.of(to));
}
/**
* Copies a file .
* If the given destination exists and is a directory , the copy is created
* in that directory . Otherwise , the copy will be placed at the destination ,
* possibly overwriting any existing file .
* < p > Similar to the shell " cp " command : { @ code cp from to } .
*
* @ param from the file to be copied
* @ param to where to copy the file
* @ throws IOException if an error occurred while copying the file
*/
public void copyFile(Path from, Path to) throws IOException {
if (Files.isDirectory(to)) {
to = to.resolve(from.getFileName());
} else if (to.getParent() != null ) {
Files.createDirectories(to.getParent());
}
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
/**
* Copies the contents of a directory to another directory .
* < p > Similar to the shell command : { @ code rsync fromDir / toDir / } .
*
* @ param fromDir the directory containing the files to be copied
* @ param toDir the destination to which to copy the files
*/
public void copyDir(String fromDir, String toDir) {
copyDir(Path.of(fromDir), Path.of(toDir));
}
/**
* Copies the contents of a directory to another directory .
* The destination direction should not already exist .
* < p > Similar to the shell command : { @ code rsync fromDir / toDir / } .
*
* @ param fromDir the directory containing the files to be copied
* @ param toDir the destination to which to copy the files
*/
public void copyDir(Path fromDir, Path toDir) {
try {
if (toDir.getParent() != null ) {
Files.createDirectories(toDir.getParent());
}
Files.walkFileTree(fromDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path fromSubdir, BasicFileAttributes attrs)
throws IOException {
Files.copy(fromSubdir, toDir.resolve(fromDir.relativize(fromSubdir)));
return FileVisitResult.CONTINUE ;
}
@Override
public FileVisitResult visitFile(Path fromFile, BasicFileAttributes attrs)
throws IOException {
Files.copy(fromFile, toDir.resolve(fromDir.relativize(fromFile)));
return FileVisitResult.CONTINUE ;
}
});
} catch (IOException e) {
throw new Error("Could not copy " + fromDir + " to " + toDir + ": " + e, e);
}
}
/**
* Creates one or more directories .
* For each of the series of paths , a directory will be created ,
* including any necessary parent directories .
* < p > Similar to the shell command : { @ code mkdir - p paths } .
*
* @ param paths the directories to be created
* @ throws IOException if an error occurred while creating the directories
*/
public void createDirectories(String... paths) throws IOException {
if (paths.length == 0 )
throw new IllegalArgumentException("no directories specified" );
for (String p : paths)
Files.createDirectories(Path.of(p));
}
/**
* Creates one or more directories .
* For each of the series of paths , a directory will be created ,
* including any necessary parent directories .
* < p > Similar to the shell command : { @ code mkdir - p paths } .
*
* @ param paths the directories to be created
* @ throws IOException if an error occurred while creating the directories
*/
public void createDirectories(Path... paths) throws IOException {
if (paths.length == 0 )
throw new IllegalArgumentException("no directories specified" );
for (Path p : paths)
Files.createDirectories(p);
}
/**
* Deletes one or more files , awaiting confirmation that the files
* no longer exist . Any directories to be deleted must be empty .
* < p > Similar to the shell command : { @ code rm files } .
*
* @ param files the names of the files to be deleted
* @ throws IOException if an error occurred while deleting the files
*/
public void deleteFiles(String... files) throws IOException {
deleteFiles(List.of(files).stream().map(Paths::get).collect(Collectors.toList()));
}
/**
* Deletes one or more files , awaiting confirmation that the files
* no longer exist . Any directories to be deleted must be empty .
* < p > Similar to the shell command : { @ code rm files } .
*
* @ param paths the paths for the files to be deleted
* @ throws IOException if an error occurred while deleting the files
*/
public void deleteFiles(Path... paths) throws IOException {
deleteFiles(List.of(paths));
}
/**
* Deletes one or more files , awaiting confirmation that the files
* no longer exist . Any directories to be deleted must be empty .
* < p > Similar to the shell command : { @ code rm files } .
*
* @ param paths the paths for the files to be deleted
* @ throws IOException if an error occurred while deleting the files
*/
public void deleteFiles(List<Path> paths) throws IOException {
if (paths.isEmpty())
throw new IllegalArgumentException("no files specified" );
IOException ioe = null ;
for (Path path : paths) {
ioe = deleteFile(path, ioe);
}
if (ioe != null ) {
throw ioe;
}
ensureDeleted(paths);
}
/**
* Deletes all content of a directory ( but not the directory itself ) ,
* awaiting confirmation that the content has been deleted .
*
* @ param root the directory to be cleaned
* @ throws IOException if an error occurs while cleaning the directory
*/
public void cleanDirectory(Path root) throws IOException {
if (!Files.isDirectory(root)) {
throw new IOException(root + " is not a directory" );
}
Files.walkFileTree(root, new SimpleFileVisitor<>() {
private IOException ioe = null ;
// for each directory we visit, maintain a list of the files that we try to delete
private final Deque<List<Path>> dirFiles = new LinkedList<>();
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes a) {
ioe = deleteFile(file, ioe);
dirFiles.peekFirst().add(file);
return FileVisitResult.CONTINUE ;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes a) {
if (!dir.equals(root)) {
dirFiles.peekFirst().add(dir);
}
dirFiles.addFirst(new ArrayList<>());
return FileVisitResult.CONTINUE ;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
if (e != null ) {
throw e;
}
if (ioe != null ) {
throw ioe;
}
ensureDeleted(dirFiles.removeFirst());
if (!dir.equals(root)) {
ioe = deleteFile(dir, ioe);
}
return FileVisitResult.CONTINUE ;
}
});
}
/**
* Internal method to delete a file , using { @ code Files . delete } .
* It does not wait to confirm deletion , nor does it retry .
* If an exception occurs it is either returned or added to the set of
* suppressed exceptions for an earlier exception .
*
* @ param path the path for the file to be deleted
* @ param ioe the earlier exception , or null
* @ return the earlier exception or an exception that occurred while
* trying to delete the file
*/
private IOException deleteFile(Path path, IOException ioe) {
try {
Files.delete (path);
} catch (IOException e) {
if (ioe == null ) {
ioe = e;
} else {
ioe.addSuppressed(e);
}
}
return ioe;
}
/**
* Wait until it is confirmed that a set of files have been deleted .
*
* @ param paths the paths for the files to be deleted
* @ throws IOException if a file has not been deleted
*/
private void ensureDeleted(Collection<Path> paths)
throws IOException {
for (Path path : paths) {
ensureDeleted(path);
}
}
/**
* Wait until it is confirmed that a file has been deleted .
*
* @ param path the path for the file to be deleted
* @ throws IOException if problems occur while deleting the file
*/
private void ensureDeleted(Path path) throws IOException {
long startTime = System.currentTimeMillis();
do {
// Note: Files.notExists is not the same as !Files.exists
if (Files.notExists(path)) {
return ;
}
System.gc(); // allow finalizers and cleaners to run
try {
Thread .sleep(RETRY_DELETE_MILLIS);
} catch (InterruptedException e) {
throw new IOException("Interrupted while waiting for file to be deleted: " + path, e);
}
} while ((System.currentTimeMillis() - startTime) <= MAX_RETRY_DELETE_MILLIS);
throw new IOException("File not deleted: " + path);
}
private static final int RETRY_DELETE_MILLIS = isWindows() ? (int )(500 * timeoutFactor): 0 ;
private static final int MAX_RETRY_DELETE_MILLIS = isWindows() ? (int )(15 * 1000 * timeoutFactor) : 0 ;
/**
* Moves a file .
* If the given destination exists and is a directory , the file will be moved
* to that directory . Otherwise , the file will be moved to the destination ,
* possibly overwriting any existing file .
* < p > Similar to the shell " mv " command : { @ code mv from to } .
*
* @ param from the file to be moved
* @ param to where to move the file
* @ throws IOException if an error occurred while moving the file
*/
public void moveFile(String from, String to) throws IOException {
moveFile(Path.of(from), Path.of(to));
}
/**
* Moves a file .
* If the given destination exists and is a directory , the file will be moved
* to that directory . Otherwise , the file will be moved to the destination ,
* possibly overwriting any existing file .
* < p > Similar to the shell " mv " command : { @ code mv from to } .
*
* @ param from the file to be moved
* @ param to where to move the file
* @ throws IOException if an error occurred while moving the file
*/
public void moveFile(Path from, Path to) throws IOException {
if (Files.isDirectory(to)) {
to = to.resolve(from.getFileName());
} else {
Files.createDirectories(to.getParent());
}
Files.move(from, to, StandardCopyOption.REPLACE_EXISTING);
}
/**
* Reads the lines of a file .
* The file is read using the default character encoding .
*
* @ param path the file to be read
* @ return the lines of the file
* @ throws IOException if an error occurred while reading the file
*/
public List<String> readAllLines(String path) throws IOException {
return readAllLines(path, null );
}
/**
* Reads the lines of a file .
* The file is read using the default character encoding .
*
* @ param path the file to be read
* @ return the lines of the file
* @ throws IOException if an error occurred while reading the file
*/
public List<String> readAllLines(Path path) throws IOException {
return readAllLines(path, null );
}
/**
* Reads the lines of a file using the given encoding .
*
* @ param path the file to be read
* @ param encoding the encoding to be used to read the file
* @ return the lines of the file .
* @ throws IOException if an error occurred while reading the file
*/
public List<String> readAllLines(String path, String encoding) throws IOException {
return readAllLines(Path.of(path), encoding);
}
/**
* Reads the lines of a file using the given encoding .
*
* @ param path the file to be read
* @ param encoding the encoding to be used to read the file
* @ return the lines of the file
* @ throws IOException if an error occurred while reading the file
*/
public List<String> readAllLines(Path path, String encoding) throws IOException {
return Files.readAllLines(path, getCharset(encoding));
}
private Charset getCharset(String encoding) {
return (encoding == null ) ? Charset.defaultCharset() : Charset.forName(encoding);
}
/**
* Find . java files in one or more directories .
* < p > Similar to the shell " find " command : { @ code find paths - name \ * . java } .
*
* @ param paths the directories in which to search for . java files
* @ return the . java files found
* @ throws IOException if an error occurred while searching for files
*/
public Path[] findJavaFiles(Path... paths) throws IOException {
return findFiles(".java" , paths);
}
/**
* Find files matching the file extension , in one or more directories .
* < p > Similar to the shell " find " command : { @ code find paths - name \ * . ext } .
*
* @ param fileExtension the extension to search for
* @ param paths the directories in which to search for files
* @ return the files matching the file extension
* @ throws IOException if an error occurred while searching for files
*/
public Path[] findFiles(String fileExtension, Path... paths) throws IOException {
Set<Path> files = new TreeSet<>(); // use TreeSet to force a consistent order
for (Path p : paths) {
Files.walkFileTree(p, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.getFileName().toString().endsWith(fileExtension)) {
files.add(file);
}
return FileVisitResult.CONTINUE ;
}
});
}
return files.toArray(new Path[0 ]);
}
/**
* Writes a file containing the given content .
* Any necessary directories for the file will be created .
*
* @ param path where to write the file
* @ param content the content for the file
* @ throws IOException if an error occurred while writing the file
*/
public void writeFile(String path, String content) throws IOException {
writeFile(Path.of(path), content);
}
/**
* Writes a file containing the given content .
* Any necessary directories for the file will be created .
*
* @ param path where to write the file
* @ param content the content for the file
* @ throws IOException if an error occurred while writing the file
*/
public void writeFile(Path path, String content) throws IOException {
Path dir = path.getParent();
if (dir != null )
Files.createDirectories(dir);
try (BufferedWriter w = Files.newBufferedWriter(path)) {
w.write(content);
}
}
/**
* Writes one or more files containing Java source code .
* For each file to be written , the filename will be inferred from the
* given base directory , the package declaration ( if present ) and from the
* the name of the first class , interface or enum declared in the file .
* < p > For example , if the base directory is / my / dir / and the content
* contains " package p ; class C { } " , the file will be written to
* / my / dir / p / C . java .
* < p > Note : the content is analyzed using regular expressions ;
* errors can occur if any contents have initial comments that might trip
* up the analysis .
*
* @ param dir the base directory
* @ param contents the contents of the files to be written
* @ throws IOException if an error occurred while writing any of the files .
*/
public void writeJavaFiles(Path dir, String... contents) throws IOException {
if (contents.length == 0 )
throw new IllegalArgumentException("no content specified for any files" );
for (String c : contents) {
new JavaSource(c).write(dir);
}
}
/**
* Returns the path for the binary of a JDK tool within { @ link # testJDK } .
*
* @ param tool the name of the tool
* @ return the path of the tool
*/
public Path getJDKTool(String tool) {
return Path.of(testJDK, "bin" , tool);
}
/**
* Returns a string representing the contents of an { @ code Iterable } as a list .
*
* @ param < T > the type parameter of the { @ code Iterable }
* @ param items the iterable
* @ return the string
*/
<T> String toString(Iterable<T> items) {
return StreamSupport.stream(items.spliterator(), false )
.map(Objects::toString)
.collect(Collectors.joining("," , "[" , "]" ));
}
/**
* An in - memory Java source file .
* It is able to extract the file name from simple source text using
* regular expressions .
*/
public static class JavaSource extends SimpleJavaFileObject {
private final String source;
/**
* Creates a in - memory file object for Java source code .
*
* @ param className the name of the class
* @ param source the source text
*/
public JavaSource(String className, String source) {
super (URI.create(className), JavaFileObject.Kind.SOURCE);
this .source = source;
}
/**
* Creates a in - memory file object for Java source code .
* The name of the class will be inferred from the source code .
*
* @ param source the source text
*/
public JavaSource(String source) {
super (URI.create(getJavaFileNameFromSource(source)),
JavaFileObject.Kind.SOURCE);
this .source = source;
}
/**
* Writes the source code to a file in the current directory .
*
* @ throws IOException if there is a problem writing the file
*/
public void write() throws IOException {
write(currDir);
}
/**
* Writes the source code to a file in a specified directory .
*
* @ param dir the directory
* @ throws IOException if there is a problem writing the file
*/
public void write(Path dir) throws IOException {
Path file = dir.resolve(getJavaFileNameFromSource(source));
Files.createDirectories(file.getParent());
try (BufferedWriter out = Files.newBufferedWriter(file)) {
out.write(source.replace("\n" , lineSeparator));
}
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
private final static Pattern commentPattern =
Pattern.compile("(?s)(\\s+//.*?\n|/\\*.*?\\*/)");
private final static Pattern modulePattern =
Pattern.compile("module\\s+((?:\\w+\\.)*)" );
private final static Pattern packagePattern =
Pattern.compile("package\\s+(((?:\\w+\\.)*)\\w+)" );
private final static Pattern classPattern =
Pattern.compile("(?:public\\s+)?(?:class|enum|interface|record)\\s+((\\w|\\$)+)" );
/**
* Extracts the Java file name from the class declaration .
* This method is intended for simple files and uses regular expressions .
* Comments in the source are stripped before looking for the
* declarations from which the name is derived .
*/
static String getJavaFileNameFromSource(String source) {
StringBuilder sb = new StringBuilder();
Matcher matcher = commentPattern.matcher(source);
int start = 0 ;
while (matcher.find()) {
sb.append(source, start, matcher.start());
start = matcher.end();
}
sb.append(source.substring(start));
source = sb.toString();
String packageName = null ;
matcher = modulePattern.matcher(source);
if (matcher.find())
return "module-info.java" ;
matcher = packagePattern.matcher(source);
if (matcher.find()) {
packageName = matcher.group(1 ).replace("." , "/" );
validateName(packageName);
}
matcher = classPattern.matcher(source);
if (matcher.find()) {
String className = matcher.group(1 ) + ".java" ;
validateName(className);
return (packageName == null ) ? className : packageName + "/" + className;
} else if (packageName != null ) {
return packageName + "/package-info.java" ;
} else {
throw new Error("Could not extract the java class " +
"name from the provided source" );
}
}
}
/**
* Extracts the Java file name from the class declaration .
* This method is intended for simple files and uses regular expressions ,
* so comments matching the pattern can make the method fail .
*
* @ param source the source text
* @ return the Java file name inferred from the source
* @ deprecated This is a legacy method for compatibility with ToolBox v1 .
* Use { @ link JavaSource # getName JavaSource . getName } instead .
*/
@Deprecated
public static String getJavaFileNameFromSource(String source) {
return JavaSource.getJavaFileNameFromSource(source);
}
private static final Set<String> RESERVED_NAMES = Set.of(
"con" , "prn" , "aux" , "nul" ,
"com1" , "com2" , "com3" , "com4" , "com5" , "com6" , "com7" , "com8" , "com9" ,
"lpt1" , "lpt2" , "lpt3" , "lpt4" , "lpt5" , "lpt6" , "lpt7" , "lpt8" , "lpt9"
);
/**
* Validates if a given name is a valid file name
* or path name on known platforms .
*
* @ param name the name
* @ throws IllegalArgumentException if the name is a reserved name
*/
public static void validateName(String name) {
for (String part : name.split("[./\\\\]" )) {
if (RESERVED_NAMES.contains(part.toLowerCase(Locale.US))) {
throw new IllegalArgumentException("Name: " + name + " is" +
"a reserved name on Windows, " +
"and will not work!" );
}
}
}
public static class MemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> {
private interface Content {
byte [] getBytes();
String getString();
}
/**
* Maps binary class names to generated content .
*/
private final Map<Location, Map<String, Content>> files;
/**
* Constructs a memory file manager which stores output files in memory ,
* and delegates to a default file manager for input files .
*/
public MemoryFileManager() {
this (ToolProvider.getSystemJavaCompiler().getStandardFileManager(null , null , null ));
}
/**
* Constructs a memory file manager which stores output files in memory ,
* and delegates to a specified file manager for input files .
*
* @ param fileManager the file manager to be used for input files
*/
public MemoryFileManager(JavaFileManager fileManager) {
super (fileManager);
files = new HashMap<>();
}
@Override
public JavaFileObject getJavaFileForOutput(Location location,
String name,
JavaFileObject.Kind kind,
FileObject sibling)
{
return new MemoryFileObject(location, name, kind);
}
/**
* Returns the set of names of files that have been written to a given
* location .
*
* @ param location the location
* @ return the set of file names
*/
public Set<String> getFileNames(Location location) {
Map<String, Content> filesForLocation = files.get(location);
return (filesForLocation == null )
? Collections.emptySet() : filesForLocation.keySet();
}
/**
* Returns the content written to a file in a given location ,
* or null if no such file has been written .
*
* @ param location the location
* @ param name the name of the file
* @ return the content as an array of bytes
*/
public byte [] getFileBytes(Location location, String name) {
Content content = getFile(location, name);
return (content == null ) ? null : content.getBytes();
}
/**
* Returns the content written to a file in a given location ,
* or null if no such file has been written .
*
* @ param location the location
* @ param name the name of the file
* @ return the content as a string
*/
public String getFileString(Location location, String name) {
Content content = getFile(location, name);
return (content == null ) ? null : content.getString();
}
private Content getFile(Location location, String name) {
Map<String, Content> filesForLocation = files.get(location);
return (filesForLocation == null ) ? null : filesForLocation.get(name);
}
private void save(Location location, String name, Content content) {
files.computeIfAbsent(location, k -> new HashMap<>())
.put(name, content);
}
/**
* A writable file object stored in memory .
*/
private class MemoryFileObject extends SimpleJavaFileObject {
private final Location location;
private final String name;
/**
* Constructs a memory file object .
*
* @ param location the location in which to save the file object
* @ param name binary name of the class to be stored in this file object
* @ param kind the kind of file object
*/
MemoryFileObject(Location location, String name, JavaFileObject.Kind kind) {
super (URI.create("mfm:///" + name.replace('.','/') + kind.extension),
Kind.CLASS );
this .location = location;
this .name = name;
}
@Override
public OutputStream openOutputStream() {
return new FilterOutputStream(new ByteArrayOutputStream()) {
@Override
public void close() throws IOException {
out.close();
byte [] bytes = ((ByteArrayOutputStream) out).toByteArray();
save(location, name, new Content() {
@Override
public byte [] getBytes() {
return bytes;
}
@Override
public String getString() {
return new String(bytes);
}
});
}
};
}
@Override
public Writer openWriter() {
return new FilterWriter(new StringWriter()) {
@Override
public void close() throws IOException {
out.close();
String text = out.toString();
save(location, name, new Content() {
@Override
public byte [] getBytes() {
return text.getBytes();
}
@Override
public String getString() {
return text;
}
});
}
};
}
}
}
}
Messung V0.5 in Prozent C=90 H=88 G=88
¤ Dauer der Verarbeitung: 0.29 Sekunden
¤
*© Formatika GbR, Deutschland