/******************************************************************************* * Copyright (c) 2010-2011 VIVO Harvester Team. For full list of contributors, please see the AUTHORS file provided. * All rights reserved. * This program and the accompanying materials are made available under the terms of the new BSD license which accompanies this distribution, and is available at http://www.opensource.org/licenses/bsd-license.html ******************************************************************************/ package org.vivoweb.harvester.qualify; import java.io.IOException; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.vivoweb.harvester.util.InitLog; import org.vivoweb.harvester.util.args.ArgDef; import org.vivoweb.harvester.util.args.ArgList; import org.vivoweb.harvester.util.args.ArgParser; import org.vivoweb.harvester.util.args.UsageException; import org.vivoweb.harvester.util.repo.JenaConnect; import org.vivoweb.harvester.util.repo.MemJenaConnect; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.NodeIterator; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.ResIterator; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.Statement; import com.hp.hpl.jena.rdf.model.StmtIterator; import com.hp.hpl.jena.shared.Lock; /** * Smush * @author Cornell University VIVO Team (Algorithm) * @author James Pence (jrpence@ufl.edu) (Harvester Tool) */ public class Smush { /** * SLF4J Logger */ private static Logger log = LoggerFactory.getLogger(Smush.class); /** * model containing statements to be scored */ private JenaConnect inputJC; /** * model in which to store temp copy of input and vivo data statements */ private JenaConnect outputJena; /** * the predicates to look for in inputJC model */ private List<String> inputPredicates; /** * limit match Algorithm to only match rdf nodes in inputJC whose URI begin with this namespace */ private String namespace; /** * Change the input model to match the output model */ private boolean inPlace; /** * Constructor * @param inputJena model containing statements to be smushed * @param outputJena model containing only resources about the smushed statements is returned * @param inputPredicates the predicates to look for in inputJC model * @param namespace limit match Algorithm to only match rdf nodes in inputJC whose URI begin with this namespace * @param inPlace replace the input model with the output model */ public Smush(JenaConnect inputJena, JenaConnect outputJena, List<String> inputPredicates, String namespace, boolean inPlace) { if(inputJena == null) { throw new IllegalArgumentException("Input model cannot be null"); } this.inputJC = inputJena; this.outputJena = outputJena; if(inputPredicates == null) { throw new IllegalArgumentException("Input Predicate cannot be null"); } this.inputPredicates = inputPredicates; this.namespace = namespace; this.inPlace = inPlace; } /** * Constructor * @param inputJena model containing statements to be smushed * @param inputPredicates the predicates to look for in inputJC model * @param namespace limit match Algorithm to only match rdf nodes in inputJC whose URI begin with this namespace */ public Smush(JenaConnect inputJena, List<String> inputPredicates, String namespace) { this(inputJena, null, inputPredicates, namespace, true); } /** * Constructor * @param args argument list * @throws IOException error parsing options * @throws UsageException user requested usage message */ private Smush(String... args) throws IOException, UsageException { this(getParser().parse(args)); } /** * Constructor Scoring.close(); * @param opts parsed argument list * @throws IOException error parsing options */ private Smush(ArgList opts) throws IOException { this( JenaConnect.parseConfig(opts.get("i"), opts.getValueMap("I")), JenaConnect.parseConfig(opts.get("o"), opts.getValueMap("O")), opts.getAll("P"), opts.get("n"), opts.has("r") ); } /** * Get the ArgParser * @return the ArgParser */ private static ArgParser getParser() { ArgParser parser = new ArgParser("Smush"); // Models parser.addArgument(new ArgDef().setShortOption('i').setLongOpt("inputJena-config").withParameter(true, "CONFIG_FILE").setDescription("inputJC JENA configuration filename").setRequired(false)); parser.addArgument(new ArgDef().setShortOption('I').setLongOpt("inputOverride").withParameterValueMap("JENA_PARAM", "VALUE").setDescription("override the JENA_PARAM of inputJC jena model config using VALUE").setRequired(false)); parser.addArgument(new ArgDef().setShortOption('o').setLongOpt("outputJena-config").withParameter(true, "CONFIG_FILE").setDescription("inputJC JENA configuration filename").setRequired(false)); parser.addArgument(new ArgDef().setShortOption('O').setLongOpt("outputOverride").withParameterValueMap("JENA_PARAM", "VALUE").setDescription("override the JENA_PARAM of outputJena jena model config using VALUE").setRequired(false)); // Parameters parser.addArgument(new ArgDef().setShortOption('P').setLongOpt("inputJena-predicates").withParameters(true, "PREDICATE").setDescription("PREDICATE(s) on which, to match. Multiples are done in series not simultaineously.").setRequired(true)); parser.addArgument(new ArgDef().setShortOption('n').setLongOpt("namespace").withParameter(true, "NAMESPACE").setDescription("only match rdf nodes in inputJC whose URI begin with NAMESPACE").setRequired(false)); parser.addArgument(new ArgDef().setShortOption('r').setLongOpt("replace").setDescription("replace input model with changed / output model").setRequired(false)); return parser; } /** * A simple resource smusher based on a supplied inverse-functional property. * @param inputJC - model to operate on * @param subsJC model to hold subtractions * @param addsJC model to hold additions * @param property - property for smush * @param ns - filter on resources addressed (if null then applied to whole model) */ public static void findSmushResourceChanges(JenaConnect inputJC, JenaConnect subsJC, JenaConnect addsJC, String property, String ns) { log.debug("Smushing on property <" + property + "> within "+((ns != null )?"namespace <"+ ns + ">":"any namespace")); Model inModel = inputJC.getJenaModel(); Model subsModel = subsJC.getJenaModel(); Model addsModel = addsJC.getJenaModel(); Property prop = inModel.createProperty(property); inModel.enterCriticalSection(Lock.READ); try { NodeIterator objIt = inModel.listObjectsOfProperty(prop); try { while(objIt.hasNext()) { RDFNode obj = objIt.next(); ResIterator subjIt = inModel.listSubjectsWithProperty(prop, obj); try { boolean first = true; Resource smushToThisResource = null; while (subjIt.hasNext()) { Resource subj = subjIt.next(); if(subj.getNameSpace().equals(ns) || ns == null){ if (first) { smushToThisResource = subj; first = false; log.debug("Smush running for <"+subj+">"); } else { log.trace("Smushing <"+subj+"> into <"+smushToThisResource+">"); StmtIterator stmtIt = inModel.listStatements(subj,(Property)null,(RDFNode)null); try { while(stmtIt.hasNext()) { Statement stmt = stmtIt.next(); log.trace("Changing <"+stmt.getPredicate()+"> <"+stmt.getObject()+"> from <"+stmt.getSubject()+"> to <"+smushToThisResource+">"); subsModel.add(stmt.getSubject(), stmt.getPredicate(), stmt.getObject()); addsModel.add(smushToThisResource, stmt.getPredicate(), stmt.getObject()); } } finally { stmtIt.close(); } stmtIt = inModel.listStatements((Resource) null, (Property)null, subj); try { while(stmtIt.hasNext()) { Statement stmt = stmtIt.next(); log.trace("Changing <"+stmt.getSubject()+"> <"+stmt.getPredicate()+"> from <"+stmt.getObject()+"> to <"+smushToThisResource+">"); subsModel.add(stmt.getSubject(), stmt.getPredicate(), stmt.getObject()); addsModel.add(stmt.getSubject(), stmt.getPredicate(), smushToThisResource); } } finally { stmtIt.close(); } } } } } finally { subjIt.close(); } } } finally { objIt.close(); } } finally { inModel.leaveCriticalSection(); } } /** * Execute is that method where the smushResoures method is ran for each predicate. */ public void execute() { JenaConnect subsJC = new MemJenaConnect(); JenaConnect addsJC = new MemJenaConnect(); for(String runName : this.inputPredicates) { findSmushResourceChanges(this.inputJC, subsJC, addsJC, runName, this.namespace); } if(this.inPlace){ this.inputJC.removeRdfFromJC(subsJC); this.inputJC.loadRdfFromJC(addsJC); } if(this.outputJena != null) { this.outputJena.loadRdfFromJC(this.inputJC); this.outputJena.removeRdfFromJC(subsJC); this.outputJena.loadRdfFromJC(addsJC); this.outputJena.sync(); } this.inputJC.sync(); } /** * Main method * @param args command line arguments */ public static void main(String... args) { Exception error = null; try { InitLog.initLogger(args, getParser()); log.info(getParser().getAppName() + ": Start"); new Smush(args).execute(); } catch(IllegalArgumentException e) { log.error(e.getMessage()); log.debug("Stacktrace:",e); System.out.println(getParser().getUsage()); error = e; } catch(UsageException e) { log.info("Printing Usage:"); System.out.println(getParser().getUsage()); error = e; } catch(Exception e) { log.error(e.getMessage()); log.debug("Stacktrace:",e); error = e; } finally { log.info(getParser().getAppName() + ": End"); if(error != null) { System.exit(1); } } } }