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 }