20 package net.sf.gridarta.model.validation.checks;
22 import java.awt.Point;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.InputStreamReader;
27 import java.io.OutputStream;
28 import java.io.OutputStreamWriter;
29 import java.io.Writer;
30 import java.util.concurrent.Semaphore;
31 import java.util.concurrent.TimeUnit;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
55 public class MapCheckerScriptChecker<G
extends GameObject<G, A, R>, A extends
MapArchObject<A>, R extends
Archetype<G, A, R>> extends
AbstractValidator<G, A, R> implements
MapValidator<G, A, R> {
131 super(validatorPreferences);
133 this.args = args.clone();
135 if (this.args.length < 1) {
136 throw new IllegalArgumentException(
"no script to execute");
139 boolean found =
false;
140 for (
int i = 1; i < this.args.length; i++) {
141 if (this.args[i].contains(MAP_PLACEHOLDER)) {
147 throw new IllegalArgumentException(
"the script to execute doesn't receive the map to check (" + MAP_PLACEHOLDER +
")");
153 final String[] command =
getCommand(mapModel, errorCollector);
154 if (command == null) {
157 final Process process;
159 process = Runtime.getRuntime().exec(command);
160 }
catch (
final IOException ex) {
164 @Nullable
final String output;
167 process.getOutputStream().close();
168 }
catch (
final IOException ex) {
173 output =
runProcess(process, errorCollector, mapModel, command[0]);
177 if (output != null) {
178 parseOutput(output, errorCollector, mapModel, command[0]);
192 for (
final String line : lines) {
193 if (!line.isEmpty()) {
194 final Matcher matcher = PATTERN_MAP_SQUARE_MESSAGE.matcher(line);
195 if (matcher.matches()) {
199 x = Integer.parseInt(matcher.group(1));
200 y = Integer.parseInt(matcher.group(2));
201 }
catch (
final NumberFormatException ignored) {
205 final String message = matcher.group(3);
209 }
catch (
final IndexOutOfBoundsException ignored) {
231 @Nullable String output;
232 final InputStreamReader stdoutReader =
new InputStreamReader(process.getInputStream());
235 final InputStreamReader stderrReader =
new InputStreamReader(process.getErrorStream());
242 if (!
waitForTermination(process, errorCollector, mapModel, command, stdout, stderr)) {
253 stderrReader.close();
254 }
catch (
final IOException ex) {
258 final String stdoutFailure = stdout.
getFailure();
259 if (stdoutFailure != null) {
266 stdoutReader.close();
267 }
catch (
final IOException ex) {
285 final Semaphore sem =
new Semaphore(0);
286 final int[] tmp = { -1, };
287 final Runnable runnable =
new Runnable() {
295 tmp[0] = process.waitFor();
296 }
catch (
final InterruptedException ignored) {
297 Thread.currentThread().interrupt();
305 final Thread thread =
new Thread(runnable);
308 if (!sem.tryAcquire(MAX_EXEC_TIME, TimeUnit.MILLISECONDS)) {
312 }
catch (
final InterruptedException ignored) {
321 final String stderrFailure = stderr.getFailure();
322 if (stderrFailure != null) {
326 final String stderrOutput = stderr.getOutput();
327 if (!stderrOutput.isEmpty()) {
343 if (tmpFile == null) {
345 tmpFile = File.createTempFile(
"gridarta", null);
346 }
catch (
final IOException ex) {
350 assert tmpFile != null;
351 tmpFile.deleteOnExit();
353 assert tmpFile != null;
354 final String mapPath = tmpFile.getPath();
355 final String realMapPath = mapModel.getMapFile() != null ? mapModel.getMapFile().toString() :
"";
357 try (OutputStream outputStream =
new FileOutputStream(tmpFile)) {
362 }
catch (
final IOException ex) {
367 final boolean isWindows = System.getProperty(
"os.name").contains(
"Windows");
368 final String[] result;
372 if (args[0].toLowerCase().endsWith(
".py")) {
373 result =
new String[args.length + 1];
376 command = commandFinder2.
getCommand(
"python.exe");
377 }
catch (
final IOException ex) {
378 command =
"C:" + File.separator +
"python34" + File.separator +
"python.exe";
379 if (!
new File(command).exists()) {
383 result[index++] =
"\"" + command +
"\"";
385 result =
new String[args.length];
387 result[index++] =
"\"" + commandFinder1.
getCommand(args[0]) +
"\"";
389 result =
new String[args.length];
390 result[index++] = commandFinder1.
getCommand(args[0]);
392 }
catch (
final IOException ex) {
396 final String mapPathQuoted = Matcher.quoteReplacement(mapPath);
397 final String realMapPathQuoted = Matcher.quoteReplacement(realMapPath);
398 for (
int i = 1; i < args.length; i++) {
399 final String tmp = args[i].replaceAll(QUOTED_MAP_PLACEHOLDER, mapPathQuoted).replaceAll(QUOTED_REAL_MAP_PLACEHOLDER, realMapPathQuoted);
400 result[index++] = isWindows ?
"\"" + tmp +
"\"" : tmp;
426 private String
getCommand(@NotNull
final String commandName)
throws IOException {
428 if (existingCommand != null && existingCommand.exists()) {
429 return existingCommand.getPath();
433 cachedCommand = command;
434 return command.getPath();
static File findPathFile(@NotNull final String name)
Searches for.
static final Pattern PATTERN_MAP_SQUARE_MESSAGE
The Pattern for matching per map square messages.
Utility class for string manipulation.
final MapWriter< G, A, R > mapWriter
The MapWriter for saving temporary map files.
String getCommand(@NotNull final String commandName)
Returns the command to execute.
MapSquare< G, A, R > getMapSquare()
A MapModel reflects the data of a map.
This is the base class for validators.
Reading and writing of maps, handling of paths.
A MapValidationError that indicates that the map checker script could not be found.
Interface for classes that write map files.
This package contains the framework for validating maps.
static final String MAP_ENCODING
Encoding to use for maps and other data.
Interface for Map Validators.
A SquareValidationError that represents a per map square error of the MapCheckerScriptChecker.
static final Pattern PATTERN_NEWLINE
The pattern that matches a single newline ("\n").
File cachedCommand
The command to execute.
File tmpFile
The temp file for saving maps to be checked.
static final String MAP_PLACEHOLDER
The placeholder in the command's arguments for the map to check.
Base package of all Gridarta classes.
void start()
Starts reading.
static final String QUOTED_REAL_MAP_PLACEHOLDER
The quoted placeholder in the command's arguments for the real map path.
static final int MAX_EXEC_TIME
The maximum execution time of the checker script in milliseconds.
Reflects a game object (object on a map).
Copies a Reader into a String.
Utility-class for Gridarta's I/O.
String [] getCommand(@NotNull final MapModel< G, A, R > mapModel, @NotNull final ErrorCollector< G, A, R > errorCollector)
Returns the command to execute.
Executes a script to check a map.
static final String QUOTED_MAP_PLACEHOLDER
The quoted.
A MapValidationError that indicates that the map validator script could not be executed.
String getOutput()
Returns the reader's output.
Configuration parameters for Validators.
GameObjects are the objects based on Archetypes found on maps.
final CommandFinder commandFinder1
The CommandFinder for the script to execute.
MapCheckerScriptChecker(@NotNull final ValidatorPreferences validatorPreferences, @NotNull final MapWriter< G, A, R > mapWriter, @NotNull final String[] args)
Creates a new instance.
A ValidationError that reports I/O errors related to MapCheckerScriptChecker.
static final String REAL_MAP_PLACEHOLDER
The placeholder in the command's arguments for the real map path.
final ValidatorPreferences validatorPreferences
The ValidatorPreferences to use.
boolean waitForTermination(@NotNull final Process process, @NotNull final ErrorCollector< G, A, R > errorCollector, @NotNull final MapModel< G, A, R > mapModel, @NotNull final String command, @NotNull final CopyReader stdout, @NotNull final CopyReader stderr)
Waits for a Process to terminate.
static final Pattern PATTERN_END_OF_LINE
The pattern to match end of line characters separating lines.
void validateMap(@NotNull final MapModel< G, A, R > mapModel, @NotNull final ErrorCollector< G, A, R > errorCollector)
Validate a map.
An interface for classes that collect errors.
void stop()
Stops reading.
void encodeMapFile(@NotNull MapModel< G, A, R > mapModel, @NotNull Writer writer)
Write the whole map-data into a file.
void parseOutput(@NotNull final CharSequence output, @NotNull final ErrorCollector< G, A, R > errorCollector, @NotNull final MapModel< G, A, R > mapModel, @NotNull final String command)
Parses output of the executed script.
String getFailure()
Returns the failure reason.
final CommandFinder commandFinder2
The CommandFinder for the script interpreter.
Searches for commands in the PATH environment variable.
A MapValidationError that represents a per map error of the MapCheckerScriptChecker.
String runProcess(@NotNull final Process process, @NotNull final ErrorCollector< G, A, R > errorCollector, @NotNull final MapModel< G, A, R > mapModel, @NotNull final String command)
Waits for a Process to terminate and returns its output.
Interface for MapArchObjects.
final String [] args
The command to execute and the arguments to pass.