package uk.ac.manchester.cs.jfact.kernel;
/* This file is part of the JFact DL reasoner
Copyright 2011-2013 by Ignazio Palmisano, Dmitry Tsarkov, University of Manchester
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import uk.ac.manchester.cs.jfact.kernel.actors.Actor;
import uk.ac.manchester.cs.jfact.kernel.actors.SupConceptActor;
import uk.ac.manchester.cs.jfact.kernel.options.JFactReasonerConfiguration;
import conformance.Original;
import conformance.PortedFrom;
/** taxonomy */
@PortedFrom(file = "Taxonomy.h", name = "Taxonomy")
public class Taxonomy implements Serializable {
private static final long serialVersionUID = 11000L;
/** array of taxonomy verteces */
@PortedFrom(file = "Taxonomy.h", name = "Graph")
private final List<TaxonomyVertex> graph = new ArrayList<TaxonomyVertex>();
/** aux. vertex to be included to taxonomy */
@PortedFrom(file = "Taxonomy.h", name = "Current")
protected TaxonomyVertex current = new TaxonomyVertex();
/** behaviour flag: if true, insert temporary vertex into taxonomy */
@PortedFrom(file = "Taxonomy.h", name = "willInsertIntoTaxonomy")
protected boolean willInsertIntoTaxonomy = true;
/** vertex with parent Top and child Bot, represents the fresh entity */
@PortedFrom(file = "Taxonomy.h", name = "FreshNode")
protected final TaxonomyVertex FreshNode = new TaxonomyVertex();
/** labeller for marking nodes as checked */
@PortedFrom(file = "Taxonomy.h", name = "checkLabel")
protected long visitedLabel = 1;
@Original
private final JFactReasonerConfiguration options;
/** @return current */
@PortedFrom(file = "Taxonomy.h", name = "getCurrent")
public TaxonomyVertex getCurrent() {
return current;
}
/**
* set current to a given node
*
* @param cur
* cur
*/
@PortedFrom(file = "Taxonomy.h", name = "setCurrent")
public void setCurrent(TaxonomyVertex cur) {
current = cur;
}
/**
* apply ACTOR to subgraph starting from NODE as defined by flags; this
* version is intended to work only with SupConceptActor, which requires the
* method to return as soon as the apply() method returns false
*
* @param node
* node
* @param actor
* actor
* @param needCurrent
* needCurrent
* @param onlyDirect
* onlyDirect
* @param upDirection
* upDirection
* @return false if actor does not apply
*/
@PortedFrom(file = "Taxonomy.h", name = "getRelativesInfo")
public boolean getRelativesInfo(TaxonomyVertex node, SupConceptActor actor,
boolean needCurrent, boolean onlyDirect, boolean upDirection) {
// if current node processed OK and there is no need to continue -- exit
// this is the helper to the case like getDomain():
// if there is a named concept that represent's a domain -- that's what
// we need
try {
if (needCurrent) {
if (!actor.apply(node)) {
return false;
}
if (onlyDirect) {
return true;
}
}
Queue<Iterable<TaxonomyVertex>> queue = new LinkedList<Iterable<TaxonomyVertex>>();
queue.add(node.neigh(upDirection));
while (queue.size() > 0) {
Iterable<TaxonomyVertex> neigh = queue.remove();
for (TaxonomyVertex _node : neigh) {
// recursive applicability checking
if (!isVisited(_node)) {
// label node as visited
setVisited(_node);
// if current node processed OK and there is no need to
// continue -- exit
// if node is NOT processed for some reasons -- go to
// another level
if (!actor.apply(_node) && onlyDirect) {
return false;
}
if (onlyDirect) {
continue;
}
// apply method to the proper neighbours with proper
// parameters
queue.add(_node.neigh(upDirection));
}
}
}
return true;
} finally {
clearVisited();
}
}
/**
* apply ACTOR to subgraph starting from NODE as defined by flags;
*
* @param node
* node
* @param actor
* actor
* @param needCurrent
* needCurrent
* @param onlyDirect
* onlyDirect
* @param upDirection
* upDirection
*/
@PortedFrom(file = "Taxonomy.h", name = "getRelativesInfo")
public void getRelativesInfo(TaxonomyVertex node, Actor actor,
boolean needCurrent, boolean onlyDirect, boolean upDirection) {
// if current node processed OK and there is no need to continue -- exit
// this is the helper to the case like getDomain():
// if there is a named concept that represent's a domain -- that's what
// we need
if (needCurrent && actor.apply(node) && onlyDirect) {
return;
}
List<TaxonomyVertex> queue = new LinkedList<TaxonomyVertex>();
for (TaxonomyVertex v : node.neigh(upDirection)) {
queue.add(v);
}
Set<TaxonomyVertex> pastBoundary = new HashSet<TaxonomyVertex>();
while (queue.size() > 0) {
TaxonomyVertex _node = queue.remove(0);
// recursive applicability checking
if (!isVisited(_node)) {
// label node as visited
setVisited(_node);
// if current node processed OK and there is no need to
// continue -- exit
// if node is NOT processed for some reasons -- go to
// another level
boolean applied = actor.apply(_node);
if (applied && onlyDirect) {
for (TaxonomyVertex boundary : _node.neigh(upDirection)) {
setAllVisited(boundary, upDirection, pastBoundary);
}
continue;
}
// apply method to the proper neighbours with proper
// parameters
// only pick nodes that are policy applicable
for (TaxonomyVertex v : _node.neigh(upDirection)) {
if (actor.applicable(v) || !onlyDirect) {
queue.add(v);
}
}
}
}
actor.removePastBoundaries(pastBoundary);
clearVisited();
}
/**
* set node NODE as checked within taxonomy
*
* @param node
* node
*/
@PortedFrom(file = "Taxonomy.h", name = "setVisited")
public void setVisited(TaxonomyVertex node) {
node.setChecked(visitedLabel);
}
public void setAllVisited(TaxonomyVertex node, boolean direction,
Set<TaxonomyVertex> pastBoundary) {
pastBoundary.add(node);
setVisited(node);
for (TaxonomyVertex v : node.neigh(direction)) {
setVisited(v);
setAllVisited(v, direction, pastBoundary);
}
}
/**
* @param node
* node
* @return check whether NODE is checked within taxonomy
*/
@PortedFrom(file = "Taxonomy.h", name = "isVisited")
public boolean isVisited(TaxonomyVertex node) {
return node.isChecked(visitedLabel);
}
/** clear the CHECKED label from all the taxonomy vertex */
@PortedFrom(file = "Taxonomy.h", name = "clearCheckedLabel")
protected void clearVisited() {
visitedLabel++;
}
/**
* @param pTop
* pTop
* @param pBottom
* pBottom
* @param c
* c
*/
public Taxonomy(ClassifiableEntry pTop, ClassifiableEntry pBottom,
JFactReasonerConfiguration c) {
options = c;
graph.add(new TaxonomyVertex(pBottom));
graph.add(new TaxonomyVertex(pTop));
// set up fresh node
FreshNode.addNeighbour(true, getTopVertex());
FreshNode.addNeighbour(false, getBottomVertex());
}
/** @return reasoner configuration */
public JFactReasonerConfiguration getOptions() {
return options;
}
/** @return TOP of taxonomy */
@PortedFrom(file = "Taxonomy.h", name = "getTopVertex")
public TaxonomyVertex getTopVertex() {
return graph.get(1);
}
/** @return BOTTOM of taxonomy */
@PortedFrom(file = "Taxonomy.h", name = "getBottomVertex")
public TaxonomyVertex getBottomVertex() {
return graph.get(0);
}
/**
* @param e
* e
* @return node for fresh entity E
*/
@PortedFrom(file = "Taxonomy.h", name = "getFreshVertex")
public TaxonomyVertex getFreshVertex(ClassifiableEntry e) {
FreshNode.setSample(e, false);
return FreshNode;
}
@Override
public String toString() {
StringBuilder o = new StringBuilder();
o.append("All entries are in format:\n\"entry\" {n: parent_1 ... parent_n} {m: child_1 child_m}\n\n");
TreeSet<TaxonomyVertex> sorted = new TreeSet<TaxonomyVertex>(
new Comparator<TaxonomyVertex>() {
@Override
public int compare(TaxonomyVertex o1, TaxonomyVertex o2) {
return o1.getPrimer().getName()
.compareTo(o2.getPrimer().getName());
}
});
sorted.addAll(graph.subList(1, graph.size()));
for (TaxonomyVertex p : sorted) {
o.append(p);
}
o.append(getBottomVertex());
return o.toString();
}
/**
* remove node from the taxonomy; assume no references to the node
*
* @param node
* node
*/
@PortedFrom(file = "Taxonomy.h", name = "removeNode")
public void removeNode(TaxonomyVertex node) {
graph.remove(node);
}
/**
* @return true if taxonomy works in a query mode (no need to insert query
* vertex)
*/
@PortedFrom(file = "Taxonomy.h", name = "queryMode")
public boolean queryMode() {
return !willInsertIntoTaxonomy;
}
// flags interface
/** call this method after taxonomy is built */
@PortedFrom(file = "Taxonomy.h", name = "finalise")
public void finalise() {
// create links from leaf concepts to bottom
boolean upDirection = false;
// TODO maybe useful to index Graph
for (int i = 1; i < graph.size(); i++) {
TaxonomyVertex p = graph.get(i);
if (p.noNeighbours(upDirection)) {
p.addNeighbour(upDirection, getBottomVertex());
getBottomVertex().addNeighbour(!upDirection, p);
}
}
willInsertIntoTaxonomy = false;
// after finalisation one shouldn't add
// new entries to taxonomy
}
/** unlink the bottom from the taxonomy */
@PortedFrom(file = "Taxonomy.h", name = "deFinalise")
public void deFinalise() {
boolean upDirection = true;
TaxonomyVertex bot = getBottomVertex();
for (TaxonomyVertex p : bot.neigh(upDirection)) {
p.removeLink(!upDirection, bot);
}
bot.clearLinks(upDirection);
willInsertIntoTaxonomy = true; // it's possible again to add entries
}
/**
* @param syn
* syn
*/
@PortedFrom(file = "Taxonomy.h", name = "addCurrentToSynonym")
public void addCurrentToSynonym(TaxonomyVertex syn) {
ClassifiableEntry currentEntry = current.getPrimer();
if (queryMode()) {
// no need to insert; just mark SYN as a host to curEntry
syn.setVertexAsHost(currentEntry);
} else {
syn.addSynonym(currentEntry);
options.getLog().print("\nTAX:set ", currentEntry.getName(),
" equal ", syn.getPrimer().getName());
}
}
/** insert current node either directly or as a synonym */
@PortedFrom(file = "Taxonomy.h", name = "finishCurrentNode")
public void finishCurrentNode() {
TaxonomyVertex syn = current.getSynonymNode();
if (syn != null) {
addCurrentToSynonym(syn);
} else {
// put curEntry as a representative of Current
if (!queryMode()) {
// insert node into taxonomy
current.incorporate(options);
graph.add(current);
// we used the Current so need to create a new one
current = new TaxonomyVertex();
}
}
}
// ------------------------------------------------------------------------------
// -- classification support
// ------------------------------------------------------------------------------
/** @return true if current entry is a synonym of an already classified one */
@PortedFrom(file = "TaxonomyCreator.cpp", name = "processSynonym")
protected boolean processSynonym() {
ClassifiableEntry currentEntry = current.getPrimer();
ClassifiableEntry syn = ClassifiableEntry.resolveSynonym(currentEntry);
if (syn.equals(currentEntry)) {
return false;
}
// assert willInsertIntoTaxonomy;
if (syn.isClassified()) {
addCurrentToSynonym(syn.getTaxVertex());
return true;
}
return false;
}
/**
*
*/
public void clearLabels() {
visitedLabel++;
}
}