HIGUCHI Yuta
Committed by Gerrit Code Review

Reversible String representation for PortNumber

Change-Id: I2b5f45bd0652ef7fdcacfac64248e299dda10785
...@@ -15,6 +15,16 @@ ...@@ -15,6 +15,16 @@
15 */ 15 */
16 package org.onosproject.net; 16 package org.onosproject.net;
17 17
18 +import static com.google.common.base.Preconditions.checkArgument;
19 +import static com.google.common.base.Preconditions.checkNotNull;
20 +
21 +import java.util.Map;
22 +import java.util.regex.Matcher;
23 +import java.util.regex.Pattern;
24 +import com.google.common.base.Supplier;
25 +import com.google.common.base.Suppliers;
26 +import com.google.common.collect.ImmutableMap;
27 +import com.google.common.collect.ImmutableMap.Builder;
18 import com.google.common.primitives.UnsignedLongs; 28 import com.google.common.primitives.UnsignedLongs;
19 29
20 /** 30 /**
...@@ -26,8 +36,7 @@ public final class PortNumber { ...@@ -26,8 +36,7 @@ public final class PortNumber {
26 36
27 // TODO: revisit the max and the logical port value assignments 37 // TODO: revisit the max and the logical port value assignments
28 38
29 - private static final long MAX_NUMBER = (2L * Integer.MAX_VALUE) + 1; 39 + static final long MAX_NUMBER = (2L * Integer.MAX_VALUE) + 1;
30 -
31 40
32 static final long IN_PORT_NUMBER = -8L; 41 static final long IN_PORT_NUMBER = -8L;
33 static final long TABLE_NUMBER = -7L; 42 static final long TABLE_NUMBER = -7L;
...@@ -37,6 +46,39 @@ public final class PortNumber { ...@@ -37,6 +46,39 @@ public final class PortNumber {
37 static final long LOCAL_NUMBER = -2L; 46 static final long LOCAL_NUMBER = -2L;
38 static final long CONTROLLER_NUMBER = -3L; 47 static final long CONTROLLER_NUMBER = -3L;
39 48
49 + /**
50 + * Logical PortNumbers.
51 + */
52 + public static enum Logical {
53 + IN_PORT(IN_PORT_NUMBER),
54 + TABLE(TABLE_NUMBER),
55 + NORMAL(NORMAL_NUMBER),
56 + FLOOD(FLOOD_NUMBER),
57 + ALL(ALL_NUMBER),
58 + LOCAL(LOCAL_NUMBER),
59 + CONTROLLER(CONTROLLER_NUMBER);
60 +
61 + private final long number;
62 + private final PortNumber instance;
63 +
64 + public long number() {
65 + return number;
66 + }
67 +
68 + /**
69 + * PortNumber instance for the logical port.
70 + * @return {@link PortNumber}
71 + */
72 + public PortNumber instance() {
73 + return instance;
74 + }
75 +
76 + Logical(long number) {
77 + this.number = number;
78 + this.instance = new PortNumber(number);
79 + }
80 + }
81 +
40 public static final PortNumber IN_PORT = new PortNumber(IN_PORT_NUMBER); 82 public static final PortNumber IN_PORT = new PortNumber(IN_PORT_NUMBER);
41 public static final PortNumber TABLE = new PortNumber(TABLE_NUMBER); 83 public static final PortNumber TABLE = new PortNumber(TABLE_NUMBER);
42 public static final PortNumber NORMAL = new PortNumber(NORMAL_NUMBER); 84 public static final PortNumber NORMAL = new PortNumber(NORMAL_NUMBER);
...@@ -45,6 +87,15 @@ public final class PortNumber { ...@@ -45,6 +87,15 @@ public final class PortNumber {
45 public static final PortNumber LOCAL = new PortNumber(LOCAL_NUMBER); 87 public static final PortNumber LOCAL = new PortNumber(LOCAL_NUMBER);
46 public static final PortNumber CONTROLLER = new PortNumber(CONTROLLER_NUMBER); 88 public static final PortNumber CONTROLLER = new PortNumber(CONTROLLER_NUMBER);
47 89
90 + // lazily populated Logical port number to PortNumber
91 + static final Supplier<Map<Long, Logical>> LOGICAL = Suppliers.memoize(() -> {
92 + Builder<Long, Logical> builder = ImmutableMap.<Long, Logical>builder();
93 + for (Logical lp : Logical.values()) {
94 + builder.put(lp.number(), lp);
95 + }
96 + return builder.build();
97 + });
98 +
48 private final long number; 99 private final long number;
49 private final String name; 100 private final String name;
50 private final boolean hasName; 101 private final boolean hasName;
...@@ -136,30 +187,68 @@ public final class PortNumber { ...@@ -136,30 +187,68 @@ public final class PortNumber {
136 } 187 }
137 188
138 private String decodeLogicalPort() { 189 private String decodeLogicalPort() {
139 - if (number == CONTROLLER_NUMBER) { 190 + Logical logical = LOGICAL.get().get(number);
140 - return "CONTROLLER"; 191 + if (logical != null) {
141 - } else if (number == LOCAL_NUMBER) { 192 + // enum name
142 - return "LOCAL"; 193 + return logical.toString();
143 - } else if (number == ALL_NUMBER) { 194 + }
144 - return "ALL"; 195 + return String.format("UNKNOWN(%s)", UnsignedLongs.toString(number));
145 - } else if (number == FLOOD_NUMBER) { 196 + }
146 - return "FLOOD"; 197 +
147 - } else if (number == NORMAL_NUMBER) { 198 +
148 - return "NORMAL"; 199 + /**
149 - } else if (number == TABLE_NUMBER) { 200 + * Regular expression to match String representation of named PortNumber.
150 - return "TABLE"; 201 + *
151 - } else if (number == IN_PORT_NUMBER) { 202 + * Format: "[name](num:unsigned decimal string)"
152 - return "IN_PORT"; 203 + */
153 - } 204 + private static final Pattern NAMED = Pattern.compile("^\\[(?<name>.*)\\]\\((?<num>\\d+)\\)$");
154 - return "UNKNOWN"; 205 +
206 + private static boolean isAsciiDecimal(char c) {
207 + return '0' <= c && c <= '9';
208 + }
209 +
210 + /**
211 + * Returns PortNumber instance from String representation.
212 + *
213 + * @param s String representation equivalent to {@link PortNumber#toString()}
214 + * @return {@link PortNumber} instance
215 + * @throws IllegalArgumentException if given String was malformed
216 + */
217 + public static PortNumber fromString(String s) {
218 + checkNotNull(s);
219 + checkArgument(!s.isEmpty(), "cannot be empty");
220 +
221 + if (isAsciiDecimal(s.charAt(0))) {
222 + // unsigned decimal string
223 + return portNumber(s);
224 + } else if (s.startsWith("[")) {
225 + // named PortNumber
226 + Matcher matcher = NAMED.matcher(s);
227 + checkArgument(matcher.matches(), "Invalid named PortNumber %s", s);
228 +
229 + String name = matcher.group("name");
230 + String num = matcher.group("num");
231 + return portNumber(UnsignedLongs.parseUnsignedLong(num), name);
232 + }
233 +
234 + // Logical
235 + if (s.startsWith("UNKNOWN(") && s.endsWith(")")) {
236 + return portNumber(s.substring("UNKNOWN(".length(), s.length() - 1));
237 + } else {
238 + return Logical.valueOf(s).instance;
239 + }
155 } 240 }
156 241
157 @Override 242 @Override
158 public String toString() { 243 public String toString() {
159 - if (!isLogical()) { 244 + if (isLogical()) {
160 - return name;
161 - } else {
162 return decodeLogicalPort(); 245 return decodeLogicalPort();
246 + } else if (hasName()) {
247 + // named port
248 + return String.format("[%s](%d)", name, number);
249 + } else {
250 + // unsigned decimal string
251 + return name;
163 } 252 }
164 } 253 }
165 254
......
...@@ -15,12 +15,17 @@ ...@@ -15,12 +15,17 @@
15 */ 15 */
16 package org.onosproject.net; 16 package org.onosproject.net;
17 17
18 +import com.google.common.collect.ImmutableList;
18 import com.google.common.testing.EqualsTester; 19 import com.google.common.testing.EqualsTester;
19 import org.junit.Test; 20 import org.junit.Test;
21 +import org.onosproject.net.PortNumber.Logical;
20 22
23 +import static java.util.stream.Collectors.toList;
21 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertEquals;
22 import static org.onosproject.net.PortNumber.portNumber; 25 import static org.onosproject.net.PortNumber.portNumber;
23 26
27 +import java.util.List;
28 +
24 /** 29 /**
25 * Test of the port number. 30 * Test of the port number.
26 */ 31 */
...@@ -39,5 +44,37 @@ public class PortNumberTest { ...@@ -39,5 +44,37 @@ public class PortNumberTest {
39 assertEquals("incorrect long value", 12345, portNumber(12345).toLong()); 44 assertEquals("incorrect long value", 12345, portNumber(12345).toLong());
40 } 45 }
41 46
47 + @Test
48 + public void decimalPortNumberIsReconstructableFromString() {
49 + List<PortNumber> ps = ImmutableList.<PortNumber>builder()
50 + .add(portNumber(0))
51 + .add(portNumber(1))
52 + .add(portNumber(6653))
53 + .add(portNumber(PortNumber.MAX_NUMBER))
54 + .build();
55 + ps.forEach(p -> assertEquals(p, PortNumber.fromString(p.toString())));
56 + }
57 +
58 + @Test
59 + public void logicalPortNumberIsReconstructableFromString() {
60 + List<PortNumber> ps = ImmutableList.copyOf(Logical.values())
61 + .stream().map(Logical::instance).collect(toList());
62 +
63 + ps.forEach(p -> assertEquals(p, PortNumber.fromString(p.toString())));
64 +
65 + PortNumber unknown = portNumber(-42);
66 + assertEquals(unknown, PortNumber.fromString(unknown.toString()));
67 + }
68 +
69 + @Test
70 + public void namedPortNumberIsReconstructableFromString() {
71 + List<PortNumber> ps = ImmutableList.<PortNumber>builder()
72 + .add(portNumber(0, "Zero"))
73 + .add(portNumber(1, "[ONE]"))
74 + .add(portNumber(6653, "OpenFlow (1.3+)"))
75 + .add(portNumber(PortNumber.MAX_NUMBER, "(大)"))
76 + .build();
77 + ps.forEach(p -> assertEquals(p, PortNumber.fromString(p.toString())));
78 + }
42 79
43 } 80 }
......