diff --git a/src/main/java/net/floodlightcontroller/util/EnumBitmaps.java b/src/main/java/net/floodlightcontroller/util/EnumBitmaps.java new file mode 100644 index 0000000000000000000000000000000000000000..a7503eb48360180cafa961b4fbf923a62758e751 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/util/EnumBitmaps.java @@ -0,0 +1,141 @@ +package net.floodlightcontroller.util; + +import java.util.EnumSet; +import java.util.Set; + +/** + * A utility class to convert between integer based bitmaps for (OpenFlow) + * flags and Enum and EnumSet based representations. + * + * The enum used to represent individual flags needs to implement the + * BitmapableEnum interface. + * + * Example: + * {@code + * int bitmap = 0x11; // OFPPC_PORT_DOWN | OFPPC_NO_STP + * EnumSet<OFPortConfig> s = toEnumSet(OFPortConfig.class, bitmap); + * // s will contain OFPPC_PORT_DOWN and OFPPC_NO_STP + * } + * + * {@code + * EnumSet<OFPortConfig> s = EnumSet.of(OFPPC_NO_STP, OFPPC_PORT_DOWN); + * int bitmap = toBitmap(s); // returns 0x11 + * } + * @author gregor + * + */ +public class EnumBitmaps { + /** + * Enums used to represent individual flags needs to implement this + * interface + */ + public interface BitmapableEnum { + /** Return the value in the bitmap that the enum constant represents. + * The returned value must have only a single bit set. E.g.,1<<3 + */ + int getValue(); + } + + /** + * Convert an integer bitmap to an EnumSet. + * + * See class description for example + * @param type The Enum class to use. Must implement BitmapableEnum + * @param bitmap The integer bitmap + * @return A newly allocated EnumSet representing the bits set in the + * bitmap + * @throws NullPointerException if type is null + * @throws IllegalArgumentException if any enum constant from type has + * more than one bit set. + * @throws IllegalArgumentException if the bitmap has any bits set not + * represented by an enum constant. + */ + public static <E extends Enum<E> & BitmapableEnum> + EnumSet<E> toEnumSet(Class<E> type, int bitmap) { + if (type == null) + throw new NullPointerException("Given enum type must not be null"); + EnumSet<E> s = EnumSet.noneOf(type); + // allSetBitmap will eventually have all valid bits for the given + // type set. + int allSetBitmap = 0; + for (E element: type.getEnumConstants()) { + if (Integer.bitCount(element.getValue()) != 1) { + String msg = String.format("The %s (%x) constant of the " + + "enum %s is supposed to represent a bitmap entry but " + + "has more than one bit set.", + element.toString(), element.getValue(), type.getName()); + throw new IllegalArgumentException(msg); + } + allSetBitmap |= element.getValue(); + if ((bitmap & element.getValue()) != 0) + s.add(element); + } + if (((~allSetBitmap) & bitmap) != 0) { + // check if only valid flags are set in the given bitmap + String msg = String.format("The bitmap %x for enum %s has " + + "bits set that are presented by any enum constant", + bitmap, type.getName()); + throw new IllegalArgumentException(msg); + } + return s; + } + + /** + * Return the bitmap mask with all possible bits set. E.g., If a bitmap + * has the individual flags 0x1, 0x2, and 0x8 (note the missing 0x4) then + * the mask will be 0xb (1011 binary) + * + * @param type The Enum class to use. Must implement BitmapableEnum + * @throws NullPointerException if type is null + * @throws IllegalArgumentException if any enum constant from type has + * more than one bit set + * @return an integer with all possible bits for the given bitmap enum + * type set. + */ + public static <E extends Enum<E> & BitmapableEnum> + int getMask(Class<E> type) { + if (type == null) + throw new NullPointerException("Given enum type must not be null"); + // allSetBitmap will eventually have all valid bits for the given + // type set. + int allSetBitmap = 0; + for (E element: type.getEnumConstants()) { + if (Integer.bitCount(element.getValue()) != 1) { + String msg = String.format("The %s (%x) constant of the " + + "enum %s is supposed to represent a bitmap entry but " + + "has more than one bit set.", + element.toString(), element.getValue(), type.getName()); + throw new IllegalArgumentException(msg); + } + allSetBitmap |= element.getValue(); + } + return allSetBitmap; + } + + /** + * Convert the given EnumSet to the integer bitmap representation + * @param set The EnumSet to convert. The enum must implement + * BitmapableEnum + * @return the integer bitmap + * @throws IllegalArgumentException if an enum constant from the set (!) has + * more than one bit set + * @throws NullPointerException if the set is null + */ + public static <E extends Enum<E> & BitmapableEnum> + int toBitmap(Set<E> set) { + if (set == null) + throw new NullPointerException("Given set must not be null"); + int bitmap = 0; + for (E element: set) { + if (Integer.bitCount(element.getValue()) != 1) { + String msg = String.format("The %s (%x) constant in the set " + + "is supposed to represent a bitmap entry but " + + "has more than one bit set.", + element.toString(), element.getValue()); + throw new IllegalArgumentException(msg); + } + bitmap |= element.getValue(); + } + return bitmap; + } +} diff --git a/src/main/java/org/openflow/protocol/OFPhysicalPort.java b/src/main/java/org/openflow/protocol/OFPhysicalPort.java index 06a98e6f7b91b43e671cd74ea3f03de3ab684c21..86bae5bef1321838fd58bda7097ba4c7feab5414 100644 --- a/src/main/java/org/openflow/protocol/OFPhysicalPort.java +++ b/src/main/java/org/openflow/protocol/OFPhysicalPort.java @@ -1,7 +1,7 @@ /** * Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior * University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -23,6 +23,7 @@ import java.util.Arrays; import net.floodlightcontroller.core.web.serializers.ByteArrayMACSerializer; import net.floodlightcontroller.core.web.serializers.UShortSerializer; +import net.floodlightcontroller.util.EnumBitmaps; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.jboss.netty.buffer.ChannelBuffer; @@ -36,43 +37,57 @@ public class OFPhysicalPort { public final static int MINIMUM_LENGTH = 48; public final static int OFP_ETH_ALEN = 6; - public enum OFPortConfig { + public enum OFPortConfig implements EnumBitmaps.BitmapableEnum { OFPPC_PORT_DOWN (1 << 0) { + @Override public String toString() { return "port-down (0x1)"; } }, OFPPC_NO_STP (1 << 1) { + @Override public String toString() { return "no-stp (0x2)"; } }, OFPPC_NO_RECV (1 << 2) { + @Override public String toString() { return "no-recv (0x4)"; } }, OFPPC_NO_RECV_STP (1 << 3) { + @Override public String toString() { return "no-recv-stp (0x8)"; } }, OFPPC_NO_FLOOD (1 << 4) { + @Override public String toString() { return "no-flood (0x10)"; } }, OFPPC_NO_FWD (1 << 5) { + @Override public String toString() { return "no-fwd (0x20)"; } }, OFPPC_NO_PACKET_IN (1 << 6) { + @Override public String toString() { return "no-pkt-in (0x40)"; } + }, + OFPPC_BSN_MIRROR_DEST (1 << 31) { + @Override + public String toString() { + return "bsn-mirror-dest (0x80000000)"; + } }; + protected int value; private OFPortConfig(int value) { @@ -82,6 +97,7 @@ public class OFPhysicalPort { /** * @return the value */ + @Override public int getValue() { return value; } @@ -89,31 +105,37 @@ public class OFPhysicalPort { public enum OFPortState { OFPPS_LINK_DOWN (1 << 0) { + @Override public String toString() { return "link-down (0x1)"; } }, OFPPS_STP_LISTEN (0 << 8) { + @Override public String toString() { return "listen (0x0)"; } }, OFPPS_STP_LEARN (1 << 8) { + @Override public String toString() { return "learn-no-relay (0x100)"; } }, OFPPS_STP_FORWARD (2 << 8) { + @Override public String toString() { return "forward (0x200)"; } }, OFPPS_STP_BLOCK (3 << 8) { + @Override public String toString() { return "block-broadcast (0x300)"; } }, OFPPS_STP_MASK (3 << 8) { + @Override public String toString() { return "block-broadcast (0x300)"; } @@ -133,63 +155,75 @@ public class OFPhysicalPort { } } - public enum OFPortFeatures { + public enum OFPortFeatures implements EnumBitmaps.BitmapableEnum { OFPPF_10MB_HD (1 << 0) { + @Override public String toString() { return "10mb-hd (0x1)"; } }, OFPPF_10MB_FD (1 << 1) { + @Override public String toString() { return "10mb-fd (0x2)"; } }, OFPPF_100MB_HD (1 << 2) { + @Override public String toString() { return "100mb-hd (0x4)"; } }, OFPPF_100MB_FD (1 << 3) { + @Override public String toString() { return "100mb-fd (0x8)"; } }, OFPPF_1GB_HD (1 << 4) { + @Override public String toString() { return "1gb-hd (0x10)"; } }, OFPPF_1GB_FD (1 << 5) { + @Override public String toString() { return "1gb-fd (0x20)"; } }, OFPPF_10GB_FD (1 << 6) { + @Override public String toString() { return "10gb-fd (0x40)"; } }, OFPPF_COPPER (1 << 7) { + @Override public String toString() { return "copper (0x80)"; } }, OFPPF_FIBER (1 << 8) { + @Override public String toString() { return "fiber (0x100)"; } }, OFPPF_AUTONEG (1 << 9) { + @Override public String toString() { return "autoneg (0x200)"; } }, OFPPF_PAUSE (1 << 10) { + @Override public String toString() { return "pause (0x400)"; } }, OFPPF_PAUSE_ASYM (1 << 11) { + @Override public String toString() { return "pause-asym (0x800)"; } @@ -204,6 +238,7 @@ public class OFPhysicalPort { /** * @return the value */ + @Override public int getValue() { return value; } diff --git a/src/test/java/net/floodlightcontroller/util/EnumBitmapsTest.java b/src/test/java/net/floodlightcontroller/util/EnumBitmapsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ade178e7f90bcc7822a48170b2f11ebcc2299fb5 --- /dev/null +++ b/src/test/java/net/floodlightcontroller/util/EnumBitmapsTest.java @@ -0,0 +1,212 @@ +package net.floodlightcontroller.util; + +import static org.junit.Assert.*; + +import java.util.EnumSet; + +import org.junit.Test; + +import net.floodlightcontroller.util.EnumBitmaps.BitmapableEnum; + +public class EnumBitmapsTest { + + private enum ColorEnum implements BitmapableEnum { + RED (1 << 0), + GREEN (1 << 1), + BLUE ( 1 << 3); // note (1<<2) is missing + + private int value; + + private ColorEnum(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + } + + private enum ExtremeEnum implements BitmapableEnum { + SMALL (1 << 0), + BIG ( 1 << 31); + + private int value; + + private ExtremeEnum(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + + } + + private enum InvalidEnum implements BitmapableEnum { + FOO_VALID (0x1), + BAR_INVALID (0x6); // Error: more than one bit set + + private int value; + + private InvalidEnum(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + } + + private enum InvalidEnum2 implements BitmapableEnum { + FOOFOO_INVALID (0x0), // error: no bit set + BARBAR_VALID (0x1); + + private int value; + + private InvalidEnum2(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + } + + @Test + public void testNormalBehavior() { + EnumSet<ColorEnum> set = null; + int bitmap = 0; + + // With color enum. + bitmap = 0; + set = EnumBitmaps.toEnumSet(ColorEnum.class, bitmap); + assertEquals(EnumSet.noneOf(ColorEnum.class), set); + assertEquals(bitmap, EnumBitmaps.toBitmap(set)); + + bitmap = ColorEnum.RED.getValue(); + set = EnumBitmaps.toEnumSet(ColorEnum.class, bitmap); + assertEquals(EnumSet.of(ColorEnum.RED), set); + assertEquals(bitmap, EnumBitmaps.toBitmap(set)); + + bitmap = ColorEnum.BLUE.getValue(); + set = EnumBitmaps.toEnumSet(ColorEnum.class, bitmap); + assertEquals(EnumSet.of(ColorEnum.BLUE), set); + assertEquals(bitmap, EnumBitmaps.toBitmap(set)); + + bitmap = ColorEnum.RED.getValue() | ColorEnum.GREEN.getValue(); + set = EnumBitmaps.toEnumSet(ColorEnum.class, bitmap); + assertEquals(EnumSet.of(ColorEnum.RED, ColorEnum.GREEN), set); + assertEquals(bitmap, EnumBitmaps.toBitmap(set)); + + bitmap = ColorEnum.RED.getValue() | ColorEnum.GREEN.getValue() | + ColorEnum.BLUE.getValue(); + set = EnumBitmaps.toEnumSet(ColorEnum.class, bitmap); + assertEquals(EnumSet.of(ColorEnum.RED, ColorEnum.GREEN, ColorEnum.BLUE), + set); + assertEquals(bitmap, EnumBitmaps.toBitmap(set)); + + assertEquals(0xb, EnumBitmaps.getMask(ColorEnum.class)); + + // with extreme enum. Make sure 1 << 31 is handled correctly + bitmap = 1 << 31; + EnumSet<ExtremeEnum> extremeSet = + EnumBitmaps.toEnumSet(ExtremeEnum.class, bitmap); + assertEquals(EnumSet.of(ExtremeEnum.BIG), extremeSet); + assertEquals(bitmap, EnumBitmaps.toBitmap(extremeSet)); + + bitmap = (1 << 31) | (1 << 0); + extremeSet = EnumBitmaps.toEnumSet(ExtremeEnum.class, bitmap); + assertEquals(EnumSet.of(ExtremeEnum.BIG, ExtremeEnum.SMALL), extremeSet); + assertEquals(bitmap, EnumBitmaps.toBitmap(extremeSet)); + + assertEquals(0x80000001, EnumBitmaps.getMask(ExtremeEnum.class)); + + // there are some cases were InvalidEnum's can actually be used. + // It's fine if a developer chooses to change the behavior and make + // the cases below fail! + EnumSet<InvalidEnum> s1 = EnumSet.of(InvalidEnum.FOO_VALID); + assertEquals(InvalidEnum.FOO_VALID.getValue(), + EnumBitmaps.toBitmap(s1)); + EnumSet<InvalidEnum2> s2 = EnumSet.of(InvalidEnum2.BARBAR_VALID); + assertEquals(InvalidEnum2.BARBAR_VALID.getValue(), + EnumBitmaps.toBitmap(s2)); + } + + + @Test + public void testExceptions() { + // Exception when using an invalid enum + try { + EnumBitmaps.toEnumSet(InvalidEnum.class, 0); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + try { + EnumBitmaps.toEnumSet(InvalidEnum.class, 1); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + try { + EnumBitmaps.getMask(InvalidEnum.class); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + try { + EnumSet<InvalidEnum> set = EnumSet.allOf(InvalidEnum.class); + EnumBitmaps.toBitmap(set); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + try { + EnumSet<InvalidEnum> set = EnumSet.of(InvalidEnum.BAR_INVALID); + EnumBitmaps.toBitmap(set); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + + // Again with a different one + // Exception when using an invalid enum + try { + EnumBitmaps.toEnumSet(InvalidEnum2.class, 0); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + try { + EnumBitmaps.toEnumSet(InvalidEnum2.class, 1); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + try { + EnumBitmaps.getMask(InvalidEnum2.class); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + try { + EnumSet<InvalidEnum2> set = EnumSet.allOf(InvalidEnum2.class); + EnumBitmaps.toBitmap(set); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + try { + EnumSet<InvalidEnum2> set = EnumSet.of(InvalidEnum2.FOOFOO_INVALID); + EnumBitmaps.toBitmap(set); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { } + + // NPEs + try { + EnumBitmaps.toEnumSet(null, 0); + fail("Expected exception not thrown"); + } catch (NullPointerException e) { } + try { + EnumBitmaps.getMask(null); + fail("Expected exception not thrown"); + } catch (NullPointerException e) { } + try { + EnumBitmaps.toBitmap(null); + fail("Expected exception not thrown"); + } catch (NullPointerException e) { } + + // Bits set that aren't covered by the enum + try { + EnumBitmaps.toEnumSet(ColorEnum.class, 1 << 23); + fail("Expected exception not thrown"); + } catch(IllegalArgumentException e) { } + } + +}