OpenFlow Meter Provider - still need to parse meter stats

Change-Id: I4b313ea003b3801f81ec7d4823bf69de37fd8643
diff --git a/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java b/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java
new file mode 100644
index 0000000..a9a3889
--- /dev/null
+++ b/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.provider.of.meter.impl;
+
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterId;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFMeterFlags;
+import org.projectfloodlight.openflow.protocol.OFMeterMod;
+import org.projectfloodlight.openflow.protocol.OFMeterModCommand;
+import org.projectfloodlight.openflow.protocol.meterband.OFMeterBand;
+import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDrop;
+import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDscpRemark;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Builder for a meter modification.
+ */
+public final class MeterModBuilder {
+
+    private final Logger log = getLogger(getClass());
+
+    private final long xid;
+    private final OFFactory factory;
+    private Meter.Unit unit = Meter.Unit.KB_PER_SEC;
+    private boolean burst = false;
+    private Integer id;
+    private Collection<Band> bands;
+
+    public MeterModBuilder(long xid, OFFactory factory) {
+        this.xid = xid;
+        this.factory = factory;
+    }
+
+    public static MeterModBuilder builder(long xid, OFFactory factory) {
+        return new MeterModBuilder(xid, factory);
+    }
+
+    public MeterModBuilder withRateUnit(Meter.Unit unit) {
+        this.unit = unit;
+        return this;
+    }
+
+    public MeterModBuilder burst() {
+        this.burst = true;
+        return this;
+    }
+
+    public MeterModBuilder withId(MeterId meterId) {
+        this.id = meterId.id();
+        return this;
+    }
+
+    public MeterModBuilder withBands(Collection<Band> bands) {
+        this.bands = bands;
+        return this;
+    }
+
+    public OFMeterMod add() {
+        validate();
+        OFMeterMod.Builder builder = builderMeterMod();
+        builder.setCommand(OFMeterModCommand.ADD.ordinal());
+        return builder.build();
+    }
+
+    public OFMeterMod remove() {
+        validate();
+        OFMeterMod.Builder builder = builderMeterMod();
+        builder.setCommand(OFMeterModCommand.DELETE.ordinal());
+        return builder.build();
+    }
+
+    public OFMeterMod modify() {
+        validate();
+        OFMeterMod.Builder builder = builderMeterMod();
+        builder.setCommand(OFMeterModCommand.MODIFY.ordinal());
+        return builder.build();
+    }
+
+    private OFMeterMod.Builder builderMeterMod() {
+        OFMeterMod.Builder builder = factory.buildMeterMod();
+        int flags = 0;
+        if (burst) {
+            // covering loxi short comings.
+            flags |= 1 << OFMeterFlags.BURST.ordinal();
+        }
+        switch (unit) {
+            case PKTS_PER_SEC:
+                flags |= 1 << OFMeterFlags.PKTPS.ordinal();
+                break;
+            case KB_PER_SEC:
+                flags |= 1 << OFMeterFlags.KBPS.ordinal();
+                break;
+            default:
+                log.warn("Unknown unit type {}", unit);
+        }
+        builder.setBands(buildBands());
+        builder.setFlags(flags)
+                .setMeterId(id)
+                .setXid(xid);
+        return builder;
+    }
+
+    private List<OFMeterBand> buildBands() {
+        return bands.stream().map(b -> {
+            switch (b.type()) {
+                case DROP:
+                    OFMeterBandDrop.Builder dropBuilder =
+                            factory.meterBands().buildDrop();
+                    if (burst) {
+                        dropBuilder.setBurstSize(b.burst());
+                    }
+                    dropBuilder.setRate(b.rate());
+                    return dropBuilder.build();
+                case REMARK:
+                    OFMeterBandDscpRemark.Builder remarkBand =
+                            factory.meterBands().buildDscpRemark();
+                    if (burst) {
+                        remarkBand.setBurstSize(b.burst());
+                    }
+                    remarkBand.setRate(b.rate());
+                    remarkBand.setPrecLevel(b.dropPrecedence());
+                    return remarkBand.build();
+                default:
+                    log.warn("Unknown band type {}", b.type());
+                    return null;
+            }
+        }).filter(value -> value != null).collect(Collectors.toList());
+    }
+
+    private void validate() {
+        checkNotNull(id, "id cannot be null");
+        checkNotNull(bands, "Must have bands");
+        checkArgument(bands.size() > 0, "Must have at lease one band");
+    }
+}