// This file is part of Nanobrowser. // Copyright 2012, Tobias Kuhn, http://www.tkuhn.ch // // Nanobrowser 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. // // Nanobrowser 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 Nanobrowser. // If not, see http://www.gnu.org/licenses/. package ch.tkuhn.nanobrowser; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.trustyuri.rdf.TransformRdf; import org.openrdf.model.BNode; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.query.BindingSet; import org.openrdf.rio.RDFFormat; import com.google.common.collect.ImmutableList; public class SentenceElement extends ThingElement { private static final long serialVersionUID = -7967327315454171639L; public static final String TYPE_URI = "http://purl.org/nanopub/x/AIDA-Sentence"; public static final String AIDA_URI_BASE = "http://purl.org/aida/"; // TODO: express this in OWL private static final List<String> symmetricRelations = ImmutableList.of( "http://purl.org/nanopub/x/hasSameMeaning", "http://purl.org/nanopub/x/hasDifferentMeaning", "http://purl.org/nanopub/x/hasOppositeMeaning", "http://purl.org/nanopub/x/hasNonoppositeMeaning", "http://purl.org/nanopub/x/hasConflictingMeaning", "http://purl.org/nanopub/x/hasConsistentMeaning", "http://purl.org/nanopub/x/hasRelatedMeaning", "http://purl.org/nanopub/x/hasUnrelatedMeaning" ); public SentenceElement(String uri) { super(uri); } public static SentenceElement withText(String text) throws AidaException { if (text == null) { throw new AidaException("Empty sentence."); } text = text.replaceAll("\\s+", " ").replaceAll("^ ", "").replaceAll(" $", ""); if (text.isEmpty()) { throw new AidaException("Empty sentence."); } else if (text.matches(".*[a-z]+://.*")) { throw new AidaException("The sentence may not contain URIs."); } else if (text.indexOf(" ") == -1) { throw new AidaException("The sentence must consist of at least two words."); } else if (text.length() < 10) { throw new AidaException("The sentence is too short: it needs at least ten characters."); } else if (text.length() > 250) { throw new AidaException("The sentence is too long: at most 250 characters are allowed."); } else if (!text.substring(text.length()-1).equals(".")) { throw new AidaException("The sentence has to end with a full stop."); } try { return new SentenceElement(AIDA_URI_BASE + URLEncoder.encode(text, "UTF8")); } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); } return null; } private static final String allSentencesQuery = "select distinct ?s where { {?a npx:asSentence ?s} " + "filter regex(str(?s), \"^http://purl.org/aida/\", \"i\") }"; public static List<SentenceElement> getAllSentences(int limit) { String lm = (limit >= 0) ? " limit " + limit : ""; List<BindingSet> result = TripleStoreAccess.getTuples(allSentencesQuery + lm); List<SentenceElement> l = new ArrayList<SentenceElement>(); for (BindingSet bs : result) { Value v = bs.getValue("s"); if (v instanceof BNode) continue; l.add(new SentenceElement(v.stringValue())); } return l; } public static boolean isSentenceURI(String uri) { return uri.startsWith(AIDA_URI_BASE); } private static final String nanopubsQuery = "select distinct ?p where { ?p np:hasAssertion ?a . ?a npx:asSentence <@> . " + "?p np:hasPublicationInfo ?info . graph ?info { ?p dc:created ?d } }"; public List<NanopubElement> getNanopubs() { String query = nanopubsQuery.replaceAll("@", getURI()); List<BindingSet> result = TripleStoreAccess.getTuples(query); List<NanopubElement> nanopubs = new ArrayList<NanopubElement>(); for (BindingSet bs : result) { Value v = bs.getValue("p"); if (v instanceof BNode) continue; nanopubs.add(new NanopubElement(v.stringValue())); } return nanopubs; } private static final String opinionsQuery = "select ?p ?t ?pub where { " + "?pub np:hasAssertion ?ass . ?pub np:hasPublicationInfo ?info . " + "graph ?info { ?pub dc:created ?d } . " + "graph ?ass { ?p npx:hasOpinion ?o . ?o rdf:type ?t . ?o npx:opinionOn <@> } " + "} order by asc(?d)"; public List<Opinion> getOpinions(boolean excludeNullOpinions) { String query = opinionsQuery.replaceAll("@", getURI()); List<BindingSet> result = TripleStoreAccess.getTuples(query); Map<String, Opinion> opinionMap = new HashMap<String, Opinion>(); for (BindingSet bs : result) { Value p = bs.getValue("p"); Value t = bs.getValue("t"); Value pub = bs.getValue("pub"); if (p instanceof BNode || t instanceof BNode || pub instanceof BNode) continue; if (excludeNullOpinions && t.stringValue().equals(Opinion.NULL_TYPE)) { opinionMap.remove(p.stringValue()); } else { AgentElement agent = new AgentElement(p.stringValue()); NanopubElement nanopub = new NanopubElement(pub.stringValue()); Opinion opinion = new Opinion(agent, t.stringValue(), this, nanopub); opinionMap.put(p.stringValue(), opinion); } } return new ArrayList<Opinion>(opinionMap.values()); } private static final String relatedSentencesQuery = "select ?s ?r ?o ?pub where { { " + "{ ?pub np:hasAssertion ?ass . graph ?ass { <@> ?r ?o } } union " + "{ ?pub np:hasAssertion ?ass . graph ?ass { ?s ?r <@> } } " + "} . ?pub np:hasPublicationInfo ?info. graph ?info { ?pub dc:created ?d } . " + "filter regex(str(?r), \"^http://purl.org/nanopub/x/(isImprovedVersionOf|has.*Meaning)\", \"i\") " + "} order by asc(?d)"; public List<Triple<SentenceElement,SentenceElement>> getRelatedSentences() { String query = relatedSentencesQuery.replaceAll("@", getURI()); List<BindingSet> result = TripleStoreAccess.getTuples(query); Map<String, Triple<SentenceElement,SentenceElement>> sentencesMap = new HashMap<String, Triple<SentenceElement,SentenceElement>>(); for (BindingSet bs : result) { Value s = bs.getValue("s"); String r = bs.getValue("r").stringValue(); if (s == null) { if (!symmetricRelations.contains(r)) continue; s = bs.getValue("o"); } Value pub = bs.getValue("pub"); if (s instanceof BNode || s instanceof BNode || pub instanceof BNode) continue; if (!s.stringValue().equals(getURI())) { SentenceElement sentence = new SentenceElement(s.stringValue()); Triple<SentenceElement,SentenceElement> t = new Triple<SentenceElement,SentenceElement>( sentence, new ThingElement(r), this, new NanopubElement(pub.stringValue())); sentencesMap.put(sentence.getURI(), t); } } return new ArrayList<Triple<SentenceElement,SentenceElement>>(sentencesMap.values()); } public URI publish(AgentElement author, boolean isExample, String provenance) { String types = ""; URI uri = null; if (isExample) types = ": a npx:ExampleNanopub ."; try { String pubURI = NanobrowserApplication.getProperty("nanopub-server-baseuri") + "pub/"; String nanopubString = NanopubElement.getTemplate("publish") .replaceAll("@ROOT@", pubURI) .replaceAll("@PUBINFO@", types) .replaceAll("@AGENT@", author.getURI()) .replaceAll("@SENTENCE@", getURI()) .replaceAll("@DATETIME@", NanobrowserApplication.getTimestamp()) .replaceAll("@PROV@", provenance); ByteArrayOutputStream out = new ByteArrayOutputStream(); uri = TransformRdf.transform(new ByteArrayInputStream(nanopubString.getBytes()), RDFFormat.TRIG, out, pubURI); String query = TripleStoreAccess.getNanopublishQuery(new ByteArrayInputStream(out.toByteArray())); TripleStoreAccess.runUpdateQuery(query); } catch (Exception ex) { ex.printStackTrace(); } return uri; } public void publishSentenceRelation(SentenceRelation rel, SentenceElement other, AgentElement author) { try { String pubURI = NanobrowserApplication.getProperty("nanopub-server-baseuri") + "meta/"; String nanopubString = NanopubElement.getTemplate("sentencerel") .replaceAll("@ROOT@", pubURI) .replaceAll("@AGENT@", author.getURI()) .replaceAll("@SENTENCE1@", other.getURI()) .replaceAll("@RELATION@", rel.getURI()) .replaceAll("@SENTENCE2@", getURI()) .replaceAll("@DATETIME@", NanobrowserApplication.getTimestamp()); ByteArrayOutputStream out = new ByteArrayOutputStream(); TransformRdf.transform(new ByteArrayInputStream(nanopubString.getBytes()), RDFFormat.TRIG, out, pubURI); String query = TripleStoreAccess.getNanopublishQuery(new ByteArrayInputStream(out.toByteArray())); TripleStoreAccess.runUpdateQuery(query); } catch (Exception ex) { ex.printStackTrace(); } } private static final String getAllOpinionGraphsQuery = "select ?g ?ass ?info where { graph ?ass { ?a npx:hasSameMeaning ?c } . " + "graph ?g { ?pub np:hasAssertion ?ass . ?pub np:hasPublicationInfo ?info } }"; private static final String deleteGraphQuery = "delete from graph identified by <@> { ?a ?b ?c } where { ?a ?b ?c }"; public static void deleteAllOpinionNanopubs() { for (BindingSet bs : TripleStoreAccess.getTuples(getAllOpinionGraphsQuery)) { TripleStoreAccess.runUpdateQuery(deleteGraphQuery.replaceAll("@", bs.getValue("g").stringValue())); TripleStoreAccess.runUpdateQuery(deleteGraphQuery.replaceAll("@", bs.getValue("ass").stringValue())); TripleStoreAccess.runUpdateQuery(deleteGraphQuery.replaceAll("@", bs.getValue("info").stringValue())); } } private static final String textSearchQuery = "select distinct ?s where { { ?a npx:asSentence ?s } union { ?x npx:hasSameMeaning ?s } . " + "filter regex(str(?s), \"@R\", \"i\") }"; private static final String textSearchRegex = "^http://purl.org/aida/.*@W"; // TODO Use proper text indexing public static List<SentenceElement> search(String searchText, int limit) { searchText = searchText .replaceAll("\\s+", " ") .replaceFirst("^ ", "") .replaceFirst(" $", "") .replaceAll("[^a-zA-Z0-9 ]", "") .replaceAll(" ", "(%20|~+)"); String query = textSearchQuery .replaceAll("@R", textSearchRegex) .replaceAll("@W", searchText) .replaceAll("~", "\\\\\\\\") + ((limit >= 0) ? " limit " + limit : "" ); List<BindingSet> result = TripleStoreAccess.getTuples(query); List<SentenceElement> sentences = new ArrayList<SentenceElement>(); for (BindingSet bs : result) { Value v = bs.getValue("s"); sentences.add(new SentenceElement(v.stringValue())); } return sentences; } public String getSentenceText() { try { return URLDecoder.decode(getShortName(), "UTF8"); } catch (Exception ex) { ex.printStackTrace(); } return null; } public SentenceItem createGUIItem(String id, int guiItemStyle) { return new SentenceItem(id, this, guiItemStyle); } }