ONOS-3503 Remove OchPort out of core.

- Implementation of a Behavior OpticalDevice has the knowledge of
  translating annotations into optical specific port.
- OpticalDeviceServiceView checks if the Device is a OpticalDevice
  and translate all the Ports to optical specific port before returning.

- This commit contains feedbacks, issues, and fixes by Michele Santuari.

- Note: 3 more Port types to go (OduClt, Oms, Otu)

Change-Id: I4cbda8bc1922fbdd4dac8de8d02294bad74b8058
diff --git a/core/api/src/main/java/org/onosproject/net/optical/device/DefaultOpticalDevice.java b/core/api/src/main/java/org/onosproject/net/optical/device/DefaultOpticalDevice.java
new file mode 100644
index 0000000..5a30cf7
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/optical/device/DefaultOpticalDevice.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016 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.net.optical.device;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Map;
+import java.util.Optional;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.AbstractBehaviour;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.optical.OchPort;
+import org.onosproject.net.optical.OpticalDevice;
+import org.onosproject.net.optical.device.port.OchPortMapper;
+import org.onosproject.net.optical.device.port.PortMapper;
+import org.onosproject.net.optical.utils.ForwardingDevice;
+import org.slf4j.Logger;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
+
+// FIXME This needs to be moved back to org.onosproject.net.optical.impl
+// after optical driver package separation process is complete.
+/**
+ * Implementation of {@link OpticalDevice}.
+ * <p>
+ * Currently supports
+ * <ul>
+ *  <li> {@link OchPort}
+ * </ul>
+ */
+@Beta
+public class DefaultOpticalDevice
+        extends AbstractBehaviour
+        implements OpticalDevice, ForwardingDevice {
+
+    private static final Logger log = getLogger(DefaultOpticalDevice.class);
+
+    // shared Port type handler map.
+    // TODO Is there a use case, where we need to differentiate this map per Device?
+    private static final Map<Class<? extends Port>, PortMapper<? extends Port>> MAPPERS
+        = ImmutableMap.<Class<? extends Port>, PortMapper<? extends Port>>builder()
+            .put(OchPort.class, new OchPortMapper())
+            // TODO add other optical port type here
+            .build();
+
+
+
+    // effectively final
+    private Device delegate;
+
+    // Default constructor required as a Behaviour.
+    public DefaultOpticalDevice() {}
+
+    @Override
+    public Device delegate() {
+        if (delegate == null) {
+            // dirty work around.
+            // wanted to pass delegate Device at construction,
+            // but was not possible. A Behaviour requires no-arg constructor.
+            checkState(data() != null, "DriverData must exist");
+            DriverData data = data();
+            DeviceService service = DefaultServiceDirectory.getService(DeviceService.class);
+            delegate = checkNotNull(service.getDevice(data.deviceId()),
+                                    "No Device found for %s", data.deviceId());
+        }
+        return delegate;
+    }
+
+    @Override
+    public <T extends Port> boolean portIs(Port port, Class<T> portClass) {
+
+        PortMapper<? extends Port> mapper = MAPPERS.get(portClass);
+        if (mapper != null) {
+            return mapper.is(port);
+        }
+        return false;
+    }
+
+    @Override
+    public <T extends Port> Optional<T> portAs(Port port, Class<T> portClass) {
+        PortMapper<? extends Port> mapper = MAPPERS.get(portClass);
+        if (mapper != null) {
+            return (Optional<T>) (mapper.as(port));
+        }
+        return Optional.empty();
+    }
+
+    @Override
+    public Port port(Port port) {
+        for (PortMapper<? extends Port> mapper : MAPPERS.values()) {
+            if (mapper.is(port)) {
+                return mapper.as(port).map(Port.class::cast).orElse(port);
+            }
+        }
+        return port;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return delegate().equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return delegate().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("delegate", delegate)
+                .toString();
+    }
+}