Committed by
Gerrit Code Review
Reversible String representation for PortNumber
Change-Id: I2b5f45bd0652ef7fdcacfac64248e299dda10785
Showing
2 changed files
with
146 additions
and
20 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 | + */ |
| 204 | + private static final Pattern NAMED = Pattern.compile("^\\[(?<name>.*)\\]\\((?<num>\\d+)\\)$"); | ||
| 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; | ||
| 153 | } | 239 | } |
| 154 | - return "UNKNOWN"; | ||
| 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