001    // This file is part of AceWiki.
002    // Copyright 2008-2012, AceWiki developers.
003    // 
004    // AceWiki is free software: you can redistribute it and/or modify it under the terms of the GNU
005    // Lesser General Public License as published by the Free Software Foundation, either version 3 of
006    // the License, or (at your option) any later version.
007    // 
008    // AceWiki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
009    // even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
010    // Lesser General Public License for more details.
011    // 
012    // You should have received a copy of the GNU Lesser General Public License along with AceWiki. If
013    // not, see http://www.gnu.org/licenses/.
014    
015    package ch.uzh.ifi.attempto.acewiki.core;
016    
017    import java.util.ArrayList;
018    import java.util.List;
019    import java.util.Map;
020    import java.util.TreeMap;
021    
022    import ch.uzh.ifi.attempto.base.Logger;
023    
024    /**
025     * This class represents an AceWiki ontology which consists of ontology element definitions and
026     * of ontological statements. Each ontology element has its own article that consists of
027     * ontological statements.
028     * 
029     * @author Tobias Kuhn
030     */
031    public class Ontology {
032            
033            private AceWikiEngine engine;
034            private CachingReasoner reasoner;
035            private StatementFactory statementFactory;
036            private AceWikiStorage storage;
037            private Logger logger;
038            
039            private Map<Long, OntologyElement> idIndex = new TreeMap<Long, OntologyElement>();
040            
041            private final String name;
042            private final String baseURI;
043            private long idCount = 0;
044            private long stateID = 0;
045            
046            private Map<String, String> parameters;
047            
048            /**
049             * Creates a new empty ontology with the given name and parameters.
050             * 
051             * @param name The name of the ontology.
052             * @param parameters The parameters.
053             */
054            Ontology(String name, Map<String, String> parameters, AceWikiStorage storage) {
055                    this.name = name.toString();  // null value throws an exception
056                    this.parameters = parameters;
057                    this.storage = storage;
058                    
059                    logger = new Logger(parameters.get("context:logdir") + "/" + name, "onto", 0);
060                    
061                    engine = AbstractAceWikiEngine.createLanguageEngine(this);
062                    reasoner = new CachingReasoner(engine.getReasoner());
063                    reasoner.init(this);
064                    statementFactory = new StatementFactory(this);
065                    
066                    String b = getParameter("baseuri");
067                    if (b == null || b.equals("")) {
068                            baseURI = "http://attempto.ifi.uzh.ch/acewiki/default/";
069                    } else {
070                            if (b.endsWith("/")) {
071                                    baseURI = b;
072                            } else {
073                                    baseURI = b + "/";
074                            }
075                    }
076            }
077            
078            /**
079             * Returns the AceWiki engine.
080             * 
081             * @return The AceWiki engine.
082             */
083            public AceWikiEngine getEngine() {
084                    return engine;
085            }
086            
087            /**
088             * Returns the reasoner object in the form of a caching reasoner.
089             * 
090             * @return The reasoner.
091             */
092            public CachingReasoner getReasoner() {
093                    return reasoner;
094            }
095            
096            /**
097             * Returns the statement factory.
098             * 
099             * @return The statement factory.
100             */
101            public StatementFactory getStatementFactory() {
102                    return statementFactory;
103            }
104            
105            /**
106             * Returns the storage object.
107             * 
108             * @return The storage object.
109             */
110            public AceWikiStorage getStorage() {
111                    return storage;
112            }
113            
114            /**
115             * Registers the given ontology element.
116             * 
117             * @param element The ontology element to register.
118             */
119            public synchronized void register(OntologyElement element) {
120                    if (contains(element)) {
121                            log("error: element already registered");
122                            throw new RuntimeException("Registration failed: Element is already registered.");
123                    }
124                    element.initOntology(this);
125                    
126                    log("register: " + element);
127                    stateID++;
128                    
129                    if (element.getId() == -1) {
130                            element.initId(nextId());
131                    }
132                    idIndex.put(element.getId(), element);
133                    if (element.getId() > idCount) idCount = element.getId();
134                    
135                    engine.getWordIndex().elementAdded(element);
136                    
137                    getReasoner().loadElement(element);
138                    getReasoner().flushElements();
139                    
140                    getStorage().save(element);
141            }
142            
143            /**
144             * Removes the given ontology element from the ontology.
145             * 
146             * @param element The ontology element to be removed.
147             */
148            public synchronized void remove(OntologyElement element) {
149                    if (!contains(element)) {
150                            log("error: unknown element");
151                            return;
152                    }
153                    
154                    log("remove: " + element.getWord());
155                    stateID++;
156                    
157                    engine.getWordIndex().elementRemoved(element);
158                    
159                    idIndex.remove(element.getId());
160                    for (Sentence s : element.getArticle().getSentences()) {
161                            retractSentence(s);
162                    }
163                    storage.save(element);
164                    
165                    getReasoner().unloadElement(element);
166                    getReasoner().flushElements();
167            }
168            
169            /**
170             * Changes the word forms of the given ontology element.
171             * 
172             * @param element The ontology element to be changed.
173             * @param serializedWords The serialized word forms.
174             */
175            public synchronized void change(OntologyElement element, String serializedWords) {
176                    if (contains(element)) {
177                            engine.getWordIndex().elementBeforeChange(element);
178                            getReasoner().unloadElement(element);
179                            element.setWords(serializedWords);
180                            engine.getWordIndex().elementAfterChange(element);
181                            getReasoner().loadElement(element);
182                            refresh(element);
183                    } else {
184                            element.setWords(serializedWords);
185                    }
186            }
187            
188            /**
189             * Returns all the sentences that use the given ontology element.
190             * 
191             * @param element The ontology element.
192             * @return A list of all sentence that contain the ontology element.
193             */
194            public synchronized List<Sentence> getReferences(OntologyElement element) {
195                    List<Sentence> list = new ArrayList<Sentence>();
196                    for (OntologyElement el : idIndex.values()) {
197                            for (Sentence s : el.getArticle().getSentences()) {
198                                    if (s.contains(element)) {
199                                            list.add(s);
200                                    }
201                            }
202                    }
203                    return list;
204            }
205            
206            /**
207             * Returns the ontology element with the given name, or null if there is no such element.
208             * 
209             * @param name The name of the ontology element.
210             * @return The ontology element.
211             */
212            public synchronized OntologyElement getElement(String name) {
213                    return engine.getWordIndex().getElement(name);
214            }
215            
216            /**
217             * Returns the ontology element with the given id, or null if there is no such element.
218             * 
219             * @param id The id of the ontology element.
220             * @return The ontology element.
221             */
222            public synchronized OntologyElement get(long id) {
223                    return idIndex.get(id);
224            }
225            
226            /**
227             * Returns all ontology elements. The list is a copy of the internal list.
228             * 
229             * @return A list of all ontology elements.
230             */
231            public synchronized List<OntologyElement> getOntologyElements() {
232                    return new ArrayList<OntologyElement>(idIndex.values());
233            }
234            
235            /**
236             * Returns true if the given ontology element is contained by the ontology (identity check).
237             * 
238             * @param ontologyElement The ontology element.
239             * @return true if the ontology element is contained by the ontology.
240             */
241            public synchronized boolean contains(OntologyElement ontologyElement) {
242                    return idIndex.containsValue(ontologyElement);
243            }
244            
245            /**
246             * Returns the name of the ontology.
247             * 
248             * @return The name of the ontology.
249             */
250            public String getName() {
251                    return name;
252            }
253            
254            /**
255             * Returns the URI of the ontology (baseURI + name).
256             * 
257             * @return The URI of the ontology.
258             */
259            public String getURI() {
260                    return baseURI + name;
261            }
262            
263            /**
264             * Refreshes the given ontology element. All sentences that use the ontology element are
265             * updated.
266             * 
267             * @param element The ontology element to be refreshed.
268             */
269            synchronized void refresh(OntologyElement element) {
270                    for (Sentence s : getReferences(element)) {
271                            if (s.isIntegrated()) {
272                                    retractSentence(s);
273                                    s.update();
274                                    commitSentence(s);
275                            } else {
276                                    s.update();
277                            }
278                    }
279                    storage.save(element);
280            }
281            
282            /**
283             * Commits the sentence. This means that it is added to the reasoner.
284             * 
285             * @param sentence The sentence to be commited.
286             * @throws InconsistencyException if the sentence is inconsistent with the existing sentences.
287             */
288            protected synchronized void commitSentence(Sentence sentence) throws InconsistencyException {
289                    if (sentence == null || sentence.isIntegrated()) return;
290                    
291                    if (!sentence.isReasonable()) {
292                            sentence.setIntegrated(true);
293                            return;
294                    }
295                    
296                    log("commit sentence");
297                    
298                    try {
299                            getReasoner().loadSentence(sentence);
300                    } catch (InconsistencyException ex) {
301                            log("not consistent!");
302                            getReasoner().unloadSentence(sentence);
303                            throw ex;
304                    } catch (Throwable t) {
305                            log("error encountered!");
306                            t.printStackTrace();
307                            System.gc();
308                            getReasoner().unloadSentence(sentence);
309                            return;
310                    }
311                    
312                    if (!getReasoner().isConsistent()) {
313                            log("not consistent!");
314                            getReasoner().unloadSentence(sentence);
315                            throw new InconsistencyException();
316                    }
317                    
318                    log("consistent!");
319                    sentence.setIntegrated(true);
320                    stateID++;
321            }
322            
323            /**
324             * This method tries to reassert a sentence that is not yet integrated.
325             * 
326             * @param sentence The sentence to be reasserted.
327             * @throws InconsistencyException if the sentence is inconsistent with the existing sentences.
328             */
329            public synchronized void reassert(Sentence sentence) throws InconsistencyException {
330                    commitSentence(sentence);
331                    getStorage().save(sentence.getArticle().getOntologyElement());
332            }
333            
334            /**
335             * Retracts the sentence. This means that the sentence is removed from the reasoner.
336             * 
337             * @param sentence The sentence to be retracted.
338             */
339            protected synchronized void retractSentence(Sentence sentence) {
340                    if (
341                            sentence == null ||
342                            !sentence.isIntegrated() ||
343                            !sentence.isReasonable()
344                    ) return;
345                    
346                    log("retract sentence");
347                    stateID++;
348                    getReasoner().unloadSentence(sentence);
349                    sentence.setIntegrated(false);
350            }
351            
352            /**
353             * This method retracts an integrated sentence so that it is still part of the wiki
354             * article but does not participate in reasoning anymore.
355             * 
356             * @param sentence The sentence to be retracted.
357             */
358            public synchronized void retract(Sentence sentence) {
359                    retractSentence(sentence);
360                    getStorage().save(sentence.getArticle().getOntologyElement());
361            }
362            
363            /**
364             * Writes a log entry.
365             * 
366             * @param text Log text.
367             */
368            public void log(String text) {
369                    logger.log("onto", text);
370            }
371            
372            private long nextId() {
373                    return ++idCount;
374            }
375            
376            /**
377             * Returns the state id of the ontology. This id increases each time the ontology changes (more
378             * precisely: each time the part of the ontology that participates in reasoning changes). This
379             * id is used to find out whether cached information is still valid or has to be recalculated.
380             * 
381             * @return The state id of the ontology.
382             */
383            public long getStateID() {
384                    return stateID;
385            }
386            
387            /**
388             * Returns a parameter value.
389             * 
390             * @param name The parameter name.
391             * @return The parameter value.
392             */
393            public String getParameter(String name) {
394                    return parameters.get(name);
395            }
396            
397    }