public abstract class AbstractAction implements FlowTableInterface {

    /**
     * Representation of the different types of actions.
     */
    public enum Action {
        /**
         * An empty action.
         */
        NULL(0),
        /**
         * Forward to a destination in unicast.
         */
        FORWARD_U(1),
        /**
         * Forward to a destination in broadcast.
         */
        FORWARD_B(2),
        /**
         * Drops the packet.
         */
        DROP(3),
        /**
         * Creates a new Request packet containing the current packet and sends
         * it to the controller.
         */
        ASK(4),
        /**
         * Invokes a function.
         */
        FUNCTION(5),
        /**
         * Sets a byte in the status register or in the packet.
         */
        SET(6),
        /**
         * Matches the packet against the FlowTable.
         */
        MATCH(7);

        /**
         * A byte representing the action.
         */
        private final byte value;

        /**
         * Contains all the possible action values.
         */
        private static final Action[] A_VALUES = Action.values();

        /**
         * Returns the corresponting Action given a byte.
         *
         * @param value a byte representing the Action
         * @return the corresponding Action
         */
        public static Action fromByte(final byte value) {
            return A_VALUES[value];
        }

        /**
         * Creates a new Action.
         *
         * @param v a byte representing the action.
         */
        Action(final int v) {
            value = (byte) v;
        }
    }

    /**
     * The index of the Action array representation were the type is stored.
     */
    protected static final int TYPE_INDEX = 0;

    /**
     * The index of the Action array representation were the action values are
     * stored.
     */
    protected static final int VALUE_INDEX = 1;

    /**
     * Stores the action as a byte array.
     */
    private byte[] action;

    /**
     * Constructor for the FlowTableAction object.
     *
     * @param actionType the type of action that will be created
     * @param size the size of the action
     */
    public AbstractAction(final Action actionType, final int size) {
        action = new byte[size + 1];
        setType(actionType);
    }

    /**
     * Constructor for the FlowTableAction object.
     *
     * @param value a byte array representing the action
     */
    public AbstractAction(final byte[] value) {
        action = value;
    }

    /**
     * Getter method to obtain the Type of AbstractAction. The possible types of
     * actions are SDN_WISE_FORWARD_U, SDN_WISE_FORWARD_B, SDN_WISE_DROP,
     * SDN_WISE_MODIFY, SDN_WISE_AGGREGATE, SDN_WISE_FORWARD_UP.
     *
     * @return value of the type action.
     */
    public final Action getType() {
        return Action.fromByte(action[TYPE_INDEX]);
    }

    @Override
    public final byte[] toByteArray() {
        return Arrays.copyOf(action, action.length);
    }

