Committed by
Gerrit Code Review
Reversible String representation for PortNumber
Change-Id: I2b5f45bd0652ef7fdcacfac64248e299dda10785
Showing
2 changed files
with
147 additions
and
21 deletions
... | @@ -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 | } | ... | ... |
-
Please register or login to post a comment