PageRenderTime 37ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/org.integratedmodelling.thinklab.core/src/org/integratedmodelling/thinklab/graph/KnowledgeGraph.java

https://github.com/fvilla/thinklab
Java | 473 lines | 219 code | 95 blank | 159 comment | 55 complexity | 6495caa7aacb6fbcdc7ff74eeaab3b9b MD5 | raw file
  1. /**
  2. * KnowledgeGraph.java
  3. * ----------------------------------------------------------------------------------
  4. *
  5. * Copyright (C) 2008 www.integratedmodelling.org
  6. * Created: Apr 25, 2008
  7. *
  8. * ----------------------------------------------------------------------------------
  9. * This file is part of Thinklab.
  10. *
  11. * Thinklab is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 3 of the License, or
  14. * (at your option) any later version.
  15. *
  16. * Thinklab is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with the software; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * ----------------------------------------------------------------------------------
  26. *
  27. * @copyright 2008 www.integratedmodelling.org
  28. * @author Ferdinando Villa (fvilla@uvm.edu)
  29. * @date Apr 25, 2008
  30. * @license http://www.gnu.org/licenses/gpl.txt GNU General Public License v3
  31. * @link http://www.integratedmodelling.org
  32. **/
  33. package org.integratedmodelling.thinklab.graph;
  34. import java.io.BufferedReader;
  35. import java.io.BufferedWriter;
  36. import java.io.DataInputStream;
  37. import java.io.File;
  38. import java.io.FileInputStream;
  39. import java.io.IOException;
  40. import java.io.InputStreamReader;
  41. import java.io.OutputStream;
  42. import java.io.OutputStreamWriter;
  43. import java.util.HashSet;
  44. import java.util.Set;
  45. import org.integratedmodelling.thinklab.KnowledgeManager;
  46. import org.integratedmodelling.thinklab.exception.ThinklabException;
  47. import org.integratedmodelling.thinklab.exception.ThinklabIOException;
  48. import org.integratedmodelling.thinklab.exception.ThinklabUserErrorException;
  49. import org.integratedmodelling.thinklab.interfaces.knowledge.IConcept;
  50. import org.integratedmodelling.thinklab.interfaces.knowledge.IInstance;
  51. import org.integratedmodelling.thinklab.interfaces.knowledge.IKnowledgeSubject;
  52. import org.integratedmodelling.thinklab.interfaces.knowledge.IProperty;
  53. import org.integratedmodelling.thinklab.interfaces.knowledge.IRelationship;
  54. import org.jgrapht.graph.DefaultDirectedGraph;
  55. /**
  56. * A directed graph whose nodes are knowledge objects and whose edges may be
  57. * tagged by properties. Implement followRelationship to define what gets in and
  58. * what not.
  59. *
  60. * @author Ferdinando Villa
  61. *
  62. */
  63. public abstract class KnowledgeGraph extends
  64. DefaultDirectedGraph<Object, Object> {
  65. IKnowledgeSubject root = null;
  66. private static final long serialVersionUID = 9151878779989532686L;
  67. /*
  68. * if true, do not create further links to objects that are already part of
  69. * the graph.
  70. */
  71. boolean forceTreeGeometry = false;
  72. /*
  73. * if false, do not follow concepts or instances that are part of a
  74. * different concept space than what we start at.
  75. */
  76. boolean crossConceptSpace = false;
  77. /*
  78. * if true and starting at a class, existing instances are followed and
  79. * processed. Not recommended in a multi-user environment at least with the
  80. * protege implementation (instances are all visibles).
  81. */
  82. boolean followInstances = false;
  83. /*
  84. * if true, edges between vertexes will only be created once, and their
  85. * content set to what given in edgeContent
  86. */
  87. boolean collapseEdges = false;
  88. Object edgeContent = null;
  89. /*
  90. * when starting at an instance, 0 means that the class is not added, 1
  91. * means that it's added but not further followed, and >1 means that the
  92. * whole class structure is generated.
  93. */
  94. int followClassLevel = 0;
  95. /*
  96. * remember if we have started the current graph from a concept or an
  97. * instance
  98. */
  99. boolean rootIsConcept = false;
  100. public KnowledgeGraph() {
  101. super(PropertyEdge.class);
  102. }
  103. /**
  104. * The graph may or may not have a root. It's up to the implementation to
  105. * define it. It's obviously a good thing to have if the graph is a tree.
  106. *
  107. * @param root
  108. */
  109. public void setRoot(IKnowledgeSubject root) {
  110. this.root = root;
  111. }
  112. public IKnowledgeSubject getRoot() {
  113. return this.root;
  114. }
  115. protected void buildGraph(IKnowledgeSubject root) throws ThinklabException {
  116. buildGraph(root, null);
  117. }
  118. /*
  119. * can be called as many times as you want unless forceTreeGeometry is true.
  120. */
  121. private void buildGraph(IKnowledgeSubject rootConcept, Set<String> refs)
  122. throws ThinklabException {
  123. if (refs == null && forceTreeGeometry && this.vertexSet().size() > 0)
  124. throw new ThinklabUserErrorException(
  125. "knowledge graph: tree geometry requested, cannot add a new root concept");
  126. String uri = rootConcept.getURI();
  127. if (refs == null) {
  128. refs = new HashSet<String>();
  129. if (rootConcept instanceof IConcept) {
  130. rootIsConcept = true;
  131. }
  132. } else if (refs.contains(uri)) {
  133. return;
  134. }
  135. refs.add(uri);
  136. this.addVertex(rootConcept);
  137. /*
  138. * do not follow further if we're graphing an instance and we're not
  139. * supposed to graph more than the direct type.
  140. */
  141. if (rootConcept instanceof IConcept && !rootIsConcept
  142. && followClassLevel == 0) {
  143. return;
  144. }
  145. if (rootConcept instanceof IConcept) {
  146. /*
  147. * see what edge we want for the children
  148. */
  149. for (IConcept c : ((IConcept) rootConcept).getChildren()) {
  150. if (followProperty((IConcept) rootConcept, c, null)) {
  151. buildGraph(c, refs);
  152. this.addEdge(c, rootConcept);
  153. }
  154. }
  155. } else {
  156. /*
  157. * if we're graphing instances and we want concepts, add the concept
  158. */
  159. if (!rootIsConcept && followClassLevel > 0) {
  160. if (followRelationship(rootConcept, null,
  161. ((IInstance) rootConcept).getDirectType())) {
  162. buildGraph(((IInstance) rootConcept).getDirectType(), refs);
  163. this.addEdge(rootConcept, ((IInstance) rootConcept)
  164. .getDirectType());
  165. }
  166. }
  167. }
  168. /*
  169. * if it's a concept, we want to follow the range of each direct
  170. * property
  171. */
  172. if (rootConcept instanceof IConcept) {
  173. IConcept source = (IConcept) rootConcept;
  174. for (IProperty property : source.getProperties()) {
  175. for (IConcept target : source.getPropertyRange(property)) {
  176. if (!followProperty(source, target, property))
  177. continue;
  178. boolean isThere = refs.contains(target.getURI());
  179. /*
  180. * follow the object if it's not there
  181. */
  182. if (!isThere) {
  183. buildGraph(target, refs);
  184. }
  185. /*
  186. * add edge unless we want a tree and it's there already
  187. */
  188. if (!(isThere && forceTreeGeometry)) {
  189. PropertyEdge edge = (PropertyEdge) this.addEdge(rootConcept, target);
  190. if (edge != null)
  191. edge.setProperty(property);
  192. }
  193. isThere = refs.contains(target.getURI());
  194. }
  195. }
  196. }
  197. /**
  198. * Follow relationships
  199. */
  200. for (IRelationship r : rootConcept.getRelationships()) {
  201. boolean doit = followRelationship(rootConcept, r, null);
  202. if (doit) {
  203. if (r.isObject()) {
  204. IInstance inst = r.getValue().asObjectReference()
  205. .getObject();
  206. boolean isThere = refs.contains(inst.getURI());
  207. /*
  208. * follow the object if it's not there
  209. */
  210. if (!isThere && (!rootIsConcept || followInstances)) {
  211. buildGraph(inst, refs);
  212. }
  213. /*
  214. * add edge unless we want a tree and it's there already
  215. */
  216. if (!(isThere && forceTreeGeometry)) {
  217. ((PropertyEdge) this.addEdge(rootConcept, inst)).setProperty(
  218. r.getProperty());
  219. }
  220. isThere = refs.contains(inst.getURI());
  221. } else if (r.isClassification()) {
  222. IConcept conc = r.getValue().getConcept();
  223. /*
  224. * follow concept if we are allowed
  225. */
  226. if (rootIsConcept || followClassLevel > 0) {
  227. buildGraph(conc, refs);
  228. this.addEdge(rootConcept, conc);
  229. }
  230. }
  231. }
  232. }
  233. }
  234. public void forceTreeGeometry(boolean what) {
  235. forceTreeGeometry = what;
  236. }
  237. public void followInstances(boolean what) {
  238. followInstances = what;
  239. }
  240. public void collapseEdges(boolean collapse, Object content) {
  241. collapseEdges = collapse;
  242. edgeContent = content;
  243. }
  244. /**
  245. * Quick and dirty method to show the graph in a window. Depends on graphviz
  246. * installed.
  247. *
  248. * @throws ThinklabException
  249. */
  250. public void show() throws ThinklabException {
  251. class NPP implements GraphViz.NodePropertiesProvider {
  252. public int getNodeHeight(Object o) {
  253. return 0;
  254. }
  255. public String getNodeId(Object o) {
  256. return o.toString().replace(':', '_').replace(' ', '_');
  257. }
  258. public int getNodeWidth(Object o) {
  259. return 0;
  260. }
  261. }
  262. GraphViz gv = new GraphViz();
  263. gv.loadGraph(this, new NPP());
  264. gv.show();
  265. }
  266. public void dump(OutputStream o) {
  267. BufferedWriter w = new BufferedWriter(new OutputStreamWriter(o));
  268. try {
  269. for (Object c : this.vertexSet()) {
  270. w.write(c.toString());
  271. int edgecount = 0;
  272. for (Object p : this.outgoingEdgesOf(c)) {
  273. if (edgecount++ == 0)
  274. w.write(": --- " + ((PropertyEdge) p).property
  275. + " --> (");
  276. else
  277. w.write(", ");
  278. w.write(this.getEdgeTarget(p).toString());
  279. }
  280. if (edgecount > 0)
  281. w.write(")\n");
  282. else
  283. w.write("\n");
  284. }
  285. } catch (IOException e) {
  286. // TODO Auto-generated catch block
  287. e.printStackTrace();
  288. }
  289. }
  290. public void dump() {
  291. dump(System.out);
  292. }
  293. /**
  294. * Read a graph from a simple text format. Each non-empty line in the file can be
  295. * a comment (starting with #) or a set of edges.
  296. *
  297. * If null property edges are desired, the first token (whitespace-separated) should
  298. * be the semantic type of the source concept, and the remaining are concepts that
  299. * should be linked to it.
  300. *
  301. * If the property for the edge is specified, the first token is the semantic type
  302. * of the property with a colon appended. The remaining token are concepts, structured
  303. * as said above.
  304. *
  305. * @param infile
  306. * @throws ThinklabException
  307. */
  308. public void read(File infile) throws ThinklabException {
  309. try {
  310. FileInputStream fstream = new FileInputStream(infile);
  311. DataInputStream in = new DataInputStream(fstream);
  312. BufferedReader br = new BufferedReader(new InputStreamReader(in));
  313. String strLine;
  314. while ((strLine = br.readLine()) != null) {
  315. strLine = strLine.trim();
  316. if (strLine.startsWith("#") || strLine.equals(""))
  317. continue;
  318. /* tokenize */
  319. String[] tokens = strLine.split("\\s+");
  320. if (tokens.length < 1)
  321. continue;
  322. IProperty property = null;
  323. /* see if we have a property as first token, which must have a : at the end */
  324. int start = 0;
  325. if (tokens[0].endsWith(":")) {
  326. start = 1;
  327. property =
  328. KnowledgeManager.get().requireProperty(
  329. tokens[0].substring(0, tokens[0].length()-2));
  330. }
  331. /* first remaining token is concept, others (if any) are its dependents */
  332. IConcept source = KnowledgeManager.get().requireConcept(tokens[start]);
  333. addVertex(source);
  334. /* add any remaining edges and vertices */
  335. for (int i = start + 1; i < tokens.length; i++) {
  336. IConcept target = KnowledgeManager.get().requireConcept(tokens[i]);
  337. addVertex(target);
  338. PropertyEdge e = (PropertyEdge) addEdge(source, target);
  339. if (e != null)
  340. e.setProperty(property);
  341. }
  342. }
  343. in.close();
  344. } catch (IOException e) {
  345. throw new ThinklabIOException(e);
  346. }
  347. }
  348. /**
  349. * Called at each relationship found. If it returns an edge, the associated
  350. * knowledge is linked and (if unknown) processed. Otherwise the cycle
  351. * stops. It's called on literals and classifications as well; of course it
  352. * should return null if called on a literal.
  353. *
  354. * @param source
  355. * @param relationship
  356. * the relationship. If null, type is not null and the
  357. * relationship is direct type
  358. * @param type
  359. * the concept that we're linking to. If not null, we're
  360. * processing a direct type and relationship is null.
  361. * @return
  362. */
  363. protected abstract boolean followRelationship(IKnowledgeSubject source,
  364. IRelationship relationship, IConcept type);
  365. /**
  366. * Called at each explicit relationship between concepts, caused by a range
  367. * statement or specialized through a restriction. Also called for isa,
  368. * passing a null property.
  369. *
  370. * @param source
  371. * @param target
  372. * @param property
  373. * if null, we're processing children or direct types
  374. * @return
  375. */
  376. protected abstract boolean followProperty(IConcept source, IConcept target,
  377. IProperty property);
  378. }