diff --git a/src/main/java/cryptator/Cryptagen.java b/src/main/java/cryptator/Cryptagen.java index 4aa0aef..a6e979c 100644 --- a/src/main/java/cryptator/Cryptagen.java +++ b/src/main/java/cryptator/Cryptagen.java @@ -16,13 +16,12 @@ import java.util.logging.Level; import java.util.logging.Logger; -import cryptator.cmd.AbstractOptionsParser; import cryptator.cmd.CryptaBiConsumer; +import cryptator.cmd.OptionsParserWithLog; import cryptator.cmd.WordArray; import cryptator.config.CryptagenConfig; import cryptator.gen.CryptaListGenerator; import cryptator.solver.CryptaModelException; -import cryptator.solver.CryptaSolver; public final class Cryptagen { @@ -44,6 +43,8 @@ public static int doMain(final String[] args) { final WordArray words = buildWords(config.getArguments(), config); if (words != null) { return generate(words, config); + } else { + LOGGER.log(Level.WARNING, "Empty word list."); } } JULogUtil.flushLogs(); @@ -96,25 +97,14 @@ private static WordArray buildWords(final List arguments, final Cryptage } } - private static final class CryptagenOptionsParser extends AbstractOptionsParser { + private static final class CryptagenOptionsParser extends OptionsParserWithLog { - protected CryptagenOptionsParser() { - super(Cryptagen.class, new CryptagenConfig()); - } + private static final String ARG_NAME = "WORDS..."; - @Override - protected void configureLoggers() { - if (config.isVerbose()) { - JULogUtil.configureLoggers(Level.ALL); - } else { - JULogUtil.setLevel(Level.WARNING, CryptaSolver.LOGGER); - } + protected CryptagenOptionsParser() { + super(Cryptagen.class, new CryptagenConfig(), ARG_NAME, JULogUtil.getDefaultLogManager()); } - @Override - public String getArgumentName() { - return "WORDS..."; - } } private static CryptaBiConsumer buildBiConsumer(final CryptagenConfig config) { @@ -127,6 +117,7 @@ private static CryptaBiConsumer buildBiConsumer(final CryptagenConfig config) { } private static int generate(final WordArray words, final CryptagenConfig config) { + LOGGER.log(Level.CONFIG, () -> "Word List Features:\n" + words.toDimacs()); final CryptaListGenerator gen = new CryptaListGenerator(words, config, LOGGER); final CryptaBiConsumer cons = buildBiConsumer(config); try { diff --git a/src/main/java/cryptator/Cryptamancer.java b/src/main/java/cryptator/Cryptamancer.java index 94c8127..c15386c 100644 --- a/src/main/java/cryptator/Cryptamancer.java +++ b/src/main/java/cryptator/Cryptamancer.java @@ -12,9 +12,9 @@ import java.util.logging.Level; import java.util.logging.Logger; -import cryptator.cmd.AbstractOptionsParser; +import cryptator.cmd.OptionsParserWithLog; import cryptator.config.CryptaConfig; -import cryptator.config.CryptamancerConfig; +import cryptator.config.CryptaLogConfig; import cryptator.game.CryptaGameDecision; import cryptator.game.CryptaGameEngine; import cryptator.game.CryptaGameException; @@ -29,22 +29,12 @@ public final class Cryptamancer { public static final Logger LOGGER = Logger.getLogger(Cryptamancer.class.getName()); - private static class CryptamancerOptionsParser extends AbstractOptionsParser { + private static class CryptamancerOptionsParser extends OptionsParserWithLog { - protected CryptamancerOptionsParser() { - super(Cryptamancer.class, new CryptamancerConfig()); - } - - @Override - protected void configureLoggers() { - if (config.isVerbose()) { - JULogUtil.setLevel(Level.CONFIG, getLogger(), CryptaGameEngine.LOGGER); - } - } + private static final String ARG_NAME = "CRYPTARITHM"; - @Override - public String getArgumentName() { - return "CRYPTARITHM"; + protected CryptamancerOptionsParser() { + super(Cryptamancer.class, new CryptaLogConfig(), ARG_NAME, JULogUtil.getDefaultLogManager()); } @Override @@ -57,7 +47,7 @@ private Cryptamancer() { super(); } - public static ICryptaNode parseCryptarithm(final CryptamancerConfig config) { + public static ICryptaNode parseCryptarithm(final CryptaLogConfig config) { final String cryptarithm = config.getArguments().get(0); try { return Cryptator.parseCryptarithm(cryptarithm, new CryptaParserWrapper(), LOGGER); @@ -111,7 +101,7 @@ public static void main(final String[] args) throws Exception { if (!optparser.parseOptions(args)) { return; } - final CryptamancerConfig config = optparser.getConfig(); + final CryptaLogConfig config = optparser.getConfig(); final ICryptaNode node = parseCryptarithm(config); if (node == null) { diff --git a/src/main/java/cryptator/Cryptator.java b/src/main/java/cryptator/Cryptator.java index 3f744be..0b6b337 100644 --- a/src/main/java/cryptator/Cryptator.java +++ b/src/main/java/cryptator/Cryptator.java @@ -11,8 +11,8 @@ import java.util.logging.Level; import java.util.logging.Logger; -import cryptator.cmd.AbstractOptionsParser; import cryptator.cmd.CryptaBiConsumer; +import cryptator.cmd.OptionsParserWithLog; import cryptator.config.CryptatorConfig; import cryptator.parser.CryptaParserException; import cryptator.parser.CryptaParserWrapper; @@ -54,22 +54,12 @@ public static int doMain(final String[] args) { return exitCode; } - private static class CryptatorOptionsParser extends AbstractOptionsParser { + private static class CryptatorOptionsParser extends OptionsParserWithLog { - CryptatorOptionsParser() { - super(Cryptator.class, new CryptatorConfig()); - } - - @Override - public String getArgumentName() { - return "CRYPTARITHMS..."; - } + private static final String ARG_NAME = "CRYPTARITHMS..."; - @Override - protected void configureLoggers() { - if (config.isVerbose()) { - JULogUtil.configureLoggers(Level.ALL); - } + CryptatorOptionsParser() { + super(Cryptator.class, new CryptatorConfig(), ARG_NAME, JULogUtil.getDefaultLogManager()); } } diff --git a/src/main/java/cryptator/JULogUtil.java b/src/main/java/cryptator/JULogUtil.java index 9828fa8..de13780 100644 --- a/src/main/java/cryptator/JULogUtil.java +++ b/src/main/java/cryptator/JULogUtil.java @@ -18,6 +18,7 @@ import cryptator.game.CryptaGameEngine; import cryptator.solver.CryptaSolver; +import cryptator.specs.ICryptaLogManager; public final class JULogUtil { @@ -62,7 +63,7 @@ public static void setLevel(final Level level, final Logger... loggers) { } public static void flushLogs(final Logger logger) { - logger.log(Level.FINE, "Flush logger {0}", logger.getName()); + logger.log(Level.FINEST, "Flush logger {0}", logger.getName()); for (Handler handler : logger.getHandlers()) { handler.flush(); } @@ -78,6 +79,33 @@ public static void flushLogs() { flushLogs(manager.getLogger(name)); } } + } + + public static final ICryptaLogManager DEFAULT_LOG_MANAGER = new DefaultLogManager(); + + public static ICryptaLogManager getDefaultLogManager() { + return DEFAULT_LOG_MANAGER; + } + + private static class DefaultLogManager implements ICryptaLogManager { + + @Override + public void setQuiet() { + ICryptaLogManager.super.setQuiet(); + JULogUtil.setLevel(Level.INFO, Cryptagen.LOGGER, Cryptator.LOGGER); + } + + @Override + public void setNormal() { + ICryptaLogManager.super.setNormal(); + JULogUtil.setLevel(Level.CONFIG, Cryptagen.LOGGER); + } + + @Override + public void setVerbose() { + ICryptaLogManager.super.setVerbose(); + JULogUtil.setLevel(Level.FINE, Cryptagen.LOGGER); + } } diff --git a/src/main/java/cryptator/cmd/AbstractOptionsParser.java b/src/main/java/cryptator/cmd/AbstractOptionsParser.java index 9f1d377..b3cbf0a 100644 --- a/src/main/java/cryptator/cmd/AbstractOptionsParser.java +++ b/src/main/java/cryptator/cmd/AbstractOptionsParser.java @@ -9,6 +9,7 @@ package cryptator.cmd; import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; import java.util.logging.Level; import java.util.logging.Logger; @@ -24,21 +25,26 @@ public abstract class AbstractOptionsParser { protected final E config; - protected AbstractOptionsParser(final Class mainClass, final E config) { + private final String argumentName; + + protected AbstractOptionsParser(final Class mainClass, final E config, final String argumentName) { super(); this.mainClass = mainClass; this.config = config; + this.argumentName = argumentName; } public final Logger getLogger() { return Logger.getLogger(mainClass.getName()); } - protected final String getCommandName() { + private final String getCommandName() { return mainClass.getName(); } - protected abstract String getArgumentName(); + private final String getArgumentName() { + return argumentName; + } protected abstract void configureLoggers(); @@ -69,7 +75,8 @@ public final boolean parseOptions(final String[] args) { configureLoggers(); if (checkConfiguration()) { if (checkArguments()) { - getLogger().log(Level.CONFIG, "Parse options [OK]\n{0}", config); + getLogger().log(Level.CONFIG, "Parse options [OK]"); + getLogger().log(Level.FINE, "Configuration:\n{0}", config); return true; } else { getLogger().log(Level.SEVERE, "Parse arguments [FAIL]\n{0}", config.getArguments()); @@ -83,25 +90,35 @@ public final boolean parseOptions(final String[] args) { } if (getLogger().isLoggable(Level.INFO)) { - getLogger().info(buildHelpMessage(parser)); + getLogger().info(buildHelpMessage(parser, OptionHandlerFilter.PUBLIC)); } return false; } - private String buildHelpMessage(CmdLineParser parser) { - StringBuilder b = new StringBuilder(); - b.append(" Help:\n"); - b.append("java ").append(getCommandName()).append(" [options...] ").append(getArgumentName()).append("\n"); + private String printUsage(CmdLineParser parser, OptionHandlerFilter filter) { final ByteArrayOutputStream os = new ByteArrayOutputStream(); - parser.printUsage(os); - b.append(os.toString()); - b.append("\nExamples:"); - b.append("\njava ").append(getCommandName()).append(" ") - .append(parser.printExample(OptionHandlerFilter.REQUIRED)).append(" ").append(getArgumentName()); + parser.printUsage(new OutputStreamWriter(os), null, filter); + return os.toString(); + } + + private String printExample(String options) { + return "java " + getCommandName() + " " + options + " " + getArgumentName(); + } + + private String printExample(CmdLineParser parser, OptionHandlerFilter filter) { + return printExample(parser.printExample(filter)); + } - b.append("\njava ").append(getCommandName()).append(" ").append(parser.printExample(OptionHandlerFilter.ALL)) - .append(" ").append(getArgumentName()); + private String buildHelpMessage(CmdLineParser parser, OptionHandlerFilter filter) { + StringBuilder b = new StringBuilder(); + b.append(" Help message:\n"); + b.append(printExample("[options...]")).append("\n"); + b.append(printUsage(parser, filter)); + b.append("\nExamples:"); + b.append("\n").append(printExample(parser, OptionHandlerFilter.REQUIRED)); + b.append("\n").append(printExample(parser, filter)); return b.toString(); } + } diff --git a/src/main/java/cryptator/cmd/CryptaBiConsumer.java b/src/main/java/cryptator/cmd/CryptaBiConsumer.java index 20accb3..64d24f0 100644 --- a/src/main/java/cryptator/cmd/CryptaBiConsumer.java +++ b/src/main/java/cryptator/cmd/CryptaBiConsumer.java @@ -133,7 +133,7 @@ private class SolutionChecker implements BiConsumer extends AbstractOptionsParser { + + private final ICryptaLogManager logManager; + + public OptionsParserWithLog(Class mainClass, E config, String argumentName, ICryptaLogManager logManager) { + super(mainClass, config, argumentName); + this.logManager = logManager; + } + + @Override + protected void configureLoggers() { + config.getVerbosity().applyTo(logManager); + } + +} diff --git a/src/main/java/cryptator/cmd/Verbosity.java b/src/main/java/cryptator/cmd/Verbosity.java new file mode 100644 index 0000000..b664466 --- /dev/null +++ b/src/main/java/cryptator/cmd/Verbosity.java @@ -0,0 +1,37 @@ +/** + * This file is part of cryptator, https://github.com/arnaud-m/cryptator + * + * Copyright (c) 2022, Université Côte d'Azur. All rights reserved. + * + * Licensed under the BSD 3-clause license. + * See LICENSE file in the project root for full license information. + */ +package cryptator.cmd; + +import cryptator.specs.ICryptaLogManager; + +public enum Verbosity { + SILENT, QUIET, NORMAL, VERBOSE, VERY_VERBOSE, DEBUG; + + public void applyTo(ICryptaLogManager manager) { + setVerbosity(manager, this); + } + + public static void setVerbosity(ICryptaLogManager manager, Verbosity verbosity) { + if (verbosity != null && manager != null) { + if (verbosity.equals(SILENT)) { + manager.setSilent(); + } else if (verbosity.equals(QUIET)) { + manager.setQuiet(); + } else if (verbosity.equals(NORMAL)) { + manager.setNormal(); + } else if (verbosity.equals(VERBOSE)) { + manager.setVerbose(); + } else if (verbosity.equals(VERY_VERBOSE)) { + manager.setVeryVerbose(); + } else if (verbosity.equals(DEBUG)) { + manager.setDebug(); + } + } + } +} diff --git a/src/main/java/cryptator/cmd/WordArray.java b/src/main/java/cryptator/cmd/WordArray.java index 5bd69e2..da0b142 100644 --- a/src/main/java/cryptator/cmd/WordArray.java +++ b/src/main/java/cryptator/cmd/WordArray.java @@ -70,6 +70,11 @@ public final int getUB() { return ub; } + public String toDimacs() { + return "c WORDS " + words.length + "\nc RIGHT_MEMBER " + (hasRightMember() ? "FIXED" : "FREE") + + "\nc DOUBLY_TRUE " + (isDoublyTrue() ? lb + "-" + ub : "NO"); + } + @Override public String toString() { return "WordArray [words=" + Arrays.toString(words) + ", rightMember=" + rightMember + ", lb=" + lb + ", ub=" diff --git a/src/main/java/cryptator/config/CryptaCmdConfig.java b/src/main/java/cryptator/config/CryptaCmdConfig.java index c58f8c5..6439d1e 100644 --- a/src/main/java/cryptator/config/CryptaCmdConfig.java +++ b/src/main/java/cryptator/config/CryptaCmdConfig.java @@ -11,7 +11,7 @@ import org.kohsuke.args4j.Option; import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler; -public class CryptaCmdConfig extends CryptaConfig { +public class CryptaCmdConfig extends CryptaLogConfig { @Option(name = "-c", handler = ExplicitBooleanOptionHandler.class, usage = "check solutions by evaluation") private boolean checkSolution; @@ -19,9 +19,6 @@ public class CryptaCmdConfig extends CryptaConfig { @Option(name = "-g", handler = ExplicitBooleanOptionHandler.class, usage = "export solutions to graphviz format") private boolean exportGraphiz; - @Option(name = "-v", handler = ExplicitBooleanOptionHandler.class, usage = "increase the verbosity of the program") - private boolean verbose; - public final boolean isExportGraphiz() { return exportGraphiz; } @@ -30,8 +27,4 @@ public final boolean isCheckSolution() { return checkSolution; } - public final boolean isVerbose() { - return verbose; - } - } diff --git a/src/main/java/cryptator/config/CryptamancerConfig.java b/src/main/java/cryptator/config/CryptaLogConfig.java similarity index 51% rename from src/main/java/cryptator/config/CryptamancerConfig.java rename to src/main/java/cryptator/config/CryptaLogConfig.java index e4327a4..c99a73c 100644 --- a/src/main/java/cryptator/config/CryptamancerConfig.java +++ b/src/main/java/cryptator/config/CryptaLogConfig.java @@ -9,15 +9,16 @@ package cryptator.config; import org.kohsuke.args4j.Option; -import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler; -public class CryptamancerConfig extends CryptaConfig { +import cryptator.cmd.Verbosity; - @Option(name = "-v", handler = ExplicitBooleanOptionHandler.class, usage = "increase the verbosity of the program") - private boolean verbose; +public class CryptaLogConfig extends CryptaConfig { - public final boolean isVerbose() { - return verbose; + @Option(name = "-v", usage = "increase the verbosity of the program") + private Verbosity verbosity = Verbosity.NORMAL; + + public final Verbosity getVerbosity() { + return verbosity; } } diff --git a/src/main/java/cryptator/gen/CryptaListGenerator.java b/src/main/java/cryptator/gen/CryptaListGenerator.java index f15381e..04d530c 100644 --- a/src/main/java/cryptator/gen/CryptaListGenerator.java +++ b/src/main/java/cryptator/gen/CryptaListGenerator.java @@ -36,8 +36,6 @@ public class CryptaListGenerator implements ICryptaGenerator { - private static final int TICK = 1000; - private final WordArray words; private final CryptagenConfig config; @@ -85,13 +83,8 @@ private Consumer buildConsumer(final CryptaGenModel gen, private void sequentialSolve(final CryptaGenModel gen, final Consumer cons) { final Solver s = gen.getModel().getSolver(); - int cpt = 1; while (s.solve()) { - if ((cpt % TICK) == 0) { - logger.log(Level.INFO, "{0}K tentatives", cpt / TICK); - } cons.accept(gen.recordCryptarithm()); - cpt++; } } @@ -140,9 +133,9 @@ private class LogConsumer implements Consumer { @Override public void accept(final ICryptaNode t) { - if (logger.isLoggable(Level.CONFIG)) { - logger.log(Level.CONFIG, "Candidate cryptarithm:\n{0}", TreeUtils.writeInorder(t)); - clog.logOnSolution(solution); + clog.logOnSolution(solution); + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "Candidate cryptarithm:\n{0}", TreeUtils.writeInorder(t)); } } diff --git a/src/main/java/cryptator/solver/CryptaSolver.java b/src/main/java/cryptator/solver/CryptaSolver.java index 7f42b08..a29c8f5 100644 --- a/src/main/java/cryptator/solver/CryptaSolver.java +++ b/src/main/java/cryptator/solver/CryptaSolver.java @@ -16,11 +16,11 @@ import cryptator.choco.ChocoLogger; import cryptator.config.CryptaConfig; +import cryptator.gen.TransformWord; import cryptator.specs.ICryptaModeler; import cryptator.specs.ICryptaNode; import cryptator.specs.ICryptaSolution; import cryptator.specs.ICryptaSolver; -import cryptator.tree.CryptaFeatures; import cryptator.tree.TreeUtils; public final class CryptaSolver implements ICryptaSolver { @@ -73,15 +73,16 @@ public void unsetBignum() { } private static void logOnCryptarithm(final ICryptaNode cryptarithm) { - if (LOGGER.isLoggable(Level.CONFIG)) { - final CryptaFeatures feat = TreeUtils.computeFeatures(cryptarithm); - LOGGER.log(Level.CONFIG, "Declare instance:\ni {0}\nc POST_ORDER {1}", - new Object[] {feat.buildInstanceName(), TreeUtils.writePostorder(cryptarithm)}); - LOGGER.log(Level.CONFIG, "Cryptarithm features:\n{0}", feat); + if (LOGGER.isLoggable(Level.INFO)) { + LOGGER.log(Level.INFO, "Declare instance:\ni {0}", + TransformWord.removeWhitespaces(TreeUtils.writeInorder(cryptarithm))); + if (LOGGER.isLoggable(Level.CONFIG)) { + LOGGER.log(Level.CONFIG, "Cryptarithm features:\n{0}", TreeUtils.computeFeatures(cryptarithm)); + } } } - private static void logOnConfiguration(final CryptaConfig config) { + public static void logOnConfiguration(final CryptaConfig config) { LOGGER.log(Level.CONFIG, "Configuration:\n{0}", config); } diff --git a/src/main/java/cryptator/specs/ICryptaLogManager.java b/src/main/java/cryptator/specs/ICryptaLogManager.java new file mode 100644 index 0000000..8082c1b --- /dev/null +++ b/src/main/java/cryptator/specs/ICryptaLogManager.java @@ -0,0 +1,61 @@ +/** + * This file is part of cryptator, https://github.com/arnaud-m/cryptator + * + * Copyright (c) 2022, Université Côte d'Azur. All rights reserved. + * + * Licensed under the BSD 3-clause license. + * See LICENSE file in the project root for full license information. + */ +package cryptator.specs; + +import java.util.logging.Level; + +import cryptator.JULogUtil; + +/** + * The Interface ILogManager manages the logging verbosity. + */ +public interface ICryptaLogManager { + + /** + * Sets the silent level. + */ + default void setSilent() { + JULogUtil.configureLoggers(Level.OFF); + } + + /** + * Sets the quiet level. + */ + default void setQuiet() { + JULogUtil.configureLoggers(Level.WARNING); + } + + /** + * Sets the normal level. + */ + default void setNormal() { + JULogUtil.configureLoggers(Level.INFO); + } + + /** + * Sets the verbose level. + */ + default void setVerbose() { + JULogUtil.configureLoggers(Level.CONFIG); + } + + /** + * Sets the very verbose level. + */ + default void setVeryVerbose() { + JULogUtil.configureLoggers(Level.FINE); + } + + /** + * Sets the debug level. + */ + default void setDebug() { + JULogUtil.configureLoggers(Level.ALL); + } +} diff --git a/src/main/java/cryptator/tree/CryptaFeatures.java b/src/main/java/cryptator/tree/CryptaFeatures.java index 67319bd..e42e1af 100644 --- a/src/main/java/cryptator/tree/CryptaFeatures.java +++ b/src/main/java/cryptator/tree/CryptaFeatures.java @@ -98,11 +98,6 @@ private final String buildOperators() { return operators.stream().map(String::valueOf).collect(Collectors.joining(" ", "", "")); } - public final String buildInstanceName() { - return String.format("N%02dC%03d-L%02d-%02d-%s", wordCount, charCount, minWordLength, maxWordLength, - buildSymbols()); - } - @Override public String toString() { StringBuilder b = new StringBuilder(); diff --git a/src/test/java/cryptator/CommandTest.java b/src/test/java/cryptator/CommandTest.java index ab84a87..25d7915 100644 --- a/src/test/java/cryptator/CommandTest.java +++ b/src/test/java/cryptator/CommandTest.java @@ -32,7 +32,7 @@ public void testCryptatorException() throws FileNotFoundException { @Test public void testCryptator() throws FileNotFoundException { String[] args = {"-b", "10", "-c", "TRUE", "-g", "FALSE", "-h", "TRUE", "-l", "FALSE", "-max", "0", "-min", "0", - "-s", "1", "-t", "5", "-v", "FALSE", "-z", "FALSE", "www+imac=crash "}; + "-s", "1", "-t", "5", "-v", "SILENT", "-z", "FALSE", "www+imac=crash "}; assertEquals(0, Cryptator.doMain(args)); } @@ -44,13 +44,13 @@ public void testCryptagenException() throws FileNotFoundException { @Test public void testCryptagen() throws FileNotFoundException { - String[] args = {"-c", "FALSE", "-g", "FALSE", "-v", "FALSE", "www", "imac", "crash"}; + String[] args = {"-c", "FALSE", "-g", "FALSE", "-v", "SILENT", "www", "imac", "crash"}; assertEquals(0, Cryptagen.doMain(args)); } @Test public void testCryptagenDoublyTrue() throws FileNotFoundException { - String[] args = {"-c", "FALSE", "-g", "FALSE", "-v", "FALSE", "3", "4"}; + String[] args = {"-c", "FALSE", "-g", "FALSE", "-v", "SILENT", "3", "4"}; assertEquals(0, Cryptagen.doMain(args)); }