//The MIT License
//
// Copyright (c) 2004 Mindswap Research Group, University of Maryland, College Park
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
package org.mindswap.swoop.renderer.entity;
import java.awt.Component;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.mindswap.swoop.SwoopDisplayPanel;
import org.mindswap.swoop.SwoopModel;
import org.mindswap.swoop.TermsDisplay;
import org.mindswap.swoop.renderer.BaseEntityRenderer;
import org.mindswap.swoop.renderer.SwoopEditableRenderer;
import org.mindswap.swoop.renderer.SwoopEntityRenderer;
import org.mindswap.swoop.renderer.SwoopRenderingVisitor;
import org.mindswap.swoop.utils.HyperDAML;
import org.semanticweb.owl.impl.model.OWLDataFactoryImpl;
import org.semanticweb.owl.impl.model.OWLIndividualImpl;
import org.semanticweb.owl.impl.model.OWLObjectPropertyInstanceImpl;
import org.semanticweb.owl.impl.model.OWLObjectSomeRestrictionImpl;
import org.semanticweb.owl.impl.model.OWLOntologyImpl;
import org.semanticweb.owl.io.RendererException;
import org.semanticweb.owl.io.vocabulary.OWLVocabularyAdapter;
import org.semanticweb.owl.io.vocabulary.RDFSVocabularyAdapter;
import org.semanticweb.owl.io.vocabulary.RDFVocabularyAdapter;
import org.semanticweb.owl.model.OWLAnnotationInstance;
import org.semanticweb.owl.model.OWLAnnotationProperty;
import org.semanticweb.owl.model.OWLClass;
import org.semanticweb.owl.model.OWLDataFactory;
import org.semanticweb.owl.model.OWLDataProperty;
import org.semanticweb.owl.model.OWLDataRange;
import org.semanticweb.owl.model.OWLDataType;
import org.semanticweb.owl.model.OWLDataValue;
import org.semanticweb.owl.model.OWLDescription;
import org.semanticweb.owl.model.OWLDifferentIndividualsAxiom;
import org.semanticweb.owl.model.OWLEntity;
import org.semanticweb.owl.model.OWLEnumeration;
import org.semanticweb.owl.model.OWLException;
import org.semanticweb.owl.model.OWLIndividual;
import org.semanticweb.owl.model.OWLIndividualAxiom;
import org.semanticweb.owl.model.OWLNamedObject;
import org.semanticweb.owl.model.OWLObjectProperty;
import org.semanticweb.owl.model.OWLObjectPropertyInstance;
import org.semanticweb.owl.model.OWLOntology;
import org.semanticweb.owl.model.OWLProperty;
import org.semanticweb.owl.model.OWLPropertyAxiom;
public class NLEntityRenderer extends BaseEntityRenderer implements SwoopEntityRenderer {
public static final String INDENT = " ";
protected Set allURIs;
private List shortNames;
private Map known;
private int reservedNames;
private String contentType = "text/html";
public String getContentType() {
return contentType;
}
public String getName() {
return "Natural Language";
}
public SwoopRenderingVisitor createVisitor() {
return new NLVisitor(this, swoopModel);
}
public void render(OWLEntity entity, SwoopModel swoopModel, Writer writer) throws RendererException {
super.render(entity, swoopModel, writer);
}
/*
* SwoopRenderer method
*
*/
public Component getDisplayComponent( SwoopDisplayPanel panel )
{
if (!(panel instanceof TermsDisplay ))
throw new IllegalArgumentException();
return super.getEditorPane( this.getContentType(), (TermsDisplay)panel );
}
protected void renderAnnotationContent(Object o) throws OWLException {
if (o instanceof URI) {
print(o.toString());
} else if (o instanceof OWLIndividual) {
print(((OWLIndividual) o).getURI().toString());
} else if (o instanceof OWLDataValue) {
OWLDataValue dv = (OWLDataValue) o;
print("\"" + escape(dv.getValue()) + "\"");
/* Only show it if it's not string */
URI dvdt = dv.getURI();
String dvlang = dv.getLang();
if (dvdt != null) {
print("^^" + dvdt);
// if (!dv.getURI().toString().equals(
// XMLSchemaSimpleDatatypeVocabulary.INSTANCE.getString())) {
// print( "^^" + dv.getURI() );
// }
} else {
if (dvlang != null) {
print("@" + dvlang);
}
}
} else {
print("\""+o.toString()+"\"");
}
}
/** Render the annotations for an object */
protected void renderAnnotations(OWLNamedObject object ) throws OWLException {
/* Bit nasty this -- annotations result in a new axiom */
if (!object.getAnnotations(reasoner.getOntology()).isEmpty()) {
for (Iterator it = object.getAnnotations(reasoner.getOntology()).iterator(); it.hasNext();) {
OWLAnnotationInstance oai = (OWLAnnotationInstance) it.next();
//print("(" + getShortForm(oai.getProperty().getURI()) + " ");
/* Just whack out the content. This isn't quite right... */
renderAnnotationContent(oai.getContent());
// print( "\"" + oai.getContent() + "\"" );
print(" ");
/* Do we need to do this??? */
visitor.reset();
oai.accept(visitor);
// if (it.hasNext()) {
// println();
// }
}
}
}
protected void renderClass(OWLClass clazz) throws OWLException {
boolean done = false;
String className = this.getShortForm(clazz.getURI());
// ** global reset tree **
((NLVisitor) visitor).resetNLTree(className, NLVisitor.LINK_EQUIVALENT, 0);
if(!clazz.getAnnotations(reasoner.getOntology()).isEmpty()) {
println("Annotations:");
renderAnnotations(clazz);
println("\n");
done = true;
}
if (clazz.getEquivalentClasses(reasoner.getOntologies()).size() > 0) {
println("Definition: (Necessary and Sufficient Conditions)");
for (Iterator it = clazz.getEquivalentClasses(reasoner.getOntologies()).iterator(); it.hasNext();) {
OWLDescription eq = (OWLDescription) it.next();
eq.accept(visitor);
// ** local reset tree **
((NLVisitor) visitor).setLinkContext(NLVisitor.LINK_EQUIVALENT);
((NLVisitor) visitor).resetParent();
}
visitor.reset();
((NLVisitor) visitor).printTree();
// do post processing here, before printing out the final string
print(postProcess(visitor.result()));
done = true;
println();
println();
}
if (!clazz.getSuperClasses(reasoner.getOntologies()).isEmpty()) {
// ** global reset tree **
((NLVisitor) visitor).resetNLTree(className, NLVisitor.LINK_SUBCLASS, 0);
println("Details: (Necessary Conditions)");
for (Iterator it = clazz.getSuperClasses(reasoner.getOntologies()).iterator(); it.hasNext();) {
OWLDescription eq = (OWLDescription) it.next();
eq.accept(visitor);
// ** local reset tree **
((NLVisitor) visitor).setLinkContext(NLVisitor.LINK_SUBCLASS);
((NLVisitor) visitor).resetParent();
}
visitor.reset();
((NLVisitor) visitor).printTree();
print(postProcess(visitor.result()));
done = true;
}
/*
* This has changed -- used to be simply a oneof in the class definition. We now get a
* special keyword in the vocabulary
*/
for (Iterator it = clazz.getEnumerations(reasoner.getOntologies()).iterator(); it.hasNext();) {
OWLDescription eq = (OWLDescription) it.next();
println(
"EnumeratedClass"
+ className
);
/* We know that the description has to be a oneof */
try {
OWLEnumeration enumeration = (OWLEnumeration) eq;
for (Iterator iit = enumeration.getIndividuals().iterator(); iit.hasNext();) {
OWLIndividual desc = (OWLIndividual) iit.next();
visitor.reset();
desc.accept(visitor);
print(" " + postProcess(visitor.result()));
// if (iit.hasNext()) {
// print(" ");
// }
}
println("");
done = true;
} catch (ClassCastException ex) {
throw new RendererException(ex.getMessage());
}
}
if (!done) {
/* We need to give at least an empty definition */
println(
className
+ " is an undefined class."
);
}
}
protected void renderIndividual(OWLIndividual ind) throws OWLException {
OWLIndividualImpl test = (OWLIndividualImpl) ind;
OWLDataFactoryImpl factory = (OWLDataFactoryImpl) test.getOWLDataFactory();
boolean done = false;
String indName = this.getShortForm(ind.getURI());
// ** global reset tree **
//((NLVisitor) visitor).resetNLTree(indName, NLVisitor.LINK_EQUIVALENT);
if(!ind.getAnnotations(reasoner.getOntology()).isEmpty()) {
println("Annotations:");
renderAnnotations(ind);
println("\n");
done = true;
}
// ** global reset tree **
((NLVisitor) visitor).resetNLTree(indName, NLVisitor.LINK_SUBCLASS, 3);
println("Details:");
for ( Iterator it = ind.getTypes( reasoner.getOntologies() ).iterator(); it.hasNext(); ) {
OWLDescription eq = (OWLDescription) it.next();
eq.accept(visitor);
// ** local reset tree **
((NLVisitor) visitor).setLinkContext(NLVisitor.LINK_SUBCLASS);
((NLVisitor) visitor).resetParent();
}
// ** global reset tree **
//((NLVisitor) visitor).resetNLTree(indName, NLVisitor.LINK_SOMEVALUES, 3);
((NLVisitor) visitor).setLinkContext(NLVisitor.LINK_SUBCLASS);
((NLVisitor) visitor).resetParent();
Map oPropVals = ind.getObjectPropertyValues( reasoner.getOntologies() );
for ( Iterator it = oPropVals.keySet().iterator(); it.hasNext(); ) {
OWLObjectProperty currKey = (OWLObjectProperty) it.next();
Set targetSet = (Set) oPropVals.get( currKey );
System.out.println( targetSet.getClass() );
for ( Iterator t = targetSet.iterator(); t.hasNext(); ) {
OWLIndividual target = (OWLIndividual) t.next();
System.out.println( target + " ++ " + target.getClass() );
OWLObjectPropertyInstance propInst = new OWLObjectPropertyInstanceImpl( factory, ind, currKey, target );
propInst.accept( visitor );
}
// ** local reset tree **
((NLVisitor) visitor).setLinkContext(NLVisitor.LINK_SUBCLASS);
((NLVisitor) visitor).resetParent();
}
visitor.reset();
((NLVisitor) visitor).printTree();
print(postProcess(visitor.result()));
done = true;
if (!done) {
/* We need to give at least an empty definition */
println(
indName
+ " is an undefined individual."
);
}
return;
}
protected void renderAnnotationProperty(OWLAnnotationProperty prop) throws OWLException {
println(" <b>AnnotationProperty</b>(" + getShortForm(prop.getURI()) + ")");
}
protected void renderObjectProperty(OWLObjectProperty prop) throws OWLException {
// TODO: Not implemented yet
pw.print("Natural Language rendering of properties not implemented yet");
return;
}
protected void renderDataProperty(OWLDataProperty prop) throws OWLException {
// TODO: Not implemented yet
pw.print("Natural Language rendering of properties not implemented yet");
return;
}
protected void renderDataType(OWLDataType datatype) throws OWLException {
println("<b>Datatype</b>(" + getShortForm(datatype.getURI()) + ")");
}
protected void println() {
pw.println();
}
protected void renderEntity() throws OWLException {
// turn qnames off in Natural Language mode
//if (swoopModel.showQNames()) swoopModel.setShowQNames(false);
print("<html><body><code><pre><FONT FACE=\""+swoopModel.getFontFace()+"\" SIZE="+fontSize+">");
super.renderEntity();
print("</FONT></pre></code></body></html>");
}
protected void renderPropertyAxiom(OWLPropertyAxiom axiom) throws OWLException {
}
protected String sanitize(String str) {
str = str.replaceAll("&", "&");
str = str.replaceAll("<", "<");
str = str.replaceAll(">", ">");
return str;
}
protected void renderIndividualAxiom(OWLIndividualAxiom axiom) throws OWLException {
OWLIndividual ind = (OWLIndividual) entity;
pw.println("<rdf:Description rdf:about=\"" + ind.getURI() + "\">");
Set individuals = axiom.getIndividuals();
for (Iterator it = individuals.iterator(); it.hasNext(); ) {
OWLIndividual oi = (OWLIndividual) it.next();
if(oi.equals(ind)) continue;
pw.println(INDENT + "<"
+ (axiom instanceof OWLDifferentIndividualsAxiom ? "owl:differentFrom" : "owl:sameAs")
+ " rdf:resource=\""
// + getShortForm(oi.getURI())
+ oi.getURI()
+ "\"/>");
}
}
public String getShortForm(URI uri) throws OWLException {
String sf = shortForm(uri);
if (sf.indexOf(":")>=0) sf = sf.substring(sf.indexOf(":")+1, sf.length());
return sf;
}
protected void renderForeignEntity(OWLEntity ent) throws OWLException {
}
// eliminate the duplicate words, example has has
// split the string into words, eliminate the ones that are duplicate and rebuild it
// right now it only works for 1 word , but it can be extended if needed to handle n-grams
// it only works when the repetition occurs immediately after, as its purpose is to clean up
// the has has stuff
protected String eliminateDuplicateWords(String str) {
String[] tokens = str.split("( )+");
String strTokens = "";
for (int i=0; i<tokens.length-1;i++) {
if (!tokens[i].equalsIgnoreCase(tokens[i+1]))
strTokens += (tokens[i] + " ");
}
strTokens += tokens[tokens.length-1];
return strTokens;
}
protected boolean startsWithVowel(String str) {
if (str.toLowerCase().charAt(0) == 'a' || str.charAt(0) == 'e' ||
str.charAt(0) == 'i' || str.charAt(0) == 'o' ||
str.charAt(0) == 'u' )
return true;
else return false;
}
protected String fixAorAN(String str) {
String[] tokens = str.split("( )+");
String strTokens = "";
for (int i=0; i<tokens.length-1;i++) {
if (tokens[i].equals("a") && startsWithVowel(tokens[i+1]))
strTokens += ("an ");
else
strTokens += (tokens[i] + " ");
}
strTokens += tokens[tokens.length-1];
return strTokens;
}
protected String removeThingClause(String str) {
str = str.replaceAll("and is a thing", "");
str = str.replaceAll("and is not a thing", "");
return str;
}
protected String insertHyperlinks(String tmp) {
//TODO
// tmp = " " + tmp +" ";
// Hashtable hLinks = ((NLVisitor) visitor).hyperlinkMap;
// for (Iterator iter = hLinks.keySet().iterator(); iter.hasNext(); ) {
// String key = iter.next().toString();
// String value = hLinks.get(key).toString();
// tmp = tmp.replaceAll(" "+key+" ", value);
// }
return tmp;
}
protected String removeHasIsClause(String str) {
str = str.replaceAll("has is", "is");
return str;
}
protected String insertBreaks(String str) {
int pos = 0;
String newStr = "";
int breakPos = 80;
while (str.length()>pos+breakPos) {
newStr += str.substring(pos, pos+breakPos);
if (!newStr.endsWith(" ")) newStr += "-";
newStr += "<br>";
str = str.substring(pos+breakPos, str.length());
pos += breakPos;
}
newStr += str;
return newStr;
}
protected String postProcess(String str) {
String tmp = eliminateDuplicateWords(str);
tmp = fixAorAN(tmp);
tmp = removeThingClause(tmp);
// tmp = removeHasIsClause(tmp);
// tmp = insertBreaks(tmp);
//TODO: Need to improve the hyperlinking code
tmp = insertHyperlinks(tmp); // do this right at the end of post processing
return tmp;
}
}