2015/05/28更新 代码在 https://github.com/zhoujiagen/semanticWebTutorialUsingJena

前言

该手册参考和扩展“Hebeler J, Fisher M, et al.Web 3.0与Semantic Web编程[M]. 清华大学出版社, 北京.2010.”中的HelloWorld。

在描述中,不将Web本体与描述逻辑的术语做区分,尽量采用Web本体的英文表述,下面列出Web本体与描述逻辑的术语对应关系:

Web本体描述逻辑
Class  Concept

Property

Object Property

Data  Property

Role
IndividualInstance

计划记录类容:

1 Jena RDF API使用

2 用SPARQL做RDF navigate

3 手动本体对准(ontology alignment)

4 Jena OWL推理(Inference)

5 Jena Rule推理

内容

0 数据!数据!数据!

数据准备思路:将TBox与ABox分开,两个本体(TBox1, ABox1, TBox2, ABox2),其中ABox1与TBox1的命名空间不同,ABox2与TBox2的命名空间相同。

TBox1:FOAF的规范(http://xmlns.com/foaf/spec/)

ABox1:生成自FOAF数据的链接(http://www.ldodds.com/foaf/foaf-a-matic)

TBox2, ABox2: 如何用Protege建立本体(http://protegewiki.stanford.edu/wiki/Ontology101, http://130.88.198.11/tutorials/protegeowltutorial/),话外Protege官网现在变得好炫.

1 Jena RDF API使用

这里只涉及如何用RDF文件填充Model,code sketch如下:

Model model = ModelFactory.createDefaultModel();
InputStream is = FileManager.get().open("E:/码农的世界/码农的自我修养/semantic web/workspace/foafData.rdf");
model.read(is, "http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl");
is.close();

2 用SPARQL做RDF navigate

SPARQL的全称是SPARQL Protocol and RDF Query Language,既是一个定义如何执行RDF查询、更新操作的协议,也是一个RDF查询语言。

Jena中的Graph抽象比较好,这是个类似于关系型数据库中视图的概念,只是Graph中基础的数据结构是形如<Subject, Predicate, Object>的Triple,SPARQL作为查询语言,就是在Graph中Triple中定位和导航数据。

Jena ARQ API的code sketch如下:

//组装查询字符串StringBuilder sb = new StringBuilder();
sb.append("PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>").append(NEWLINE).append("PREFIX owl: <http://www.w3.org/2002/07/owl#>")
                .append(NEWLINE).append("PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>").append(NEWLINE)
                .append("PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>").append(NEWLINE).append("PREFIX foaf: <http://xmlns.com/foaf/0.1/>")
                .append(NEWLINE).append("PREFIX myfoaf: <http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#>").append(NEWLINE)
                .append("PREFIX people: <http://www.people.com#>").append(NEWLINE);
sb.append("SELECT DISTINCT ?name").append(NEWLINE).append("WHERE { myfoaf:me foaf:name ?name}").append(NEWLINE);
//执行查询
sparql(model, sb.toString(), "?name");

private static void sparql(Model model, String query, String field) {
        Query q = QueryFactory.create(query);
        QueryExecution qexec = QueryExecutionFactory.create(q, model);
        System.out.println("Plan to run SPARQL query: ");
        System.out.println(BOUNDARY);
        System.out.println(query);
        System.out.println(BOUNDARY);
        ResultSet rs = qexec.execSelect();
        while (rs.hasNext()) {
            QuerySolution qs = rs.nextSolution();
            RDFNode name = qs.get(field);// 暂用RDFNode
            if (name != null) {
                System.out.println("Hello to " + name);
            } else {
                System.out.println("No friends found!");
            }
        }
        qexec.close();
    }

3 手动本体对准(ontology alignment)

这个例子涉及4个对准:

两个命名空间中的Class等价、两个命名空间中Property等价、两个命名空间中Property蕴涵和两个命名空间中Individual等价(异名同义)。

下图中红色箭头表示本体对准关系,没有展示Property之间的对准关系(该图用Graphviz手工生成,自动生成在下一步的计划中):

Jena语义Web开发101-LMLPHP

本体对准code sketch如下,其中Model shema仅加载两个本体的TBox:

// [1]people:Individual = foaf:Person
Resource resource = schema.createResource(PEOPLE_NS + "#Individual");
Property property = schema.createProperty(OWL_URL + "equivalentClass");
Resource object = schema.createResource(FOAF_URL + "Person");
schema.add(resource, property, object);

// [2]people:hasName = foaf:name
resource = schema.createResource(PEOPLE_NS + "#hasName");
property = schema.createProperty(OWL_URL + "equivalentProperty");
object = schema.createResource(FOAF_URL + "name");
schema.add(resource, property, object);

// [3]people:hasFriend < foaf:knows
resource = schema.createResource(PEOPLE_NS + "#hasFriend");
property = schema.createProperty(RDFS_URL + "subPropertyOf");
object = schema.createResource(FOAF_URL + "knows");
schema.add(resource, property, object);

// [4]myfoaf:me = people:individual_5
resource = schema.createResource(MYFOAF_NS + "#me");
property = schema.createProperty(OWL_URL + "sameAs");
object = schema.createResource(PEOPLE_NS + "#individual_5");
schema.add(resource, property, object);
    

4 Jena OWL推理(Inference)

下表是OWL支持的推理形式的概要:

OWL构造/描述逻辑支持的推理

语义构造

推理规则

概念包含

C DC(a) ⇒ D(a)

概念互斥

C D ⊑ ⊥ ∧ C(a) ∧ D(b) ⇒ a b

关系包含

R SR(a, b) ⇒ S(a, b)

关系定义域

domain(R, C) ∧ R(a, b) ⇒ C(a)

关系值域

range(R, C) ∧ R(a, b) ⇒ C(b)

自反关系

Rid(C) ⇒ R(a, a)

传递关系

R(a, b) ∧ R(b, c) ⇒ R(a, c)

对称关系

RR⁻∧ R(a, b) ⇒ R(b, a)

关系组合

RS R ′∧ R(a, b) ∧ S(b, c) ⇒ R′(a, c)

互逆关系

R(a, b) ⇒ R⁻(a, b)  R⁻(a, b) ⇒ R(a, b)

函数关系

R(a, b) ∧ R(a, c) ⇒ bc

HasKey(C, R)∧ C(a) ∧ C(b) ∧ R(a, c) ∧ R(b, c) ⇒ bc

Jena API关于OWL推理的基本策略是用Reasoner绑定TBox,在代表ABox的Model对象基础上创建出推理后模型IntModel对象。

code sketch如下:

 InfModel inferredModel = null;

Reasoner reasoner = ReasonerRegistry.getOWLReasoner();
reasoner = reasoner.bindSchema(schema);// tbox
inferredModel = ModelFactory.createInfModel(reasoner, friendsModel);// abox

5 Jena Rule推理

尽管语义Web事实上的规则语言标准是SWRL,但Jena提供了自己的规则语言,一些值得关注的特性是Jena规则支持前向推理和后向推理、可以动态生成匿名节点。

使用Jena Rule执行推理的code sketch如下:

String rule = "[gmailFriend: (?person <http://xmlns.com/foaf/0.1/mbox_sha1sum> ?email), strConcat(?email, ?lit), regex(?lit, '(.*gmail.com)')"
                + "-> (?person " + RDF_TYPE + " <http://www.people.com#GmailPerson>)]";
Reasoner ruleReasoner = new GenericRuleReasoner(Rule.parseRules(rule));
ruleReasoner = ruleReasoner.bindSchema(schema);
inferredModel = ModelFactory.createInfModel(ruleReasoner, friendsModel);
    

6 总结

本手册记录了Jena如何加载RDF文件填充Model、如何用SPARQL导航Model、如何执行OWL和Jena Rule推理,也简单介绍了本体对准。

数据

foafSchema.rdf       (NS=http://xmlns.com/foaf/0.1/)

下载链接为http://xmlns.com/foaf/spec/index.rdf,将其重名为foafSchema.rdf。

foafData.rdf            (NS=http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl)

<?xml version="1.0"?>

<!DOCTYPE rdf:RDF [
    <!ENTITY foaf "http://xmlns.com/foaf/0.1/" >
    <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" >
    <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#" >
    <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
]>

<rdf:RDF xmlns="http://www.w3.org/2002/07/owl#"
     xml:base="http://www.w3.org/2002/07/owl"
     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
     xmlns:foaf="http://xmlns.com/foaf/0.1/"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <Ontology rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl">
        <imports rdf:resource="http://xmlns.com/foaf/0.1/"/>
    </Ontology>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Annotation properties
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <AnnotationProperty rdf:about="&foaf;givenname"/>
    <AnnotationProperty rdf:about="&foaf;mbox_sha1sum"/>
    <AnnotationProperty rdf:about="&foaf;nick"/>
    <AnnotationProperty rdf:about="&foaf;homepage"/>
    <AnnotationProperty rdf:about="&foaf;depiction"/>
    <AnnotationProperty rdf:about="&foaf;title"/>
    <AnnotationProperty rdf:about="&foaf;workInfoHomepage"/>
    <AnnotationProperty rdf:about="&foaf;workplaceHomepage"/>
    <AnnotationProperty rdf:about="&foaf;family_name"/>
    <AnnotationProperty rdf:about="&foaf;knows"/>
    <AnnotationProperty rdf:about="&foaf;schoolHomepage"/>
    <AnnotationProperty rdf:about="&foaf;phone"/>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Datatypes
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Individuals
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Ontology -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Ontology">
        <rdf:type rdf:resource="http://schema.org/Person"/>
        <foaf:name rdf:datatype="&xsd;string">I. M. Ontology</foaf:name>
        <rdfs:seeAlso rdf:datatype="&xsd;string">http://ont.com</rdfs:seeAlso>
        <foaf:mbox_sha1sum rdf:datatype="&xsd;string">mailto:[email protected]</foaf:mbox_sha1sum>
    </NamedIndividual>

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Reasoner -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Reasoner">
        <rdf:type rdf:resource="http://schema.org/Person"/>
        <foaf:name rdf:datatype="&xsd;string">Ican Reason</foaf:name>
        <rdfs:seeAlso rdf:datatype="&xsd;string">http://reasoner.com</rdfs:seeAlso>
        <foaf:mbox_sha1sum rdf:datatype="&xsd;string">maillto:[email protected]</foaf:mbox_sha1sum>
    </NamedIndividual>

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Statement -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Statement">
        <rdf:type rdf:resource="http://schema.org/Person"/>
        <foaf:name rdf:datatype="&xsd;string">Makea Statement</foaf:name>
        <rdfs:seeAlso rdf:datatype="&xsd;string">http://statement.com</rdfs:seeAlso>
        <foaf:mbox_sha1sum rdf:datatype="&xsd;string">mailto:[email protected]</foaf:mbox_sha1sum>
    </NamedIndividual>

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://blog.sina.com.cn/zhoujiagenontology -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://blog.sina.com.cn/zhoujiagenontology"/>

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://s8.sinaimg.cn/mw690/002RJaAlty6FSYutTO777&amp;690 -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://s8.sinaimg.cn/mw690/002RJaAlty6FSYutTO777&amp;690">
        <rdf:type rdf:resource="&foaf;Image"/>
    </NamedIndividual>

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://www.cqu.edu.cn/ -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://www.cqu.edu.cn/"/>

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://www.founderinternational.com/ -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://www.founderinternational.com/"/>

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#me -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#me">
        <rdf:type rdf:resource="http://schema.org/Person"/>
        <foaf:givenname rdf:datatype="&xsd;string">Semantic</foaf:givenname>
        <foaf:name rdf:datatype="&xsd;string">Semantic Web</foaf:name>
        <foaf:family_name rdf:datatype="&xsd;string">Web</foaf:family_name>
        <foaf:nick rdf:datatype="&xsd;string">Webby</foaf:nick>
        <foaf:title rdf:datatype="&xsd;string">master</foaf:title>
        <foaf:mbox_sha1sum rdf:datatype="&xsd;string">[email protected]</foaf:mbox_sha1sum>
        <foaf:knows rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Ontology"/>
        <foaf:knows rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Reasoner"/>
        <foaf:knows rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#Statement"/>
        <foaf:homepage rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://blog.sina.com.cn/zhoujiagenontology"/>
        <foaf:depiction rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://s8.sinaimg.cn/mw690/002RJaAlty6FSYutTO777&amp;690"/>
        <foaf:schoolHomepage rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://www.cqu.edu.cn/"/>
        <foaf:workplaceHomepage rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://www.founderinternational.com/"/>
        <foaf:workInfoHomepage rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#http://www.founderinternational.com/"/>
        <foaf:phone rdf:resource="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#tel:13552854032"/>
    </NamedIndividual>

    <!-- http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#tel:13552854032 -->

    <NamedIndividual rdf:about="http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#tel:13552854032"/>
</rdf:RDF>

<!-- Generated by the OWL API (version 3.2.5.1912) http://owlapi.sourceforge.net -->

 peopleSchema.rdf (NS=http://www.people.com)

<?xml version="1.0"?>

<!DOCTYPE rdf:RDF [
    <!ENTITY owl "http://www.w3.org/2002/07/owl#" >
    <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" >
    <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#" >
    <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
]>

<rdf:RDF xmlns="http://www.people.com#"
     xml:base="http://www.people.com"
     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
     xmlns:owl="http://www.w3.org/2002/07/owl#"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <owl:Ontology rdf:about="http://www.people.com"/>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Datatypes
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Object Properties
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!-- http://www.people.com#hasFriend -->

    <owl:ObjectProperty rdf:about="http://www.people.com#hasFriend">
        <rdfs:range rdf:resource="http://www.people.com#Individual"/>
        <rdfs:domain rdf:resource="http://www.people.com#Individual"/>
    </owl:ObjectProperty>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Data properties
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!-- http://www.people.com#hasName -->

    <owl:DatatypeProperty rdf:about="http://www.people.com#hasName">
        <rdfs:domain rdf:resource="http://www.people.com#Individual"/>
        <rdfs:range rdf:resource="&xsd;string"/>
    </owl:DatatypeProperty>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Classes
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!-- http://www.people.com#GmailPerson -->

    <owl:Class rdf:about="http://www.people.com#GmailPerson">
        <rdfs:subClassOf rdf:resource="http://www.people.com#Individual"/>
    </owl:Class>

    <!-- http://www.people.com#Individual -->

    <owl:Class rdf:about="http://www.people.com#Individual"/>
</rdf:RDF>

<!-- Generated by the OWL API (version 3.2.5.1912) http://owlapi.sourceforge.net -->

peopleData.rdf       (NS=http://www.people.com)

<?xml version="1.0"?>

<!DOCTYPE rdf:RDF [
    <!ENTITY www "http://www.people.com#" >
    <!ENTITY owl "http://www.w3.org/2002/07/owl#" >
    <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" >
    <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#" >
    <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
]>

<rdf:RDF xmlns="http://www.people.com#"
     xml:base="http://www.people.com"
     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
     xmlns:owl="http://www.w3.org/2002/07/owl#"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
     xmlns:www="http://www.people.com#"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <owl:Ontology rdf:about="http://www.people.com"/>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Datatypes
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Object Properties
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!-- http://www.people.com#hasFriend -->

    <owl:ObjectProperty rdf:about="&www;hasFriend">
        <rdfs:range rdf:resource="&www;Individual"/>
        <rdfs:domain rdf:resource="&www;Individual"/>
    </owl:ObjectProperty>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Data properties
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!-- http://www.people.com#hasName -->

    <owl:DatatypeProperty rdf:about="&www;hasName">
        <rdfs:domain rdf:resource="&www;Individual"/>
        <rdfs:range rdf:resource="&xsd;string"/>
    </owl:DatatypeProperty>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Classes
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!-- http://www.people.com#GmailPerson -->

    <owl:Class rdf:about="&www;GmailPerson">
        <rdfs:subClassOf rdf:resource="&www;Individual"/>
    </owl:Class>

    <!-- http://www.people.com#Individual -->

    <owl:Class rdf:about="&www;Individual"/>

    <!--
    ///////////////////////////////////////////////////////////////////////////////////////
    //
    // Individuals
    //
    ///////////////////////////////////////////////////////////////////////////////////////
     -->

    <!-- http://www.people.com#individual_5 -->

    <owl:NamedIndividual rdf:about="&www;individual_5">
        <rdf:type rdf:resource="&www;Individual"/>
        <hasName rdf:datatype="&xsd;string">Sem Web</hasName>
        <hasFriend rdf:resource="&www;individual_6"/>
        <hasFriend rdf:resource="&www;individual_7"/>
    </owl:NamedIndividual>

    <!-- http://www.people.com#individual_6 -->

    <owl:NamedIndividual rdf:about="&www;individual_6">
        <rdf:type rdf:resource="&www;Individual"/>
        <hasName rdf:datatype="&xsd;string">Web O Data</hasName>
    </owl:NamedIndividual>

    <!-- http://www.people.com#individual_7 -->

    <owl:NamedIndividual rdf:about="&www;individual_7">
        <rdf:type rdf:resource="&www;Individual"/>
        <hasName rdf:datatype="&xsd;string">Mr. OWL</hasName>
    </owl:NamedIndividual>
</rdf:RDF>

<!-- Generated by the OWL API (version 3.2.5.1912) http://owlapi.sourceforge.net -->

代码

代码1 常量类

 package util;

 /**
  * <ul>
  * <li>Author: zhoujg | Date: 2014-3-23 下午1:25:14</li>
  * <li>Description: 常量类</li>
  * </ul>
  */
 public class Constants {
     public static final String NEWLINE = System.getProperty("line.separator");
     public static final String TAB = System.getProperty("\t");

     public static final String BOUNDARY = "-----------------------------------------------------------------------";

     public static final String RDF_URL = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
     public static final String RDFS_URL = "http://www.w3.org/2000/01/rdf-schema#";
     public static final String OWL_URL = "http://www.w3.org/2002/07/owl#";
     public static final String XSD_URL = "http://www.w3.org/2001/XMLSchema#";
     public static final String FOAF_URL = "http://xmlns.com/foaf/0.1/";

     public static final String RDF_TYPE = "<" + RDF_URL + "type>";
 }

代码2 HelloSemanticWeb

 package helloworld;

 import java.io.IOException;
 import java.io.InputStream;

 import com.hp.hpl.jena.query.Query;
 import com.hp.hpl.jena.query.QueryExecution;
 import com.hp.hpl.jena.query.QueryExecutionFactory;
 import com.hp.hpl.jena.query.QueryFactory;
 import com.hp.hpl.jena.query.QuerySolution;
 import com.hp.hpl.jena.query.ResultSet;
 import com.hp.hpl.jena.rdf.model.InfModel;
 import com.hp.hpl.jena.rdf.model.Model;
 import com.hp.hpl.jena.rdf.model.ModelFactory;
 import com.hp.hpl.jena.rdf.model.Property;
 import com.hp.hpl.jena.rdf.model.RDFNode;
 import com.hp.hpl.jena.rdf.model.Resource;
 import com.hp.hpl.jena.reasoner.Reasoner;
 import com.hp.hpl.jena.reasoner.ReasonerRegistry;
 import com.hp.hpl.jena.reasoner.rulesys.GenericRuleReasoner;
 import com.hp.hpl.jena.reasoner.rulesys.Rule;
 import com.hp.hpl.jena.util.FileManager;
 import static util.Constants.*;

 /**
  * <ul>
  * <li>Author: zhoujg | Date: 2014-3-23 下午12:56:51</li>
  * <li>Description: Jena语义Web编程之HelloWorld</li>
  * </ul>
  */
 class HelloSemanticWeb {
     // FOAF命名空间
     private static final String FOAF_NS = "http://xmlns.com/foaf/0.1/";
     // FOAF文件绝对路径
     private static final String FOAF_SCHEMA_FN = "E:/码农的世界/码农的自我修养/semantic web/workspace/foafSchema.rdf";

     // myfoaf命名空间
     private static final String MYFOAF_NS = "http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl";
     // myfoaf文件绝对路径
     private static final String MYFOAF_DATA_FN = "E:/码农的世界/码农的自我修养/semantic web/workspace/foafData.rdf";

     // poeple命名空间
     private static final String PEOPLE_NS = "http://www.people.com";
     // people文件的绝对路径
     private static final String PEOPLE_SCHEMA_FN = "E:/码农的世界/码农的自我修养/semantic web/workspace/peopleSchema.rdf";
     private static final String PEOPLE_DATA_FN = "E:/码农的世界/码农的自我修养/semantic web/workspace/peopleData.rdf";

     // Jena的RDF模型
     private static Model friendsModel = null;
     // 所有本体的Schema模型
     private static Model schema = null;

     // 推理后模型
     private static InfModel inferredModel = null;

     public static void main(String[] args) throws IOException {
         version2();
     }

     /** version 1: rdf navigate using sparql query */
     public static void version1() throws IOException {
         System.out.println("Load my FOAF friends");
         friendsModel = populateMyFOAFFriends(MYFOAF_DATA_FN);

         System.out.println("Say Hello to myself");
         sayHelloToMyself(friendsModel);

         System.out.println("Say Hello to my friends");
         sayHelloToMyFriends(friendsModel);
     }

     /** version 2: ontology integration using alignment */
     public static void version2() throws IOException {
         System.out.println("Load the data");
         loadABox();

         System.out.println("Generate the schema to contain all ontology's tbox");
         loadTBox();

         // 本体对准
         alignmentInTBox();

         // 绑定到推理机
         bindReasoner();

         // 执行查询
         sayHelloToMyself(inferredModel);
         sayHelloToMyFriends(inferredModel);
     }

     /** version 3: using jena rules */
     public static void version3() throws IOException {
         System.out.println("Load the data");
         loadABox();

         System.out.println("Generate the schema to contain all ontology's tbox");
         loadTBox();

         // 本体对准
         alignmentInTBox();

         // 绑定到规则推理机
         bindJenaReasoner();

         // 执行查询
         sayHelloToGmailFriends(inferredModel);
     }

     private static void bindJenaReasoner() {
         final String rule = "[gmailFriend: (?person <http://xmlns.com/foaf/0.1/mbox_sha1sum> ?email), strConcat(?email, ?lit), regex(?lit, '(.*gmail.com)')"
                 + "-> (?person " + RDF_TYPE + " <http://www.people.com#GmailPerson>)]";
         Reasoner ruleReasoner = new GenericRuleReasoner(Rule.parseRules(rule));
         ruleReasoner = ruleReasoner.bindSchema(schema);
         inferredModel = ModelFactory.createInfModel(ruleReasoner, friendsModel);
     }

     /** 加载所有本体的ABox */
     private static void loadABox() {
         friendsModel = ModelFactory.createDefaultModel();
         InputStream is = FileManager.get().open(MYFOAF_DATA_FN);// MyFOAF的data
         friendsModel.read(is, MYFOAF_NS);

         is = FileManager.get().open(PEOPLE_DATA_FN);// people的data
         friendsModel.read(is, PEOPLE_NS);
     }

     /** 加载所有本体的TBox */
     private static void loadTBox() {
         schema = ModelFactory.createDefaultModel();
         InputStream is = FileManager.get().open(FOAF_SCHEMA_FN);// FOAF的Schema
         schema.read(is, FOAF_NS);

         is = FileManager.get().open(PEOPLE_SCHEMA_FN);// people的Schema
         schema.read(is, PEOPLE_NS);
     }

     /** 本体对准ontology alignment */
     private static void alignmentInTBox() {
         // [1]people:Individual = foaf:Person
         Resource resource = schema.createResource(PEOPLE_NS + "#Individual");
         Property property = schema.createProperty(OWL_URL + "equivalentClass");
         Resource object = schema.createResource(FOAF_URL + "Person");
         schema.add(resource, property, object);

         // [2]people:hasName = foaf:name
         resource = schema.createResource(PEOPLE_NS + "#hasName");
         property = schema.createProperty(OWL_URL + "equivalentProperty");
         object = schema.createResource(FOAF_URL + "name");
         schema.add(resource, property, object);

         // [3]people:hasFriend < foaf:knows
         resource = schema.createResource(PEOPLE_NS + "#hasFriend");
         property = schema.createProperty(RDFS_URL + "subPropertyOf");
         object = schema.createResource(FOAF_URL + "knows");
         schema.add(resource, property, object);

         // [4]myfoaf:me = people:individual_5
         resource = schema.createResource(MYFOAF_NS + "#me");
         property = schema.createProperty(OWL_URL + "sameAs");
         object = schema.createResource(PEOPLE_NS + "#individual_5");
         schema.add(resource, property, object);
     }

     private static void bindReasoner() {
         Reasoner reasoner = ReasonerRegistry.getOWLReasoner();
         reasoner = reasoner.bindSchema(schema);// tbox
         inferredModel = ModelFactory.createInfModel(reasoner, friendsModel);// abox
     }

     /** 填充模型 */
     private static Model fillModel(String base, String filePath) throws IOException {
         Model model = ModelFactory.createDefaultModel();
         InputStream is = FileManager.get().open(filePath);
         model.read(is, base);
         is.close();
         return model;
     }

     /** MyFOAF填充模型 */
     private static Model populateMyFOAFFriends(String filePath) throws IOException {
         return fillModel(MYFOAF_NS, filePath);
     }

     /** RDF模型导航:SPARQL查询 - 查询自己name */
     private static void sayHelloToMyself(Model model) {
         String query = generateMyselfSPARQLQuery();
         sparql(model, query, "?name");
     }

     private static void sayHelloToGmailFriends(Model model) {
         String query = generateGmailFriendsSPARQLQuery();
         sparql(model, query, "?name");
     }

     /** RDF模型导航:SPARQL查询 - 查询朋友name */
     private static void sayHelloToMyFriends(Model model) {
         String query = generateFriendsSPARQLQuery();
         sparql(model, query, "?name");
     }

     /** 在RDF模型中执行SPARQL查询 */
     private static void sparql(Model model, String query, String field) {
         Query q = QueryFactory.create(query);
         QueryExecution qexec = QueryExecutionFactory.create(q, model);
         System.out.println("Plan to run SPARQL query: ");
         System.out.println(BOUNDARY);
         System.out.println(query);
         System.out.println(BOUNDARY);
         ResultSet rs = qexec.execSelect();
         while (rs.hasNext()) {
             QuerySolution qs = rs.nextSolution();
             RDFNode name = qs.get(field);// 暂用RDFNode
             if (name != null) {
                 System.out.println("Hello to " + name);
             } else {
                 System.out.println("No friends found!");
             }
         }
         qexec.close();
     }

     private static String generateMyselfSPARQLQuery() {
         StringBuilder sb = generateSPARQLPREFIX();
         // 添加查询语句
         sb.append("SELECT DISTINCT ?name").append(NEWLINE).append("WHERE { myfoaf:me foaf:name ?name}").append(NEWLINE);
         return sb.toString();
     }

     private static String generateGmailFriendsSPARQLQuery() {
         StringBuilder sb = generateSPARQLPREFIX();
         sb.append("SELECT DISTINCT ?name WHERE {?name rdf:type people:GmailPerson}");
         return sb.toString();
     }

     private static String generateFriendsSPARQLQuery() {
         StringBuilder sb = generateSPARQLPREFIX();
         sb.append("SELECT DISTINCT ?name").append(NEWLINE).append("WHERE { myfoaf:me foaf:knows ?friend. ").append("?friend foaf:name ?name}")
                 .append(NEWLINE);
         return sb.toString();
     }

     /** 添加SPARQL查询前缀PREFIX */
     private static StringBuilder generateSPARQLPREFIX() {
         StringBuilder sb = new StringBuilder();
         sb.append("PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>").append(NEWLINE).append("PREFIX owl: <http://www.w3.org/2002/07/owl#>")
                 .append(NEWLINE).append("PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>").append(NEWLINE)
                 .append("PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>").append(NEWLINE).append("PREFIX foaf: <http://xmlns.com/foaf/0.1/>")
                 .append(NEWLINE).append("PREFIX myfoaf: <http://blog.sina.com.cn/zhoujiagenontology/helloworld.owl#>").append(NEWLINE)
                 .append("PREFIX people: <http://www.people.com#>").append(NEWLINE);
         return sb;
     }
 }
05-11 14:44