/*
* Copyright (c) 2012-2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package org.eclipse.moquette.spi.impl.subscriptions;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
class TreeNode {
private class ClientIDComparator implements Comparator<Subscription> {
public int compare(Subscription o1, Subscription o2) {
return o1.getClientId().compareTo(o2.getClientId());
}
}
TreeNode m_parent;
Token m_token;
List<TreeNode> m_children = new ArrayList<TreeNode>();
List<Subscription> m_subscriptions = new ArrayList<Subscription>();
TreeNode(TreeNode parent) {
this.m_parent = parent;
}
Token getToken() {
return m_token;
}
void setToken(Token topic) {
this.m_token = topic;
}
void addSubscription(Subscription s) {
//avoid double registering for same clientID, topic and QoS
if (m_subscriptions.contains(s)) {
return;
}
//remove existing subscription for same client and topic but different QoS
int existingSubIdx = Collections.binarySearch(m_subscriptions, s, new ClientIDComparator());
if (existingSubIdx >= 0) {
m_subscriptions.remove(existingSubIdx);
}
m_subscriptions.add(s);
}
void addChild(TreeNode child) {
m_children.add(child);
}
boolean isLeaf() {
return m_children.isEmpty();
}
/**
* Search for children that has the specified token, if not found return
* null;
*/
TreeNode childWithToken(Token token) {
for (TreeNode child : m_children) {
if (child.getToken().equals(token)) {
return child;
}
}
return null;
}
List<Subscription> subscriptions() {
return m_subscriptions;
}
void matches(Queue<Token> tokens, List<Subscription> matchingSubs) {
Token t = tokens.poll();
//check if t is null <=> tokens finished
if (t == null) {
matchingSubs.addAll(m_subscriptions);
//check if it has got a MULTI child and add its subscriptions
for (TreeNode n : m_children) {
if (n.getToken() == Token.MULTI || n.getToken() == Token.SINGLE) {
matchingSubs.addAll(n.subscriptions());
}
}
return;
}
//we are on MULTI, than add subscriptions and return
if (m_token == Token.MULTI) {
matchingSubs.addAll(m_subscriptions);
return;
}
for (TreeNode n : m_children) {
if (n.getToken().match(t)) {
//Create a copy of token, else if navigate 2 sibling it
//consumes 2 elements on the queue instead of one
n.matches(new LinkedBlockingQueue<Token>(tokens), matchingSubs);
//TODO don't create a copy n.matches(tokens, matchingSubs);
}
}
}
/**
* Return the number of registered subscriptions
*/
int size() {
int res = m_subscriptions.size();
for (TreeNode child : m_children) {
res += child.size();
}
return res;
}
void removeClientSubscriptions(String clientID) {
//collect what to delete and then delete to avoid ConcurrentModification
List<Subscription> subsToRemove = new ArrayList<Subscription>();
for (Subscription s : m_subscriptions) {
if (s.clientId.equals(clientID)) {
subsToRemove.add(s);
}
}
for (Subscription s : subsToRemove) {
m_subscriptions.remove(s);
}
//go deep
for (TreeNode child : m_children) {
child.removeClientSubscriptions(clientID);
}
}
/**
* Deactivate all topic subscriptions for the given clientID.
* */
void deactivate(String clientID) {
for (Subscription s : m_subscriptions) {
if (s.clientId.equals(clientID)) {
s.setActive(false);
}
}
//go deep
for (TreeNode child : m_children) {
child.deactivate(clientID);
}
}
/**
* Activate all topic subscriptions for the given clientID.
* */
public void activate(String clientID) {
for (Subscription s : m_subscriptions) {
if (s.clientId.equals(clientID)) {
s.setActive(true);
}
}
//go deep
for (TreeNode child : m_children) {
child.activate(clientID);
}
}
/**
* @return the set of subscriptions for the given client.
* */
Set<Subscription> findAllByClientID(String clientID) {
Set<Subscription> subs = new HashSet<Subscription>();
for (Subscription s : m_subscriptions) {
if (s.clientId.equals(clientID)) {
subs.add(s);
}
}
//go deep
for (TreeNode child : m_children) {
subs.addAll(child.findAllByClientID(clientID));
}
return subs;
}
}