/**
* Copyright (C) 2012-2013 Selventa, Inc.
*
* This file is part of the OpenBEL Framework.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The OpenBEL Framework 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms under LGPL v3:
*
* This license does not authorize you and you are prohibited from using the
* name, trademarks, service marks, logos or similar indicia of Selventa, Inc.,
* or, in the discretion of other licensors or authors of the program, the
* name, trademarks, service marks, logos or similar indicia of such authors or
* licensors, in any marketing or advertising materials relating to your
* distribution of the program or any covered product. This restriction does
* not waive or limit your obligation to keep intact all copyright notices set
* forth in the program as delivered to you.
*
* If you distribute the program in whole or in part, or any modified version
* of the program, and you assume contractual liability to the recipient with
* respect to the program or modified version, then you will indemnify the
* authors and licensors of the program for any liabilities that these
* contractual assumptions directly impose on those licensors and authors.
*/
package org.openbel.framework.tools.kamstore;
import static java.lang.String.format;
import static org.openbel.framework.common.Strings.SYSTEM_CONFIG_PATH;
import static org.openbel.framework.common.Strings.VERBOSE_HELP;
import static org.openbel.framework.common.cfg.SystemConfiguration.getSystemConfiguration;
import static org.openbel.framework.core.StandardOptions.ARG_SYSCFG;
import static org.openbel.framework.core.StandardOptions.LONG_OPT_DEBUG;
import static org.openbel.framework.core.StandardOptions.LONG_OPT_HELP;
import static org.openbel.framework.core.StandardOptions.LONG_OPT_SYSCFG;
import static org.openbel.framework.core.StandardOptions.LONG_OPT_VERBOSE;
import static org.openbel.framework.core.StandardOptions.SHORT_OPT_VERBOSE;
import static org.openbel.framework.core.StandardOptions.SHRT_OPT_HELP;
import static org.openbel.framework.core.StandardOptions.SHRT_OPT_SYSCFG;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.cli.Option;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.openbel.framework.api.KAMStore;
import org.openbel.framework.api.KAMStoreException;
import org.openbel.framework.api.KAMStoreImpl;
import org.openbel.framework.api.internal.KamDbObject;
import org.openbel.framework.api.internal.KAMCatalogDao.KamInfo;
import org.openbel.framework.common.SimpleOutput;
import org.openbel.framework.common.cfg.SystemConfiguration;
import org.openbel.framework.common.enums.ExitCode;
import org.openbel.framework.compiler.kam.KAMStoreSchemaService;
import org.openbel.framework.compiler.kam.KAMStoreSchemaServiceImpl;
import org.openbel.framework.core.CommandLineApplication;
import org.openbel.framework.core.df.DBConnection;
import org.openbel.framework.core.df.DatabaseService;
import org.openbel.framework.core.df.DatabaseServiceImpl;
public class KamComparator extends CommandLineApplication {
private static final String KAM_COMPARATOR_NAME = "Kam Comparator";
private static final String KAM_COMPARATOR_DESC =
"Compares two KAMs by topology and data";
public static final String[] TOPOLOGY_LABELS = new String[] {
"KAM node count", "KAM edge count", "Average KAM node degree",
"Average KAM node in degree", "Average KAM node out degree",
"Density of KAM (0...1)" };
public static final String[] DATA_LABELS = new String[] {
"BEL document count", "Namespace count",
"Annotation definition count",
"Annotation count", "Statement count", "Term count",
"Parameter count", "Unique parameter count" };
public static final int PRECISION = 4;
private enum Mode {
LIST, HELP, COMPARE
}
private Mode mode;
private String kam1Name, kam2Name;
private boolean noPreserve, verbose;
private String outputFilename;
private final DatabaseService dbservice;
private final SystemConfiguration sysconfig;
public KamComparator(String[] args) {
super(args);
final SimpleOutput reportable = new SimpleOutput();
reportable.setErrorStream(System.err);
reportable.setOutputStream(System.out);
setReportable(reportable);
printApplicationInfo();
initializeSystemConfiguration();
sysconfig = getSystemConfiguration();
dbservice = new DatabaseServiceImpl();
}
public void run() throws IOException, KAMStoreException, SQLException {
processOptions();
if (mode == Mode.HELP) {
printHelp(true);
}
if (verbose) {
reportable.output("Using KAM Store URL: " + sysconfig.getKamURL());
reportable
.output("Using KAM Store User: " + sysconfig.getKamUser());
}
DBConnection dbConnection = dbservice.dbConnection(
sysconfig.getKamURL(),
sysconfig.getKamUser(),
sysconfig.getKamPassword());
KAMStore kAMStore = new KAMStoreImpl(dbConnection);
// See if we need to set up the KAM Store schemas
final KAMStoreSchemaService kamSchemaService =
new KAMStoreSchemaServiceImpl(dbservice);
// Load the KAM catalog schema, if it doesn't exist (see #88).
kamSchemaService.setupKAMCatalogSchema();
try {
if (mode == Mode.LIST) {
List<KamInfo> kamInfos = kAMStore.getCatalog();
printKamCatalogSummary(kamInfos);
} else if (mode == Mode.COMPARE) {
final KamComparisonXHTMLWriter xhtmlWriter =
createOutputFileWriter();
final String[] kamNames = new String[] { kam1Name, kam2Name };
// Get the KAM catalog information for each KAM.
final KamInfo[] kamInfos = new KamInfo[kamNames.length];
for (int i = 0; i < kamNames.length; ++i) {
final KamInfo kamInfo = kAMStore.getKamInfo(kamNames[i]);
if (kamInfo == null) {
reportable.error("No KAM found with name '"
+ kamNames[i] + "'");
bail(ExitCode.GENERAL_FAILURE);
}
kamInfos[i] = kamInfo;
}
// Create the KamComparison that will be displayed and/or written to the
// output file.
final KamComparison cmp = new KamComparison(kamNames);
for (KamInfo kamInfo : kamInfos) {
final String kamName = kamInfo.getName();
final KAMStoreStatisticsDaoImpl dao =
new KAMStoreStatisticsDaoImpl(
kamInfo.getKamDbObject().getSchemaName(),
dbConnection);
cmp.setKamNodeCount(kamName, dao.getKamNodeCount());
cmp.setKamEdgeCount(kamName, dao.getKamEdgeCount());
cmp.setBELDocumentCount(kamName, dao.getBELDocumentCount());
cmp.setNamespaceCount(kamName, dao.getNamespaceCount());
cmp.setAnnotationDefinitionCount(kamName,
dao.getAnnotationDefinitonCount());
cmp.setAnnotationCount(kamName, dao.getAnnotationCount());
cmp.setStatementCount(kamName, dao.getStatementCount());
cmp.setTermCount(kamName, dao.getTermCount());
cmp.setParameterCount(kamName, dao.getParameterCount());
cmp.setUniqueParameterCount(kamName,
dao.getUniqueParameterCount());
}
// Write to stdout.
writeComparison(cmp);
// Write the output to the output file.
if (xhtmlWriter != null) {
xhtmlWriter.writeKamComparison(cmp);
xhtmlWriter.close();
}
}
} finally {
kAMStore.teardown();
dbConnection.getConnection().close();
}
}
private KamComparisonXHTMLWriter createOutputFileWriter()
throws IOException {
KamComparisonXHTMLWriter xhtmlWriter = null;
if (!StringUtils.isBlank(outputFilename)) {
File outputFile = new File(outputFilename);
if (outputFile.isDirectory()) {
reportable.error(format(
"Output file can not be a directory - %s",
outputFile.getAbsolutePath()));
bail(ExitCode.GENERAL_FAILURE);
} else if (!noPreserve && outputFile.isFile()) {
reportable.warning(format("Output file already exists: %s, " +
"specify the --no-preserve option to overwrite.",
outputFilename));
} else {
if (verbose) {
reportable.output(format("Creating output file: %s",
outputFilename));
}
xhtmlWriter =
new KamComparisonXHTMLWriter(new FileWriter(outputFile));
xhtmlWriter.open();
}
}
return xhtmlWriter;
}
private void writeComparison(KamComparison cmp) {
final String[] kamNames = cmp.getKamNames();
final String kam1Name = kamNames[0], kam2Name = kamNames[1];
// Set the column widths to the next multiple of 4, and to at least
// 16 characters.
final int min = 16;
final int c0 =
(Math.max(maxLength(TOPOLOGY_LABELS), maxLength(DATA_LABELS)) / 4 + 1) * 4;
final int c1 = Math.max(min, (kam1Name.length() / 4 + 1) * 4);
final int c2 = Math.max(min, (kam2Name.length() / 4 + 1) * 4);
final String titleFormat =
"%-" + (c0 + 4) + "s%-" + c1 + "s%-" + c2 + "s%n";
final String sectionFormat = "%-" + (c0 + 4) + "s%n";
final String integralRowFormat =
" %-" + c0 + "s%-" + c1 + "d%-" + c2 + "d%n";
final String floatingPointRowFormat = " %-" + c0 + "s%-" + c1 + "." +
PRECISION + "f%-" + c2 + "." + PRECISION + "f%n";
StringBuilder bldr = new StringBuilder();
bldr.append(format(titleFormat, "", kam1Name, kam2Name));
// Compare the KAMs by topology.
bldr.append(format(sectionFormat, "Topology"));
bldr.append(format(integralRowFormat, TOPOLOGY_LABELS[0],
cmp.getKamNodeCount(kam1Name),
cmp.getKamNodeCount(kam2Name)));
bldr.append(format(integralRowFormat, TOPOLOGY_LABELS[1],
cmp.getKamEdgeCount(kam1Name),
cmp.getKamEdgeCount(kam2Name)));
bldr.append(format(floatingPointRowFormat, TOPOLOGY_LABELS[2],
cmp.getAverageKamNodeDegree(kam1Name),
cmp.getAverageKamNodeDegree(kam2Name)));
bldr.append(format(floatingPointRowFormat, TOPOLOGY_LABELS[3],
cmp.getAverageKamNodeInDegree(kam1Name),
cmp.getAverageKamNodeInDegree(kam2Name)));
bldr.append(format(floatingPointRowFormat, TOPOLOGY_LABELS[4],
cmp.getAverageKamNodeOutDegree(kam1Name),
cmp.getAverageKamNodeOutDegree(kam2Name)));
bldr.append(format(floatingPointRowFormat, TOPOLOGY_LABELS[5],
cmp.getDensity(kam1Name), cmp.getDensity(kam2Name)));
// Compare the KAMs by data.
bldr.append(format(sectionFormat, "Data"));
bldr.append(format(integralRowFormat, DATA_LABELS[0],
cmp.getBELDocumentCount(kam1Name),
cmp.getBELDocumentCount(kam2Name)));
bldr.append(format(integralRowFormat, DATA_LABELS[1],
cmp.getNamespaceCount(kam1Name),
cmp.getNamespaceCount(kam2Name)));
bldr.append(format(integralRowFormat, DATA_LABELS[2],
cmp.getAnnotationDefinitionCount(kam1Name),
cmp.getAnnotationDefinitionCount(kam2Name)));
bldr.append(format(integralRowFormat, DATA_LABELS[3],
cmp.getAnnotationCount(kam1Name),
cmp.getAnnotationCount(kam2Name)));
bldr.append(format(integralRowFormat, DATA_LABELS[4],
cmp.getStatementCount(kam1Name),
cmp.getStatementCount(kam2Name)));
bldr.append(format(integralRowFormat, DATA_LABELS[5],
cmp.getTermCount(kam1Name),
cmp.getTermCount(kam2Name)));
bldr.append(format(integralRowFormat, DATA_LABELS[6],
cmp.getParameterCount(kam1Name),
cmp.getParameterCount(kam2Name)));
bldr.append(format(integralRowFormat, DATA_LABELS[7],
cmp.getUniqueParameterCount(kam1Name),
cmp.getUniqueParameterCount(kam2Name)));
reportable.output("", bldr.toString());
}
private static int maxLength(String[] strings) {
int max = 0;
for (String s : strings) {
final int length = s.length();
if (length > max) {
max = length;
}
}
return max;
}
protected void processOptions() {
if (hasOption(SHRT_OPT_HELP)) {
mode = Mode.HELP;
} else if (hasOption("l")) {
mode = Mode.LIST;
} else {
mode = Mode.COMPARE;
final List<String> kamArgs = getExtraneousArguments();
if (kamArgs == null || kamArgs.size() != 2) {
// print out the usage if less then 2 arguments are given
printUsage();
getReportable().error("\n");
getReportable().error(
"You must specify two KAM names to compare.");
end();
// Unreachable return stmt - for static analysis purposes
return;
}
kam1Name = kamArgs.get(0);
kam2Name = kamArgs.get(1);
}
if (hasOption("o")) {
outputFilename = getOptionValue("o");
}
noPreserve = hasOption("no-preserve");
verbose = hasOption(SHORT_OPT_VERBOSE);
}
protected void printKamCatalogSummary(List<KamInfo> kamInfos) {
// Get a list of all the KAMs available in the KAM store
reportable.output("Available KAMs:");
reportable.output("\tName\tLast Compiled\tSchema Name");
reportable.output("\t------\t-------------\t-----------");
for (KamInfo kamInfo : kamInfos) {
KamDbObject kamDb = kamInfo.getKamDbObject();
reportable.output(String.format("\t%s\t%s\t%s",
kamDb.getName(), kamDb.getLastCompiled(),
kamDb.getSchemaName()));
}
System.out.print("\n");
}
@Override
public String getApplicationName() {
return KAM_COMPARATOR_NAME;
}
@Override
public String getApplicationShortName() {
return KAM_COMPARATOR_NAME;
}
@Override
public String getApplicationDescription() {
return KAM_COMPARATOR_DESC;
}
@Override
public String getUsage() {
StringBuilder bldr = new StringBuilder();
bldr.append("[--debug]");
bldr.append(" [-h]");
bldr.append(" [-l]");
bldr.append(" -o <output file name> [--no-preserve]");
bldr.append(" [-s <system config file name>]");
bldr.append(" [-v]");
bldr.append(" <KAM name> <KAM name>");
return bldr.toString();
}
@Override
public List<Option> getCommandLineOptions() {
final List<Option> options = new LinkedList<Option>();
options.add(new Option(
null,
LONG_OPT_DEBUG,
false,
"Enables debug mode. This mode outputs additional debug information "
+
"as the application runs."));
options.add(new Option(SHRT_OPT_HELP, LONG_OPT_HELP, false,
"Enables help mode. This mode shows the help information."));
options.add(new Option("l", "list", false,
"Lists the KAMs stored in the KAMStore."));
final Option oOpt =
new Option(
"o",
"output-file",
true,
"Optional. If present, indicates the name of the output file "
+
"to write the KAM comparisions to in XHTML format.");
oOpt.setArgName("output filename");
options.add(oOpt);
options.add(new Option(
null,
"no-preserve",
false,
"Optional. If present, if a file exist with the same name as the output "
+
"file then that file will be overwritten."));
final Option sOpt = new Option(SHRT_OPT_SYSCFG, LONG_OPT_SYSCFG, false,
SYSTEM_CONFIG_PATH);
sOpt.setArgName(ARG_SYSCFG);
options.add(sOpt);
options.add(new Option(SHORT_OPT_VERBOSE, LONG_OPT_VERBOSE, false,
VERBOSE_HELP));
return options;
}
/**
* @param args
*/
public static void main(String[] args) {
try {
KamComparator app = new KamComparator(args);
app.run();
} catch (Exception e) {
Throwable cause = ExceptionUtils.getRootCause(e);
System.err.println("Unable to run " + KAM_COMPARATOR_NAME);
System.err.println("Reason: "
+ (cause == null ? e.getMessage() : cause.getMessage()));
}
}
}