    @Override
    public final boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        AbstractAction other = (AbstractAction) obj;
        return Arrays.equals(other.action, action);
    }

    @Override
    public final int hashCode() {
        return Arrays.hashCode(action);
    }

    /**
     * Sets the type of AbstractAction.
     *
     * @param value to be set.
     * @return this AbstractAction.
     */
    protected final AbstractAction setType(final Action value) {
        action[TYPE_INDEX] = value.value;
        return this;
    }

    /**
     * Sets a value for the action. A value is a parameter that an action uses
     * to achieve its result.
     *
     * @param index the index in the action array where the value is stored
     * @param value the value to be stored
     * @return the AbstracAction itself
     */
    protected final AbstractAction setValue(final int index, final int value) {
        if (index < 0 || index >= action.length) {
            throw new ArrayIndexOutOfBoundsException("Index out of bound");
        } else {
            action[index + 1] = (byte) value;
        }
        return this;
    }

    /**
     * Sets a value for the action. A value is a parameter that an action uses
     * to achieve its result.
     *
     * @param value the value to be stored as a byte array
     * @return the AbstracAction itself
     */
    protected final AbstractAction setValue(final byte[] value) {
        Action type = getType();
        action = ByteBuffer.allocate(value.length + 1)
                .put(type.value)
                .put(value).array();
        return this;
    }

    /**
     * Gets a value from the action. A value is a parameter that an action uses
     * to achieve its result.
     *
     * @return a byte array containing all the values
     */
    protected final byte[] getValue() {
        return Arrays.copyOfRange(action, VALUE_INDEX, action.length);
    }

    /**
     * Gets a value from the action. A value is a parameter that an action uses
     * to achieve its result.
     *
     * @param index the index in the action array where the value is stored
     * @return the value of the action
     */
    protected final int getValue(final int index) {
        if (index < 0 || index >= action.length) {
            throw new ArrayIndexOutOfBoundsException("Index out of bound");
        } else {
            return action[index + 1];
        }
    }

    @Override
    public String toString() {
        return getType().name();
    }

    /**
     * Returns the size of the action array.
     *
     * @return the size of the action array
     */
    protected final int getActionLength() {
        return action.length;
    }

    /**
     * Returns the size of the values stored.
     *
     * @return the size of the values stored
     */
    protected final int getValueLength() {
        return action.length - 1;
    }

}
 public abstract class AbstractForwardAction extends AbstractAction {

    /**
     * The index in the action array where the address of the next hop is
     * located.
     */
    private static final byte NXH_INDEX = 0;

    /**
     * Creates a new AbstractAction given an ActionType.
     *
     * @param actionType the action type of the Abstract Action
     */
    public AbstractForwardAction(final Action actionType) {
        super(actionType, 2);
    }

    /**
     * Creates a new AbstractAction given an array of bytes.
     *
     * @param value the byte array
     */
    public AbstractForwardAction(final byte[] value) {
        super(value);
    }

    /**
     * Sets the next hop toward which the packet will be sent.
     *
     * @param addr the NodeAddress of the next hop
     * @return the action itself
     */
    public final AbstractForwardAction setNextHop(final NodeAddress addr) {
        setValue(NXH_INDEX, addr.getHigh());
        setValue(NXH_INDEX + 1, addr.getLow());
        return this;
    }

    /**
     * Gets the next hop toward which the packet will be sent.
     *
     * @return the NodeAddress of the next hop
     */
    public final NodeAddress getNextHop() {
        return new NodeAddress(getValue(NXH_INDEX), getValue(NXH_INDEX + 1));
    }
}
 public final class FlowTableEntry implements FlowTableInterface {

    /**
     * Contains the list of windows of the FlowTableEntry. All the windows must
     * be sadisfied in order to execute the actions.
     */
    private final List<Window> windows = new LinkedList<>();
    /**
     * Contains the list of actions of the FlowTableEntry. The actions are
     * executed when all the windows are satisfied.
     */
    private final List<AbstractAction> actions = new LinkedList<>();
    /**
     * Statistics on the usages and time to live of the FlowTableEntry.
     */
    private Stats stats = new Stats();

    /**
     * Creates a FlowTableEntry object from a String.
     *
     * @param s the String used to create the FlowTableEntry
     * @return a new FlowTableEntry object
     */
    public static FlowTableEntry fromString(final String s) {
        String val = s.toUpperCase();
        FlowTableEntry res = new FlowTableEntry();

        String[] strWindows = (val.substring(
                val.indexOf("(") + 1, val.indexOf(")"))).split("&&");

        for (String w : strWindows) {
            res.addWindow(Window.fromString(w.trim()));
        }

        String[] strActions = (val.substring(
                val.indexOf("{") + 1, val.indexOf("}"))).trim().split(";");

        for (String a : strActions) {
            res.addAction(ActionBuilder.build(a.trim()));
        }
        return res;
    }

    /**
     * Simple constructor for the FlowTableEntry object.
     *
     * It creates new Window instances setting all the values to 0.
     */
    public FlowTableEntry() {
    }

    /**
     * Constructor for the FlowTableEntry object. It initializes new Window[],
     * AbstractAction and Stats instances.
     *
     * @param entry From byte array to FlowTableEntry
     */
    public FlowTableEntry(final byte[] entry) {
        int i = 0;

        int nWindows = entry[i];

        for (i = 1; i <= nWindows; i += Window.SIZE) {
            windows.add(new Window(
                    Arrays.copyOfRange(entry, i, i + Window.SIZE)));
        }

        while (i < entry.length - (Stats.SIZE)) {
            int len = entry[i++];
            actions.add(ActionBuilder.build(
                    Arrays.copyOfRange(entry, i, i + len)));
            i += len;
        }

        stats = new Stats(
                Arrays.copyOfRange(
                        entry, entry.length - Stats.SIZE, entry.length)
        );

    }

    @Override
    public String toString() {
        StringBuilder out = new StringBuilder("if (");

        windows.stream().map((Window w) -> {
            StringBuilder part = new StringBuilder();
            part.append(w.toString());
            return part;
        }).filter((part) -> (!part.toString().isEmpty())).forEach((part) -> {
            if (out.toString().equals("if (")) {
                out.append(part);
            } else {
                out.append(" && ").append(part);
            }
        });
        if (!out.toString().isEmpty()) {
            out.append(") { ");
            actions.stream().forEach((a) -> {
                out.append(a.toString()).append("; ");
            });
            out.append("} (")
                    .append(getStats().toString())
                    .append(')');
        }
        return out.toString().toUpperCase();
    }

    /**
     * Getter method to obtain the window array of the FlowTable entry.
     *
     * @return the window[] of the FlowTable
     */
    public List<Window> getWindows() {
        return windows;
    }

    /**
     * Sets a window list in the FlowTable entry.
     *
     * @param w the windows list to set
     */
    public void setWindows(final List<Window> w) {
        windows.clear();
        windows.addAll(w);
    }

    /**
     * Adds a window to the list of the windows in the FlowTable entry.
     *
     * @param w element to be appended
     * @return true
     */
    public boolean addWindow(final Window w) {
        return windows.add(w);
    }

    /**
     * Getter method to obtain the AbstractAction part of the FlowTable entry.
     *
     * @return the action of the FlowTable
     */
    public List<AbstractAction> getActions() {
        return actions;
    }

    /**
     * Setter method to set the AbstractAction part of the FlowTable entry.
     *
     * @param a the action to set
     */
    public void setAction(final List<AbstractAction> a) {
        actions.clear();
        actions.addAll(a);
    }

    /**
     * Adds an action to the FlowTable entry.
     *
     * @param a element to be appended
     * @return true
     */
    public boolean addAction(final AbstractAction a) {
        return actions.add(a);
    }

    /**
     * Getter method to obtain the Statistics of the FlowTable entry.
     *
     * @return the statistics of the FlowTable entry.
     */
    public Stats getStats() {
        return stats;
    }

    /**
     * Setter method to set statistics of the FlowTable entry.
     *
     * @param s the statistics will be set.
     */
    public void setStats(final Stats s) {
        stats = s;
    }

    @Override
    public byte[] toByteArray() {
        int size = (1 + windows.size() * Window.SIZE) + Stats.SIZE;
        for (AbstractAction a : actions) {
            size = size + a.getActionLength() + 1;
        }

        ByteBuffer target = ByteBuffer.allocate(size);
        target.put((byte) (windows.size() * Window.SIZE));

        windows.stream().forEach((fw) -> {
            target.put(fw.toByteArray());
        });

        actions.stream().map((a) -> {
            target.put((byte) a.getActionLength());
            return a;
        }).forEach((a) -> {
            target.put(a.toByteArray());
        });

        target.put(stats.toByteArray());

        return target.array();
    }

    @Override
    public int hashCode() {
        int hash = Objects.hashCode(windows) + Objects.hashCode(actions)
                + Objects.hashCode(stats);
        return hash;
    }

    /**
     * Indicates whether the windows of another FlowTableEntry object are "equal
     * to" the windows of this one.
     *
     * @param other the other FlowTableEntry object
     * @return {@code true} if this object is the same as the obj argument;
     * {@code false} otherwise.
     */
    public boolean equalWindows(final FlowTableEntry other) {
        return Objects.deepEquals(windows, other.windows);
    }

    @Override
    public boolean equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final FlowTableEntry other = (FlowTableEntry) obj;
        if (!Objects.deepEquals(windows, other.windows)) {
            return false;
        }
        return Objects.deepEquals(actions, other.actions);
    }

}
 public final class ForwardUnicastAction extends AbstractForwardAction {

    /**
     * Creates a ForwardUnicast action. The next hop is set by using the String.
     * An example of a string is "FORWARD_U 0.1" without quotes.
     *
     * @param str the string representing the ForwardUnicast action
     */
    public ForwardUnicastAction(final String str) {
        super(FORWARD_U);
        if (FORWARD_U.name().equals(str.split(" ")[0].trim())) {
            setNextHop(new NodeAddress(str.split(" ")[1].trim()));
        } else {
            throw new IllegalArgumentException();
        }
    }

    /**
     * Creates a ForwardUnicast action. The next hop is set by using the a
     * NodeAddress.
     *
     * @param nextHop the next hop address
     */
    public ForwardUnicastAction(final NodeAddress nextHop) {
        super(FORWARD_U);
        setNextHop(nextHop);
    }

    /**
     * Creates a ForwardUnicast action.
     *
     * @param value a byte array representing the ForwardAction
     */
    public ForwardUnicastAction(final byte[] value) {
        super(value);
    }

    @Override
    public String toString() {
        return FORWARD_U.name() + " " + getNextHop().intValue();
    }
} public final class FunctionAction extends AbstractAction {

    /**
     * Field indexes.
     */
    private static final byte ARGS_INDEX = 1, ID_INDEX = 0;

    /**
     * Creates a FunctionAction object.
     *
     * @param value an array representing the FunctionAction object
     */
    public FunctionAction(final byte[] value) {
        super(value);
    }

    /**
     * Creates a Function action starting from a String. The string specifies
     * the name of the action, the id of the function and an array of bytes. An
     * example is "FUNCTION 1 0 1 2 3 4 5 6" without quotes.
     *
     * @param str the String representing the action
     */
    public FunctionAction(final String str) {
        super(FUNCTION, 0);
        String[] tmp = str.split(" ");
        if (tmp[0].equals(FUNCTION.name())) {
            byte[] args = new byte[tmp.length - 1];
            for (int i = 0; i < args.length; i++) {
                args[i] = (byte) (Integer.parseInt(tmp[i + 1]));
            }
            setValue(args);
        } else {
            throw new IllegalArgumentException();
        }
    }

    /**
     * Gets the list of arguments that will be provided to the function.
     *
     * @return the list of arguments as a byte array
     */
    public byte[] getArgs() {
        byte[] value = getValue();
        return Arrays.copyOfRange(value, ARGS_INDEX, value.length);
    }

    /**
     * Gets the id of the function that will be invoked. When istalled each
     * function has a id.
     *
     * @return the id of the function
     */
    public int getId() {
        return getValue(ID_INDEX);
    }

    /**
     * Sets the list of arguments that will be provided to the function.
     *
     * @param args the list of arguments as a byte array
     * @return the FunctionAction itself
     */
    public FunctionAction setArgs(final byte[] args) {
        int i = 0;
        for (byte b : args) {
            setValue(ARGS_INDEX + i, b);
            i++;
        }
        return this;
    }

    /**
     * Sets the id of the function that will be invoked. When istalled each
     * function has a id.
     *
     * @param id identificator of the function
     * @return the FunctionAction itself
     */
    public FunctionAction setId(final int id) {
        setValue(ID_INDEX, id);
        return this;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(FUNCTION.name());
        sb.append(' ').append(getId()).append(' ');
        for (byte b : getArgs()) {
            sb.append(Byte.toUnsignedInt(b)).append(' ');
        }
        return sb.toString();
    }
}
 public final class SetAction extends AbstractAction {

    /**
     * SetAction operators.
     */
    public static final byte ADD = 0,
            AND = 5,
            DIV = 3,
            MOD = 4,
            MUL = 2,
            OR = 6,
            SUB = 1,
            XOR = 7;

    /**
     * Field indexes and lengths.
     */
    private static final byte LEFT_BIT = 1,
            LEFT_INDEX_H = 3,
            LEFT_INDEX_L = 4,
            LEFT_LEN = 2,
            OP_BIT = 3,
            OP_INDEX = 0, OP_LEN = 3,
            RES_BIT = 0,
            RES_INDEX_H = 1,
            RES_INDEX_L = 2,
            RES_LEN = 1,
            RIGHT_BIT = 6,
            RIGHT_INDEX_H = 5,
            RIGHT_INDEX_L = 6,
            RIGHT_LEN = LEFT_LEN;

    /**
     * Constans for String parsing.
     */
    private static final int FULL_SET = 6,
            HALF_SET = 4,
            RES = 1,
            LHS = 3,
            RHS = 5,
            OP = 4;

    /**
     * The size of the action.
     */
    private static final byte SIZE = 7;

    /**
     * Simple constructor for the Window object.
     *
     * Set action[] values at zero.
     */
    public SetAction() {
        super(SET, SIZE);
    }

    /**
     * Creates a SetAction given array of bytes.
     *
     * @param value the array of bytes representing the action
     */
    public SetAction(final byte[] value) {
        super(value);
    }

    /**
     * Creates a SetAction given a String. The String must contain: the name of
     * the Action, a result containing where the result should be stored.
     * Possible values are: "P." for packet and "R." for status register. A
     * number indicating the index where to store the result. Therefore a
     * possbile result location is "P.10" if you want to store the result at the
     * 10th byte of the packet. Then an equal sign and two operands separated by
     * the operator choosen. If an operand is a constant simply write it. A
     * complete example is "SET P.10 = R.11 + 12". This example stores the
     * result of the sum between the status register at byte 11, and the
     * constant value 12 in the 10th byte of the packet. Another possible value
     * is to set a result without making an operation. So an accepted string can
     * also be "SET R.10 = 11"
     *
     * @param val the String representing the action
     */
    public SetAction(final String val) {
        super(SET, SIZE);
        String[] operands = val.split(" ");
        if (operands.length == FULL_SET) {
            String res = operands[RES];
            String lhs = operands[LHS];
            String rhs = operands[RHS];

            int[] tmpRes = getResFromString(res);
            int[] tmpLhs = getOperandFromString(lhs);
            int[] tmpRhs = getOperandFromString(rhs);

            setResLocation(tmpRes[0]);
            setRes(tmpRes[1]);

            setLhsLocation(tmpLhs[0]);
            setLhs(tmpLhs[1]);

            setOperator(getOperatorFromString(operands[OP]));

            setRhsLocation(tmpRhs[0]);
            setRhs(tmpRhs[1]);

        } else if (operands.length == HALF_SET) {

            String res = operands[RES];
            String lhs = operands[LHS];

            int[] tmpRes = getResFromString(res);
            int[] tmpLhs = getOperandFromString(lhs);

            setResLocation(tmpRes[0]);
            setRes(tmpRes[1]);

            setLhsLocation(tmpLhs[0]);
            setLhs(tmpLhs[1]);

            setRhsLocation(NULL);
            setRhs(0);
        }

    }

    /**
     * Getter method to obtain Pos.
     *
     * @return an int value of pos.
     */
    public int getLhs() {
        return mergeBytes(getValue(LEFT_INDEX_H), getValue(LEFT_INDEX_L));
    }

    /**
     * Getter method to obtain lhs Location.
     *
     * @return an int value of location.
     */
    public int getLhsLocation() {
        return getBitRange(getValue(OP_INDEX), LEFT_BIT, LEFT_LEN);
    }

    /**
     * Getter method to obtain memory in string.
     *
     * @return a string value of memory.
     */
    public String getLhsToString() {
        switch (getLhsLocation()) {
            case NULL:
                return "";
            case CONST:
                return String.valueOf(getLhs());
            case PACKET:
                return "P." + NetworkPacket.getNetworkPacketByteName(getLhs());
            case STATUS:
                return "R." + getLhs();
            default:
                return "";
        }
    }

    /**
     * Getter method to obtain Operator.
     *
     * @return an int value of operator.
     */
    public int getOperator() {
        return getBitRange(getValue(OP_INDEX), OP_BIT, OP_LEN);
    }

    /**
     * Gets the operator id from a String.
     *
     * @param val the char representing the operator
     * @return the operator id starting from a string.
     */
    public int getOperatorFromString(final String val) {
        switch (val.trim()) {
            case ("+"):
                return ADD;
            case ("-"):
                return SUB;
            case ("*"):
                return MUL;
            case ("/"):
                return DIV;
            case ("%"):
                return MOD;
            case ("&"):
                return AND;
            case ("|"):
                return OR;
            case ("^"):
                return XOR;
            default:
                throw new IllegalArgumentException();
        }
    }

    /**
     * Gets the operator as a String.
     *
     * @return a string representation of the operator.
     */
    public String getOperatorToString() {
        switch (getOperator()) {
            case (ADD):
                return " + ";
            case (SUB):
                return " - ";
            case (MUL):
                return " * ";
            case (DIV):
                return " / ";
            case (MOD):
                return " % ";
            case (AND):
                return " & ";
            case (OR):
                return " | ";
            case (XOR):
                return " ^ ";
            default:
                return "";
        }
    }

    /**
     * Getter method to obtain High Value.
     *
     * @return an int value of high value.
     */
    public int getRes() {
        return mergeBytes(getValue(RES_INDEX_H), getValue(RES_INDEX_L));
    }

    /**
     * Gets the result location from a String. See the SetAction(String)
     * constructor for more information on the format.
     *
     * @param val the String representing the operand
     * @return an array contianing the result location
     */
    public int[] getResFromString(final String val) {
        int[] tmp = new int[2];
        String[] strVal = val.split("\\.");
        switch (strVal[0]) {
            case "P":
                tmp[0] = PACKET;
                break;
            case "R":
                tmp[0] = STATUS;
                break;
            default:
                throw new IllegalArgumentException();
        }

        if (tmp[0] == PACKET) {
            tmp[1] = NetworkPacket.getNetworkPacketByteFromName(strVal[1]);
        } else {
            tmp[1] = Integer.parseInt(strVal[1]);
        }
        return tmp;
    }

    /**
     * Getter method to obtain Size.
     *
     * @return an int value of SIZE.
     */
    public int getResLocation() {
        return getBitRange(getValue(OP_INDEX), RES_BIT, RES_LEN) + 2;
    }

    /**
     * Getter method to obtain Size in string.
     *
     * @return a string in SIZE.
     */
    public String getResToString() {
        switch (getResLocation()) {
            case PACKET:
                return SET.name() + " P."
                        + NetworkPacket.getNetworkPacketByteName(getRes())
                        + " = ";
            case STATUS:
                return SET.name() + " R." + getRes() + " = ";
            default:
                return "";
        }
    }

    /**
     * Getter method to obtain High Value.
     *
     * @return an int value of high value.
     */
    public int getRhs() {
        return mergeBytes(getValue(RIGHT_INDEX_H), getValue(RIGHT_INDEX_L));
    }

    /**
     * Getter method to obtain rhs Location.
     *
     * @return an int value of location.
     */
    public int getRhsLocation() {
        return getBitRange(getValue(OP_INDEX), RIGHT_BIT, RIGHT_LEN);
    }

    /**
     * Getter method to obtain memory in string.
     *
     * @return a string value of memory.
     */
    public String getRhsToString() {
        switch (getRhsLocation()) {
            case NULL:
                return "";
            case CONST:
                return String.valueOf(getRhs());
            case PACKET:
                return "P." + NetworkPacket.getNetworkPacketByteName(getRhs());
            case STATUS:
                return "R." + getRhs();
            default:
                return "";
        }
    }

    /**
     * Setter method to set offsetIndex of action[].
     *
     * @param val value to set
     * @return this Window
     */
    public SetAction setLhs(final int val) {
        setValue(LEFT_INDEX_L, (byte) val);
        setValue(LEFT_INDEX_H, (byte) val >>> Byte.SIZE);
        return this;
    }

    /**
     * Setter method to set OP_INDEX of action[].
     *
     * @param value value to set
     * @return this Window
     */
    public SetAction setLhsLocation(final int value) {
        setValue(OP_INDEX, (byte) setBitRange(getValue(OP_INDEX),
                LEFT_BIT, LEFT_LEN, value));
        return this;
    }

    /**
     * Setter method to set OP_INDEX of action[].
     *
     * @param value value to set
     * @return this Window
     */
    public SetAction setOperator(final int value) {
        setValue(OP_INDEX, (byte) setBitRange(getValue(OP_INDEX),
                OP_BIT, OP_LEN, value));
        return this;
    }

    /**
     * Setter method to set highValueIndex of action[].
     *
     * @param val value to set
     * @return this Window
     */
    public SetAction setRes(final int val) {
        setValue(RES_INDEX_L, (byte) val);
        setValue(RES_INDEX_H, (byte) val >>> Byte.SIZE);
        return this;
    }

    /**
     * Setter method to set OP_INDEX of action[].
     *
     * @param value value to set
     * @return this Window
     */
    public SetAction setResLocation(final int value) {
        setValue(OP_INDEX, (byte) setBitRange(getValue(OP_INDEX),
                RES_BIT, RES_LEN, value));
        return this;
    }

    /**
     * Setter method to set highValueIndex of action[].
     *
     * @param val value to set
     * @return this Window
     */
    public SetAction setRhs(final int val) {
        setValue(RIGHT_INDEX_L, (byte) val);
        setValue(RIGHT_INDEX_H, (byte) val >>> Byte.SIZE);
        return this;
    }

    /**
     * Setter method to set OP_INDEX of action[].
     *
     * @param value value to set
     * @return this Window
     */
    public SetAction setRhsLocation(final int value) {
        setValue(OP_INDEX, (byte) setBitRange(getValue(OP_INDEX),
                RIGHT_BIT, RIGHT_LEN, value));
        return this;
    }

    @Override
    public String toString() {
        String f = getResToString();
        String l = getLhsToString();
        String r = getRhsToString();
        String o = getOperatorToString();

        if (!l.isEmpty() && !r.isEmpty()) {
            return f + l + o + r;
        } else if (r.isEmpty()) {
            return f + l;
        } else {
            return f + r;
        }
    }

} public static final byte ADD = 0,
        AND = 5,
        DIV = 3,
        MOD = 4,
        MUL = 2,
        OR = 6,
        SUB = 1,
        XOR = 7; public final class Stats implements FlowTableInterface {

    /**
     * The size in byte of the Statistical info.
     */
    public static final byte SIZE = 2;
    /**
     * A FlowTableEntry with TTL = 255 can be deleted only by the controller.
     */
    public static final int ENTRY_TTL_PERMANENT = 255;
    /**
     * The maximum TTL of a FlowTableEntry.
     */
    public static final int SDN_WISE_RL_TTL_MAX = 254;

    /**
     * Stats field indexes.
     */
    private static final byte TTL_INDEX = 0, COUNT_INDEX = 1;

    /**
     * An array of byte containing the Statistical information.
     */
    private final byte[] stats = new byte[SIZE];

    /**
     * Simple constructor for the FlowTableStats object.
     *
     * It sets the statistical fields to the default values.
     */
    public Stats() {
        stats[TTL_INDEX] = (byte) SDN_WISE_RL_TTL_MAX;
        stats[COUNT_INDEX] = 0;
    }

    /**
     * Constructor for the FlowTableStats object.
     *
     * @param value byte array to copy into the statistical part.
     */
    public Stats(final byte[] value) {
        switch (value.length) {
            case 2:
                stats[TTL_INDEX] = value[TTL_INDEX];
                stats[COUNT_INDEX] = value[COUNT_INDEX];
                break;
            case 1:
                stats[TTL_INDEX] = value[TTL_INDEX];
                stats[COUNT_INDEX] = 0;
                break;
            default:
                stats[TTL_INDEX] = (byte) SDN_WISE_RL_TTL_MAX;
                stats[COUNT_INDEX] = 0;
                break;
        }
    }

    /**
     * Getter Method to obtain the ttl value. When the TTL of an entry is equal
     * to 0 the entry is remove from the FlowTable.
     *
     * @return value of ttl of stats[].
     */
    public int getTtl() {
        return Byte.toUnsignedInt(stats[TTL_INDEX]);
    }

    /**
     * Getter Method to obtain count value. The count value represent the number
     * of times an entry has been executed in the FlowTable. This value is not
     * sent to a node.
     *
     * @return value of count of stats[].
     */
    public int getCounter() {
        return Byte.toUnsignedInt(stats[COUNT_INDEX]);
    }

    /**
     * Setter Method to set count value. The count value represent the number of
     * times an entry has been executed in the FlowTable. This value is not sent
     * to a node.
     *
     * @param count to be set
     * @return this Stats
     */
    public Stats setCounter(final int count) {
        stats[COUNT_INDEX] = (byte) count;
        return this;
    }

    /**
     * Increases the usage counter.
     *
     * @return the object itself
     */
    public Stats increaseCounter() {
        stats[COUNT_INDEX]++;
        return this;
    }

    @Override
    public String toString() {
        if (getTtl() == ENTRY_TTL_PERMANENT) {
            return "TTL: PERM, U: " + getCounter();
        } else {
            return "TTL: " + getTtl() + ", U: " + getCounter();
        }
    }

    @Override
    public byte[] toByteArray() {
        return Arrays.copyOf(stats, SIZE);
    }

    /**
     * Turns the FlowTableEntry into a permanent entry. Can be deleted only by a
     * Controller
     *
     * @return the object itself
     */
    public Stats setPermanent() {
        setTtl(ENTRY_TTL_PERMANENT);
        return this;
    }

    /**
     * Restores the TTL of a entry to its maximum.
     *
     * @return the object itself
     */
    public Stats restoreTtl() {
        setTtl(SDN_WISE_RL_TTL_MAX);
        return this;
    }

    /**
     * Decrement by a certain value the TTL of an entry.
     *
     * @param value how much the ttl will be decremented
     * @return the object itself
     */
    public Stats decrementTtl(final int value) {
        setTtl(getTtl() - value);
        return this;
    }

    /**
     * Sets the TTL of an entry.
     *
     * @param ttl the new value of the ttl
     * @return the object itself
     */
    private Stats setTtl(final int ttl) {
        stats[TTL_INDEX] = (byte) ttl;
        return this;
    }
}
 public static final int ENTRY_TTL_PERMANENT = 255;
 public final class Window implements FlowTableInterface {

    /**
     * Window Operators.
     */
    public static final byte EQUAL = 0,
            GREATER = 2,
            GREATER_OR_EQUAL = 4,
            LESS = 3,
            LESS_OR_EQUAL = 5,
            NOT_EQUAL = 1;

    /**
     * The size of the window as an array of bytes.
     */
    public static final byte SIZE = 5;

    /**
     * Window Sizes.
     */
    public static final byte W_SIZE_1 = 0, W_SIZE_2 = 1;

    /**
     * Operators and operands lenghts and indexes.
     */
    private static final byte LEFT_BIT = 3, LEFT_INDEX_H = 1, LEFT_INDEX_L = 2,
            LEFT_LEN = 2, OP_BIT = 5, OP_INDEX = 0, OP_LEN = 3,
            RIGHT_BIT = 1, RIGHT_INDEX_H = 3, RIGHT_INDEX_L = 4,
            RIGHT_LEN = LEFT_LEN,
            SIZE_BIT = 0,
            SIZE_LEN = 1, WIN_LEN = 3;

    /**
     * Stores the window as a byte array.
     */
    private final byte[] window = new byte[SIZE];

    /**
     * Creates a Window given a String. The String must contain: the two values
     * to compare divided by an operator. Possible values are: Possible values
     * are: "P." for packet and "R." for status registern then a number
     * indicating the index where to retrieve the operand. Therefore a possbile
     * result location is "P.10" if you want to compare the 10th byte of the
     * packet. Then operator and the second operand. A complete example is "P.10
     * != R.11". A list of accepted operators can be found in the
     * getOperatorFromString method.
     *
     * @param val the String representing the action
     * @return the window object
     */
    public static Window fromString(final String val) {
        Window w = new Window();
        String[] operands = val.split(" ");
        if (operands.length == WIN_LEN) {
            String lhs = operands[0];
            int[] tmpLhs = Window.getOperandFromString(lhs);
            w.setLhsLocation(tmpLhs[0]);
            w.setLhs(tmpLhs[1]);
            w.setOperator(w.getOperatorFromString(operands[1]));

            String rhs = operands[2];
            int[] tmpRhs = Window.getOperandFromString(rhs);
            w.setRhsLocation(tmpRhs[0]);
            w.setRhs(tmpRhs[1]);

            if ("P.SRC".equals(lhs)
                    || "P.DST".equals(lhs)
                    || "P.NXH".equals(lhs)
                    || "P.SRC".equals(rhs)
                    || "P.DST".equals(rhs)
                    || "P.NXH".equals(rhs)) {
                w.setSize(W_SIZE_2);
            }
        }
        return w;
    }

    /**
     * Simple constructor for the FlowTableWindow object.
     *
     * Set window[] values at zero.
     */
    public Window() {
        Arrays.fill(window, (byte) 0);
    }

    /**
     * Constructor for the FlowTableWindow object.
     *
     * @param value byte array contains value to copy in actions[]
     */
    public Window(final byte[] value) {
        if (value.length == SIZE) {
            System.arraycopy(value, 0, window, 0, value.length);
        } else {
            Arrays.fill(window, (byte) 0);
        }
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Window other = (Window) obj;
        return Arrays.equals(other.window, window);
    }

    /**
     * Getter method to obtain Pos.
     *
     * @return an int value of pos.
     */
    public int getLhs() {
        return Utils.mergeBytes(window[LEFT_INDEX_H], window[LEFT_INDEX_L]);
    }

    /**
     * Getter method to obtain lhs Location.
     *
     * @return an int value of location.
     */
    public int getLhsLocation() {
        return getBitRange(window[OP_INDEX], LEFT_BIT, LEFT_LEN);
    }

    /**
     * Getter method to obtain memory in string.
     *
     * @return a string value of memory.
     */
    public String getLhsToString() {
        switch (getLhsLocation()) {
            case CONST:
                return String.valueOf(getLhs());
            case PACKET:
                return "P." + NetworkPacket.getNetworkPacketByteName(getLhs());
            case STATUS:
                return "R." + getLhs();
            default:
                return "";
        }
    }

    /**
     * Gets an operand given a string. See the fromString method for more
     * details.
     *
     * @param val a String representing the operator
     * @return an array representing the operator
     */
    public static int[] getOperandFromString(final String val) {
        int[] tmp = new int[2];
        String[] strVal = val.split("\\.");
        switch (strVal[0]) {
            case "P":
                tmp[0] = PACKET;
                break;
            case "R":
                tmp[0] = STATUS;
                break;
            default:
                tmp[0] = CONST;
                break;
        }

        switch (tmp[0]) {
            case PACKET:
                tmp[1] = NetworkPacket.getNetworkPacketByteFromName(strVal[1]);
                break;
            case CONST:
                tmp[1] = Integer.parseInt(strVal[0]);
                break;
            default:
                tmp[1] = Integer.parseInt(strVal[1]);
                break;
        }
        return tmp;
    }

    /**
     * Getter method to obtain Operator.
     *
     * @return an int value of operator.
     */
    public int getOperator() {
        return getBitRange(window[OP_INDEX], OP_BIT, OP_LEN);
    }

    /**
     * Gets the operator given a String. See the fromString method for more
     * details.
     *
     * @param val a String representing the operand
     * @return an array representing the operand
     */
    private int getOperatorFromString(final String val) {
        switch (val) {
            case ("=="):
                return EQUAL;
            case ("!="):
                return NOT_EQUAL;
            case (">"):
                return GREATER;
            case ("<"):
                return LESS;
            case (">="):
                return GREATER_OR_EQUAL;
            case ("<="):
                return LESS_OR_EQUAL;
            default:
                throw new IllegalArgumentException();
        }
    }

    /**
     * Getter method to obtain Operator in String.
     *
     * @return a string of operator.
     */
    public String getOperatorToString() {
        switch (getOperator()) {
            case (EQUAL):
                return " == ";
            case (NOT_EQUAL):
                return " != ";
            case (GREATER):
                return " > ";
            case (LESS):
                return " < ";
            case (GREATER_OR_EQUAL):
                return " >= ";
            case (LESS_OR_EQUAL):
                return " <= ";
            default:
                return "";
        }
    }

    /**
     * Getter method to obtain High Value.
     *
     * @return an int value of high value.
     */
    public int getRhs() {
        return Utils.mergeBytes(window[RIGHT_INDEX_H], window[RIGHT_INDEX_L]);
    }

    /**
     * Getter method to obtain rhs Location.
     *
     * @return an int value of location.
     */
    public int getRhsLocation() {
        return getBitRange(window[OP_INDEX], RIGHT_BIT, RIGHT_LEN);
    }

    /**
     * Getter method to obtain memory in string.
     *
     * @return a string value of memory.
     */
    public String getRhsToString() {
        switch (getRhsLocation()) {
            case CONST:
                return String.valueOf(getRhs());
            case PACKET:
                return "P." + NetworkPacket.getNetworkPacketByteName(getRhs());
            case STATUS:
                return "R." + getRhs();
            default:
                return "";
        }
    }

    /**
     * Getter method to obtain Size.
     *
     * @return an int value of size.
     */
    public int getSize() {
        return getBitRange(window[OP_INDEX], SIZE_BIT, SIZE_LEN);
    }

    /**
     * Getter method to obtain Size in string.
     *
     * @return a string in size.
     */
    public String getSizeToString() {
        return String.valueOf(getSize() + 1);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(window);
    }

    /**
     * Setter method to set offsetIndex of window[].
     *
     * @param val value to set
     * @return this Window
     */
    public Window setLhs(final int val) {
        window[LEFT_INDEX_H] = (byte) (val >>> Byte.SIZE);
        window[LEFT_INDEX_L] = (byte) val;
        return this;
    }

    /**
     * Setter method to set OP_INDEX of window[].
     *
     * @param value value to set
     * @return this Window
     */
    public Window setLhsLocation(final int value) {
        window[OP_INDEX] = (byte) setBitRange(
                window[OP_INDEX], LEFT_BIT, LEFT_LEN, value);
        return this;
    }

    /**
     * Setter method to set OP_INDEX of window[].
     *
     * @param value value to set
     * @return this Window
     */
    public Window setOperator(final int value) {
        window[OP_INDEX] = (byte) setBitRange(
                window[OP_INDEX], OP_BIT, OP_LEN, value);
        return this;
    }

    /**
     * Setter method to set highValueIndex of window[].
     *
     * @param val value to set
     * @return this Window
     */
    public Window setRhs(final int val) {
        window[RIGHT_INDEX_H] = (byte) (val >>> Byte.SIZE);
        window[RIGHT_INDEX_L] = (byte) val;
        return this;
    }

    /**
     * Setter method to set OP_INDEX of window[].
     *
     * @param value value to set
     * @return this Window
     */
    public Window setRhsLocation(final int value) {
        window[OP_INDEX] = (byte) setBitRange(
                window[OP_INDEX], RIGHT_BIT, RIGHT_LEN, value);
        return this;
    }

    /**
     * Setter method to set OP_INDEX of window[].
     *
     * @param value value to set
     * @return this Window
     */
    public Window setSize(final int value) {
        window[OP_INDEX] = (byte) setBitRange(
                window[OP_INDEX], SIZE_BIT, SIZE_LEN, value);
        return this;
    }

    @Override
    public byte[] toByteArray() {
        return Arrays.copyOf(window, SIZE);
    }

    @Override
    public String toString() {
        return getLhsToString() + getOperatorToString() + getRhsToString();
    }

}
 public static final byte EQUAL = 0,
        GREATER = 2,
        GREATER_OR_EQUAL = 4,
        LESS = 3,
        LESS_OR_EQUAL = 5,
        NOT_EQUAL = 1;
 public static final byte W_SIZE_1 = 0, W_SIZE_2 = 1;
 public static Window fromString(final String val) {
    Window w = new Window();
    String[] operands = val.split(" ");
    if (operands.length == WIN_LEN) {
        String lhs = operands[0];
        int[] tmpLhs = Window.getOperandFromString(lhs);
        w.setLhsLocation(tmpLhs[0]);
        w.setLhs(tmpLhs[1]);
        w.setOperator(w.getOperatorFromString(operands[1]));

        String rhs = operands[2];
        int[] tmpRhs = Window.getOperandFromString(rhs);
        w.setRhsLocation(tmpRhs[0]);
        w.setRhs(tmpRhs[1]);

        if ("P.SRC".equals(lhs)
                || "P.DST".equals(lhs)
                || "P.NXH".equals(lhs)
                || "P.SRC".equals(rhs)
                || "P.DST".equals(rhs)
                || "P.NXH".equals(rhs)) {
            w.setSize(W_SIZE_2);
        }
    }
    return w;
}
 public interface FunctionInterface {

    /**
     * This interface must be implemented by any function that will be sent to a
     * node. A node can learn new functions that will be executed by using an
     * action SDN_WISE_FORWARD_UP and the corresponding ID of the function. A
     * function can be installed in a node by using the sendFunction method of a
     * controller.
     *
     * @param adcRegister an HashMap containing measurement info.
     * @param flowTable an ArrayList containing the FlowTable of the node.
     * @param neighborTable a Set containing the Neighbors table of the node.
     * @param statusRegister an int[] containing the status of the node.
     * @param acceptedId an ArrayList of NodeAddress containing the aliases for
     * the address of the node.
     * @param flowTableQueue messages added in this queue will be matched in the
     * flow table.
     * @param txQueue messages added in this queue will be sent immediately.
     * @param args is an array of parameters passed by the action.
     * @param np the NetworkPacket that triggered this function.
     */
    void function(
            HashMap<String, Object> adcRegister,
            List<FlowTableEntry> flowTable,
            Set<Neighbor> neighborTable,
            ArrayList<Integer> statusRegister,
            List<NodeAddress> acceptedId,
            ArrayBlockingQueue<NetworkPacket> flowTableQueue,
            ArrayBlockingQueue<NetworkPacket> txQueue,
            byte[] args,
            NetworkPacket np
    );
}
 public interface Dischargeable {

    /**
     * Gets the battery level as a percent of the MAX_LEVEL.
     *
     * @return the Battery level in the range [0-255].
     */
    int getByteLevel();

    /**
     * Gets the battery level of the Battery.
     *
     * @return the battery level of the node as a double. Can't be negative.
     */
    double getLevel();

    /**
     * Setter for the battery level of the Battery.
     *
     * @param batteryLevel the battery level. If negative, the battery level is
     * set to 0.
     */
    void setLevel(double batteryLevel);

    /**
     * Simulates the battery consumption for staying alive for n seconds.
     *
     * @param n the number of seconds the node is turned on.
     * @return the Battery object
     */
    Battery keepAlive(int n);

    /**
     * Simulates the battery consumption for receiving nByte bytes.
     *
     * @param nBytes the number of bytes received over the radio
     * @return the Battery object
     */
    Battery receiveRadio(int nBytes);

    /**
     * Simulates the battery consumption for sending nByte bytes.
     *
     * @param nBytes the number of bytes sent over the radio
     * @return the Battery object
     */
    Battery transmitRadio(int nBytes);

} public static final byte ENTRY_TTL_DECR = 10;
 public static final byte SDN_WISE_DFLT_CNT_DATA_MAX = 10,
        SDN_WISE_DFLT_CNT_BEACON_MAX = 10,
        SDN_WISE_DFLT_CNT_REPORT_MAX = 2 * SDN_WISE_DFLT_CNT_BEACON_MAX,
        SDN_WISE_DFLT_CNT_UPDTABLE_MAX = 6; // TTL = 150s
 public static final int SDN_WISE_DFLT_RSSI_MIN = 180;
 public static final int SDN_WISE_STATUS_LEN = 10000;
 public class BeaconPacket extends NetworkPacket {

    /**
     * Distance is at payload position 0, the residual charge at position 1.
     */
    private static final byte DIST_INDEX = 0,
            BATT_INDEX = 1;

    /**
     * This constructor initialize a beacon packet starting from a byte array.
     *
     * @param data the byte array representing the beacon packet
     */
    public BeaconPacket(final byte[] data) {
        super(data);
    }

    /**
     * This constructor initialize a beacon packet starting from a int array.
     *
     * @param data the int array representing the beacon packet, all int are
     * casted to byte
     */
    public BeaconPacket(final int[] data) {
        super(data);
    }

    /**
     * This constructor initialize a beacon packet starting from a
     * NetworkPacket.
     *
     * @param data the NetworkPacket representing the beacon packet
     */
    public BeaconPacket(final NetworkPacket data) {
        super(data.toByteArray());
    }

    /**
     * This constructor initialize a beacon packet. The type of the packet is
     * set to {@code BEACON} and the destination address is
     * {@code BROADCAST_ADDR}.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param sink sink address of the source node
     * @param distance the distance from the sink in no. of hops
     * @param battery the residual charge of the node
     */
    public BeaconPacket(final int net, final NodeAddress src,
            final NodeAddress sink,
            final int distance, final int battery) {
        super(net, src, BROADCAST_ADDR);
        setTyp(BEACON);
        setSinkAddress(sink);
        setDistance((byte) distance);
        setBattery((byte) battery);
    }

    /**
     * Getter for the number of hops between the source node and the sink.
     *
     * @return the number of hops between the source node and the sink
     */
    public final int getDistance() {
        return Byte.toUnsignedInt(getPayloadAt(DIST_INDEX));
    }

    /**
     * Setter for the number of hops between the source node and the sink.
     *
     * @param value the number of hops between the source node and the sink
     * @return the packet itself
     */
    public final BeaconPacket setDistance(final byte value) {
        setPayloadAt(value, DIST_INDEX);
        return this;
    }

    /**
     * Returns an estimation of the residual charge of the batteries of the
     * node. The possible values are: [0x00-0xFF] 0x00 = no charge, 0xFF = full
     * charge.
     *
     * @return an estimation of the residual charge of the batteries of the node
     */
    public final int getBattery() {
        return Byte.toUnsignedInt(getPayloadAt(BATT_INDEX));
    }

    /**
     * Set the battery level in the packet. The possible values are: [0x00-0xFF]
     * 0x00 = no charge, 0xFF = full charge.
     *
     * @param value the value of the battery level
     * @return the packet itself
     */
    public final BeaconPacket setBattery(final byte value) {
        setPayloadAt(value, BATT_INDEX);
        return this;
    }

    /**
     * Set the address of the sink to which this node is connected.
     *
     * @param addr the address of the sink
     * @return the packet itself
     */
    public final BeaconPacket setSinkAddress(final NodeAddress addr) {
        setNxh(addr);
        return this;
    }

    /**
     * Get the address of the sink to which this node is connected.
     *
     * @return the address of the sink
     */
    public final NodeAddress getSinkAddress() {
        return getNxh();
    }
}
 public class ConfigPacket extends NetworkPacket {

    /**
     * The value indicating if the ConfigProperty is a READ or WRITE.
     */
    private static final byte CNF_WRITE = 1;

    /**
     * Constants for parsing.
     */
    private static final int MASK_POS = 7, MASK = 0x7F;

    /**
     * This constructor initialize a config packet starting from a byte array.
     *
     * @param data the byte array representing the config packet
     */
    public ConfigPacket(final byte[] data) {
        super(data);
    }

    /**
     * This constructor initialize a config packet starting from a int array.
     *
     * @param data the int array representing the config packet, all int are
     * casted to byte
     */
    public ConfigPacket(final int[] data) {
        super(data);
    }

    /**
     * This constructor initialize a config packet starting from a
     * NetworkPacket.
     *
     * @param data the NetworkPacket representing the beacon packet
     */
    public ConfigPacket(final NetworkPacket data) {
        super(data.toByteArray());
    }

    /**
     * This constructor initialize a config packet. The type of the packet is
     * set to {@code CONFIG} and the read/write bit is set to {@code READ}.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param dst destination address of the packet
     * @param read the name of the property to read
     */
    public ConfigPacket(final int net, final NodeAddress src,
            final NodeAddress dst,
            final ConfigProperty read) {
        super(net, src, dst);
        setConfigId(read).setTyp(CONFIG);
    }

    /**
     * This constructor initialize a config packet. The type of the packet is
     * set to {@code CONFIG} and the read/write bit is set to {@code WRITE}.
     *
     * @param net the Network ID of the node
     * @param src source address
     * @param dst destination address
     * @param write the name of the property to write
     * @param value the value to be written
     */
    public ConfigPacket(final int net, final NodeAddress src,
            final NodeAddress dst,
            final ConfigProperty write,
            final byte[] value) {
        super(net, src, dst);
        setConfigId(write).setWrite().setParams(value, write.size)
                .setTyp(CONFIG);
    }

    /**
     * Returns true if the Config packet is a write packet.
     *
     * @return a boolean indicating if the packet is a write packet
     */
    public final boolean isWrite() {
        int value = Byte.toUnsignedInt(getPayloadAt((byte) 0)) >> MASK_POS;
        return (value == CNF_WRITE);
    }

    /**
     * Returns the Configuration ID of the property to read/write.
     *
     * @return the ConfigProperty set in the packet
     */
    public final ConfigProperty getConfigId() {
        return ConfigProperty.fromByte((byte) (getPayloadAt((byte) 0) & MASK));
    }

    /**
     * Sets the value of the property to write. If the ConfigProperty of the
     * packet is a Get or Remove it contains the index of the item. If it is an
     * Add, the item itself.
     *
     * @param bytes the value of the property as a byte[]
     * @param size the size of the property
     * @return the packet itself
     */
    public final ConfigPacket setParams(final byte[] bytes, final int size) {
        if (size != -1) {
            for (int i = 0; i < size; i++) {
                setPayloadAt(bytes[i], i + 1);
            }
        } else {
            for (int i = 0; i < bytes.length; i++) {
                setPayloadAt(bytes[i], i + 1);
            }
        }
        return this;
    }

    /**
     * Gets the value of the read property.
     *
     * @return the configuration property as a byte[]
     */
    public final byte[] getParams() {
        return getPayloadFromTo(1, getPayloadSize());
    }

    /**
     * Sets the Config packet to be a write ConfigPacket.
     *
     * @return the packet itself
     */
    private ConfigPacket setWrite() {
        setPayloadAt((byte) ((getPayloadAt(0)) | (CNF_WRITE << MASK_POS)), 0);
        return this;
    }

    /**
     * Sets the Config ID of the packet.
     *
     * @param id the id to be set
     * @return the packet itself
     */
    private ConfigPacket setConfigId(final ConfigProperty id) {
        setPayloadAt(id.value, 0);
        return this;
    }

    /**
     * Configuration Properties.
     */
    public enum ConfigProperty {
        /**
         * Restarts the node.
         */
        RESET(0, 0),
        /**
         * Network ID. Can be read/written.
         */
        MY_NET(1, 1),
        /**
         * Address of the node. Can be read/written.
         */
        MY_ADDRESS(2, 2),
        /**
         * Default Packet TTL. Can be read/written.
         */
        PACKET_TTL(3, 1),
        /**
         * Filter packets depending on RSSI. Can be read/written.
         */
        RSSI_MIN(4, 1),
        /**
         * Seconds between beacons. Can be read/written.
         */
        BEACON_PERIOD(5, 2),
        /**
         * Seconds between reports. Can be read/written.
         */
        REPORT_PERIOD(6, 2),
        /**
         * Reports between resets. Can be read/written.
         */
        RESET_PERIOD(7, 2),
        /**
         * TTL of a FlowTableEntry. Can be read/written.
         */
        RULE_TTL(8, 1),
        /**
         * Adds an alias to the list of aliases of the node. write only.
         */
        ADD_ALIAS(9, 2),
        /**
         * Removes an alias from the list of aliases of the node. write only.
         */
        REM_ALIAS(10, 1),
        /**
         * Gets an alias from the list of aliases of the node. read only.
         */
        GET_ALIAS(11, 1),
        /**
         * Adds a rule to the FlowTable of the node. write only.
         */
        ADD_RULE(12, -1),
        /**
         * Removes a rule from the FlowTable of the node. write only.
         */
        REM_RULE(13, 1),
        /**
         * Gets a rule from the FlowTable of the node. read only.
         */
        GET_RULE(14, 1),
        /**
         * Adds a function to the node. write only.
         */
        ADD_FUNCTION(15, -1),
        /**
         * Removes a function from the node. write only.
         */
        REM_FUNCTION(16, 1),
        /**
         * Gets a function from the node. read only.
         */
        GET_FUNCTION(17, 1);

        /**
         * The id of the configguration parameter.
         */
        private final byte value;

        /**
         * The size of the configuration parameter.
         */
        private final int size;

        /**
         * A byte array representation of all the possible values.
         */
        private static final ConfigProperty[] VALUES = ConfigProperty.values();

        /**
         * Creates a ConfigProperty given a byte array.
         *
         * @param value the byte array representing the config property
         * @return the config property object
         */
        public static ConfigProperty fromByte(final byte value) {
            return VALUES[value];
        }

        /**
         * Gets the size of the ConfigProperty.
         *
         * @return the size of the ConfigProperty
         */
        public int getSize() {
            return size;
        }

        /**
         * Creates a config property given a size and an id.
         *
         * @param v id of the ConfigProperty
         * @param s size of the ConfigProperty
         */
        ConfigProperty(final int v, final int s) {
            value = (byte) v;
            size = s;
        }
    }
}
 public enum ConfigProperty {
    /**
     * Restarts the node.
     */
    RESET(0, 0),
    /**
     * Network ID. Can be read/written.
     */
    MY_NET(1, 1),
    /**
     * Address of the node. Can be read/written.
     */
    MY_ADDRESS(2, 2),
    /**
     * Default Packet TTL. Can be read/written.
     */
    PACKET_TTL(3, 1),
    /**
     * Filter packets depending on RSSI. Can be read/written.
     */
    RSSI_MIN(4, 1),
    /**
     * Seconds between beacons. Can be read/written.
     */
    BEACON_PERIOD(5, 2),
    /**
     * Seconds between reports. Can be read/written.
     */
    REPORT_PERIOD(6, 2),
    /**
     * Reports between resets. Can be read/written.
     */
    RESET_PERIOD(7, 2),
    /**
     * TTL of a FlowTableEntry. Can be read/written.
     */
    RULE_TTL(8, 1),
    /**
     * Adds an alias to the list of aliases of the node. write only.
     */
    ADD_ALIAS(9, 2),
    /**
     * Removes an alias from the list of aliases of the node. write only.
     */
    REM_ALIAS(10, 1),
    /**
     * Gets an alias from the list of aliases of the node. read only.
     */
    GET_ALIAS(11, 1),
    /**
     * Adds a rule to the FlowTable of the node. write only.
     */
    ADD_RULE(12, -1),
    /**
     * Removes a rule from the FlowTable of the node. write only.
     */
    REM_RULE(13, 1),
    /**
     * Gets a rule from the FlowTable of the node. read only.
     */
    GET_RULE(14, 1),
    /**
     * Adds a function to the node. write only.
     */
    ADD_FUNCTION(15, -1),
    /**
     * Removes a function from the node. write only.
     */
    REM_FUNCTION(16, 1),
    /**
     * Gets a function from the node. read only.
     */
    GET_FUNCTION(17, 1);

    /**
     * The id of the configguration parameter.
     */
    private final byte value;

    /**
     * The size of the configuration parameter.
     */
    private final int size;

    /**
     * A byte array representation of all the possible values.
     */
    private static final ConfigProperty[] VALUES = ConfigProperty.values();

    /**
     * Creates a ConfigProperty given a byte array.
     *
     * @param value the byte array representing the config property
     * @return the config property object
     */
    public static ConfigProperty fromByte(final byte value) {
        return VALUES[value];
    }

    /**
     * Gets the size of the ConfigProperty.
     *
     * @return the size of the ConfigProperty
     */
    public int getSize() {
        return size;
    }

    /**
     * Creates a config property given a size and an id.
     *
     * @param v id of the ConfigProperty
     * @param s size of the ConfigProperty
     */
    ConfigProperty(final int v, final int s) {
        value = (byte) v;
        size = s;
    }
}
 public class DataPacket extends NetworkPacket {

    /**
     * This constructor initialize a data packet starting from a byte array.
     *
     * @param data the byte array representing the data packet.
     */
    public DataPacket(final byte[] data) {
        super(data);
    }

    /**
     * This constructor initialize a data packet starting from a int array.
     *
     * @param data the int array representing the data packet, all int are
     * casted to byte.
     */
    public DataPacket(final int[] data) {
        super(data);
    }

    /**
     * This constructor initialize a Data packet starting from a NetworkPacket.
     *
     * @param data the NetworkPacket representing the data packet.
     */
    public DataPacket(final NetworkPacket data) {
        super(data.toByteArray());
    }

    /**
     * This constructor initialize a data packet. The type of the packet is set
     * to SDN_WISE_DATA.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param dst destination address of the packet
     * @param payload the byte[] containing the payload of the packet
     */
    public DataPacket(final int net, final NodeAddress src,
            final NodeAddress dst,
            final byte[] payload) {
        super(net, src, dst);
        setTyp(DATA);
        setPayload(payload);
    }

    /**
     * Returns the payload of the packet as a byte array.
     *
     * @return the payload of the packet
     */
    public final byte[] getData() {
        return super.getPayload();
    }
}
 public class NetworkPacket implements Cloneable {

    /**
     * The maximum length of a NetworkPAcket.
     */
    public static final byte MAX_PACKET_LENGTH = 116;

    /**
     * The indexes of the different fields in the packet.
     */
    public static final byte NET_INDEX = 0,
            LEN_INDEX = 1,
            DST_INDEX = 2,
            SRC_INDEX = 4,
            TYP_INDEX = 6,
            TTL_INDEX = 7,
            NXH_INDEX = 8,
            PLD_INDEX = 10;

    /**
     * The possible values of the type of a packet.
     */
    public static final byte DATA = 0,
            BEACON = 1,
            REPORT = 2,
            REQUEST = 3,
            RESPONSE = 4,
            OPEN_PATH = 5,
            CONFIG = 6,
            REG_PROXY = 7;

    /**
     * An SDN-WISE header is always 10 bytes long.
     */
    public static final byte DFLT_HDR_LEN = 10;

    /**
     * The maximum number of hops allowed in the network.
     */
    public static final byte DFLT_TTL_MAX = 100;

    /**
     * The byte array containing the packet.
     */
    private final byte[] data;

    /**
     * NET values less than 63 are SDN-WISE.
     */
    public static final byte THRES = 63;

    /**
     * Returns the index of a byte in the header given a string.
     *
     * @param b the byte name
     * @return the unsigned value of the byte
     */
    public static int getNetworkPacketByteFromName(final String b) {
        switch (b) {
            case "LEN":
                return LEN_INDEX;
            case "NET":
                return NET_INDEX;
            case "SRC":
                return SRC_INDEX;
            case "DST":
                return DST_INDEX;
            case "TYP":
                return TYP_INDEX;
            case "TTL":
                return TTL_INDEX;
            case "NXH":
                return NXH_INDEX;
            default:
                return Integer.parseInt(b);
        }
    }

    /**
     * Checks if a byte array is an SDN-WISE packet.
     *
     * @param data a byte array
     * @return a boolean depending if is an SDN-WISE packet or not
     */
    public static boolean isSdnWise(final byte[] data) {
        return (Byte.toUnsignedInt(data[NET_INDEX]) < THRES);
    }

    /**
     * Returns a string representation of a byte of the header.
     *
     * @param b an integer representing the index of a byte in the header
     * @return a string representation of a byte of the header
     */
    public static String getNetworkPacketByteName(final int b) {
        switch (b) {
            case (NET_INDEX):
                return "NET";
            case (LEN_INDEX):
                return "LEN";
            case (DST_INDEX):
                return "DST";
            case (SRC_INDEX):
                return "SRC";
            case (TYP_INDEX):
                return "TYP";
            case (TTL_INDEX):
                return "TTL";
            case (NXH_INDEX):
                return "NXH";
            default:
                return String.valueOf(b);
        }
    }

    /**
     * Returns a NetworkPacket given a byte array.
     *
     * @param d the d contained in the NetworkPacket
     */
    public NetworkPacket(final byte[] d) {
        data = new byte[MAX_PACKET_LENGTH];
        setArray(d);
    }

    /**
     * Creates an empty NetworkPacket. The TTL and LEN values are set to
     * default.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param dst destination address of the packet
     */
    public NetworkPacket(final int net, final NodeAddress src,
                         final NodeAddress dst) {
        data = new byte[MAX_PACKET_LENGTH];
        setNet((byte) net);
        setSrc(src);
        setDst(dst);
        setTtl(DFLT_TTL_MAX);
        setLen(DFLT_HDR_LEN);
    }

    /**
     * Returns a NetworkPacket given a int array. Integer values will be
     * truncated to byte.
     *
     * @param d the data contained in the NetworkPacket
     */
    public NetworkPacket(final int[] d) {
        data = new byte[MAX_PACKET_LENGTH];
        setArray(fromIntArrayToByteArray(d));
    }

    /**
     * Returns a NetworkPacket given a DataInputStream. Integer values will be
     * truncated to byte.
     *
     * @param dis the DataInputStreamt
     */
    public NetworkPacket(final DataInputStream dis) throws IOException {
        data = new byte[MAX_PACKET_LENGTH];
        byte[] tmpData = new byte[MAX_PACKET_LENGTH];
        int net = Byte.toUnsignedInt(dis.readByte());
        int len = Byte.toUnsignedInt(dis.readByte());
        if (len > 0) {
            tmpData[NET_INDEX] = (byte) net;
            tmpData[LEN_INDEX] = (byte) len;
            dis.readFully(tmpData, LEN_INDEX + 1, len - 2);

        }
        setArray(tmpData);
    }

    /**
     * Returns a NetworkPacket given a BufferedInputStream. It supports
     * streams where there could be bytes not belonging to a packet or
     * malformed packets.
     *
     * @param bis the BufferedInputStream
     */
    public NetworkPacket(final BufferedInputStream bis) throws IOException {
        data = new byte[MAX_PACKET_LENGTH];
        boolean startFlag = false;
        boolean idFlag = false;
        boolean found = false;
        int expected = 0;
        int b;
        byte a;
        final LinkedList<Byte> receivedBytes = new LinkedList<>();
        final LinkedList<Byte> packet = new LinkedList<>();
        byte startByte = 0x7A;
        byte stopByte = 0x7E;


        while (!found && (b = bis.read()) != -1) {
            receivedBytes.add((byte) b);
            while (!receivedBytes.isEmpty()) {
                a = receivedBytes.poll();
                if (!startFlag && a == startByte) {
                    startFlag = true;
                    packet.add(a);
                } else if (startFlag && !idFlag) {
                    packet.add(a);
                    idFlag = true;
                } else if (startFlag && idFlag && expected == 0) {
                    expected = Byte.toUnsignedInt(a);
                    packet.add(a);
                } else if (startFlag && idFlag && expected > 0
                        && packet.size() < expected + 1) {
                    packet.add(a);
                } else if (startFlag && idFlag && expected > 0
                        && packet.size() == expected + 1) {
                    packet.add(a);
                    if (a == stopByte) {
                        packet.removeFirst();
                        packet.removeLast();
                        byte[] tmpData = new byte[packet.size()];
                        for (int i = 0; i < tmpData.length; i++) {
                            tmpData[i] = packet.poll();
                        }
                        setArray(tmpData);
                        found = true;
                        break;
                    } else {
                        while (!packet.isEmpty()) {
                            receivedBytes.addFirst(packet.removeLast());
                        }
                        receivedBytes.poll();
                    }
                    startFlag = false;
                    idFlag = false;
                    expected = 0;
                }
            }
        }
    }

    /**
     * Fills the NetworkPacket with the content of an int array. Each element of
     * the array is casted to byte.
     *
     * @param array an array representing the packet
     */
    public final void setArray(final int[] array) {
        setArray(fromIntArrayToByteArray(array));
    }

    /**
     * Fills the NetworkPacket with the content of a byte array.
     *
     * @param array an array representing the packet
     */
    public final void setArray(final byte[] array) {
        if (isSdnWise(array)) {
            if (array.length <= MAX_PACKET_LENGTH && array.length
                    >= DFLT_HDR_LEN) {

                setLen(array[LEN_INDEX]);
                setNet(array[NET_INDEX]);
                setSrc(array[SRC_INDEX], array[SRC_INDEX + 1]);
                setDst(array[DST_INDEX], array[DST_INDEX + 1]);
                setTyp(array[TYP_INDEX]);
                setTtl(array[TTL_INDEX]);
                setNxh(array[NXH_INDEX], array[NXH_INDEX + 1]);
                setPayload(Arrays.copyOfRange(array, DFLT_HDR_LEN,
                        getLen()));
            } else {
                throw new IllegalArgumentException("Invalid array size: "
                        + array.length);
            }
        } else {
            System.arraycopy(array, 0, data, 0, array.length);
        }
    }

    /**
     * Returns the length of the message.
     *
     * @return an integer representing the length of the message
     */
    public final int getLen() {
        if (isSdnWise()) {
            return Byte.toUnsignedInt(data[LEN_INDEX]);
        } else {
            return data.length;
        }
    }

    /**
     * Sets the length of the message.
     *
     * @param value an integer representing the length of the message.
     * @return the packet itself
     */
    public final NetworkPacket setLen(final byte value) {
        int v = Byte.toUnsignedInt(value);
        if (v <= MAX_PACKET_LENGTH && v > 0) {
            data[LEN_INDEX] = value;
        } else {
            throw new IllegalArgumentException("Invalid length: " + v);
        }
        return this;
    }

    /**
     * Returns the NetworkId of the message.
     *
     * @return an integer representing the NetworkId of the message
     */
    public final int getNet() {
        return Byte.toUnsignedInt(data[NET_INDEX]);
    }

    /**
     * Sets the NetworkId of the message.
     *
     * @param value the networkId of the packet.
     * @return the packet itself
     */
    public final NetworkPacket setNet(final byte value) {
        data[NET_INDEX] = value;
        return this;
    }

    /**
     * Returns the address of the source node.
     *
     * @return the NodeAddress of the source node
     */
    public final NodeAddress getSrc() {
        return new NodeAddress(data[SRC_INDEX], data[SRC_INDEX + 1]);
    }

    /**
     * Sets the address of the source node.
     *
     * @param valueH the high byte of the address
     * @param valueL the low byte of the address
     * @return the packet itself
     */
    public final NetworkPacket setSrc(final byte valueH, final byte valueL) {
        data[SRC_INDEX] = valueH;
        data[SRC_INDEX + 1] = valueL;
        return this;
    }

    /**
     * Sets the address of the source node.
     *
     * @param address the NodeAddress of the source node.
     * @return the packet itself
     */
    public final NetworkPacket setSrc(final NodeAddress address) {
        setSrc(address.getHigh(), address.getLow());
        return this;
    }

    /**
     * Returns the address of the destination node.
     *
     * @return the NodeAddress of the destination node
     */
    public final NodeAddress getDst() {
        return new NodeAddress(data[DST_INDEX], data[DST_INDEX + 1]);
    }

    /**
     * Set the address of the destination node.
     *
     * @param valueH high value of the address of the destination
     * @param valueL low value of the address of the destination
     * @return the packet itself
     */
    public final NetworkPacket setDst(final byte valueH, final byte valueL) {
        data[DST_INDEX] = valueH;
        data[DST_INDEX + 1] = valueL;
        return this;
    }

    /**
     * Set the address of the destination node.
     *
     * @param address the NodeAddress value of the destination
     * @return the packet itself
     */
    public final NetworkPacket setDst(final NodeAddress address) {
        setDst(address.getHigh(), address.getLow());
        return this;
    }

    /**
     * Returns the type of the message.
     *
     * @return an integer representing the type of the message
     */
    public final int getTyp() {
        return data[TYP_INDEX];
    }

    /**
     * Sets the type of the message.
     *
     * @param value an integer representing the type of the message
     * @return the packet itself
     */
    public final NetworkPacket setTyp(final byte value) {
        data[TYP_INDEX] = value;
        return this;
    }

    /**
     * Returns the Time To Live of the message. When the TTL of a packet reaches
     * 0 the receiving node will drop the packet.
     *
     * @return an integer representing the Time To Live of the message
     */
    public final int getTtl() {
        return Byte.toUnsignedInt(data[TTL_INDEX]);
    }

    /**
     * Sets the Time To Live of the message. When the TTL of a packet reaches 0
     * the receiving node will drop the packet.
     *
     * @param value an integer representing the Time To Live of the message.
     * @return the packet itself
     */
    public final NetworkPacket setTtl(final byte value) {
        data[TTL_INDEX] = value;
        return this;
    }

    /**
     * Decrements the Time To Live of the message by 1. When the TTL of a packet
     * reaches 0 the receiving node will drop the packet.
     *
     * @return the packet itself
     */
    public final NetworkPacket decrementTtl() {
        if (data[TTL_INDEX] > 0) {
            data[TTL_INDEX]--;
        }
        return this;
    }

    /**
     * Returns the NodeAddress of the next hop towards the destination.
     *
     * @return the NodeAddress of the the next hop towards the destination node
     */
    public final NodeAddress getNxh() {
        return new NodeAddress(data[NXH_INDEX], data[NXH_INDEX + 1]);
    }

    /**
     * Sets the NodeAddress of the next hop towards the destination.
     *
     * @param valueH high value of the address of the next hop.
     * @param valueL low value of the address of the next hop.
     * @return packet itself.
     */
    public final NetworkPacket setNxh(final byte valueH, final byte valueL) {
        data[NXH_INDEX] = valueH;
        data[NXH_INDEX + 1] = valueL;
        return this;
    }

    /**
     * Sets the NodeAddress of the next hop towards the destination.
     *
     * @param address the NodeAddress address of the next hop.
     * @return packet itself.
     */
    public final NetworkPacket setNxh(final NodeAddress address) {
        setNxh(address.getHigh(), address.getLow());
        return this;
    }

    /**
     * Sets the NodeAddress of the next hop towards the destination.
     *
     * @param address a string representing the address of the next hop.
     * @return packet itself.
     */
    public final NetworkPacket setNxh(final String address) {
        setNxh(new NodeAddress(address));
        return this;
    }

    /**
     * Gets the payload size of the packet.
     *
     * @return the packet payload size.
     */
    public final int getPayloadSize() {
        return (getLen() - DFLT_HDR_LEN);
    }

    /**
     * Returns a String representation of the NetworkPacket.
     *
     * @return a String representation of the NetworkPacket
     */
    @Override
    public final String toString() {
        return Arrays.toString(toIntArray());
    }

    /**
     * Returns a byte array representation of the NetworkPacket.
     *
     * @return a byte array representation of the NetworkPacket
     */
    public final byte[] toByteArray() {
        return Arrays.copyOf(data, getLen());
    }

    /**
     * Returns an int array representation of the NetworkPacket.
     *
     * @return a int array representation of the NetworkPacket
     */
    public final int[] toIntArray() {
        int[] tmp = new int[getLen()];
        for (int i = 0; i < tmp.length; i++) {
            tmp[i] = Byte.toUnsignedInt(data[i]);
        }
        return tmp;
    }

    @Override
    public final NetworkPacket clone() throws CloneNotSupportedException {
        super.clone();
        return new NetworkPacket(data.clone());
    }

    /**
     * Checks if this NetworkPacket is an SDN-WISE packet.
     *
     * @return a boolean depending if is an SDN-WISE packet or not
     */
    public final boolean isSdnWise() {
        return (Byte.toUnsignedInt(data[NET_INDEX]) < THRES);
    }

    /**
     * Casts an int array to a byte array.
     *
     * @param array an int array
     * @return a byte array
     */
    private byte[] fromIntArrayToByteArray(final int[] array) {
        byte[] dataToByte = new byte[array.length];
        for (int i = 0; i < array.length; i++) {
            dataToByte[i] = (byte) array[i];
        }
        return dataToByte;
    }

    /**
     * Returns the payload of the packet as a byte array.
     *
     * @return the payload of the packet
     */
    protected final byte[] getPayload() {
        return Arrays.copyOfRange(data, DFLT_HDR_LEN,
                getLen());
    }

    /**
     * Sets the p of the packet from a byte array.
     *
     * @param p the p of the packet.
     * @return the p of the packet.
     */
    protected final NetworkPacket setPayload(final byte[] p) {
        if (p.length + DFLT_HDR_LEN <= MAX_PACKET_LENGTH) {
            System.arraycopy(p, 0, data, DFLT_HDR_LEN, p.length);
            setLen((byte) (p.length + DFLT_HDR_LEN));
        } else {
            throw new IllegalArgumentException("Payload exceeds packet size");
        }
        return this;
    }

    /**
     * Sets the payload size of the packet.
     *
     * @param size the payload size.
     * @return the packet itself
     */
    protected final NetworkPacket setPayloadSize(final int size) {
        if (DFLT_HDR_LEN + size <= MAX_PACKET_LENGTH) {
            setLen((byte) (DFLT_HDR_LEN + size));
        } else {
            throw new IllegalArgumentException("Index cannot be greater than "
                    + "the maximum payload size: " + size);
        }
        return this;
    }

    /**
     * Sets a single payload byte.
     *
     * @param i the i of the payload. The first byte of the payload is 0.
     * @param d the new data to be set.
     * @return the packet itself
     */
    protected final NetworkPacket setPayloadAt(final byte d, final int i) {
        if (DFLT_HDR_LEN + i < MAX_PACKET_LENGTH) {
            data[DFLT_HDR_LEN + i] = d;
            if ((i + DFLT_HDR_LEN) >= getLen()) {
                setLen((byte) (DFLT_HDR_LEN + i + 1));
            }
        } else {
            throw new IllegalArgumentException("Index cannot be greater than "
                    + "the maximum payload size");
        }
        return this;
    }

    /**
     * Sets a part of the payload of the NetworkPacket. Differently from
     * copyPayload this method updates also the length of the packet
     *
     * @param src        the new data to be set.
     * @param srcPos     starting from this byte of src.
     * @param payloadPos copying to this byte of payload.
     * @param length     this many bytes.
     * @return the packet itself
     */
    protected final NetworkPacket setPayload(final byte[] src,
                                             final int srcPos,
                                             final int payloadPos,
                                             final int length) {

        if (srcPos < 0 || payloadPos < 0 || length < 0) {
            throw new IllegalArgumentException("Negative index");
        } else {
            copyPayload(src, srcPos, payloadPos, length);
            setPayloadSize(length + payloadPos);
        }
        return this;
    }

    /**
     * Copy a part of the payload of the NetworkPacket. Differently from
     * copyPayload this method does not update the length of the packet
     *
     * @param src        the new data to be set.
     * @param srcPos     starting from this byte of src.
     * @param payloadPos copying to this byte of payload.
     * @param length     this many bytes.
     * @return the packet itself
     */
    protected final NetworkPacket copyPayload(final byte[] src,
                                              final int srcPos,
                                              final int payloadPos,
                                              final int length) {
        for (int i = 0; i < length; i++) {
            setPayloadAt(src[i + srcPos], i + payloadPos);
        }
        return this;
    }

    /**
     * Gets a byte from the payload of the packet at position i.
     *
     * @param i the offset of the byte.
     * @return the byte of the payload.
     */
    protected final byte getPayloadAt(final int i) {
        if (i + DFLT_HDR_LEN < getLen()) {
            return data[DFLT_HDR_LEN + i];
        } else {
            throw new IllegalArgumentException("Index cannot be greater than "
                    + "the maximum payload size");
        }
    }

    /**
     * Gets a byte array from the payload of the packet from position start to
     * position end.
     *
     * @param start starting index inclusive.
     * @param stop  stop index exclusive.
     * @return the byte of the payload.
     */
    protected final byte[] getPayloadFromTo(final int start, final int stop) {
        if (start > stop) {
            throw new IllegalArgumentException(
                    "Start must be equal or less than stop");
        }
        if (stop < 0) {
            throw new IllegalArgumentException(
                    "Stop must be greater than 0");
        }
        if (start + DFLT_HDR_LEN > getLen()) {
            throw new IllegalArgumentException(
                    "Start is greater than packet size");
        }
        int newStop = Math.min(stop + DFLT_HDR_LEN, getLen());
        return Arrays.copyOfRange(data, start + DFLT_HDR_LEN, newStop);
    }

    /**
     * Gets a part of the payload of the packet from position start, to position
     * end.
     *
     * @param start start the copy from this byte.
     * @param end   to this byte.
     * @return a byte[] part of the payload.
     */
    protected final byte[] copyPayloadOfRange(final int start, final int end) {
        return Arrays.copyOfRange(data, DFLT_HDR_LEN + start,
                DFLT_HDR_LEN + end);
    }

}
 public static final byte DFLT_HDR_LEN = 10;
 public static final byte DFLT_TTL_MAX = 100;
 public class OpenPathPacket extends NetworkPacket {

    /**
     * The index where the size of the windows is located.
     */
    private static final int WINDOWS_SIZE_INDEX = 0;

    /**
     * This constructor initialize an open path packet starting from a byte
     * array.
     *
     * @param data the byte array representing the open path packet.
     */
    public OpenPathPacket(final byte[] data) {
        super(data);
    }

    /**
     * This constructor initialize an open path packet. The type of the packet
     * is set to {@code OPEN_PATH}.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param dst destination address of the packet
     * @param path the list of NodeAddresses included in the path
     */
    public OpenPathPacket(final int net, final NodeAddress src,
            final NodeAddress dst,
            final List<NodeAddress> path) {
        super(net, src, dst);
        setTyp(OPEN_PATH);
        setPayloadAt((byte) 0, WINDOWS_SIZE_INDEX);
        setPath(path);
    }

    /**
     * This constructor initialize an open path packet starting from a int
     * array.
     *
     * @param data the int array representing the open path packet, all int are
     * casted to byte.
     */
    public OpenPathPacket(final int[] data) {
        super(data);
    }

    /**
     * This constructor initialize an open path packet starting from a
     * NetworkPacket.
     *
     * @param data the NetworkPacket representing the open path packet.
     */
    public OpenPathPacket(final NetworkPacket data) {
        super(data.toByteArray());
    }

    /**
     * Setter for the path in the Open Path packet. A path is a list of
     * NodeAddress objects. Each node receiving this method will learn two
     * rules. One to reach the first node in the path and one to reach the last
     * one in the path.
     *
     * @param path a list containing all the node in a path.
     * @return the packet itself
     */
    public final OpenPathPacket setPath(final List<NodeAddress> path) {
        int i = (getPayloadAt(WINDOWS_SIZE_INDEX) * Window.SIZE) + 1;
        for (NodeAddress addr : path) {
            setPayloadAt(addr.getHigh(), i);
            i++;
            setPayloadAt(addr.getLow(), i);
            i++;
        }
        return this;
    }

    /**
     * Getter for the path in the Open Path packet. A path is a list of
     * NodeAddress objects. Each node receiving this method will learn two
     * rules. One to reach the first node in the path and one to reach the last
     * one in the path.
     *
     * @return the list of NodeAddress in the path.
     */
    public final List<NodeAddress> getPath() {
        LinkedList<NodeAddress> list = new LinkedList<>();
        byte[] payload = getPayload();
        int p = (getPayloadAt(WINDOWS_SIZE_INDEX) * Window.SIZE) + 1;
        for (int i = p; i < payload.length - 1; i += 2) {
            list.add(new NodeAddress(payload[i], payload[i + 1]));
        }
        return list;
    }

    /**
     * Adds a list of windows to filter flows in the path.
     *
     * @param conditions optinal windows
     * @return the packet itself
     */
    public final OpenPathPacket setWindows(final List<Window> conditions) {
        List<NodeAddress> tmp = getPath();

        setPayloadAt((byte) conditions.size(), WINDOWS_SIZE_INDEX);
        int i = WINDOWS_SIZE_INDEX + 1;

        for (Window w : conditions) {
            byte[] win = w.toByteArray();
            setPayload(win, 0, i, win.length);
            i = i + win.length;
        }
        setPath(tmp);
        return this;
    }

    /**
     * Gets the list of optional windows.
     *
     * @return the list of windows
     */
    public final List<Window> getWindows() {
        LinkedList<Window> w = new LinkedList<>();

        int nWindows = getPayloadAt(WINDOWS_SIZE_INDEX);
        int j = 0;
        for (int i = 0; i < nWindows; i++) {
            Window win = new Window(getPayloadFromTo(
                    WINDOWS_SIZE_INDEX + 1 + j,
                    WINDOWS_SIZE_INDEX + 1 + Window.SIZE + j));
            w.add(win);
            j = j + Window.SIZE;
        }
        return w;
    }
}
 public class ReportPacket extends BeaconPacket {

    /**
     * The maximum number of neighbors allowed in a single packet is 35.
     */
    private static final byte MAX_NEIG = 35,
            NEIGH_INDEX = 2,
            NEIGH_SIZE = 3;

    /**
     * This constructor initialize a report packet starting from a byte array.
     *
     * @param data the byte array representing the report packet.
     */
    public ReportPacket(final byte[] data) {
        super(data);
    }

    /**
     * This constructor initialize a report packet. The type of the packet is
     * set to SDN_WISE_REPORT.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param dst destination address of the packet
     * @param distance the distance in hops between the source node and the sink
     * @param battery the battery level of the source node
     */
    public ReportPacket(final int net, final NodeAddress src,
            final NodeAddress dst,
            final int distance,
            final int battery) {
        super(net, src, dst, distance, battery);
        setDst(dst);
        setTyp(REPORT);
    }

    /**
     * This constructor initialize a report packet starting from a int array.
     *
     * @param data the int array representing the report packet, all int are
     * casted to byte.
     */
    public ReportPacket(final int[] data) {
        super(data);
    }

    /**
     * This constructor initialize a report packet starting from a
     * NetworkPacket.
     *
     * @param data the NetworkPacket representing the report packet.
     */
    public ReportPacket(final NetworkPacket data) {
        super(data.toByteArray());
    }

    /**
     * Getter for the number of neighbors of the source node.
     *
     * @return the number of neighbors.
     */
    public final int getNeigborsSize() {
        return Byte.toUnsignedInt(getPayloadAt(NEIGH_INDEX));
    }

    /**
     * Setter for the number of neighbors of the source node.
     *
     * @param value the number of neighbors.
     * @return the packet itself
     */
    public final ReportPacket setNeighbors(final int value) {
        if (value <= MAX_NEIG) {
            setPayloadAt((byte) value, NEIGH_INDEX);
            setPayloadSize((byte) (NEIGH_SIZE + value * NEIGH_SIZE));
        } else {
            throw new IllegalArgumentException("Too many neighbors");
        }
        return this;
    }

    /**
     * Getter for the NodeAddress of the i-th node in the neighbor list.
     *
     * @param i the i-th node in the neighbors list
     * @return the NodeAddress of the i-th node in the neighbors list
     */
    public final NodeAddress getNeighborAddress(final int i) {
        if (i <= MAX_NEIG) {
            return new NodeAddress(
                    getPayloadAt(NEIGH_INDEX + 1 + (i * NEIGH_SIZE)),
                    getPayloadAt(NEIGH_INDEX + 2 + (i * NEIGH_SIZE)));
        } else {
            throw new IllegalArgumentException(
                    "Index exceeds max number of neighbors");
        }
    }

    /**
     * Setter for the NodeAddress of the i-th node in the neighbor list.
     *
     * @param addr the address of the i-th NodeAddress.
     * @param i the position where the NodeAddress will be inserted.
     * @return the packet itself
     */
    public final ReportPacket setNeighborAddressAt(final NodeAddress addr,
            final int i) {
        if (i <= MAX_NEIG) {
            setPayloadAt(addr.getHigh(), (NEIGH_INDEX + 1 + (i * NEIGH_SIZE)));
            setPayloadAt(addr.getLow(), (NEIGH_INDEX + 2 + (i * NEIGH_SIZE)));
            if (getNeigborsSize() < i) {
                setNeighbors(i);
            }
            return this;
        } else {
            throw new IllegalArgumentException(
                    "Index exceeds max number of neighbors");
        }
    }

    /**
     * Getter for the rssi value between the i-th node in the neighbor list and
     * the source node.
     *
     * @param i the i-th node in the neighbors list
     * @return the rssi value
     */
    public final int getLinkQuality(final int i) {
        if (i <= MAX_NEIG) {
            return getPayloadAt(NEIGH_INDEX + ((i + 1) * NEIGH_SIZE));
        } else {
            throw new IllegalArgumentException(
                    "Index exceeds max number of neighbors");
        }
    }

    /**
     * Setter for the rssi value between the i-th node in the neighbor list and
     * the source node.
     *
     * @param i the i-th node in the neighbors list.
     * @param value the weight of the link.
     * @return the packet itself
     */
    public final ReportPacket setLinkQualityAt(final byte value, final int i) {
        if (i <= MAX_NEIG) {
            setPayloadAt(value, NEIGH_INDEX + ((i + 1) * NEIGH_SIZE));
            if (getNeigborsSize() < i) {
                setNeighbors(i);
            }
            return this;
        } else {
            throw new IllegalArgumentException(
                    "Index exceeds max number of neighbors");
        }
    }

    /**
     * Gets the list of Neighbors.
     *
     * @return an HashMap filled with the neighbors and their weights.
     */
    public final HashMap<NodeAddress, Byte> getNeighbors() {
        HashMap<NodeAddress, Byte> map = new HashMap<>();
        int nNeig = getNeigborsSize();
        for (int i = 0; i < nNeig; i++) {
            map.put(getNeighborAddress(i),
                    (byte) getLinkQuality(i));
        }
        return map;
    }

    /**
     * Sets the list of Neighbors.
     *
     * @param map the map of neighbors to be set
     * @return the packet itself
     */
    public final ReportPacket setNeighbors(
            final HashMap<NodeAddress, Byte> map) {
        int i = 0;
        for (Map.Entry<NodeAddress, Byte> entry : map.entrySet()) {
            setNeighborAddressAt(entry.getKey(), i);
            setLinkQualityAt(entry.getValue(), i);
            i++;
        }
        setNeighbors((byte) map.size());
        return this;
    }

}
 public class RequestPacket extends NetworkPacket {

    /**
     * Indexes and lengths of the fields.
     */
    private static final byte ID_INDEX = 0, PART_INDEX = 1,
            REQUEST_HEADER_SIZE = 3,
            REQUEST_PAYLOAD_SIZE = NetworkPacket.MAX_PACKET_LENGTH
            - (DFLT_HDR_LEN + REQUEST_HEADER_SIZE), TOTAL_INDEX = 2;

    /**
     * Creates an array of request packets from a byte array.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param dst destination address of the packet
     * @param id the identificator of the request
     * @param buf the original packet
     * @return an array of Request packets
     */
    public static RequestPacket[] createPackets(
            final int net,
            final NodeAddress src,
            final NodeAddress dst,
            final byte id,
            final byte[] buf) {

        int i;
        if (buf.length > REQUEST_PAYLOAD_SIZE) {
            i = 2;
        } else {
            i = 1;
        }

        int remaining = buf.length % REQUEST_PAYLOAD_SIZE;
        RequestPacket[] ll = new RequestPacket[i];

        byte[] payload;

        if (i == 1) {
            payload = new byte[remaining];
        } else {
            payload = new byte[REQUEST_PAYLOAD_SIZE];
        }

        System.arraycopy(buf, 0, payload, 0, payload.length);
        RequestPacket np = new RequestPacket(net, src, dst, id, 0, i, payload);
        ll[0] = np;

        if (i > 1) {
            payload = new byte[remaining];
            System.arraycopy(buf, REQUEST_PAYLOAD_SIZE, payload, 0, remaining);
            np = new RequestPacket(net, src, dst, id, 1, i, payload);
            ll[1] = np;
        }

        return ll;
    }

    /**
     * Merges two Request packet to obtain the original packet.
     *
     * @param rp0 the first request packet
     * @param rp1 the second request packet
     * @return the NetworkPacket contained in the two Request packets
     */
    public static NetworkPacket mergePackets(final RequestPacket rp0,
            final RequestPacket rp1) {
        if (rp0.getPart() == 0) {
            return new NetworkPacket(
                    concatByteArray(rp0.getData(), rp1.getData()));
        } else {
            return new NetworkPacket(
                    concatByteArray(rp1.getData(), rp0.getData()));
        }
    }

    /**
     * This constructor initialize a Request packet starting from a byte array.
     *
     * @param data the byte array representing the data packet.
     */
    public RequestPacket(final byte[] data) {
        super(data);
    }

    /**
     * This constructor initialize a Request packet starting from a
     * NetworkPacket.
     *
     * @param data the NetworkPacket representing the data packet.
     */
    public RequestPacket(final NetworkPacket data) {
        super(data.toByteArray());
    }

    /**
     * This constructor initialize a data packet starting from a int array.
     *
     * @param data the int array representing the data packet, all ints are
     * casted to byte.
     */
    public RequestPacket(final int[] data) {
        super(data);
    }

    /**
     * Construct a Request packet from its fields. It is used only inside this
     * class.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param dst destination address of the packet
     * @param id identificator of the request
     * @param part identificator of the part of the request
     * @param total the total number of parts
     * @param data the data payload of the request
     */
    private RequestPacket(final int net,
            final NodeAddress src,
            final NodeAddress dst,
            final int id,
            final int part,
            final int total,
            final byte[] data) {
        super(net, src, dst);
        setTyp(REQUEST);
        setId(id).setTotal(total).setPart(part).setData(data);
    }

    /**
     * Gets the data payload of the request.
     *
     * @return a byte array representing the data payload of a single packet
     */
    public final byte[] getData() {
        return getPayloadFromTo(TOTAL_INDEX + 1, getPayloadSize());
    }

    /**
     * Gets the size of the data payload of the request.
     *
     * @return data payload size in bytes
     */
    public final int getDataSize() {
        return getPayloadSize() - (TOTAL_INDEX + 1);
    }

    /**
     * Gets the ID of the request. The request ID is used to identify requests
     * coming from the same node.
     *
     * @return the id of the request
     */
    public final int getId() {
        return getPayloadAt(ID_INDEX);
    }

    /**
     * Gets the part number of the Request packet. A NetworkPacket cannot be
     * longer than NetworkPacket.MAX_PACKET_LENGTH, so it can be incapsulated in
     * maximum two packets. Therefore this value is 0 or 1.
     *
     * @return the part number
     */
    public final int getPart() {
        return getPayloadAt(PART_INDEX);
    }

    /**
     * Gets the Total expected number of parts. A NetworkPacket cannot be longer
     * than NetworkPacket.MAX_PACKET_LENGTH, so it can be incapsulated in
     * maximum two packets. Therefore this value is 1 or 2.
     *
     * @return the total number of parts
     */
    public final int getTotal() {
        return getPayloadAt(TOTAL_INDEX);
    }

    /**
     * Sets the data payload of the request.
     *
     * @param data the data payload
     * @return the packet itself
     */
    private RequestPacket setData(final byte[] data) {
        setPayload(data, 0, TOTAL_INDEX + 1, data.length);
        return this;
    }

    /**
     * Sets the ID of the request. The request ID is used to identify requests
     * coming from the same node.
     *
     * @param id the id to be set
     * @return the packet itself
     */
    private RequestPacket setId(final int id) {
        setPayloadAt((byte) id, ID_INDEX);
        return this;
    }

    /**
     * Sets the part number of the Request packet. A NetworkPacket cannot be
     * longer than NetworkPacket.MAX_PACKET_LENGTH, so it can be incapsulated in
     * maximum two packets. Therefore this value is 0 or 1.
     *
     * @param part the value of part
     * @return the packet itself
     */
    private RequestPacket setPart(final int part) {
        setPayloadAt((byte) part, PART_INDEX);
        return this;
    }

    /**
     * Sets the Total expected number of parts. A NetworkPacket cannot be longer
     * than NetworkPacket.MAX_PACKET_LENGTH, so it can be incapsulated in
     * maximum two packets. Therefore this value is 1 or 2.
     *
     * @return the total number of parts
     * @param total the expected number of parts
     */
    private RequestPacket setTotal(final int total) {
        setPayloadAt((byte) total, TOTAL_INDEX);
        return this;
    }

} public class ResponsePacket extends NetworkPacket {

    /**
     * This constructor initialize a response packet starting from a byte array.
     *
     * @param data the byte array representing the response packet.
     */
    public ResponsePacket(final byte[] data) {
        super(data);
    }

    /**
     * This constructor initialize a response packet starting from a
     * NetworkPacket.
     *
     * @param data the NetworkPacket representing the response packet.
     */
    public ResponsePacket(final NetworkPacket data) {
        super(data.toByteArray());
    }

    /**
     * This constructor initialize a response packet. The type of the packet is
     * set to SDN_WISE_RESPONSE.
     *
     * @param net Network ID of the packet
     * @param src source address of the packet
     * @param dst destination address of the packet
     * @param entry the FlowTableEntry sent from the Control Plane
     */
    public ResponsePacket(final int net, final NodeAddress src,
            final NodeAddress dst,
            final FlowTableEntry entry) {
        super(net, src, dst);
        setTyp(RESPONSE);
        setRule(entry);
    }

    /**
     * This constructor initialize a response packet starting from a int array.
     *
     * @param data the int array representing the response packet, all int are
     * casted to byte.
     */
    public ResponsePacket(final int[] data) {
        super(data);
    }

    /**
     * Setter for the rule in the response packet. sadsadas
     *
     * @param rule the FlowTableEntry item used in the NetworkPacket.
     * @return the packet itself
     */
    public final ResponsePacket setRule(final FlowTableEntry rule) {
        byte[] tmp = rule.toByteArray();
        // the last byte is for stats so it is useless to send it in a response
        setPayload(Arrays.copyOf(tmp, tmp.length - 1));
        return this;
    }

    /**
     * Getter for the rule in the response packet.
     *
     * @return the rule as a FlowTableEntry
     */
    public final FlowTableEntry getRule() {
        FlowTableEntry rule = new FlowTableEntry(getPayload());
        return rule;
    }
}
 public final class Neighbor {

    /**
     * The address of the neighbor.
     */
    private final NodeAddress addr;

    /**
     * The residual charge and rssi of the neighbor.
     */
    private final int rssi, batt;

    /**
     * The default value for residual charge and rssi.
     */
    private static final int DEFAULT = 0xFF;

    /**
     * Constructs a Neighbor given its attributes.
     *
     * @param a NodeAddress Object.
     * @param r r of the NodeAddress.
     * @param b battery value of the NodeAddress.
     */
    public Neighbor(final NodeAddress a, final int r, final int b) {
        addr = a;
        rssi = r;
        batt = b;
    }

    /**
     * Constructs a Neighbor object.
     */
    public Neighbor() {
        addr = NodeAddress.BROADCAST_ADDR;
        rssi = DEFAULT;
        batt = DEFAULT;
    }

    /**
     * Getter method to obtain NodeAddress object.
     *
     * @return a NodeAddress.
     */
    public NodeAddress getAddr() {
        return addr;
    }

    /**
     * Getter method to obtain rssi of a NodeAddress object.
     *
     * @return int value rssi of the NodeAddress.
     */
    public int getRssi() {
        return rssi;
    }

    /**
     * Getter method to obtain battery value of a NodeAddress object.
     *
     * @return int value battery of the NodeAddress.
     */
    public int getBatt() {
        return batt;
    }
}
 public final class NodeAddress implements Comparable<NodeAddress>, Serializable {

    /**
     * The default broadcast address.
     */
    public static final NodeAddress BROADCAST_ADDR = new NodeAddress("255.255");

    /**
     * The serialVersionUID as specified in the Serializable interface.
     */
    private static final long serialVersionUID = 1L;

    /**
     * A byte array containing the address. A NodeAddress is two bytes long.
     */
    private final byte[] addr = new byte[2];

    /**
     * Constructor method to create a Node Address from an int.
     *
     * @param a int value to set a Node Address.
     */
    public NodeAddress(final int a) {
        addr[0] = (byte) (a >>> Byte.SIZE);
        addr[1] = (byte) a;
    }

    /**
     *
     * Constructor method to create a Node Address from a byte array.
     *
     * @param a byte array value to set a Node Address.
     */
    public NodeAddress(final byte[] a) {
        if (a.length == 2) {
            addr[0] = a[0];
            addr[1] = a[1];
        }
    }

    /**
     * Constructor method to create a Node Address from a string.
     *
     * @param a string value to set a Node Address.
     */
    public NodeAddress(final String a) {
        String[] add = a.split("\\s*\\.\\s*");
        if (add.length == 2) {
            addr[0] = (byte) Integer.parseInt(add[0]);
            addr[1] = (byte) Integer.parseInt(add[1]);
        } else {
            int adr = Integer.parseInt(a);
            addr[0] = (byte) (adr >>> Byte.SIZE);
            addr[1] = (byte) adr;
        }
    }

    /**
     * Constructor method to create a Node Address from two int.
     *
     * @param addr0 int value to set fist part of Node Address.
     * @param addr1 int value to set second part of a Node Address.
     */
    public NodeAddress(final int addr0, final int addr1) {
        addr[0] = (byte) addr0;
        addr[1] = (byte) addr1;
    }

    @Override
    public int compareTo(final NodeAddress other) {
        return Integer.valueOf(intValue()).compareTo(other.intValue());
    }

    @Override
    public boolean equals(final Object obj) {
        return obj instanceof NodeAddress
                && ((NodeAddress) obj).intValue() == intValue();
    }

    /**
     * Gets the Node Address in Byte.
     *
     * @return a byte array of Node Address.
     */
    public byte[] getArray() {
        return new byte[]{addr[0], addr[1]};
    }

    /**
     * Gets the first Byte of a NodeAddress.
     *
     * @return a byte value of High Part of a NodeAddress.
     */
    public byte getHigh() {
        return addr[0];
    }

    /**
     * Gets the last Byte of a NodeAddress.
     *
     * @return a byte value of Low Part of a NodeAddress.
     */
    public byte getLow() {
        return addr[1];
    }

    @Override
    public int hashCode() {
        return Integer.valueOf(intValue()).hashCode();
    }

    /**
     * Returns the NodeAddress as an integer.
     *
     * @return int value of the NodeAddress.
     */
    public int intValue() {
        return mergeBytes(addr[0], addr[1]);
    }

    /**
     * Checks if the address is a broadcast address.
     *
     * @return true if equal to 255.255 false otherwise
     */
    public boolean isBroadcast() {
        return equals(BROADCAST_ADDR);
    }

    /**
     * Gets the Node Address in Byte.
     *
     * @return a byte array of Node Address.
     */
    public Byte[] toByteArray() {
        return new Byte[]{addr[0], addr[1]};
    }

    @Override
    public String toString() {
        return Byte.toUnsignedInt(addr[0]) + "." + Byte.toUnsignedInt(addr[1]);
    }
}
 public static int mergeBytes(final int high, final int low) {
    int h = Byte.toUnsignedInt((byte) high);
    int l = Byte.toUnsignedInt((byte) low);
    return (h << Byte.SIZE) | l;
} public static byte[] splitInteger(final int value) {
    ByteBuffer b = ByteBuffer.allocate(2);
    b.putShort((short) value);
    return b.array();
}
import com.github.sdnwiselab.sdnwise.flowtable.AbstractAction;
import static com.github.sdnwiselab.sdnwise.flowtable.AbstractAction.Action.FORWARD_U;
import com.github.sdnwiselab.sdnwise.flowtable.AbstractForwardAction;
import com.github.sdnwiselab.sdnwise.flowtable.FlowTableEntry;
import static com.github.sdnwiselab.sdnwise.flowtable.FlowTableInterface.CONST;
import static com.github.sdnwiselab.sdnwise.flowtable.FlowTableInterface.NULL;
import static com.github.sdnwiselab.sdnwise.flowtable.FlowTableInterface.PACKET;
import static com.github.sdnwiselab.sdnwise.flowtable.FlowTableInterface.STATUS;
import com.github.sdnwiselab.sdnwise.flowtable.ForwardUnicastAction;
import com.github.sdnwiselab.sdnwise.flowtable.FunctionAction;
import com.github.sdnwiselab.sdnwise.flowtable.SetAction;
import static com.github.sdnwiselab.sdnwise.flowtable.SetAction.ADD;
import static com.github.sdnwiselab.sdnwise.flowtable.SetAction.AND;
import static com.github.sdnwiselab.sdnwise.flowtable.SetAction.DIV;
import static com.github.sdnwiselab.sdnwise.flowtable.SetAction.MOD;
import static com.github.sdnwiselab.sdnwise.flowtable.SetAction.MUL;
import static com.github.sdnwiselab.sdnwise.flowtable.SetAction.OR;
import static com.github.sdnwiselab.sdnwise.flowtable.SetAction.SUB;
import static com.github.sdnwiselab.sdnwise.flowtable.SetAction.XOR;
import com.github.sdnwiselab.sdnwise.flowtable.Stats;
import static com.github.sdnwiselab.sdnwise.flowtable.Stats.ENTRY_TTL_PERMANENT;
import com.github.sdnwiselab.sdnwise.flowtable.Window;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.EQUAL;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.NOT_EQUAL;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.GREATER;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.GREATER_OR_EQUAL;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.LESS;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.LESS_OR_EQUAL;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.W_SIZE_1;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.W_SIZE_2;
import static com.github.sdnwiselab.sdnwise.flowtable.Window.fromString;
import com.github.sdnwiselab.sdnwise.function.FunctionInterface;
import com.github.sdnwiselab.sdnwise.mote.battery.Dischargeable;
import static com.github.sdnwiselab.sdnwise.mote.core.Constants.ENTRY_TTL_DECR;
import static com.github.sdnwiselab.sdnwise.mote.core.Constants.SDN_WISE_DFLT_CNT_BEACON_MAX;
import static com.github.sdnwiselab.sdnwise.mote.core.Constants.SDN_WISE_DFLT_CNT_REPORT_MAX;
import static com.github.sdnwiselab.sdnwise.mote.core.Constants.SDN_WISE_DFLT_CNT_UPDTABLE_MAX;
import static com.github.sdnwiselab.sdnwise.mote.core.Constants.SDN_WISE_DFLT_RSSI_MIN;
import static com.github.sdnwiselab.sdnwise.mote.core.Constants.SDN_WISE_STATUS_LEN;
import com.github.sdnwiselab.sdnwise.packet.BeaconPacket;
import com.github.sdnwiselab.sdnwise.packet.ConfigPacket;
import com.github.sdnwiselab.sdnwise.packet.ConfigPacket.ConfigProperty;
import com.github.sdnwiselab.sdnwise.packet.DataPacket;
import com.github.sdnwiselab.sdnwise.packet.NetworkPacket;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.BEACON;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.CONFIG;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.DATA;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.DFLT_HDR_LEN;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.DFLT_TTL_MAX;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.DST_INDEX;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.OPEN_PATH;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.REPORT;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.REQUEST;
import static com.github.sdnwiselab.sdnwise.packet.NetworkPacket.RESPONSE;
import com.github.sdnwiselab.sdnwise.packet.OpenPathPacket;
import com.github.sdnwiselab.sdnwise.packet.ReportPacket;
import com.github.sdnwiselab.sdnwise.packet.RequestPacket;
import com.github.sdnwiselab.sdnwise.packet.ResponsePacket;
import com.github.sdnwiselab.sdnwise.util.Neighbor;
import com.github.sdnwiselab.sdnwise.util.NodeAddress;
import static com.github.sdnwiselab.sdnwise.util.Utils.mergeBytes;
import static com.github.sdnwiselab.sdnwise.util.Utils.splitInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
     */
    private int searchRule(final FlowTableEntry rule) {
        int i = 0;
        for (FlowTableEntry fte : flowTable) {
            if (fte.equalWindows(rule)) {
                return i;
            }
            i++;
        }
        return -1;
    }

    /**
     * Decrement the time to live of the FlowTableEntries.
     */
    private void updateTable() {
        int i = 0;
        for (Iterator<FlowTableEntry> it = flowTable.iterator();
                it.hasNext();) {
            i++;
            FlowTableEntry fte = it.next();
            int ttl = fte.getStats().getTtl();
            if (ttl != ENTRY_TTL_PERMANENT) {
                if (ttl >= ENTRY_TTL_DECR) {
                    fte.getStats().decrementTtl(ENTRY_TTL_DECR);
                } else {
                    it.remove();
                    log(Level.INFO, "Removing rule at position " + i);
                    if (i == 0) {
                        reset();
                    }
                }
            }
        }
    }

    /**
     * Sends a NetworkPacket to the controller.
     *
     * @param pck the packet to be sent
     */
    protected abstract void controllerTX(NetworkPacket pck);

    /**
     * The behavior of the node when it receives a DataPacket.
     *
     * @param packet a DataPacket
     */
    protected abstract void dataCallback(DataPacket packet);

    /**
     * Turns on the node. A mote is turned on after it receives it first beacon.
     * a Sink is always turned on.
     *
     * @param active true to activate the node, false to disactivate it
     */
    protected final void setActive(final boolean active) {
        this.isActive = active;
    }

    /**
     * Gets the NodeAddress of the actual Sink for the node.
     *
     * @return the NodeAddress of the Sink
     */
    protected abstract NodeAddress getActualSinkAddress();

    /**
     * Gets the NodeAddress of the next best hop toward the actual Sink.
     *
     * @return the NodeAddress of the next best hop
     */
    protected final NodeAddress getNextHopVsSink() {
        return ((AbstractForwardAction) (flowTable.get(0).getActions().get(0)))
                .getNextHop();
    }

    /**
     * Gets the distance from the Sink in number of hops.
     *
     * @return the distance from the Sink
     */
    protected final int getSinkDistance() {
        return sinkDistance;
    }

    /**
     * Set the distance from the Sink in terms of number of hops.
     *
     * @param distance the distance from the Sink
     */
    protected final void setSinkDistance(final int distance) {
        this.sinkDistance = distance;
    }

    /**
     * Gets the RSSI of the next best hop towards the Sink.
     *
     * @return the RSSI of the next best hop towards the Sink
     */
    protected final int getSinkRssi() {
        return sinkRssi;
    }

    /**
     * Sets the RSSI of the next best hop towards the Sink.
     *
     * @param rssi the RSSI of the next best hop towards the Sink
     */
    protected final void setSinkRssi(final int rssi) {
        this.sinkRssi = rssi;
    }

    /**
     * Sets the default values to the configuration parameters of the node.
     */
    protected final void initSdnWise() {
        cntBeaconMax = SDN_WISE_DFLT_CNT_BEACON_MAX;
        cntReportMax = SDN_WISE_DFLT_CNT_REPORT_MAX;
        cntUpdtableMax = SDN_WISE_DFLT_CNT_UPDTABLE_MAX;
        rssiMin = SDN_WISE_DFLT_RSSI_MIN;