/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT 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, version 3 of the License.
*
* OpenIoT 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 OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
*/
package org.openiot.cupus.subForest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.openiot.cupus.artefact.ActiveSubscription;
import org.openiot.cupus.artefact.Announcement;
import org.openiot.cupus.artefact.Publication;
import org.openiot.cupus.artefact.Subscription;
import org.openiot.cupus.common.SubscriptionDataStructure;
/**
*
* @author Aleksandar, Eugen
*/
public class ActSubTree {
public static final int ADDED_AS_PARENT = 13;
private ActSubNode rootElement;
protected Set<UUID> activeSubscribersSet = new HashSet<UUID>();
protected HashMap<UUID, Long> activeSubscribersCounter = new HashMap<UUID, Long>();
public ActSubTree(ActSubNode rootNode) {
this.addNode(rootNode, null); //will set this as new root
}
public int getTreeDepth(ActSubNode node) {
int i = 0;
if (!node.getChildren().isEmpty()) {
int temp;
for (ActSubNode e : node.getChildren()) {
temp = getTreeDepth(e) + 1;
if (temp > i) {
i = temp;
}
}
} else {
i = 1;
}
return i;
}
/**
* This method adds all the subscribers (counters) from the given tree's
* activeSubscribersCounter map to "this" tree's activeSubscribersSet/
* activeSubscribersCounter maps.
*
* @param excludeRoot If "excludeRoot" is set to true then the subscriber from the
* subscription of the root node of the "other" tree is subtracted/removed from the
* map of subscribers/subcriberCounters of "this" tree.
*/
protected void addAllSubscribers(ActSubTree other, boolean excludeRoot){
//add all from other tree
for (Map.Entry<UUID, Long> entry : other.activeSubscribersCounter.entrySet()){
Long counter = this.activeSubscribersCounter.get(entry.getKey());
if (counter==null){
this.activeSubscribersSet.add(entry.getKey());
this.activeSubscribersCounter.put(entry.getKey(), entry.getValue());
} else {
this.activeSubscribersCounter.put(entry.getKey(), counter+entry.getValue());
}
}
if (!excludeRoot)
return;
// minus the root of the other tree
UUID otherRootSubscriber = other.getRootElement().data.getSubscriberID();
Long counter = this.activeSubscribersCounter.get(otherRootSubscriber);
if (counter==1){
this.activeSubscribersSet.remove(otherRootSubscriber);
this.activeSubscribersCounter.remove(otherRootSubscriber);
} else {
this.activeSubscribersCounter.put(otherRootSubscriber, counter-1);
}
}
protected HashSet<UUID> findMatchingSubscribers(Publication publication) {
HashSet<UUID> subscribers = new HashSet<UUID>();
findMatchingSubscribers(publication, this.rootElement, subscribers);
return subscribers;
}
private void findMatchingSubscribers(Publication publication, ActSubNode node, HashSet<UUID> subscribers){
if (subscribers.size()==activeSubscribersSet.size()) {
return;
}
if (node.data.coversPublication(publication)) {
subscribers.add(node.data.getSubscriberID());
for (ActSubNode e : node.getChildren()) {
findMatchingSubscribers(publication, e, subscribers);
}
}
}
protected HashSet<Subscription> findMatchingSubscriptions(Announcement announcement) {
HashSet<Subscription> subscriptions = new HashSet<Subscription>();
findMatchingSubscriptions(announcement, this.rootElement, subscriptions);
return subscriptions;
}
private void findMatchingSubscriptions(Announcement announcement, ActSubNode node, HashSet<Subscription> subscriptions){
if (announcement.coversSubscription(node.data)) {
subscriptions.add(node.data.getSubscription());
for (ActSubNode e : node.getChildren()) {
findMatchingSubscriptions(announcement, e, subscriptions);
}
}
}
/**
* Return the root ActSubNode of the tree.
*
* @return the root element.
*/
public ActSubNode getRootElement() {
return this.rootElement;
}
/**
* Set the root Element for the tree.
*
* @param rootElement the root element to set.
*/
public void setRootElement(ActSubNode rootElement) {
this.rootElement = rootElement;
}
/**
* Returns the Tree<T> as a List of ActSubNode<T> objects. The elements of
* the List are generated from a pre-order traversal of the tree.
*
* @return a List<ActSubNode<T>>.
*/
public List<ActSubNode> toList() {
List<ActSubNode> list = new ArrayList<ActSubNode>();
walk(rootElement, list);
return list;
}
/**
* Returns a String representation of the Tree. The elements are generated
* from a pre-order traversal of the Tree.
*
* @return the String representation of the Tree.
*/
@Override
public String toString() {
return toList().toString();
}
/**
* Walks the Tree in pre-order style. This is a recursive method, and is
* called from the toList() method with the root element as the first
* argument. It appends to the second argument, which is passed by reference
* * as it recurses down the tree.
*
* @param element the starting element.
* @param list the output of the walk.
*/
private void walk(ActSubNode element, List<ActSubNode> list) {
list.add(element);
for (ActSubNode data : element.getChildren()) {
walk(data, list);
}
}
public int addNode(ActSubNode newNode, ActSubNode startNode) {
if (startNode == null) {
this.setRootElement(newNode);
this.activeSubscribersSet.add(newNode.data.getSubscriberID());
this.activeSubscribersCounter.put(newNode.data.getSubscriberID(), 1L);
return SubscriptionDataStructure.NEW_TREE_CREATED;
}
if (newNode.data.equals(startNode.data))
return SubscriptionDataStructure.SUB_ALREADY_IN_FOREST;
if (startNode.equals(this.getRootElement())) {
Long subCount = this.activeSubscribersCounter.get(newNode.data.getSubscriberID());
if (subCount==null){
this.activeSubscribersSet.add(newNode.data.getSubscriberID());
this.activeSubscribersCounter.put(newNode.data.getSubscriberID(), 1L);
} else {
this.activeSubscribersCounter.put(newNode.data.getSubscriberID(), subCount+1);
}
}
if (newNode.data.getSubscription().coversSubscription(startNode.data.getSubscription())) {
newNode.addChild(startNode);
if (startNode.equals(this.rootElement)){
setRootElement(newNode);
return SubscriptionDataStructure.NEW_TREE_ROOT;
} else {
return ADDED_AS_PARENT;
}
} else if (startNode.data.getSubscription().coversSubscription(newNode.data.getSubscription())) {
if (startNode.getChildren().isEmpty()) {
startNode.addChild(newNode);
return SubscriptionDataStructure.SUB_ADDED;
}
List<ActSubNode> toBeRemoved = new ArrayList<ActSubNode>();
for (ActSubNode e : startNode.getChildren()) {
switch (addNode(newNode, e)){ //try to add the node to each of the children...
case (ADDED_AS_PARENT):
toBeRemoved.add(e); //try other children to see if new node is a common parent to more...
break;
case (SubscriptionDataStructure.SUB_NOT_ADDED):
break; //try other children...
case (SubscriptionDataStructure.SUB_ADDED):
return SubscriptionDataStructure.SUB_ADDED;
case (SubscriptionDataStructure.SUB_ALREADY_IN_FOREST):
return SubscriptionDataStructure.SUB_ALREADY_IN_FOREST;
case (SubscriptionDataStructure.NEW_TREE_CREATED):
return SubscriptionDataStructure.NEW_TREE_CREATED; //should not happen, let the forest deal with it...
case (SubscriptionDataStructure.NEW_TREE_ROOT):
System.err.println("ActSubTree: New ROOT?!?! Probably multiple pointers at root...");
case (SubscriptionDataStructure.ERROR_ADDING_SUB):
default:
return SubscriptionDataStructure.ERROR_ADDING_SUB;
}
}
startNode.children.removeAll(toBeRemoved); //remove all the children that put the new node as their parent...
startNode.addChild(newNode);
return SubscriptionDataStructure.SUB_ADDED;
} else {
return SubscriptionDataStructure.SUB_NOT_ADDED;
}
}
public void removeNode(ActiveSubscription subscription, ActSubNode rootNode) {
for (ActSubNode e : rootNode.getChildren()) {
if (e.data.equals(subscription)) {
for (ActSubNode f : e.getChildren()) {
rootNode.children.add(f);
}
rootNode.children.remove(e);
long temp;
if (this.activeSubscribersCounter.containsKey(subscription.getSubscriberID())) {
temp = this.activeSubscribersCounter.remove(subscription.getSubscriberID());
} else {
temp = 0;
}
this.activeSubscribersCounter.put(subscription.getSubscriberID(), temp - 1);
return;
} else {
if (e.data.getSubscription().coversSubscription(subscription.getSubscription())) {
removeNode(subscription, e);
}
}
}
}
public boolean coversPublication(Publication publication) {
if (this.getRootElement().data.coversPublication(publication)) {
return true;
}
return false;
}
public void deleteSubscriber(UUID subscriberID) {
for (ActSubNode e : this.toList()) {
if (e.data.getSubscriberID().equals(subscriberID)) {
this.removeNode(e.data, rootElement);
}
}
activeSubscribersSet.remove(subscriberID);
activeSubscribersCounter.remove(subscriberID);
}
public boolean contains(Subscription sub, ActSubNode root) {
if (this.rootElement.data.getSubscription().equals(sub)) {
return true;
} else {
for (ActSubNode e : root.getChildren()) {
contains(sub, e);
}
}
return false;
}
}