001    /**
002     * OntologyConnector.java
003     * jCOLIBRI2 framework. 
004     * @author Juan A. Recio-García.
005     * GAIA - Group for Artificial Intelligence Applications
006     * http://gaia.fdi.ucm.es
007     * 07/06/2007
008     */
009    package jcolibri.connector;
010    
011    import java.io.FileWriter;
012    import java.net.URL;
013    import java.util.ArrayList;
014    import java.util.Collection;
015    import java.util.Iterator;
016    
017    import javax.xml.parsers.DocumentBuilder;
018    import javax.xml.parsers.DocumentBuilderFactory;
019    
020    import jcolibri.cbrcore.Attribute;
021    import jcolibri.cbrcore.CBRCase;
022    import jcolibri.cbrcore.CaseBaseFilter;
023    import jcolibri.cbrcore.CaseComponent;
024    import jcolibri.cbrcore.Connector;
025    import jcolibri.connector.ontologyutils.OntologyInfo;
026    import jcolibri.connector.ontologyutils.OntologyMapping;
027    import jcolibri.datatypes.Instance;
028    import jcolibri.exception.InitializingException;
029    import jcolibri.util.FileIO;
030    import jcolibri.util.ProgressController;
031    
032    import org.w3c.dom.Document;
033    import org.w3c.dom.Node;
034    import org.w3c.dom.NodeList;
035    
036    import es.ucm.fdi.gaia.ontobridge.OntoBridge;
037    import es.ucm.fdi.gaia.ontobridge.OntologyDocument;
038    
039    /**
040     * Implements a generic Ontology connector.
041     * It uses OntoBridge to manage the ontologies and the reasoner.
042     * To configure this connector create a configuration xml file following this schema:
043     * <a href="OntologyConnector.xsd">/doc/configfilesSchemas/OntologyConnector.xsd</a>:<p>
044     * <img src="OntologyConnectorSchema.jpg">
045     * <p>
046     * This connector only maps case structures without compound attributes.
047     * All attributes must be Instance typed.
048     * 
049     * For a complete example see Test 10.
050     * 
051     * @author Juan A. Recio-Garcia
052     * @version 2.0
053     * @see jcolibri.test.test10.Test10
054     */
055    public class OntologyConnector implements Connector {
056            
057            private Class descriptionClass;
058            private Class solutionClass;
059            private Class justOfSolutionClass;
060            private Class resultClass;
061            
062            private OntologyInfo mainOntologyInfo;
063            private ArrayList<OntologyInfo> subOntologiesInfo;
064            
065            private String CaseMainConcept;
066            
067            private ArrayList<OntologyMapping> descriptionMappings;
068            private ArrayList<OntologyMapping> solutionMappings;
069            private ArrayList<OntologyMapping> justOfSolutionMappings;
070            private ArrayList<OntologyMapping> resultMappings;
071            
072            private boolean modified;
073            
074            private OntologyInfo getOntologyInfo(Node node)
075            {
076                    OntologyInfo oi = new OntologyInfo();
077                    NodeList ontologyNodes = node.getChildNodes();
078                    for(int i=0; i<ontologyNodes.getLength(); i++)
079                    {
080                            Node n = ontologyNodes.item(i);
081                            if(n.getNodeName().equals("URL"))
082                                    oi.setUrl(n.getTextContent());
083                            else if(n.getNodeName().equals("LocalCopy"))
084                                    oi.setLocalCopy(n.getTextContent());    
085                    }
086                    return oi;
087            }
088            
089            private void getOntologyMappings(Node mappings, ArrayList<OntologyMapping> descriptionMappings) {
090                    NodeList mappingNodes = mappings.getChildNodes();
091                    for(int i=0; i<mappingNodes.getLength(); i++)
092                    {
093                            Node n = mappingNodes.item(i);
094                            if(!n.getNodeName().equals("Map"))
095                                    continue;
096                            OntologyMapping om = new OntologyMapping();
097                            NodeList contents = n.getChildNodes();
098                            for(int j=0; j<contents.getLength(); j++)
099                            {
100                                    Node c = contents.item(j);
101                                    if(c.getNodeName().equals("Property"))
102                                            om.setProperty(c.getTextContent());
103                                    else if(c.getNodeName().equals("Concept"))
104                                            om.setConcept(c.getTextContent());
105                                    else if(c.getNodeName().equals("Attribute"))
106                                            om.setAttribute(c.getTextContent());
107                            }
108                            descriptionMappings.add(om);
109                    }
110            }
111            
112            /** 
113             * Initializes the connector from an XML config file.
114             * This method reads the configuration and launches OntoBridge with the Pellet reasoner.
115             * Then the ontologies are loaded into memory.
116             * 
117             * @see jcolibri.cbrcore.Connector#initFromXMLfile(java.net.URL)
118             */
119            public void initFromXMLfile(URL file) throws InitializingException {
120                    
121                    
122                    try {
123                            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
124                            DocumentBuilder db = dbf.newDocumentBuilder();
125                            Document doc = db.parse(file.openStream());
126                            
127                            /* Main Ontology Info */
128                            mainOntologyInfo = getOntologyInfo(doc.getElementsByTagName("MainOntology").item(0));
129    
130                            /* SubOntologies Info */
131                            subOntologiesInfo = new ArrayList<OntologyInfo>();
132                            NodeList subOntologiesNodes = doc.getElementsByTagName("SubOntology");
133                            for(int i=0; i<subOntologiesNodes.getLength(); i++)
134                                    subOntologiesInfo.add(getOntologyInfo(subOntologiesNodes.item(i)));
135                            
136                            /* Case Main Concept */
137                            this.CaseMainConcept = doc.getElementsByTagName("CaseMainConcept").item(0).getTextContent();
138                            
139                            
140                            /* Description mapping */
141                            this.descriptionClass = Class.forName(doc.getElementsByTagName("DescriptionClassName").item(0).getTextContent());
142                            Node mappings = doc.getElementsByTagName("DescriptionMappings").item(0);
143                            this.descriptionMappings = new ArrayList<OntologyMapping>();
144                            getOntologyMappings(mappings, descriptionMappings);
145                            
146                            /* Solution mapping */
147                            try{
148                                    this.solutionClass =  Class.forName(doc.getElementsByTagName("SolutionClassName").item(0).getTextContent());
149                                    mappings = doc.getElementsByTagName("SolutionMappings").item(0);
150                                    this.solutionMappings = new ArrayList<OntologyMapping>();
151                                    getOntologyMappings(mappings, solutionMappings);
152                            }catch(Exception e) {}
153                            
154                            /* JustOfSolution mapping */
155                            try{
156                                    this.justOfSolutionClass =  Class.forName(doc.getElementsByTagName("JustificationOfSolutionClassName").item(0).getTextContent());
157                                    mappings = doc.getElementsByTagName("JustificationOfSolutionMappings").item(0);
158                                    this.justOfSolutionMappings = new ArrayList<OntologyMapping>();
159                                    getOntologyMappings(mappings, justOfSolutionMappings);
160                            }catch(Exception e) {}
161                            
162                            /* result mapping */
163                            try{
164                                    this.resultClass =  Class.forName(doc.getElementsByTagName("ResultClassName").item(0).getTextContent());
165                                    mappings = doc.getElementsByTagName("ResultMappings").item(0);
166                                    this.resultMappings = new ArrayList<OntologyMapping>();
167                                    getOntologyMappings(mappings, resultMappings);
168                            }catch(Exception e) {}
169                            
170                            
171                            // Now let's initialize Ontobridge
172                            
173                            // Obtain a reference to OntoBridge
174                            OntoBridge ob = jcolibri.util.OntoBridgeSingleton.getOntoBridge();
175                            // Configure it to work with the Pellet reasoner
176                            ob.initWithPelletReasoner();
177                            // Setup the main ontology
178                            OntologyDocument mainOnto = new OntologyDocument(this.mainOntologyInfo.getUrl(), 
179                            FileIO.findFile(this.mainOntologyInfo.getLocalCopy()).toExternalForm());
180                            // Setup subontologies
181                            ArrayList<OntologyDocument> subOntologies = new ArrayList<OntologyDocument>();
182                            for(OntologyInfo oi : this.subOntologiesInfo)
183                            {
184                                    OntologyDocument subOnto = new OntologyDocument(oi.getUrl(), 
185                                    FileIO.findFile(oi.getLocalCopy()).toURI().toString());
186                                    subOntologies.add(subOnto);
187                            }
188                            
189                            // Load the ontology
190                            ob.loadOntology(mainOnto, subOntologies, false);
191            
192                            // Set modified to false
193                            this.modified = false;
194                            
195                    } catch (Exception e) {
196                            throw new InitializingException(e);
197                    }
198                    
199                    
200            }
201    
202    
203            /* (non-Javadoc)
204             * @see jcolibri.cbrcore.Connector#retrieveAllCases()
205             */
206            public Collection<CBRCase> retrieveAllCases() {
207    
208                    //Result list
209                    ArrayList<CBRCase> cases = new ArrayList<CBRCase>();
210                    
211                    //Obtain OntoBridge
212                    OntoBridge ob = jcolibri.util.OntoBridgeSingleton.getOntoBridge();
213                    
214                    ProgressController.init(this.getClass(), "Loading concepts", ProgressController.UNKNOWN_STEPS);
215                    
216                    //Obtain instances
217                    Iterator<String> caseInstances =  ob.listInstances(this.CaseMainConcept);
218                    while(caseInstances.hasNext())
219                    {
220                            String caseInstance = caseInstances.next();
221                            CBRCase _case = new CBRCase();
222                            
223                            try {
224                                    //Map description
225                                    CaseComponent description = (CaseComponent)this.descriptionClass.newInstance();
226                                    retrieveCaseComponent(ob, description, caseInstance, this.descriptionMappings);
227                                    _case.setDescription(description);
228                                    
229                                    //Map solution
230                                    if(this.solutionClass != null)
231                                    {
232                                            CaseComponent cc = (CaseComponent)this.solutionClass.newInstance();
233                                            retrieveCaseComponent(ob, cc, caseInstance, this.solutionMappings);     
234                                            _case.setSolution(cc);
235                                    }
236                                    
237                                    //Map justification of solution
238                                    if(this.justOfSolutionClass != null)
239                                    {
240                                            CaseComponent cc = (CaseComponent)this.justOfSolutionClass.newInstance();
241                                            retrieveCaseComponent(ob, cc, caseInstance, this.justOfSolutionMappings);                       
242                                            _case.setJustificationOfSolution(cc);
243                                    }
244                                    
245                                    //Map result solution
246                                    if(this.resultClass != null)
247                                    {
248                                            CaseComponent cc = (CaseComponent)this.resultClass.newInstance();
249                                            retrieveCaseComponent(ob, cc, caseInstance, this.resultMappings);
250                                            _case.setResult(cc);
251                                    }
252                                    
253                                    // If everything ok add the case to the list
254                                    cases.add(_case);
255                                    
256                            } catch (Exception e) {
257                                    org.apache.commons.logging.LogFactory.getLog(this.getClass()).error(e);
258                            }
259                            
260                            ProgressController.step(this.getClass());
261                    }
262                    ProgressController.finish(this.getClass());
263                    return cases;
264            }
265            
266            private void retrieveCaseComponent(OntoBridge ob, CaseComponent cc, String mainInstanceName, ArrayList<OntologyMapping> mappings) throws Exception
267            {
268                    //Id
269                    Instance id = new Instance(mainInstanceName);
270                    cc.getIdAttribute().setValue(cc, id);
271                    
272                    //Other attributes
273                    for(OntologyMapping om: mappings)
274                    {
275                            // Obtain CaseComponent attribute
276                            Attribute at = new Attribute(om.getAttribute(), cc.getClass());
277                            
278                            // Find values of the property. It could have several values.
279                            Iterator<String> values = ob.listPropertyValue(mainInstanceName, om.getProperty());
280                            // Find which value is instance of the concept
281                            boolean found = false;
282                            while(values.hasNext() && !found)
283                            {
284                                    String valueInstance = values.next();
285                                    if(ob.isInstanceOf(valueInstance, om.getConcept()))
286                                    {
287                                            found = true;
288                                            Instance concept = new Instance(valueInstance);
289                                            at.setValue(cc, concept);
290                                    }
291                            }
292                    }
293    
294            }
295    
296            /**
297             * UnImplemented.
298             * @see jcolibri.cbrcore.Connector#retrieveSomeCases(jcolibri.cbrcore.CaseBaseFilter)
299             */
300            public Collection<CBRCase> retrieveSomeCases(CaseBaseFilter filter) {
301                    org.apache.commons.logging.LogFactory.getLog(this.getClass()).error("retrieveSomeCases(CaseBaseFilter) method is not yet implemented");
302                    return null;
303            }
304    
305            /** 
306             * Stores cases into the ontology.
307             * @see jcolibri.cbrcore.Connector#storeCases(java.util.Collection)
308             */
309            public void storeCases(Collection<CBRCase> cases) {
310                    
311                    if(cases.isEmpty())
312                            return;
313                    else
314                            modified = true;
315                    
316                    
317                    //Obtain OntoBridge
318                    OntoBridge ob = jcolibri.util.OntoBridgeSingleton.getOntoBridge();
319                    
320                    ProgressController.init(this.getClass(), "Storing concepts/cases", cases.size());
321                    for(CBRCase _case: cases)
322                    {
323                            try {
324                                    if(!ob.existsInstance(_case.getID().toString(),this.CaseMainConcept))
325                                            ob.createInstance(this.CaseMainConcept, _case.getID().toString());
326                                    createCaseComponent(_case.getDescription(), this.descriptionMappings);
327                                    createCaseComponent(_case.getSolution(), this.solutionMappings);
328                                    createCaseComponent(_case.getJustificationOfSolution(), this.justOfSolutionMappings);
329                                    createCaseComponent(_case.getResult(), this.resultMappings);
330                            } catch (Exception e) {
331                                    org.apache.commons.logging.LogFactory.getLog(this.getClass()).error("Error storing case: "+_case+". Cause: "+ e.getMessage());
332                            }
333                            ProgressController.step(this.getClass());
334                    }
335                    ProgressController.finish(this.getClass());
336            }
337            
338            private void createCaseComponent(CaseComponent cc, ArrayList<OntologyMapping> maps) throws Exception
339            {
340                    if((cc == null)||(maps==null))
341                            return;
342                    
343                    OntoBridge ob = jcolibri.util.OntoBridgeSingleton.getOntoBridge();
344                    
345                    String mainInstance = cc.getIdAttribute().getValue(cc).toString();
346    
347                    for(OntologyMapping om: maps)
348                    {
349                            
350                            Attribute at = new Attribute(om.getAttribute(), cc.getClass());
351                            String instance = at.getValue(cc).toString();
352                            if(!ob.existsInstance(instance,om.getConcept()))
353                                    ob.createInstance(om.getConcept(), instance);
354                            ob.createOntProperty(mainInstance, om.getProperty(), instance);
355                    }
356                    
357                    
358            }
359    
360            /**
361             * If there was any modification to the ontology, the owl file is replaced with a new one that contains the changes.
362             * The new owl file is completely regenerated from scrach with the current content of the reasoner (not including the inferred model).
363             * OntoBridge uses the RDF/XML-ABBREV syntax for the owl files.
364             * 
365             * @see jcolibri.cbrcore.Connector#close()
366             */
367            public void close() {
368                    if(!modified)
369                            return;
370                    OntoBridge ob = jcolibri.util.OntoBridgeSingleton.getOntoBridge();
371                    try {
372                            ob.save(new FileWriter(FileIO.findFile(this.mainOntologyInfo.getLocalCopy()).getFile()));
373                    } catch (Exception e) {
374                            org.apache.commons.logging.LogFactory.getLog(this.getClass()).error(e);
375                    }
376    
377            }
378    
379            /**
380             * Deletes cases in the ontology. Only the main instance (case id mapped instance) is removed, so the instances mapped to attributes are keep. 
381             * @see jcolibri.cbrcore.Connector#deleteCases(java.util.Collection)
382             */
383            public void deleteCases(Collection<CBRCase> cases) {
384    
385                    if(cases.isEmpty())
386                            return;
387                    else
388                            modified = true;
389                    
390                    OntoBridge ob = jcolibri.util.OntoBridgeSingleton.getOntoBridge();
391                    
392                    ProgressController.init(this.getClass(), "Deleting concepts/cases", cases.size());
393                    for(CBRCase _case: cases)
394                    {
395                            ob.delete(_case.getID().toString());
396                            ProgressController.step(this.getClass());
397                    }
398                    ProgressController.finish(this.getClass());
399    
400            }
401    
402            
403            
404            /**************************************************************/
405            /*********** Public API for the connector configuration       */
406            /**************************************************************/
407    
408            
409            /**
410             * @return Returns the caseMainConcept.
411             */
412            public String getCaseMainConcept() {
413                    return CaseMainConcept;
414            }
415    
416            /**
417             * @return Returns the descriptionMappings.
418             */
419            public ArrayList<OntologyMapping> getDescriptionMappings() {
420                    return descriptionMappings;
421            }
422    
423            /**
424             * @return Returns the justOfSolutionMappings.
425             */
426            public ArrayList<OntologyMapping> getJustOfSolutionMappings() {
427                    return justOfSolutionMappings;
428            }
429    
430            /**
431             * @return Returns the mainOntologyInfo.
432             */
433            public OntologyInfo getMainOntologyInfo() {
434                    return mainOntologyInfo;
435            }
436    
437            /**
438             * @return Returns the resultMappings.
439             */
440            public ArrayList<OntologyMapping> getResultMappings() {
441                    return resultMappings;
442            }
443    
444            /**
445             * @return Returns the solutionMappings.
446             */
447            public ArrayList<OntologyMapping> getSolutionMappings() {
448                    return solutionMappings;
449            }
450    
451            /**
452             * @return Returns the subOntologiesInfo.
453             */
454            public ArrayList<OntologyInfo> getSubOntologiesInfo() {
455                    return subOntologiesInfo;
456            }
457            
458            
459            
460    }