summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/ChannelNode.java153
-rw-r--r--src/main/java/IChannelNode.java14
-rw-r--r--src/test/java/ChannelNodeTest.java58
3 files changed, 221 insertions, 4 deletions
diff --git a/src/main/java/ChannelNode.java b/src/main/java/ChannelNode.java
new file mode 100644
index 0000000..e984cf0
--- /dev/null
+++ b/src/main/java/ChannelNode.java
@@ -0,0 +1,153 @@
1import java.util.ArrayList;
2import java.util.List;
3import java.util.Map;
4import java.util.TreeMap;
5
6public class ChannelNode implements IChannelNode {
7 private Map<IChannelNode, Integer> incoming;
8 private Map<IChannelNode, Integer> outgoing;
9
10 public ChannelNode() {
11 incoming = new TreeMap<>();
12 outgoing = new TreeMap<>();
13 }
14
15 @Override
16 public void setConnections(Map<IChannelNode, Integer> conmap, Direction dir) throws IllegalArgumentException {
17 if(conmap == null) throw new IllegalArgumentException("connection map is null");
18 if(dir == null) throw new IllegalArgumentException("direction is null");
19 if(conmap.containsKey(this)) throw new IllegalArgumentException("connection map contains this node");
20
21 int dv = dir.getVal();
22 if(dv >= Direction.INCOMING.getVal()) incoming = conmap;
23 if(dv >= Direction.OUTGOING.getVal()) outgoing = conmap;
24 }
25
26 @Override
27 public void setNumConnections(IChannelNode node, Direction dir, int num) throws IllegalArgumentException {
28 if(node == null) throw new IllegalArgumentException("node is null");
29 if(dir == null) throw new IllegalArgumentException("dir is null");
30 if(num < 0) throw new IllegalArgumentException("num < 0");
31
32 int dv = dir.getVal();
33 if(dv >= Direction.INCOMING.getVal()) incoming.put(node, num);
34 if(dv >= Direction.OUTGOING.getVal()) outgoing.put(node, num);
35 }
36
37 @Override
38 public void addConnection(IChannelNode node, Direction dir) {
39 if(node == null) throw new IllegalArgumentException("node is null");
40 if(node == this) throw new IllegalArgumentException("node is self");
41 if(dir == null) throw new IllegalStateException("dir is null");
42
43 int dv = dir.getVal();
44 if(dv >= Direction.INCOMING.getVal()) incoming.put(node, incoming.get(node) + 1);
45 if(dv >= Direction.OUTGOING.getVal()) outgoing.put(node, outgoing.get(node) + 1);
46 }
47
48 @Override
49 public void addConnections(Iterable<IChannelNode> nodes, Direction dir) {
50 if(nodes == null) throw new IllegalArgumentException("node is null");
51 if(dir == null) throw new IllegalArgumentException("dir is null");
52 nodes.forEach((node) -> {if(node == this) throw new IllegalArgumentException("one of the included nodes is this node");});
53
54 int dv = dir.getVal(),
55 incomingV = Direction.INCOMING.getVal(),
56 outgoingV = Direction.OUTGOING.getVal();
57
58 nodes.forEach((node) -> {
59 if(dv >= incomingV) incoming.put(node, incoming.get(node) + 1);
60 if(dv >= outgoingV) outgoing.put(node, outgoing.get(node) + 1);
61 });
62 }
63
64 @Override
65 public void removeConnection(IChannelNode node, Direction dir) {
66 if(node == null) throw new IllegalArgumentException("node is null");
67 if(node == this) throw new IllegalArgumentException("node is self");
68 if(dir == null) throw new IllegalStateException("dir is null");
69
70 int dv = dir.getVal();
71 if(dv >= Direction.INCOMING.getVal()) incoming.put(node, Math.max(incoming.get(node) - 1, 0));
72 if(dv >= Direction.OUTGOING.getVal()) outgoing.put(node, Math.max(outgoing.get(node) - 1, 0));
73 }
74
75 @Override
76 public void removeConnections(Iterable<IChannelNode> nodes, Direction dir) {
77 if(nodes == null) throw new IllegalArgumentException("node is null");
78 if(dir == null) throw new IllegalArgumentException("dir is null");
79 nodes.forEach((node) -> {if(node == this) throw new IllegalArgumentException("one of the included nodes is this node");});
80
81 int dv = dir.getVal(),
82 incomingV = Direction.INCOMING.getVal(),
83 outgoingV = Direction.OUTGOING.getVal();
84
85 nodes.forEach((node) -> {
86 if(dv >= incomingV) incoming.put(node, Math.max(incoming.get(node) - 1, 0));
87 if(dv >= outgoingV) outgoing.put(node, Math.max(outgoing.get(node) - 1, 0));
88 });
89 }
90
91 @Override
92 public void clearConnections(Direction dir) {
93 if(dir == null) throw new IllegalArgumentException("dir is null");
94
95 int dv = dir.getVal();
96 if(dv >= Direction.INCOMING.getVal()) incoming.clear();
97 if(dv >= Direction.OUTGOING.getVal()) outgoing.clear();
98 }
99
100 @Override
101 public boolean connectionExists(IChannelNode node, Direction dir) {
102 if(node == null) throw new IllegalArgumentException("node is null");
103 if(dir == null) throw new IllegalArgumentException("dir is null");
104
105 switch (dir) {
106 case INCOMING -> {return incoming.containsKey(node);}
107 case OUTGOING -> {return outgoing.containsKey(node);}
108 case BOTH -> {return (incoming.containsKey(node) && outgoing.containsKey(node));}
109 default -> throw new IllegalStateException("got unknown direction");
110 }
111 }
112
113 @Override
114 public int getNumConnections(Direction dir) {
115 int total = 0;
116 int dv = dir.getVal();
117
118 if(dv >= Direction.INCOMING.getVal()) for(int i: incoming.values()) total += i;
119 if(dv >= Direction.OUTGOING.getVal()) for(int i: outgoing.values()) total += i;
120 return total;
121 }
122
123 @Override
124 public Map<IChannelNode, Integer> getIncomingConnections() {
125 return incoming;
126 }
127
128 @Override
129 public Map<IChannelNode, Integer> getOutgoingConnections() {
130 return outgoing;
131 }
132
133 @Override
134 public List<Map<IChannelNode, Integer>> getConnections(Direction dir) {
135 if(dir == null) throw new IllegalArgumentException("dir is null");
136
137 int dv = dir.getVal();
138 ArrayList<Map<IChannelNode, Integer>> res = new ArrayList<>();
139 if(dv >= Direction.INCOMING.getVal()) res.add(incoming);
140 if(dv >= Direction.OUTGOING.getVal()) res.add(outgoing);
141
142 return res;
143 }
144
145 @Override
146 public int compareTo(IChannelNode iChannelNode) {
147 if(iChannelNode == null) throw new IllegalArgumentException("comparison node is null");
148 int selfTotal = getNumConnections(Direction.BOTH),
149 theirTotal = iChannelNode.getNumConnections(Direction.BOTH);
150
151 return selfTotal - theirTotal;
152 }
153}
diff --git a/src/main/java/IChannelNode.java b/src/main/java/IChannelNode.java
index 463f3ac..baf7572 100644
--- a/src/main/java/IChannelNode.java
+++ b/src/main/java/IChannelNode.java
@@ -1,14 +1,19 @@
1import java.util.List; 1import java.util.List;
2import java.util.Map; 2import java.util.Map;
3 3
4public interface IChannelNode { 4public interface IChannelNode extends Comparable<IChannelNode> {
5 enum Direction { 5 enum Direction {
6 INCOMING, 6 INCOMING(0), // Users incoming from outside channel (aka: other channel forwarded this channel's post)
7 OUTGOING, 7 OUTGOING(1), // Users outgoing from this channel (aka: this channel forwarded someone else's post)
8 BOTH 8 BOTH(2); // Modify both incoming and outgoing counts at once
9
10 private final int val;
11 Direction(int val) {this.val = val;}
12 public int getVal() {return this.val;}
9 } 13 }
10 14
11 void setConnections(Map<IChannelNode, Integer> conmap, Direction dir); 15 void setConnections(Map<IChannelNode, Integer> conmap, Direction dir);
16 void setNumConnections(IChannelNode node, Direction dir, int num);
12 void addConnection(IChannelNode node, Direction dir); 17 void addConnection(IChannelNode node, Direction dir);
13 void addConnections(Iterable<IChannelNode> nodes, Direction dir); 18 void addConnections(Iterable<IChannelNode> nodes, Direction dir);
14 void removeConnection(IChannelNode node, Direction dir); 19 void removeConnection(IChannelNode node, Direction dir);
@@ -16,6 +21,7 @@ public interface IChannelNode {
16 void clearConnections(Direction dir); 21 void clearConnections(Direction dir);
17 22
18 boolean connectionExists(IChannelNode node, Direction dir); 23 boolean connectionExists(IChannelNode node, Direction dir);
24 int getNumConnections(Direction dir);
19 Map<IChannelNode, Integer> getIncomingConnections(); 25 Map<IChannelNode, Integer> getIncomingConnections();
20 Map<IChannelNode, Integer> getOutgoingConnections(); 26 Map<IChannelNode, Integer> getOutgoingConnections();
21 List<Map<IChannelNode, Integer>> getConnections(Direction dir); 27 List<Map<IChannelNode, Integer>> getConnections(Direction dir);
diff --git a/src/test/java/ChannelNodeTest.java b/src/test/java/ChannelNodeTest.java
index 5623d72..6268a97 100644
--- a/src/test/java/ChannelNodeTest.java
+++ b/src/test/java/ChannelNodeTest.java
@@ -23,6 +23,14 @@ class ChannelNodeTest {
23 // Null direction should always throw 23 // Null direction should always throw
24 assertThrows(IllegalArgumentException.class, () -> testNodeA.setConnections(new HashMap<>(), null)); 24 assertThrows(IllegalArgumentException.class, () -> testNodeA.setConnections(new HashMap<>(), null));
25 25
26 // Self endpoint should throw
27 Map<IChannelNode, Integer> temp = new HashMap<>();
28 temp.put(testNodeA, 1);
29
30 assertThrows(IllegalArgumentException.class, () -> testNodeA.setConnections(temp, IChannelNode.Direction.INCOMING));
31 assertThrows(IllegalArgumentException.class, () -> testNodeA.setConnections(temp, IChannelNode.Direction.OUTGOING));
32 assertThrows(IllegalArgumentException.class, () -> testNodeA.setConnections(temp, IChannelNode.Direction.BOTH));
33
26 // Should not throw 34 // Should not throw
27 assertDoesNotThrow(() -> testNodeA.setConnections(new HashMap<>(), IChannelNode.Direction.INCOMING)); 35 assertDoesNotThrow(() -> testNodeA.setConnections(new HashMap<>(), IChannelNode.Direction.INCOMING));
28 assertDoesNotThrow(() -> testNodeA.setConnections(new HashMap<>(), IChannelNode.Direction.OUTGOING)); 36 assertDoesNotThrow(() -> testNodeA.setConnections(new HashMap<>(), IChannelNode.Direction.OUTGOING));
@@ -30,6 +38,36 @@ class ChannelNodeTest {
30 38
31 39
32 40
41 // Null endpoint should throw
42 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(null, null, -1));
43 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(null, IChannelNode.Direction.INCOMING, 1));
44 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(null, IChannelNode.Direction.OUTGOING, 1));
45 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(null, IChannelNode.Direction.BOTH, 1));
46
47 // Null direction should throw
48 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(testNodeB, null, 1));
49
50 // Self endpoint should throw
51 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(testNodeA, IChannelNode.Direction.INCOMING, 1));
52 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(testNodeA, IChannelNode.Direction.OUTGOING, 1));
53 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(testNodeA, IChannelNode.Direction.BOTH, 1));
54
55 // Negative connections should throw
56 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(testNodeB, IChannelNode.Direction.INCOMING, -1));
57 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(testNodeB, IChannelNode.Direction.OUTGOING, -1));
58 assertThrows(IllegalArgumentException.class, () -> testNodeA.setNumConnections(testNodeB, IChannelNode.Direction.BOTH, -1));
59
60 // Should not throw
61 assertDoesNotThrow(() -> testNodeA.setNumConnections(testNodeB, IChannelNode.Direction.INCOMING, 10));
62 assertDoesNotThrow(() -> testNodeA.setNumConnections(testNodeB, IChannelNode.Direction.OUTGOING, 10));
63 assertDoesNotThrow(() -> testNodeA.setNumConnections(testNodeB, IChannelNode.Direction.BOTH, 3));
64
65 assertDoesNotThrow(() -> testNodeB.setNumConnections(testNodeA, IChannelNode.Direction.INCOMING, 3));
66 assertDoesNotThrow(() -> testNodeB.setNumConnections(testNodeA, IChannelNode.Direction.OUTGOING, 5));
67 assertDoesNotThrow(() -> testNodeB.setNumConnections(testNodeA, IChannelNode.Direction.BOTH, 90));
68
69
70
33 // Null endpoint should always throw 71 // Null endpoint should always throw
34 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnection(null, null)); 72 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnection(null, null));
35 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnection(null, IChannelNode.Direction.INCOMING)); 73 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnection(null, IChannelNode.Direction.INCOMING));
@@ -45,6 +83,11 @@ class ChannelNodeTest {
45 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnection(testNodeB, null)); 83 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnection(testNodeB, null));
46 assertThrows(IllegalArgumentException.class, () -> testNodeB.addConnection(testNodeA, null)); 84 assertThrows(IllegalArgumentException.class, () -> testNodeB.addConnection(testNodeA, null));
47 85
86 // Should not throw
87 assertDoesNotThrow(() -> testNodeA.addConnection(testNodeB, IChannelNode.Direction.INCOMING));
88 assertDoesNotThrow(() -> testNodeA.addConnection(testNodeB, IChannelNode.Direction.OUTGOING));
89 assertDoesNotThrow(() -> testNodeA.addConnection(testNodeB, IChannelNode.Direction.BOTH));
90
48 91
49 92
50 // Null iterable should always throw 93 // Null iterable should always throw
@@ -62,6 +105,10 @@ class ChannelNodeTest {
62 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnections(List.of(new IChannelNode[]{testNodeA}), IChannelNode.Direction.OUTGOING)); 105 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnections(List.of(new IChannelNode[]{testNodeA}), IChannelNode.Direction.OUTGOING));
63 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnections(List.of(new IChannelNode[]{testNodeA}), IChannelNode.Direction.BOTH)); 106 assertThrows(IllegalArgumentException.class, () -> testNodeA.addConnections(List.of(new IChannelNode[]{testNodeA}), IChannelNode.Direction.BOTH));
64 107
108 // Should not throw
109 assertDoesNotThrow(() -> testNodeA.addConnections(List.of(new IChannelNode[]{testNodeB}), IChannelNode.Direction.INCOMING));
110 assertDoesNotThrow(() -> testNodeA.addConnections(List.of(new IChannelNode[]{testNodeB}), IChannelNode.Direction.OUTGOING));
111 assertDoesNotThrow(() -> testNodeA.addConnections(List.of(new IChannelNode[]{testNodeB}), IChannelNode.Direction.BOTH));
65 112
66 113
67 // Null node should always throw 114 // Null node should always throw
@@ -114,7 +161,18 @@ class ChannelNodeTest {
114 161
115 162
116 // Null direction should always throw 163 // Null direction should always throw
164 assertThrows(IllegalArgumentException.class, () -> testNodeA.getNumConnections(null));
165 assertDoesNotThrow(() -> testNodeA.getNumConnections(IChannelNode.Direction.INCOMING));
166 assertDoesNotThrow(() -> testNodeA.getNumConnections(IChannelNode.Direction.OUTGOING));
167 assertDoesNotThrow(() -> testNodeA.getNumConnections(IChannelNode.Direction.BOTH));
168
169
170
171 // Null direction should always throw
117 assertThrows(IllegalArgumentException.class, () -> testNodeA.getConnections(null)); 172 assertThrows(IllegalArgumentException.class, () -> testNodeA.getConnections(null));
173 assertDoesNotThrow(() -> testNodeA.getConnections(IChannelNode.Direction.INCOMING));
174 assertDoesNotThrow(() -> testNodeA.getConnections(IChannelNode.Direction.OUTGOING));
175 assertDoesNotThrow(() -> testNodeA.getConnections(IChannelNode.Direction.BOTH));
118 } 176 }
119 177
120} // End of ChannelNodeTest \ No newline at end of file 178} // End of ChannelNodeTest \ No newline at end of file