Add Optional match fields support to PI subsystem

Change-Id: Ic458f59cab98340e40c04a0ad060d3c725ac5dbb
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java b/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
index 4b64b0c..a6b213b 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/CriterionTranslatorHelper.java
@@ -85,6 +85,7 @@
 import org.onosproject.net.pi.runtime.PiExactFieldMatch;
 import org.onosproject.net.pi.runtime.PiFieldMatch;
 import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
+import org.onosproject.net.pi.runtime.PiOptionalFieldMatch;
 import org.onosproject.net.pi.runtime.PiTernaryFieldMatch;
 import org.onosproject.net.pi.service.PiTranslationException;
 
@@ -163,6 +164,8 @@
             switch (matchType) {
                 case EXACT:
                     return new PiExactFieldMatch(fieldId, translator.exactMatch());
+                case OPTIONAL:
+                    return new PiOptionalFieldMatch(fieldId, translator.exactMatch());
                 case TERNARY:
                     final Pair<ImmutableByteSequence, ImmutableByteSequence> tp = translator.ternaryMatch();
                     return new PiTernaryFieldMatch(fieldId, tp.getLeft(), tp.getRight());
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
index 8350549..f399508 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiFlowRuleTranslatorImpl.java
@@ -44,6 +44,7 @@
 import org.onosproject.net.pi.runtime.PiFieldMatch;
 import org.onosproject.net.pi.runtime.PiLpmFieldMatch;
 import org.onosproject.net.pi.runtime.PiMatchKey;
+import org.onosproject.net.pi.runtime.PiOptionalFieldMatch;
 import org.onosproject.net.pi.runtime.PiRangeFieldMatch;
 import org.onosproject.net.pi.runtime.PiTableAction;
 import org.onosproject.net.pi.runtime.PiTableEntry;
@@ -114,7 +115,8 @@
             // Need to ignore priority if no TCAM lookup match field
             needPriority = tableModel.matchFields().stream()
                     .anyMatch(match -> match.matchType() == PiMatchType.TERNARY ||
-                            match.matchType() == PiMatchType.RANGE);
+                            match.matchType() == PiMatchType.RANGE ||
+                            match.matchType() == PiMatchType.OPTIONAL);
         }
         // Translate treatment.
         final PiTableAction piTableAction = translateTreatment(rule.treatment(), interpreter, piTableId, pipelineModel);
@@ -362,6 +364,7 @@
                     case TERNARY:
                     case LPM:
                     case RANGE:
+                    case OPTIONAL:
                         // Skip field.
                         break;
                     default:
@@ -447,6 +450,15 @@
                     fieldMatch.fieldId(), fieldModel.matchType().name(), fieldMatch.type().name()));
         }
 
+        // Check if the arbitrary bit width is supported
+        if (!fieldModel.hasBitWidth() &&
+                !fieldModel.matchType().equals(PiMatchType.EXACT) &&
+                !fieldModel.matchType().equals(PiMatchType.OPTIONAL)) {
+            throw new PiTranslationException(format(
+                    "Arbitrary bit width for field '%s' and match type %s is not supported",
+                    fieldMatch.fieldId(), fieldModel.matchType().name()));
+        }
+
         int modelBitWidth = fieldModel.bitWidth();
 
         /*
@@ -461,7 +473,6 @@
         try {
             switch (fieldModel.matchType()) {
                 case EXACT:
-                    // TODO: arbitrary bit width is supported only for the EXACT match case.
                     PiExactFieldMatch exactField = (PiExactFieldMatch) fieldMatch;
                     return new PiExactFieldMatch(fieldMatch.fieldId(),
                                                  fieldModel.hasBitWidth() ?
@@ -492,6 +503,12 @@
                     return new PiRangeFieldMatch(fieldMatch.fieldId(),
                                                  ((PiRangeFieldMatch) fieldMatch).lowValue().fit(modelBitWidth),
                                                  ((PiRangeFieldMatch) fieldMatch).highValue().fit(modelBitWidth));
+                case OPTIONAL:
+                    PiOptionalFieldMatch optionalField = (PiOptionalFieldMatch) fieldMatch;
+                    return new PiOptionalFieldMatch(fieldMatch.fieldId(),
+                                                    fieldModel.hasBitWidth() ?
+                                                            optionalField.value().fit(modelBitWidth) :
+                                                            optionalField.value());
                 default:
                     // Should never be here.
                     throw new IllegalArgumentException(