java_gen: add OFGroup
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index b9e3ee1..1636a9e 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -49,7 +49,7 @@
 class JavaModel(object):
     # registry for enums that should not be generated
     # set(${java_enum_name})
-    enum_blacklist = set(("OFDefinitions", "OFPortNo", "OFVlanId"))
+    enum_blacklist = set(("OFDefinitions", "OFPortNo", "OFVlanId", "OFGroup"))
     # registry for enum *entry* that should not be generated
     # map: ${java_enum_name} -> set(${java_entry_entry_name})
     enum_entry_blacklist = defaultdict(lambda: set(), OFFlowWildcards=set([ "NW_DST_BITS", "NW_SRC_BITS", "NW_SRC_SHIFT", "NW_DST_SHIFT" ]))
@@ -926,6 +926,8 @@
                 name = 'length'
             elif member.name == 'value_mask':
                 name = 'mask'
+            elif member.name == 'group_id':
+                name = 'group'
             else:
                 name = java_type.name_c_to_camel(member.name)
             j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 02db1a6..a999396 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -411,6 +411,18 @@
         .op(read='DatapathId.of(bb.readLong())',
             write='bb.writeLong($name.getLong())',
             default='DatapathId.NONE')
+action_type_set = JType("Set<OFActionType>") \
+        .op(read='ChannelUtilsVer10.readSupportedActions(bb)',
+            write='ChannelUtilsVer10.writeSupportedActions(bb, $name)',
+            default='ImmutableSet.<OFActionType>of()')
+of_group = JType("OFGroup") \
+         .op(version=ANY, read="OFGroup.read4Bytes(bb)", write="$name.write4Bytes(bb)", default="OFGroup.ALL")
+# the outgroup field of of_flow_stats_request has a special default value
+of_group_default_any = JType("OFGroup") \
+         .op(version=ANY, read="OFGroup.read4Bytes(bb)", write="$name.write4Bytes(bb)", default="OFGroup.ANY")
+buffer_id = JType("OFBufferId") \
+         .op(read="OFBufferId.of(bb.readInt())", write="bb.writeInt($name.getInt())", default="OFBufferId.NO_BUFFER")
+
 
 generic_t = JType("T")
 
@@ -510,6 +522,7 @@
         'of_bsn_set_l2_table_request': { 'l2_table_enable': boolean },
         'of_bsn_set_l2_table_reply': { 'l2_table_enable': boolean },
         'of_bsn_set_pktin_suppression_request': { 'enabled': boolean },
+        'of_flow_stats_request': { 'out_group': of_group_default_any },
 }
 
 
@@ -589,15 +602,13 @@
     elif field_name == "version" and c_type == "uint8_t":
         return of_version
     elif field_name == "buffer_id" and c_type == "uint32_t":
-        return JType("OFBufferId") \
-            .op(read="OFBufferId.of(bb.readInt())", write="bb.writeInt($name.getInt())", default="OFBufferId.NO_BUFFER")
+        return buffer_id
+    elif field_name == "group_id" and c_type == "uint32_t":
+        return of_group
     elif field_name == 'datapath_id':
         return datapath_id
     elif field_name == 'actions' and obj_name == 'of_features_reply':
