/**
* 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;
import static java.lang.String.format;
import static java.lang.System.currentTimeMillis;
import static org.openbel.framework.common.BELUtilities.asPath;
import static org.openbel.framework.common.BELUtilities.sizedArrayList;
import static org.openbel.framework.common.PathConstants.PROTO_NETWORK_FILENAME;
import static org.openbel.framework.common.Strings.NOT_A_PHASE1_DIR;
import static org.openbel.framework.common.Strings.NO_PROTO_NETWORKS;
import static org.openbel.framework.common.Strings.PHASE2_STAGE1_HDR;
import static org.openbel.framework.common.Strings.PHASE2_STAGE2_HDR;
import static org.openbel.framework.common.Strings.PHASE2_STAGE3_HDR;
import static org.openbel.framework.common.Strings.PHASE2_STAGE4_HDR;
import static org.openbel.framework.common.cfg.SystemConfiguration.getSystemConfiguration;
import static org.openbel.framework.tools.PhaseTwoOptions.phaseTwoOptions;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.stream.XMLStreamException;
import org.apache.commons.cli.Option;
import org.openbel.framework.common.cfg.SystemConfiguration;
import org.openbel.framework.common.index.ResourceIndex;
import org.openbel.framework.common.model.EquivalenceDataIndex;
import org.openbel.framework.common.protonetwork.model.ProtoNetwork;
import org.openbel.framework.common.protonetwork.model.ProtoNetworkError;
import org.openbel.framework.common.util.BELPathFilters.ProtonetworkFilter;
import org.openbel.framework.compiler.DefaultPhaseTwo;
import org.openbel.framework.compiler.PhaseTwoImpl;
import org.openbel.framework.core.df.cache.CacheableResourceService;
import org.openbel.framework.core.df.cache.DefaultCacheableResourceService;
import org.openbel.framework.core.df.cache.ResolvedResource;
import org.openbel.framework.core.df.cache.ResourceType;
import org.openbel.framework.core.equivalence.EquivalenceIndexerService;
import org.openbel.framework.core.equivalence.EquivalenceIndexerServiceImpl;
import org.openbel.framework.core.equivalence.EquivalenceMapResolutionFailure;
import org.openbel.framework.core.protocol.ResourceDownloadError;
import org.openbel.framework.core.protonetwork.BinaryProtoNetworkDescriptor;
import org.openbel.framework.core.protonetwork.ProtoNetworkDescriptor;
import org.openbel.framework.core.protonetwork.ProtoNetworkService;
import org.openbel.framework.core.protonetwork.ProtoNetworkServiceImpl;
import org.openbel.framework.core.protonetwork.TextProtoNetworkExternalizer;
/**
* BEL phase two compiler.
*/
public final class PhaseTwoApplication extends PhaseApplication {
private final DefaultPhaseTwo p2;
private final CacheableResourceService cache;
private final SystemConfiguration syscfg;
/** Phase two artifact directory. */
public final static String DIR_ARTIFACT = "phaseII";
private final static String NUM_PHASES = "4";
/**
* Phase two application constructor.
*
* @param args Command-line arguments
*/
public PhaseTwoApplication(String[] args) {
super(args);
syscfg = getSystemConfiguration();
cache = new DefaultCacheableResourceService();
final ProtoNetworkService protonetService =
new ProtoNetworkServiceImpl();
final EquivalenceIndexerService indexer =
new EquivalenceIndexerServiceImpl();
final PhaseTwoImpl phaseTwo =
new PhaseTwoImpl(cache, indexer, protonetService);
phaseTwo.setReportable(getReportable());
p2 = phaseTwo;
}
/**
* {@inheritDoc}
*/
@Override
public void start() {
super.start();
// load the resource index for phase II use.
String resIdxURL = syscfg.getResourceIndexURL();
try {
final ResolvedResource resolvedResource = cache.resolveResource(
ResourceType.RESOURCE_INDEX, resIdxURL);
ResourceIndex.INSTANCE.loadIndex(resolvedResource
.getCacheResourceCopy());
} catch (ResourceDownloadError e) {
stageError(e.getUserFacingMessage());
ResourceIndex.INSTANCE.loadIndex();
} catch (FileNotFoundException e) {
defaultIndex();
} catch (XMLStreamException e) {
defaultIndex();
}
processOutputDirectory();
}
private void defaultIndex() {
stageError("Could not read resource index file. Equivalencing will be "
+ "disabled.");
ResourceIndex.INSTANCE.loadIndex();
}
/**
* Processes the output directory.
*/
private void processOutputDirectory() {
final String root = outputDirectory.getAbsolutePath();
final String leaf = PhaseOneApplication.DIR_ARTIFACT;
final String path = asPath(root, leaf);
final File phaseIPath = new File(path);
// Fail if the working path doesn't contain a phase I artifact
if (!phaseIPath.isDirectory()) {
error(NOT_A_PHASE1_DIR + ": " + phaseIPath);
failUsage();
}
// Fail if the working path doesn't contain any proto-networks
final File[] networks = phaseIPath.listFiles(new ProtonetworkFilter());
if (networks.length == 0) {
error(NO_PROTO_NETWORKS + " found in " + phaseIPath);
failUsage();
}
// Create the directory artifact or fail
artifactPath = createDirectoryArtifact(outputDirectory, DIR_ARTIFACT);
processDirectories(networks);
}
/**
* Starts phase two compilation of proto-networks.
*
* @param networks
*/
private void processDirectories(final File[] networks) {
phaseOutput(format("=== %s ===", getApplicationName()));
ProtoNetwork network = stage1(networks);
Set<EquivalenceDataIndex> eqs = stage2();
if (!eqs.isEmpty()) {
network = stage3(network, eqs);
}
stage4(network);
}
/**
* Stage one merger of networks, returning the merged proto-network.
*
* @param networks Proto-networks
* @return {@link ProtoNetwork}
*/
private ProtoNetwork stage1(final File[] networks) {
beginStage(PHASE2_STAGE1_HDR, "1", NUM_PHASES);
final int netct = networks.length;
final StringBuilder bldr = new StringBuilder();
bldr.append("Merging ");
bldr.append(netct);
bldr.append(" network");
if (netct > 1) {
bldr.append("s");
}
stageOutput(bldr.toString());
long t1 = currentTimeMillis();
Collection<ProtoNetworkDescriptor> nds = sizedArrayList(netct);
for (final File network : networks) {
final String root = network.getAbsolutePath();
final String netPath = asPath(root, PROTO_NETWORK_FILENAME);
final File networkBin = new File(netPath);
nds.add(new BinaryProtoNetworkDescriptor(networkBin));
}
ProtoNetwork ret = p2.stage1Merger(nds);
new File(artifactPath.getAbsolutePath() + "/merged").mkdirs();
p2.stage4WriteEquivalentProtoNetwork(ret,
artifactPath.getAbsolutePath() + "/merged");
if (withDebug()) {
try {
TextProtoNetworkExternalizer textExternalizer =
new TextProtoNetworkExternalizer();
textExternalizer.writeProtoNetwork(ret,
artifactPath.getAbsolutePath() + "/merged");
} catch (ProtoNetworkError e) {
error("Could not write out equivalenced proto network.");
}
}
long t2 = currentTimeMillis();
bldr.setLength(0);
markTime(bldr, t1, t2);
markEndStage(bldr);
stageOutput(bldr.toString());
return ret;
}
/**
* Stage two equivalence loading, returning a set of data indices.
*
* @param equivalenceFile Equivalence file
* @return Set of equivalence data indices
*/
private Set<EquivalenceDataIndex> stage2() {
beginStage(PHASE2_STAGE2_HDR, "2", NUM_PHASES);
Set<EquivalenceDataIndex> ret = new HashSet<EquivalenceDataIndex>();
final StringBuilder bldr = new StringBuilder();
bldr.append("Loading namespace equivalences from resource index");
stageOutput(bldr.toString());
long t1 = currentTimeMillis();
try {
ret.addAll(p2.stage2LoadNamespaceEquivalences());
} catch (EquivalenceMapResolutionFailure e) {
warning(e.getUserFacingMessage());
// continue with an empty equivalence data index set
}
for (final EquivalenceDataIndex edi : ret) {
final String nsLocation = edi.getNamespaceResourceLocation();
bldr.setLength(0);
bldr.append("Equivalence for ");
bldr.append(nsLocation);
stageOutput(bldr.toString());
}
long t2 = currentTimeMillis();
bldr.setLength(0);
markTime(bldr, t1, t2);
markEndStage(bldr);
stageOutput(bldr.toString());
return ret;
}
/**
* Stage three equivalencing, returning the equivalenced proto-network.
* If parameter equivalencing yields a {@code 0} count then term and
* statement equivalencing is not run.
*
* @param network Merged proto-network
* @param equivalences Equivalence data indices
* @return {@link ProtoNetwork}
*/
private ProtoNetwork stage3(final ProtoNetwork network,
Set<EquivalenceDataIndex> equivalences) {
beginStage(PHASE2_STAGE3_HDR, "3", NUM_PHASES);
final StringBuilder bldr = new StringBuilder();
long t1 = currentTimeMillis();
int pct = stage3Parameter(network, equivalences, bldr);
stage3Term(network, pct);
stage3Statement(network, pct);
long t2 = currentTimeMillis();
final int paramct =
network.getParameterTable().getTableParameters().size();
final int termct = network.getTermTable().getTermValues().size();
final int stmtct = network.getStatementTable().getStatements().size();
bldr.setLength(0);
bldr.append(stmtct);
bldr.append(" statements, ");
bldr.append(termct);
bldr.append(" terms, ");
bldr.append(paramct);
bldr.append(" parameters");
stageOutput(bldr.toString());
bldr.setLength(0);
markTime(bldr, t1, t2);
markEndStage(bldr);
stageOutput(bldr.toString());
return network;
}
/**
* Stage three parameter equivalencing.
*
* @param network the {@link ProtoNetwork network} to equivalence
* @param equivalences the {@link Set set} of {@link EquivalenceDataIndex}
* @param bldr the {@link StringBuilder}
* @return the {@code int} count of parameter equivalences
*/
private int stage3Parameter(final ProtoNetwork network,
Set<EquivalenceDataIndex> equivalences, final StringBuilder bldr) {
bldr.append("Equivalencing parameters");
stageOutput(bldr.toString());
ProtoNetwork ret = network;
int ct = 0;
try {
ct = p2.stage3EquivalenceParameters(ret, equivalences);
stageOutput("(" + ct + " equivalences)");
} catch (IOException ioex) {
final String err = ioex.getMessage();
fatal(err);
}
return ct;
}
/**
* Stage three term equivalencing.
*
* @param network the {@link ProtoNetwork network} to equivalence
* @param pct the parameter equivalencing count to control output
*/
private void stage3Term(final ProtoNetwork network, int pct) {
stageOutput("Equivalencing terms");
int tct = p2.stage3EquivalenceTerms(network);
stageOutput("(" + tct + " equivalences)");
}
/**
* Stage three statement equivalencing.
*
* @param network the {@link ProtoNetwork network} to equivalence
* @param pct the parameter equivalencing count to control output
*/
private void stage3Statement(final ProtoNetwork network, int pct) {
stageOutput("Equivalencing statements");
int sct = p2.stage3EquivalenceStatements(network);
stageOutput("(" + sct + " equivalences)");
}
/**
* Stage four saving
*
* @param eqNetwork
* @return
*/
private ProtoNetworkDescriptor stage4(final ProtoNetwork eqNetwork) {
beginStage(PHASE2_STAGE4_HDR, "4", NUM_PHASES);
final StringBuilder bldr = new StringBuilder();
bldr.append("Saving network");
stageOutput(bldr.toString());
long t1 = currentTimeMillis();
ProtoNetworkDescriptor ret =
p2.stage4WriteEquivalentProtoNetwork(eqNetwork,
artifactPath.getAbsolutePath());
if (withDebug()) {
try {
TextProtoNetworkExternalizer textExternalizer =
new TextProtoNetworkExternalizer();
textExternalizer.writeProtoNetwork(eqNetwork,
artifactPath.getAbsolutePath());
} catch (ProtoNetworkError e) {
error("Could not write out equivalenced proto network.");
}
}
bldr.setLength(0);
long t2 = currentTimeMillis();
markTime(bldr, t1, t2);
markEndStage(bldr);
stageOutput(bldr.toString());
return ret;
}
/**
* {@inheritDoc}
*/
@Override
public PhaseTwoOptions getPhaseConfiguration() {
return phaseTwoOptions();
}
/**
* {@inheritDoc}
*/
@Override
public boolean validCommandLine() {
// We only need output from phase one. Any command-line is valid.
return true;
}
/**
* Returns {@code "Phase II: Merging proto-networks"}.
*
* @return String
*/
@Override
public String getApplicationName() {
return "Phase II: Merging proto-networks";
}
/**
* Returns {@code "Phase II"}.
*
* @return String
*/
@Override
public String getApplicationShortName() {
return "Phase II";
}
/**
* {@inheritDoc}
*/
@Override
public List<Option> getCommandLineOptions() {
return super.getCommandLineOptions();
}
/**
* Returns the application's description.
*
* @return String
*/
@Override
public String getApplicationDescription() {
final StringBuilder bldr = new StringBuilder();
bldr.append("Merges proto-networks into a composite network and ");
bldr.append("equivalences term references across namespaces.");
return bldr.toString();
}
/**
* {@inheritDoc}
*/
@Override
public String getUsage() {
StringBuilder bldr = new StringBuilder();
bldr.append("[OPTION]...");
return bldr.toString();
}
/**
* Invokes {@link #harness(PhaseApplication)} for
* {@link PhaseTwoApplication}.
*
* @param args Command-line arguments
*/
public static void main(String[] args) {
harness(new PhaseTwoApplication(args));
}
/**
* {@inheritDoc}
*/
public static String getRequiredArguments() {
// Nothing is required
return "";
}
}