001package conexp.fx.cli;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2023 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.io.File;
026import java.util.Set;
027import java.util.concurrent.Executors;
028import java.util.function.Consumer;
029
030import org.apache.commons.cli.CommandLine;
031import org.apache.commons.cli.DefaultParser;
032import org.apache.commons.cli.HelpFormatter;
033import org.apache.commons.cli.Option;
034import org.apache.commons.cli.OptionBuilder;
035import org.apache.commons.cli.Options;
036import org.apache.commons.cli.ParseException;
037
038import conexp.fx.core.algorithm.nextclosures.NextClosures2Bit;
039import conexp.fx.core.collections.Collections3;
040import conexp.fx.core.collections.Pair;
041import conexp.fx.core.context.Concept;
042import conexp.fx.core.context.ConceptLattice;
043import conexp.fx.core.context.Implication;
044import conexp.fx.core.context.MatrixContext;
045import conexp.fx.core.exporter.CXTExporter;
046import conexp.fx.core.importer.CXTImporter;
047import conexp.fx.gui.ConExpFX;
048
049public final class CLI {
050
051  public static void main(final String[] args) throws Exception {
052    new CLI(args);
053  }
054
055  private final CommandLine                commandLine;
056  private MatrixContext<String, String>    cxt;
057  private Set<Concept<String, String>>     concepts;
058  private Set<Implication<String, String>> implications;
059  private ConceptLattice<String, String>   lattice;
060
061  private CLI(final String[] args) throws Exception {
062    super();
063    try {
064      commandLine = new DefaultParser().parse(OPTIONS, args);
065    } catch (ParseException e) {
066      printHelp();
067      throw new RuntimeException("Unable to parse command line arguments. Please check supplied arguments!", e);
068    }
069    if (!commandLine.getArgList().isEmpty()) {
070      printHelp();
071      throw new RuntimeException("Unrecognized Arguments: " + commandLine.getArgList());
072    }
073    execute();
074  }
075
076  private final void printHelp() {
077    new HelpFormatter().printHelp(
078        120,
079        "java -jar conexp-fx-VERSION-jar-with_dependencies.jar [OPTIONS]",
080        "Available command line options for Concept Explorer FX",
081        OPTIONS,
082        "");
083  }
084
085  private final void execute() throws Exception {
086    if (commandLine.getOptions().length == 0 || commandLine.hasOption(GUI.getLongOpt()))
087      ConExpFX.main(new String[] {});
088    else if (commandLine.hasOption(HELP.getLongOpt()))
089      printHelp();
090    else {
091      final Consumer<Concept<String, String>> c1;
092      final Consumer<Implication<String, String>> c2;
093      final Consumer<String> c3;
094      final Consumer<Double> c4;
095      if (commandLine.hasOption(PRINT_TO_CONSOLE.getLongOpt())) {
096        c1 = System.out::println;
097        c2 = System.out::println;
098        c3 = System.out::println;
099        c4 = System.out::println;
100      } else {
101        c1 = __ -> {};
102        c2 = __ -> {};
103        c3 = __ -> {};
104        c4 = __ -> {};
105      }
106      if (!commandLine.hasOption(IMPORT_CXT.getLongOpt()))
107        throw new IllegalArgumentException(
108            "Unable to instanciate FCA service without formal context. Please specify a file in Burmeister formatting by adding a command line prefix\r\n\t--importContextFromCXT <path_to_file>");
109      final File input = new File(commandLine.getOptionValue(IMPORT_CXT.getLongOpt()));
110      cxt = CXTImporter.read(input);
111      if (commandLine.hasOption(CALC_CONCEPTS.getLongOpt()) || commandLine.hasOption(CALC_IMPLICATIONS.getLongOpt())
112          || commandLine.hasOption(CALC_NEIGHBORHOOD.getLongOpt())) {
113        final Pair<Set<Concept<String, String>>, Set<Implication<String, String>>> result =
114            NextClosures2Bit.bitCompute(cxt, Executors.newWorkStealingPool(), c1, c2, c3, c4, () -> false);
115        concepts = result.first();
116        implications = result.second();
117      }
118      if (commandLine.hasOption(CALC_NEIGHBORHOOD.getLongOpt())) {
119//        IPred.
120      }
121      if (commandLine.hasOption(WRITE_TO_FILE.getLongOpt())) {
122        final String filename = input.getName().substring(0, input.getName().lastIndexOf("."));
123        if (concepts != null)
124          Collections3.writeToFile(
125              new File(input.getParentFile(), filename + ".concepts"),
126              concepts,
127              "Formal Concepts of " + input.getAbsolutePath());
128        if (implications != null)
129          Collections3.writeToFile(
130              new File(input.getParentFile(), filename + ".implications"),
131              implications,
132              "Implications of " + input.getAbsolutePath());
133        if (lattice != null)
134          CXTExporter.export(lattice, new File(input.getParentFile(), filename + ".lattice"));
135      }
136    }
137  }
138
139//  private final void test(final CommandLine commandLine) {
140//    final String path = commandLine.getOptionValue(CLI.TEST.getLongOpt());
141//    NextClosuresTest.run(path);
142//    TestSuite.main(new String[] {});
143////    -Xms256m
144////    -Xmx2048m
145////    -Djub.customkey=oracle-1.7.0_45
146////    -Djub.consumers=CONSOLE,H2,XML
147////    -Djub.db.file=benchmarks/.benchmarks
148////    -Djub.xml.file=benchmarks/latest.xml    
149//  }
150
151  private final Options OPTIONS           = new Options();
152  @SuppressWarnings("static-access")
153  private final Option  HELP              = OptionBuilder
154      .isRequired(false)
155      .withLongOpt("showHelp")
156      .hasArg(false)
157      .withDescription("shows available command line options and their arguments.")
158      .create("help");
159  @SuppressWarnings("static-access")
160  private final Option  IMPORT_CXT        = OptionBuilder
161      .isRequired(false)
162      .withLongOpt("importContextFromCXT")
163      .hasArg(true)
164      .withArgName("file")
165      .withDescription("imports formal context file in Burmeister formatting (*.cxt)")
166      .create("import");
167  @SuppressWarnings("static-access")
168  private final Option  CALC_CONCEPTS     = OptionBuilder
169      .isRequired(false)
170      .withLongOpt("calculateConcepts")
171      .hasArg(false)
172      .withDescription("computes all formal concepts")
173      .create("concepts");
174  @SuppressWarnings("static-access")
175  private final Option  CALC_NEIGHBORHOOD = OptionBuilder
176      .isRequired(false)
177      .withLongOpt("calculateNeighborhood")
178      .hasArg(false)
179      .withDescription("computes the neighborhood relation")
180      .create("lattice");
181//  @SuppressWarnings("static-access")
182//  private final Option  CALC_LAYOUT       = OptionBuilder
183//                                              .isRequired(false)
184//                                              .withLongOpt("calculateLayout")
185//                                              .hasArg(false)
186//                                              .withDescription("computes a concept layout")
187//                                              .create("layout");
188  @SuppressWarnings("static-access")
189  private final Option  CALC_IMPLICATIONS = OptionBuilder
190      .isRequired(false)
191      .withLongOpt("calculateImplications")
192      .hasArg(false)
193      .withDescription("computes implicational base")
194      .create("implications");
195//  @SuppressWarnings("static-access")
196//  private final Option  CALC_ASSOCIATIONS = OptionBuilder
197//                                              .isRequired(false)
198//                                              .withLongOpt("calculateAssociations")
199//                                              .hasArg(true)
200//                                              .withArgName("supp")
201//                                              .withArgName("conf")
202//                                              .withDescription("computes all association rules")
203//                                              .create("associationrules");
204  @SuppressWarnings("static-access")
205  private final Option  PRINT_TO_CONSOLE  = OptionBuilder
206      .isRequired(false)
207      .withLongOpt("printToConsole")
208      .hasArg(false)
209      .withDescription("prints all results to console")
210      .create("print");
211  @SuppressWarnings("static-access")
212  private final Option  WRITE_TO_FILE     = OptionBuilder
213      .isRequired(false)
214      .withLongOpt("writeToFile")
215      .hasArg(false)
216      // .hasArgs(2)
217      // .withValueSeparator(' ')
218      // .withArgName("format")
219      // .withArgName("file")
220      .withDescription("writes all results to files")
221      .create("write");
222  @SuppressWarnings("static-access")
223  private final Option  GUI               = OptionBuilder
224      .isRequired(false)
225      .hasArg(false)
226      .withDescription("starts the JavaFX gui")
227      .withLongOpt("startGUI")
228      .create("gui");
229//  @SuppressWarnings("static-access")
230//  private final Option  TEST              = OptionBuilder
231//                                                       .isRequired(false)
232//                                                       .hasArg(true)
233//                                                       .withArgName("path")
234//                                                       .withDescription("runs the test suite")
235//                                                       .withLongOpt("runTestSuite")
236//                                                       .create("test");
237//  @SuppressWarnings("static-access")
238//  private final Option  BENCHMARK         = OptionBuilder
239//                                                       .isRequired(false)
240//                                                       .hasArg(true)
241//                                                       .withArgName("path")
242//                                                       .withDescription("runs the benchmark suite")
243//                                                       .withLongOpt("runBenchmarkSuite")
244//                                                       .create("bench");
245
246  {
247    OPTIONS.addOption(GUI);
248    OPTIONS.addOption(HELP);
249    OPTIONS.addOption(IMPORT_CXT);
250    OPTIONS.addOption(CALC_CONCEPTS);
251    OPTIONS.addOption(CALC_NEIGHBORHOOD);
252//    OPTIONS.addOption(CALC_LAYOUT);
253    OPTIONS.addOption(CALC_IMPLICATIONS);
254//    OPTIONS.addOption(CALC_ASSOCIATIONS);
255    OPTIONS.addOption(PRINT_TO_CONSOLE);
256    OPTIONS.addOption(WRITE_TO_FILE);
257//    OPTIONS.addOption(TEST);
258//    OPTIONS.addOption(BENCHMARK);
259  }
260}