-        return JType("Set<OFActionType>") \
-            .op(read='ChannelUtilsVer10.readSupportedActions(bb)',
-                write='ChannelUtilsVer10.writeSupportedActions(bb, $name)',
-                default='ImmutableSet.<OFActionType>of()')
+        return action_type_set
     elif c_type in default_mtype_to_jtype_convert_map:
         return default_mtype_to_jtype_convert_map[c_type]
     elif re.match(r'list\(of_([a-zA-Z_]+)_t\)', c_type):
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFGroup.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFGroup.java
new file mode 100644
index 0000000..a20534f
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/OFGroup.java
@@ -0,0 +1,158 @@
+package org.projectfloodlight.openflow.types;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.annotations.Immutable;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+
+import com.google.common.primitives.UnsignedInts;
+
+/**
+ * Abstraction of an logical / OpenFlow group (ofp_group) in OpenFlow.
+ * Immutable.
+ *
+ * @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
+ */
+@Immutable
+public class OFGroup implements OFValueType<OFGroup> {
+    static final int LENGTH = 4;
+
+    // private int constants (OF1.1+) to avoid duplication in the code
+    // should not have to use these outside this class
+    private static final int ZERO_VAL = 0x00;
+    private static final int MAX_VAL = 0xffffff00;
+    private static final int ALL_VAL = 0xfffffffc;
+    private static final int ANY_VAL = 0xffffffff;
+
+
+    // ////////////// public constants - use to access well known OpenFlow ports
+
+    /** Maximum number of physical and logical switch ports. */
+    public final static OFGroup MAX = new NamedGroup(MAX_VAL, "max");
+
+    /**
+     * Send the packet out the input port. This reserved port must be explicitly
+     * used in order to send back out of the input port.
+     */
+    public final static OFGroup ALL = new NamedGroup(ALL_VAL, "all");
+
+    /**
+     * Wildcard group used only for flow mod (delete) and flow stats requests.
+     * Selects all flows regardless of output port (including flows with no
+     * output port). NOTE: OpenFlow 1.0 calls this 'NONE'
+     */
+    public final static OFGroup ANY = new NamedGroup(ANY_VAL, "any");
+
+    /** group 0 in case we need it
+     */
+    public static final OFGroup ZERO = OFGroup.of(ZERO_VAL);
+
+    public static final OFGroup NO_MASK = ANY;
+    public static final OFGroup FULL_MASK = ZERO;
+
+    /** raw openflow port number as a signed 32 bit integer */
+    private final int groupNumber;
+
+    /** private constructor. use of*-Factory methods instead */
+    private OFGroup(final int portNumber) {
+        this.groupNumber = portNumber;
+    }
+
+    /**
+     * get an OFPort object corresponding to a raw 32-bit integer port number.
+     * NOTE: The port object may either be newly allocated or cached. Do not
+     * rely on either behavior.
+     *
+     * @param portNumber
+     * @return a corresponding OFPort
+     */
+    public static OFGroup of(final int groupNumber) {
+        switch(groupNumber) {
+            case ZERO_VAL:
+                return MAX;
+            case MAX_VAL:
+                return MAX;
+            case ALL_VAL:
+                return ALL;
+            case ANY_VAL:
+                return ANY;
+            default:
+                if(UnsignedInts.compare(groupNumber, MAX_VAL) > 0) {
+                    // greater than max_val, but not one of the reserved values
+                    throw new IllegalArgumentException("Unknown special group number: "
+                            + groupNumber);
+                }
+                return new OFGroup(groupNumber);
+        }
+    }
+
+    /** return the port number as a int32 */
+    public int getGroupNumber() {
+        return groupNumber;
+    }
+
+    @Override
+    public String toString() {
+        return UnsignedInts.toString(groupNumber);
+    }
+
+    /** Extension of OFPort for named groups */
+    static class NamedGroup extends OFGroup {
+        private final String name;
+
+        NamedGroup(final int portNo, final String name) {
+            super(portNo);
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+    }
+
+    @Override
+    public int getLength() {
+        return LENGTH;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof OFGroup))
+            return false;
+        OFGroup other = (OFGroup)obj;
+        if (other.groupNumber != this.groupNumber)
+            return false;
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 53;
+        int result = 1;
+        result = prime * result + groupNumber;
+        return result;
+    }
+
+    public void write4Bytes(ChannelBuffer c) {
+        c.writeInt(this.groupNumber);
+    }
+
+    public static OFGroup read4Bytes(ChannelBuffer c) throws OFParseError {
+        return OFGroup.of(c.readInt());
+    }
+
+    @Override
+    public OFGroup applyMask(OFGroup mask) {
+        return OFGroup.of(this.groupNumber & mask.groupNumber);
+    }
+
+    @Override
+    public int compareTo(OFGroup o) {
+        return UnsignedInts.compare(this.groupNumber, o.groupNumber);
+    }
+
+}