/** * */ package xhail.core; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.ProcessBuilder.Redirect; import java.nio.file.Files; import java.nio.file.Path; import java.util.AbstractMap.SimpleEntry; import java.util.Collection; import java.util.Collections; import java.util.Map; import xhail.core.entities.Grounding; import xhail.core.entities.Problem; import xhail.core.entities.Solvable; import xhail.core.entities.Values; import xhail.core.parser.Acquirer; /** * @author stefano * */ public class Dialler { public static class Builder implements Buildable<Dialler> { private Config config; private Path errors = null; private Path middle = null; private Solvable solvable; private Path source = null; private Path target = null; private Values values; public Builder(Config config, Grounding grounding) { if (null == config) throw new IllegalArgumentException("Illegal 'config' argument in Dialler.Builder(Config, Grounding): " + config); if (null == grounding) throw new IllegalArgumentException("Illegal 'grounding' argument in Dialler.Builder(Config, Grounding): " + grounding); this.config = config; this.solvable = grounding; } public Builder(Config config, Grounding grounding, Values values) { if (null == config) throw new IllegalArgumentException("Illegal 'config' argument in Dialler.Builder(Config, Grounding, Values): " + config); if (null == grounding) throw new IllegalArgumentException("Illegal 'grounding' argument in Dialler.Builder(Config, Grounding, Values): " + grounding); if (null == values) throw new IllegalArgumentException("Illegal 'values' argument in Dialler.Builder(Config, Grounding, Values): " + values); this.config = config; this.solvable = grounding; this.values = values; } public Builder(Config config, Problem problem) { if (null == config) throw new IllegalArgumentException("Illegal 'config' argument in Dialler.Builder(Config, Problem): " + config); if (null == problem) throw new IllegalArgumentException("Illegal 'problem' argument in Dialler.Builder(Config, Problem): " + problem); this.config = config; this.solvable = problem; } @Override public Dialler build() { try { this.source = Files.createTempFile("xhail", ".tmp"); this.source.toFile().deleteOnExit(); this.middle = Files.createTempFile("xhail", ".tmp"); this.middle.toFile().deleteOnExit(); this.target = Files.createTempFile("xhail", ".tmp"); this.target.toFile().deleteOnExit(); this.errors = Files.createTempFile("xhail", ".tmp"); this.errors.toFile().deleteOnExit(); } catch (Exception e) { Logger.error("cannot send data to processes"); } return new Dialler(this); } } private static int calls = 0; private static final String ERROR = "ERROR: "; private static final String WARNING = "% warning: "; public static final int calls() { return calls; } private final String[] clasp; private final Path errors; private final String[] gringo; private final Path middle; private final boolean mute; private final Solvable solvable; private final Path source; private final Path target; private final boolean output; private final boolean debug; private Dialler(Builder builder) { if (null == builder) throw new IllegalArgumentException("Illegal 'builder' argument in Dialler(Byprocess.Builder): " + builder); this.clasp = new String[null == builder.values ? 4 : 5]; this.clasp[0] = builder.config.getClasp().toAbsolutePath().toString(); this.clasp[1] = builder.middle.toAbsolutePath().toString(); this.clasp[2] = "--verbose=0"; this.clasp[3] = "--opt-mode=optN"; if (null != builder.values) this.clasp[4] = "--opt-bound=" + builder.values.toString(); this.debug = builder.config.isDebug(); this.errors = builder.errors.toAbsolutePath(); this.gringo = new String[2]; this.gringo[0] = builder.config.getGringo().toAbsolutePath().toString(); this.gringo[1] = builder.source.toAbsolutePath().toString(); this.middle = builder.middle.toAbsolutePath(); this.mute = builder.config.isMute(); this.output = builder.config.isOutput(); this.solvable = builder.solvable; this.source = builder.source.toAbsolutePath(); this.target = builder.target.toAbsolutePath(); } public Map.Entry<Values, Collection<Collection<String>>> execute(int iter) { if (iter < 0) throw new IllegalArgumentException("Illegal 'iter' argument in Dialler.execute(int): " + iter); calls += 1; try { solvable.save(iter, Files.newOutputStream(source)); try { if (debug) Logger.message(String.format("*** Info (%s): calling '%s'", Logger.SIGNATURE, String.join(" ", this.gringo))); Process gringo = new ProcessBuilder(this.gringo) // .redirectError(Redirect.to(errors.toFile())).redirectOutput(Redirect.to(middle.toFile())).start(); gringo.waitFor(); handle(Files.newInputStream(errors)); try { if (debug) Logger.message(String.format("*** Info (%s): calling '%s'", Logger.SIGNATURE, String.join(" ", this.clasp))); Process clasp = new ProcessBuilder(this.clasp).redirectOutput(Redirect.to(target.toFile())).start(); clasp.waitFor(); try { return Acquirer.from(Files.newInputStream(target)).parse(); } catch (IOException e) { if (!output) Logger.error("cannot read from 'clasp' process"); } } catch (IOException e) { if (!output) Logger.error("cannot launch 'clasp' process"); } catch (InterruptedException e) { if (!output) Logger.error("'clasp' process was interrupted"); } } catch (IOException e) { if (!output) Logger.error("cannot launch 'gringo' process"); } catch (InterruptedException e) { if (!output) Logger.error("'gringo' process was interrupted"); } } catch (IOException e) { if (!output) Logger.error("cannot write to 'gringo' process"); } return new SimpleEntry<Values, Collection<Collection<String>>>(null, Collections.emptySet()); } private void handle(InputStream stream) { if (null == stream) throw new IllegalArgumentException("Illegal 'stream' argument in Dialler.handle(InputStream): " + stream); String line, message = ""; try { BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); while (null != (line = reader.readLine())) { line = line.trim(); if (!line.isEmpty()) { if (!message.isEmpty()) message += "\n " + line; else if (line.startsWith(ERROR)) message = line.substring(ERROR.length()); else if (line.startsWith(WARNING)) { String content = line.substring(WARNING.length()); if (!"bad_solution/0 is never defined".equals(content) && !"number_abduced/2 is never defined".equals(content)) Logger.warning(mute, content); } else System.err.println(line); } } reader.close(); } catch (IOException e) { Logger.error("cannot read from child process' 'stderr'"); } if (!message.isEmpty()) Logger.error(message); } }