/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* !#
*/
package net.ontopia.topicmaps.utils;
import java.util.Iterator;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import net.ontopia.topicmaps.core.AssociationIF;
import net.ontopia.topicmaps.core.AssociationRoleIF;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.core.index.ClassInstanceIndexIF;
/**
* EXPERIMENTAL.
* @since 1.2
*/
public class TopicTreeBuilder {
protected TopicIF assocType;
protected TopicIF parentRole;
protected TopicIF childRole;
protected Collection<TopicIF> types;
public TopicTreeBuilder(TopicIF assocType,
TopicIF parentRole,
TopicIF childRole) {
if (assocType == null)
throw new NullPointerException("Association type cannot be null");
if (parentRole == null)
throw new NullPointerException("Parent role type cannot be null");
if (childRole == null)
throw new NullPointerException("Child role type cannot be null");
this.assocType = assocType;
this.parentRole = parentRole;
this.childRole = childRole;
this.types = null;
}
public void setFilterTypes(Collection<TopicIF> types) {
this.types = types;
}
/**
* EXPERIMENTAL: Builds a tree consisting of all the topics directly
* related to the one given. The node given can be placed at any
* position in the tree.
*/
public TopicTreeNode build(TopicIF topic) {
TopicIF root = getRoot(topic, new HashSet<TopicIF>());
return build(root, new HashSet<TopicIF>());
}
/**
* EXPERIMENTAL: Builds a tree consisting of all the topics
* participating in any associations of the given type. A node with
* a null topic will be introduced as the common root of all the
* subtrees.
*/
public TopicTreeNode build() {
TopicTreeNode falseroot = new TopicTreeNode(null);
TopicMapIF tm = assocType.getTopicMap();
ClassInstanceIndexIF index = (ClassInstanceIndexIF) tm.getIndex("net.ontopia.topicmaps.core.index.ClassInstanceIndexIF");
Map<TopicIF, TopicTreeNode> trees = new HashMap<TopicIF, TopicTreeNode>(); // trees, registered by root topic
Iterator<AssociationIF> it = index.getAssociations(assocType).iterator();
while (it.hasNext()) {
AssociationIF assoc = it.next();
TopicIF parent = getPlayer(assoc, parentRole);
if (parent == null || !filter(parent))
continue;
TopicIF root = getRoot(parent, new HashSet<TopicIF>());
if (trees.containsKey(root))
continue;
TopicTreeNode rootnode = build(root, new HashSet<TopicIF>());
trees.put(root, rootnode);
falseroot.getChildren().add(rootnode);
}
return falseroot;
}
// --- Internal methods
protected TopicIF getRoot(TopicIF topic, Set<TopicIF> visited) {
visited.add(topic);
Iterator<AssociationRoleIF> it = topic.getRoles().iterator();
while (it.hasNext()) {
AssociationRoleIF role = it.next();
if (!childRole.equals(role.getType()) ||
!assocType.equals(role.getAssociation().getType()))
continue;
TopicIF parent = getPlayer(role.getAssociation(), parentRole);
if (parent == null || !filter(parent))
continue;
// if we've made a loop, consider this the root
if (visited.contains(parent))
return parent;
TopicIF root = getRoot(parent, visited);
return root;
}
return topic;
}
protected TopicTreeNode build(TopicIF topic, Set<TopicIF> visited) {
TopicTreeNode node = new TopicTreeNode(topic);
if (visited.contains(topic))
return node; // we've already been here, so don't look for children again
visited.add(topic);
Iterator<AssociationRoleIF> it = topic.getRoles().iterator();
while (it.hasNext()) {
AssociationRoleIF role = it.next();
if (!parentRole.equals(role.getType()) ||
!assocType.equals(role.getAssociation().getType()))
continue;
TopicIF child = getPlayer(role.getAssociation(), childRole);
if (child == null || !filter(child))
continue;
node.getChildren().add(build(child, visited));
}
return node;
}
protected TopicIF getPlayer(AssociationIF assoc, TopicIF roleType) {
Iterator<AssociationRoleIF> it = assoc.getRoles().iterator();
while (it.hasNext()) {
AssociationRoleIF role = it.next();
if (roleType.equals(role.getType()))
return role.getPlayer();
}
return null;
}
protected boolean filter(TopicIF topic) {
if (types == null)
return true;
Iterator<TopicIF> it = topic.getTypes().iterator();
while (it.hasNext())
if (types.contains(it.next()))
return true;
return false;
}
}