Fixed matching multi topic and single selector

This commit is contained in:
selva.andre
2012-08-08 21:52:18 +02:00
parent 2ec0c76ba2
commit 1173406b0a
2 changed files with 143 additions and 78 deletions
@@ -4,6 +4,7 @@ import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -71,39 +72,35 @@ public class SubscriptionsStore {
void matches(Queue<Token> tokens, List<Subscription> matchingSubs) { void matches(Queue<Token> tokens, List<Subscription> matchingSubs) {
Token t = tokens.poll(); Token t = tokens.poll();
//check if t is null <=> tokens finished
if (t == null) { if (t == null) {
matchingSubs.addAll(m_subscriptions); 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) {
matchingSubs.addAll(n.subscriptions());
}
}
return; return;
} }
//check the matching of the current token //we are on MULTI, than add subscriptions and return
//TODO take care of MULTI and SINGLE if (m_token == Token.MULTI) {
// if (m_token == Token.MULTI) { matchingSubs.addAll(m_subscriptions);
// matchingSubs.addAll(m_subscriptions); return;
// return;
// }
List<TreeNode> matchingChildren = new ArrayList<TreeNode>();
for (TreeNode child : m_children) {
if (child.getToken().match(t)) {
matchingChildren.add(child);
}
} }
// if (matchingChildren.isEmpty()) { for (TreeNode n : m_children) {
// return; if (n.getToken().match(t)) {
// } //Create a copy of token, alse if navigate 2 sibling it
//consumes 2 elements on the queue instead of one
for (TreeNode child : matchingChildren) { n.matches(new LinkedBlockingQueue<Token>(tokens), matchingSubs);
if (child.getToken() == Token.MULTI) {
matchingSubs.addAll(m_subscriptions);
} else {
child.matches(tokens, matchingSubs);
} }
} }
} }
/** /**
* Return the number of registered subscriptions * Return the number of registered subscriptions
*/ */
@@ -132,14 +129,6 @@ public class SubscriptionsStore {
return name; return name;
} }
/**
* Return true iff the current token is compatible with t.
*
* TODO define the rules of compatibility
* if this is a MULTI or SINGLE and the other is any string => match
* of this is a normal token and also the other, and they are equals => match
* if the other is not normal token => don't match
*/
protected boolean match(Token t) { protected boolean match(Token t) {
if (t == MULTI || t == SINGLE) { if (t == MULTI || t == SINGLE) {
return false; return false;
@@ -147,9 +136,9 @@ public class SubscriptionsStore {
if (this == MULTI || this == SINGLE) { if (this == MULTI || this == SINGLE) {
return true; return true;
} else {
return equals(t);
} }
return equals(t);
} }
@Override @Override
@@ -181,7 +170,7 @@ public class SubscriptionsStore {
} }
// private List<Subscription> subscriptions = new ArrayList<Subscription>(); // private List<Subscription> subscriptions = new ArrayList<Subscription>();
private TreeNode subscriptionsRoot = new TreeNode(null); private TreeNode subscriptions = new TreeNode(null);
public void add(Subscription newSubscription) { public void add(Subscription newSubscription) {
List<Token> tokens = new ArrayList<Token>(); List<Token> tokens = new ArrayList<Token>();
@@ -192,8 +181,7 @@ public class SubscriptionsStore {
Logger.getLogger(SubscriptionsStore.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(SubscriptionsStore.class.getName()).log(Level.SEVERE, null, ex);
} }
//Deep into the tree creating missing nodes where necessary TreeNode current = subscriptions;
TreeNode current = subscriptionsRoot;
for (Token token : tokens) { for (Token token : tokens) {
TreeNode matchingChildren; TreeNode matchingChildren;
@@ -219,21 +207,11 @@ public class SubscriptionsStore {
//TODO handle the parse exception //TODO handle the parse exception
Logger.getLogger(SubscriptionsStore.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(SubscriptionsStore.class.getName()).log(Level.SEVERE, null, ex);
} }
// TreeNode current = subscriptionsRoot;
List<Subscription> matchingSubscriptions = new ArrayList<Subscription>(); Queue<Token> tokenQueue = new LinkedBlockingDeque<Token>(tokens);
subscriptionsRoot.matches(new LinkedBlockingQueue<Token>(tokens), matchingSubscriptions); List<Subscription> matchingSubs = new ArrayList<Subscription>();
return matchingSubscriptions; subscriptions.matches(tokenQueue, matchingSubs);
// for (Token token : tokens) { return matchingSubs;
// current = current.matchNext(token);
// if (current == null) {
// break;
// }
// }
// if (current != null) {
// return current.subscriptions();
// } else {
// return Collections.EMPTY_LIST;
// }
} }
public boolean contains(Subscription sub) { public boolean contains(Subscription sub) {
@@ -241,7 +219,7 @@ public class SubscriptionsStore {
} }
public int size() { public int size() {
return subscriptionsRoot.size(); return subscriptions.size();
} }
protected List<Token> splitTopic(String topic) throws ParseException { protected List<Token> splitTopic(String topic) throws ParseException {
@@ -14,12 +14,12 @@ import org.junit.Test;
* @author andrea * @author andrea
*/ */
public class SubscriptionsStoreTest { public class SubscriptionsStoreTest {
private SubscriptionsStore store; private SubscriptionsStore store;
public SubscriptionsStoreTest() { public SubscriptionsStoreTest() {
} }
@Before @Before
public void setUp() { public void setUp() {
store = new SubscriptionsStore(); store = new SubscriptionsStore();
@@ -29,91 +29,178 @@ public class SubscriptionsStoreTest {
public void testSplitTopic() throws ParseException { public void testSplitTopic() throws ParseException {
List tokens = store.splitTopic("finance/stock/ibm"); List tokens = store.splitTopic("finance/stock/ibm");
assertEqualsSeq(asArray("finance", "stock", "ibm"), tokens); assertEqualsSeq(asArray("finance", "stock", "ibm"), tokens);
tokens = store.splitTopic("/finance/stock/ibm"); tokens = store.splitTopic("/finance/stock/ibm");
assertEqualsSeq(asArray(Token.EMPTY, "finance", "stock", "ibm"), tokens); assertEqualsSeq(asArray(Token.EMPTY, "finance", "stock", "ibm"), tokens);
tokens = store.splitTopic("/"); tokens = store.splitTopic("/");
assertEqualsSeq(asArray(Token.EMPTY), tokens); assertEqualsSeq(asArray(Token.EMPTY), tokens);
} }
@Test(expected = ParseException.class) @Test(expected = ParseException.class)
public void testSplitTopicTwinsSlashAvoided() throws ParseException { public void testSplitTopicTwinsSlashAvoided() throws ParseException {
store.splitTopic("/finance//stock/ibm"); store.splitTopic("/finance//stock/ibm");
} }
@Test @Test
public void testSplitTopicMultiValid() throws ParseException { public void testSplitTopicMultiValid() throws ParseException {
List tokens = store.splitTopic("finance/stock/#"); List tokens = store.splitTopic("finance/stock/#");
assertEqualsSeq(asArray("finance", "stock", Token.MULTI), tokens); assertEqualsSeq(asArray("finance", "stock", Token.MULTI), tokens);
tokens = store.splitTopic("#"); tokens = store.splitTopic("#");
assertEqualsSeq(asArray(Token.MULTI), tokens); assertEqualsSeq(asArray(Token.MULTI), tokens);
} }
@Test(expected = ParseException.class) @Test(expected = ParseException.class)
public void testSplitTopicMultiInTheMiddleNotValid() throws ParseException { public void testSplitTopicMultiInTheMiddleNotValid() throws ParseException {
store.splitTopic("finance/#/closingprice"); store.splitTopic("finance/#/closingprice");
} }
@Test(expected = ParseException.class) @Test(expected = ParseException.class)
public void testSplitTopicMultiNotAferSeparatorNotValid() throws ParseException { public void testSplitTopicMultiNotAferSeparatorNotValid() throws ParseException {
store.splitTopic("finance#"); store.splitTopic("finance#");
} }
@Test @Test
public void testSplitTopicSingleValid() throws ParseException { public void testSplitTopicSingleValid() throws ParseException {
List tokens = store.splitTopic("finance/stock/+"); List tokens = store.splitTopic("finance/stock/+");
assertEqualsSeq(asArray("finance", "stock", Token.SINGLE), tokens); assertEqualsSeq(asArray("finance", "stock", Token.SINGLE), tokens);
tokens = store.splitTopic("+"); tokens = store.splitTopic("+");
assertEqualsSeq(asArray(Token.SINGLE), tokens); assertEqualsSeq(asArray(Token.SINGLE), tokens);
tokens = store.splitTopic("finance/+/ibm"); tokens = store.splitTopic("finance/+/ibm");
assertEqualsSeq(asArray("finance", Token.SINGLE, "ibm"), tokens); assertEqualsSeq(asArray("finance", Token.SINGLE, "ibm"), tokens);
} }
@Test(expected = ParseException.class) @Test(expected = ParseException.class)
public void testSplitTopicSingleNotAferSeparatorNotValid() throws ParseException { public void testSplitTopicSingleNotAferSeparatorNotValid() throws ParseException {
store.splitTopic("finance+"); store.splitTopic("finance+");
} }
@Test @Test
public void testMatchSimple() { public void testMatchSimple() {
store.add(new Subscription(null, "/", AbstractMessage.QOSType.MOST_ONE)); Subscription slashSub = new Subscription(null, "/", AbstractMessage.QOSType.MOST_ONE);
store.add(slashSub);
assertTrue(store.matches("finance").isEmpty()); assertTrue(store.matches("finance").isEmpty());
store.add(new Subscription(null, "/finance", AbstractMessage.QOSType.MOST_ONE)); Subscription slashFinanceSub = new Subscription(null, "/finance", AbstractMessage.QOSType.MOST_ONE);
store.add(slashFinanceSub);
assertTrue(store.matches("finance").isEmpty()); assertTrue(store.matches("finance").isEmpty());
assertTrue(store.matches("/finance").contains(slashFinanceSub));
assertTrue(store.matches("/").contains(slashSub));
} }
@Test @Test
public void testMatchSimpleMulti() { public void testMatchSimpleMulti() {
store.add(new Subscription(null, "#", AbstractMessage.QOSType.MOST_ONE)); Subscription anySub = new Subscription(null, "#", AbstractMessage.QOSType.MOST_ONE);
assertFalse(store.matches("finance").isEmpty()); store.add(anySub);
assertTrue(store.matches("finance").contains(anySub));
store.add(new Subscription(null, "finance/#", AbstractMessage.QOSType.MOST_ONE)); Subscription financeAnySub = new Subscription(null, "finance/#", AbstractMessage.QOSType.MOST_ONE);
assertFalse(store.matches("finance").isEmpty()); store.add(financeAnySub);
assertTrue(store.matches("finance").containsAll(Arrays.asList(financeAnySub, anySub)));
}
@Test
public void testMatchingDeepMulti_one_layer() {
Subscription anySub = new Subscription(null, "#", AbstractMessage.QOSType.MOST_ONE);
Subscription financeAnySub = new Subscription(null, "finance/#", AbstractMessage.QOSType.MOST_ONE);
store.add(anySub);
store.add(financeAnySub);
//Verify
assertTrue(store.matches("finance/stock").containsAll(Arrays.asList(financeAnySub, anySub)));
assertTrue(store.matches("finance/stock/ibm").containsAll(Arrays.asList(financeAnySub, anySub)));
} }
@Test
public void testMatchingDeepMulti_two_layer() {
Subscription financeAnySub = new Subscription(null, "finance/stock/#", AbstractMessage.QOSType.MOST_ONE);
store.add(financeAnySub);
//Verify
assertTrue(store.matches("finance/stock/ibm").contains(financeAnySub));
}
@Test
public void testMatchSimpleSingle() {
Subscription anySub = new Subscription(null, "+", AbstractMessage.QOSType.MOST_ONE);
store.add(anySub);
assertTrue(store.matches("finance").contains(anySub));
Subscription financeOne = new Subscription(null, "finance/+", AbstractMessage.QOSType.MOST_ONE);
store.add(financeOne);
assertTrue(store.matches("finance/stock").contains(financeOne));
}
@Test
public void testMatchManySingle() {
Subscription manySub = new Subscription(null, "+/+", AbstractMessage.QOSType.MOST_ONE);
store.add(manySub);
//verify
assertTrue(store.matches("/finance").contains(manySub));
}
@Test
public void testMatchSlashSingle() {
Subscription slashPlusSub = new Subscription(null, "/+", AbstractMessage.QOSType.MOST_ONE);
store.add(slashPlusSub);
Subscription anySub = new Subscription(null, "+", AbstractMessage.QOSType.MOST_ONE);
store.add(anySub);
//Verify
assertEquals(1, store.matches("/finance").size());
assertTrue(store.matches("/finance").contains(slashPlusSub));
assertFalse(store.matches("/finance").contains(anySub));
}
@Test
public void testMatchManyDeepSingle() {
Subscription slashPlusSub = new Subscription(null, "/finance/+/ibm", AbstractMessage.QOSType.MOST_ONE);
store.add(slashPlusSub);
Subscription slashPlusDeepSub = new Subscription(null, "/+/stock/+", AbstractMessage.QOSType.MOST_ONE);
store.add(slashPlusDeepSub);
//Verify
assertTrue(store.matches("/finance/stock/ibm").containsAll(Arrays.asList(slashPlusSub, slashPlusDeepSub)));
}
@Test
public void testMatchSimpleMulti_allTheTree() {
store.add(new Subscription(null, "#", AbstractMessage.QOSType.MOST_ONE));
assertFalse(store.matches("finance").isEmpty());
assertFalse(store.matches("finance/ibm").isEmpty());
}
@Test
public void testMatchSimpleMulti_zeroLevel() {
//check MULTI in case of zero level match
store.add(new Subscription(null, "finance/#", AbstractMessage.QOSType.MOST_ONE));
assertFalse(store.matches("finance").isEmpty());
}
private static Token[] asArray(Object... l) { private static Token[] asArray(Object... l) {
Token[] tokens = new Token[l.length]; Token[] tokens = new Token[l.length];
for (int i = 0; i < l.length; i++) { for (int i = 0; i < l.length; i++) {
Object o = l[i]; Object o = l[i];
if (o instanceof Token) { if (o instanceof Token) {
tokens[i] = (Token)o; tokens[i] = (Token) o;
} else { } else {
tokens[i] = new Token(o.toString()); tokens[i] = new Token(o.toString());
} }
} }
return tokens; return tokens;
} }
private void assertEqualsSeq(Token[] exptected, List<Token> result) { private void assertEqualsSeq(Token[] exptected, List<Token> result) {
List<Token> expectedList = Arrays.asList(exptected); List<Token> expectedList = Arrays.asList(exptected);
assertEquals(expectedList, result); assertEquals(expectedList, result);