diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:15:21 -0700 |
commit | 13d05bc8458758ee39cb829098241e89616717ee (patch) | |
tree | 22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/core/api/src/main/java/org/onosproject/ui | |
parent | 6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff) |
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/core/api/src/main/java/org/onosproject/ui')
48 files changed, 4271 insertions, 0 deletions
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/JsonUtils.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/JsonUtils.java new file mode 100644 index 00000000..2ebb5545 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/JsonUtils.java @@ -0,0 +1,143 @@ +/* + * 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.ui; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Provides convenience methods for dealing with JSON nodes, arrays etc. + */ +public final class JsonUtils { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + // non-instantiable + private JsonUtils() { } + + /** + * Wraps a message payload into an event structure for the given event + * type and sequence ID. Generally, the sequence ID should be a copy of + * the ID from the client request event. + * + * @param type event type + * @param sid sequence ID + * @param payload event payload + * @return the object node representation + */ + public static ObjectNode envelope(String type, long sid, ObjectNode payload) { + ObjectNode event = MAPPER.createObjectNode(); + event.put("event", type); + if (sid > 0) { + event.put("sid", sid); + } + event.set("payload", payload); + return event; + } + + /** + * Composes a message structure for the given message type and payload. + * + * @param type message type + * @param payload message payload + * @return the object node representation + */ + public static ObjectNode envelope(String type, ObjectNode payload) { + ObjectNode event = MAPPER.createObjectNode(); + event.put("event", type); + event.set("payload", payload); + return event; + } + + /** + * Returns the event type from the specified event. + * If the node does not have an "event" property, "unknown" is returned. + * + * @param event message event + * @return extracted event type + */ + public static String eventType(ObjectNode event) { + return string(event, "event", "unknown"); + } + + /** + * Returns the sequence identifier from the specified event, or 0 (zero) + * if the "sid" property does not exist. + * + * @param event message event + * @return extracted sequence identifier + */ + public static long sid(ObjectNode event) { + return number(event, "sid"); + } + + /** + * Returns the payload from the specified event. + * + * @param event message event + * @return extracted payload object + */ + public static ObjectNode payload(ObjectNode event) { + return (ObjectNode) event.path("payload"); + } + + /** + * Returns the specified node property as a number. + * + * @param node message event + * @param name property name + * @return property as number + */ + public static long number(ObjectNode node, String name) { + return node.path(name).asLong(); + } + + /** + * Returns the specified node property as a string. + * + * @param node message event + * @param name property name + * @return property as a string + */ + public static String string(ObjectNode node, String name) { + return node.path(name).asText(); + } + + /** + * Returns the specified node property as a string, with a default fallback. + * + * @param node object node + * @param name property name + * @param defaultValue fallback value if property is absent + * @return property as a string + */ + public static String string(ObjectNode node, String name, String defaultValue) { + return node.path(name).asText(defaultValue); + } + + /** + * Returns the specified node property as an object node. + * + * @param node object node + * @param name property name + * @return property as a node + */ + public static ObjectNode node(ObjectNode node, String name) { + return (ObjectNode) node.path(name); + } + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/RequestHandler.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/RequestHandler.java new file mode 100644 index 00000000..1678923d --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/RequestHandler.java @@ -0,0 +1,142 @@ +/* + * 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.ui; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Abstraction of an entity that handles a specific request from the + * user interface client. + * + * @see UiMessageHandler + */ +public abstract class RequestHandler { + + protected static final ObjectMapper MAPPER = new ObjectMapper(); + + private final String eventType; + private UiMessageHandler parent; + + + public RequestHandler(String eventType) { + this.eventType = eventType; + } + + // package private + void setParent(UiMessageHandler parent) { + this.parent = parent; + } + + /** + * Returns the event type that this handler handles. + * + * @return event type + */ + public String eventType() { + return eventType; + } + + /** + * Processes the incoming message payload from the client. + * + * @param sid message sequence identifier + * @param payload request message payload + */ + // TODO: remove sid from signature + public abstract void process(long sid, ObjectNode payload); + + + + // =================================================================== + // === Convenience methods... + + /** + * Returns implementation of the specified service class. + * + * @param serviceClass service class + * @param <T> type of service + * @return implementation class + * @throws org.onlab.osgi.ServiceNotFoundException if no implementation found + */ + protected <T> T get(Class<T> serviceClass) { + return parent.directory().get(serviceClass); + } + + /** + * Sends a message back to the client. + * + * @param eventType message event type + * @param sid message sequence identifier + * @param payload message payload + */ + // TODO: remove sid from signature + protected void sendMessage(String eventType, long sid, ObjectNode payload) { + parent.connection().sendMessage(eventType, sid, payload); + } + + /** + * Sends a message back to the client. + * Here, the message is preformatted; the assumption is it has its + * eventType, sid and payload attributes already filled in. + * + * @param message the message to send + */ + protected void sendMessage(ObjectNode message) { + parent.connection().sendMessage(message); + } + + /** + * Allows one request handler to pass the event on to another for + * further processing. + * Note that the message handlers must be defined in the same parent. + * + * @param eventType event type + * @param sid sequence identifier + * @param payload message payload + */ + // TODO: remove sid from signature + protected void chain(String eventType, long sid, ObjectNode payload) { + parent.exec(eventType, sid, payload); + } + + // =================================================================== + + + /** + * Returns the specified node property as a string. + * + * @param node message event + * @param key property name + * @return property as a string + */ + protected String string(ObjectNode node, String key) { + return JsonUtils.string(node, key); + } + + /** + * Returns the specified node property as a string, with a default fallback. + * + * @param node object node + * @param key property name + * @param defValue fallback value if property is absent + * @return property as a string + */ + protected String string(ObjectNode node, String key, String defValue) { + return JsonUtils.string(node, key, defValue); + } + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiConnection.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiConnection.java new file mode 100644 index 00000000..ead7b0dc --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiConnection.java @@ -0,0 +1,42 @@ +/* + * 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.ui; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Abstraction of a user interface session connection. + */ +public interface UiConnection { + + /** + * Sends the specified JSON message to the user interface client. + * + * @param message message to send + */ + void sendMessage(ObjectNode message); + + /** + * Composes a message into JSON and sends it to the user interface client. + * + * @param type message type + * @param sid message sequence number + * @param payload message payload + */ + // TODO: remove sid parameter + void sendMessage(String type, long sid, ObjectNode payload); + +}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiExtension.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiExtension.java new file mode 100644 index 00000000..1f5fbd48 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiExtension.java @@ -0,0 +1,200 @@ +/* + * 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.ui; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * User interface extension. + */ +public final class UiExtension { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private static final String VIEW_PREFIX = "app/view/"; + private static final String EMPTY = ""; + private static final String SLASH = "/"; + private static final String CSS_HTML = "css.html"; + private static final String JS_HTML = "js.html"; + + private final ClassLoader classLoader; + private final String resourcePath; + private final List<UiView> views; + private final UiMessageHandlerFactory messageHandlerFactory; + private final UiTopoOverlayFactory topoOverlayFactory; + + + // private constructor - only the builder calls this + private UiExtension(ClassLoader cl, String path, List<UiView> views, + UiMessageHandlerFactory mhFactory, + UiTopoOverlayFactory toFactory) { + this.classLoader = cl; + this.resourcePath = path; + this.views = views; + this.messageHandlerFactory = mhFactory; + this.topoOverlayFactory = toFactory; + } + + + /** + * Returns input stream containing CSS inclusion statements. + * + * @return CSS inclusion statements + */ + public InputStream css() { + return getStream(resourcePath + CSS_HTML); + } + + /** + * Returns input stream containing JavaScript inclusion statements. + * + * @return JavaScript inclusion statements + */ + public InputStream js() { + return getStream(resourcePath + JS_HTML); + } + + /** + * Returns list of user interface views contributed by this extension. + * + * @return contributed view descriptors + */ + public List<UiView> views() { + return views; + } + + /** + * Returns input stream containing specified view-specific resource. + * + * @param viewId view identifier + * @param path resource path, relative to the view directory + * @return resource input stream + */ + public InputStream resource(String viewId, String path) { + return getStream(VIEW_PREFIX + viewId + SLASH + path); + } + + /** + * Returns message handler factory, if one was defined. + * + * @return message handler factory + */ + public UiMessageHandlerFactory messageHandlerFactory() { + return messageHandlerFactory; + } + + /** + * Returns the topology overlay factory, if one was defined. + * + * @return topology overlay factory + */ + public UiTopoOverlayFactory topoOverlayFactory() { + return topoOverlayFactory; + } + + + // Returns the resource input stream from the specified class-loader. + private InputStream getStream(String path) { + InputStream stream = classLoader.getResourceAsStream(path); + if (stream == null) { + log.warn("Unable to find resource {}", path); + } + return stream; + } + + + /** + * UI Extension Builder. + */ + public static class Builder { + private ClassLoader classLoader; + + private String resourcePath = EMPTY; + private List<UiView> views = new ArrayList<>(); + private UiMessageHandlerFactory messageHandlerFactory = null; + private UiTopoOverlayFactory topoOverlayFactory = null; + + /** + * Create a builder with the given class loader. + * Resource path defaults to "". + * Views defaults to an empty list. + * Both Message and TopoOverlay factories default to null. + * + * @param cl the class loader + * @param views list of views contributed by this extension + */ + public Builder(ClassLoader cl, List<UiView> views) { + checkNotNull(cl, "Must provide a class loader"); + checkArgument(views.size() > 0, "Must provide at least one view"); + this.classLoader = cl; + this.views = views; + } + + /** + * Set the resource path. That is, path to where the CSS and JS + * files are located. This value should + * + * @param path resource path + * @return self, for chaining + */ + public Builder resourcePath(String path) { + this.resourcePath = path == null ? EMPTY : path + SLASH; + return this; + } + + /** + * Sets the message handler factory for this extension. + * + * @param mhFactory message handler factory + * @return self, for chaining + */ + public Builder messageHandlerFactory(UiMessageHandlerFactory mhFactory) { + this.messageHandlerFactory = mhFactory; + return this; + } + + /** + * Sets the topology overlay factory for this extension. + * + * @param toFactory topology overlay factory + * @return self, for chaining + */ + public Builder topoOverlayFactory(UiTopoOverlayFactory toFactory) { + this.topoOverlayFactory = toFactory; + return this; + } + + /** + * Builds the UI extension. + * + * @return UI extension instance + */ + public UiExtension build() { + return new UiExtension(classLoader, resourcePath, views, + messageHandlerFactory, topoOverlayFactory); + } + + } + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiExtensionService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiExtensionService.java new file mode 100644 index 00000000..330fbb7a --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiExtensionService.java @@ -0,0 +1,53 @@ +/* + * 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.ui; + +import java.util.List; + +/** + * Service for registering user interface extensions. + */ +public interface UiExtensionService { + + /** + * Registers the specified user interface extension. + * + * @param extension GUI extension to register + */ + void register(UiExtension extension); + + /** + * Unregisters the specified user interface extension. + * + * @param extension GUI extension to unregister + */ + void unregister(UiExtension extension); + + /** + * Returns the list of user interface extensions. + * + * @return list of extensions + */ + List<UiExtension> getExtensions(); + + /** + * Returns the user interface extension that contributed the specified view. + * + * @param viewId view identifier + * @return user interface extension + */ + UiExtension getViewExtension(String viewId); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java new file mode 100644 index 00000000..9b9a406c --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiMessageHandler.java @@ -0,0 +1,207 @@ +/* + * 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.ui; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onlab.osgi.ServiceDirectory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Abstraction of an entity capable of processing JSON messages from the user + * interface client. + * <p> + * The message structure is: + * </p> + * <pre> + * { + * "type": "<em>event-type</em>", + * "payload": { + * <em>arbitrary JSON object structure</em> + * } + * } + * </pre> + * On {@link #init initialization} the handler will create and cache + * {@link RequestHandler} instances, each of which are bound to a particular + * <em>event-type</em>. On {@link #process arrival} of a new message, + * the <em>event-type</em> is determined, and the message dispatched to the + * corresponding <em>RequestHandler</em>'s + * {@link RequestHandler#process process} method. + */ +public abstract class UiMessageHandler { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private final Map<String, RequestHandler> handlerMap = new HashMap<>(); + private final ObjectMapper mapper = new ObjectMapper(); + + private UiConnection connection; + private ServiceDirectory directory; + + + /** + * Subclasses must create and return the collection of request handlers + * for the message types they handle. + * <p> + * Note that request handlers should be stateless. When we are + * {@link #destroy destroyed}, we will simply drop our references to them + * and allow them to be garbage collected. + * + * @return the message handler instances + */ + protected abstract Collection<RequestHandler> createRequestHandlers(); + + /** + * Returns the set of message types which this handler is capable of + * processing. + * + * @return set of message types + */ + public Set<String> messageTypes() { + return Collections.unmodifiableSet(handlerMap.keySet()); + } + + /** + * Processes a JSON message from the user interface client. + * + * @param message JSON message + */ + public void process(ObjectNode message) { + String type = JsonUtils.eventType(message); + ObjectNode payload = JsonUtils.payload(message); + // TODO: remove sid + exec(type, 0, payload); + } + + /** + * Finds the appropriate handler and executes the process method. + * + * @param eventType event type + * @param sid sequence identifier + * @param payload message payload + */ + // TODO: remove sid from signature + void exec(String eventType, long sid, ObjectNode payload) { + RequestHandler requestHandler = handlerMap.get(eventType); + if (requestHandler != null) { + requestHandler.process(sid, payload); + } else { + log.warn("no request handler for event type {}", eventType); + } + } + + /** + * Initializes the handler with the user interface connection and + * service directory context. + * + * @param connection user interface connection + * @param directory service directory + */ + public void init(UiConnection connection, ServiceDirectory directory) { + this.connection = connection; + this.directory = directory; + + Collection<RequestHandler> handlers = createRequestHandlers(); + checkNotNull(handlers, "Handlers cannot be null"); + checkArgument(!handlers.isEmpty(), "Handlers cannot be empty"); + + for (RequestHandler h : handlers) { + h.setParent(this); + handlerMap.put(h.eventType(), h); + } + } + + /** + * Destroys the message handler context. + */ + public void destroy() { + this.connection = null; + this.directory = null; + handlerMap.clear(); + } + + /** + * Returns the user interface connection with which this handler was primed. + * + * @return user interface connection + */ + public UiConnection connection() { + return connection; + } + + /** + * Returns the user interface connection with which this handler was primed. + * + * @return user interface connection + */ + public ServiceDirectory directory() { + return directory; + } + + /** + * Returns implementation of the specified service class. + * + * @param serviceClass service class + * @param <T> type of service + * @return implementation class + * @throws org.onlab.osgi.ServiceNotFoundException if no implementation found + */ + protected <T> T get(Class<T> serviceClass) { + return directory.get(serviceClass); + } + + /** + * Returns a freshly minted object node. + * + * @return new object node + */ + protected ObjectNode objectNode() { + return mapper.createObjectNode(); + } + + /** + * Returns a freshly minted array node. + * + * @return new array node + */ + protected ArrayNode arrayNode() { + return mapper.createArrayNode(); + } + + /** + * Sends the specified data to the client. + * It is expected that the data is in the prescribed JSON format for + * events to the client. + * + * @param data data to be sent + */ + protected synchronized void sendMessage(ObjectNode data) { + UiConnection connection = connection(); + if (connection != null) { + connection.sendMessage(data); + } + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiMessageHandlerFactory.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiMessageHandlerFactory.java new file mode 100644 index 00000000..522daa8f --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiMessageHandlerFactory.java @@ -0,0 +1,33 @@ +/* + * 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.ui; + +import java.util.Collection; + +/** + * Abstraction of an entity capable of producing a set of message handlers + * specific to the given user interface connection. + */ +public interface UiMessageHandlerFactory { + + /** + * Produces a collection of new message handlers. + * + * @return collection of new handlers + */ + Collection<UiMessageHandler> newHandlers(); + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java new file mode 100644 index 00000000..2889422a --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java @@ -0,0 +1,122 @@ +/* + * 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.ui; + +import org.onosproject.ui.topo.PropertyPanel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Represents user interface topology view overlay. + */ +public class UiTopoOverlay { + + /** + * Logger for this overlay. + */ + protected final Logger log = LoggerFactory.getLogger(getClass()); + + private final String id; + + private boolean isActive = false; + + /** + * Creates a new user interface topology view overlay descriptor. + * + * @param id overlay identifier + */ + public UiTopoOverlay(String id) { + this.id = id; + } + + /** + * Returns the identifier for this overlay. + * + * @return the identifier + */ + public String id() { + return id; + } + + /** + * Callback invoked to initialize this overlay, soon after creation. + * This default implementation does nothing. + */ + public void init() { + } + + /** + * Callback invoked when this overlay is activated. + */ + public void activate() { + isActive = true; + } + + /** + * Callback invoked when this overlay is deactivated. + */ + public void deactivate() { + isActive = false; + } + + /** + * Returns true if this overlay is currently active. + * + * @return true if overlay active + */ + public boolean isActive() { + return isActive; + } + + /** + * Callback invoked to destroy this instance by cleaning up any + * internal state ready for garbage collection. + * This default implementation holds no state and does nothing. + */ + public void destroy() { + } + + /** + * Callback to modify the contents of the summary panel. + * This default implementation does nothing. + * + * @param pp property panel model of summary data + */ + public void modifySummary(PropertyPanel pp) { + } + + /** + * Callback to modify the contents of the details panel for + * a selected device. + * This default implementation does nothing. + * + * @param pp property panel model of summary data + */ + public void modifyDeviceDetails(PropertyPanel pp) { + } + + /** + * Callback to modify the contents of the details panel for + * a selected host. + * This default implementation does nothing. + * + * @param pp property panel model of summary data + */ + public void modifyHostDetails(PropertyPanel pp) { + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlayFactory.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlayFactory.java new file mode 100644 index 00000000..bd2f2fe6 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlayFactory.java @@ -0,0 +1,34 @@ +/* + * 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.ui; + +import java.util.Collection; + +/** + * Abstraction of an entity capable of producing one or more topology + * overlay handlers specific to a given user interface connection. + */ +public interface UiTopoOverlayFactory { + + /** + * Produces a collection of new overlay handlers. + * + * @return collection of new overlay handlers + */ + Collection<UiTopoOverlay> newOverlays(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiView.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiView.java new file mode 100644 index 00000000..2b8b7fa2 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiView.java @@ -0,0 +1,165 @@ +/* + * 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.ui; + +import com.google.common.base.MoreObjects; + +import java.util.Objects; + +/** + * Represents user interface view addition. + */ +public class UiView { + + /** + * Designates navigation menu category. + */ + public enum Category { + /** + * Represents platform related views. + */ + PLATFORM("Platform"), + + /** + * Represents network-control related views. + */ + NETWORK("Network"), + + /** + * Represents miscellaneous views. + */ + OTHER("Other"), + + /** + * Represents views that do not show in the navigation menu. + * This category should not be specified directly; rather, use + * the {@link UiViewHidden} constructor instead of {@link UiView}. + */ + HIDDEN("(hidden)"); + + private final String label; + + Category(String label) { + this.label = label; + } + + /** + * Returns display label for the category. + * + * @return display label + */ + public String label() { + return label; + } + } + + private final Category category; + private final String id; + private final String label; + private final String iconId; + + /** + * Creates a new user interface view descriptor. The navigation item + * will appear in the navigation menu under the specified category. + * + * @param category view category + * @param id view identifier + * @param label view label + */ + public UiView(Category category, String id, String label) { + this(category, id, label, null); + } + + /** + * Creates a new user interface view descriptor. The navigation item + * will appear in the navigation menu under the specified category, + * with the specified icon adornment. + * + * @param category view category + * @param id view identifier + * @param label view label + * @param iconId icon id + */ + public UiView(Category category, String id, String label, String iconId) { + this.category = category; + this.id = id; + this.label = label; + this.iconId = iconId; + } + + /** + * Returns the navigation category. + * + * @return navigation category + */ + public Category category() { + return category; + } + + /** + * Returns the view identifier. + * + * @return view id + */ + public String id() { + return id; + } + + /** + * Returns the view label. + * + * @return view label + */ + public String label() { + return label; + } + + /** + * Returns the icon ID. + * + * @return icon ID + */ + public String iconId() { + return iconId; + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final UiView other = (UiView) obj; + return Objects.equals(this.id, other.id); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("category", category) + .add("id", id) + .add("label", label) + .add("iconId", iconId) + .toString(); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiViewHidden.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiViewHidden.java new file mode 100644 index 00000000..b7fea8fe --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiViewHidden.java @@ -0,0 +1,41 @@ +/* + * 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.ui; + +import com.google.common.base.MoreObjects; + +/** + * Represents user interface view addition, except that this one should not + * have an entry in the navigation panel. + */ +public class UiViewHidden extends UiView { + + /** + * Creates a new user interface hidden view descriptor. + * + * @param id view identifier + */ + public UiViewHidden(String id) { + super(Category.HIDDEN, id, null); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id()) + .toString(); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/package-info.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/package-info.java new file mode 100644 index 00000000..dd832a59 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Mechanism for managing dynamically registered user interface extensions. + */ +package org.onosproject.ui; diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/CellComparator.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/CellComparator.java new file mode 100644 index 00000000..84d11344 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/CellComparator.java @@ -0,0 +1,46 @@ +/* + * 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.ui.table; + +/** + * Defines a comparator for cell values. + */ +public interface CellComparator { + + /** + * Compares its two arguments for order. Returns a negative integer, + * zero, or a positive integer as the first argument is less than, equal + * to, or greater than the second.<p> + * + * Note that nulls are permitted, and should be sorted to the beginning + * of an ascending sort; i.e. null is considered to be "smaller" than + * non-null values. + * + * @see java.util.Comparator#compare(Object, Object) + * + * @param o1 the first object to be compared. + * @param o2 the second object to be compared. + * @return a negative integer, zero, or a positive integer as the + * first argument is less than, equal to, or greater than the + * second. + * @throws ClassCastException if the arguments' types prevent them from + * being compared by this comparator. + */ + int compare(Object o1, Object o2); + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/CellFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/CellFormatter.java new file mode 100644 index 00000000..854ac27f --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/CellFormatter.java @@ -0,0 +1,34 @@ +/* + * 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.ui.table; + +/** + * Defines a formatter for cell values. + */ +public interface CellFormatter { + + /** + * Formats the specified value into a string appropriate for displaying + * in a table cell. Note that null values are acceptable, and will result + * in the empty string. + * + * @param value the value + * @return the formatted string + */ + String format(Object value); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableModel.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableModel.java new file mode 100644 index 00000000..d0fccb65 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableModel.java @@ -0,0 +1,304 @@ +/* + * 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.ui.table; + +import com.google.common.collect.Sets; +import org.onosproject.ui.table.cell.DefaultCellComparator; +import org.onosproject.ui.table.cell.DefaultCellFormatter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A simple model of table data. + * <p> + * Note that this is not a full MVC type model; the expected usage pattern + * is to create an empty table, add rows (by consulting the business model), + * sort rows (based on client request parameters), and finally produce the + * sorted list of rows. + * <p> + * The table also provides a mechanism for defining how cell values for a + * particular column should be formatted into strings, to help facilitate + * the encoding of the table data into a JSON structure. + * <p> + * Note that it is expected that all values for a particular column will + * be the same class. + */ +public class TableModel { + + private static final CellComparator DEF_CMP = DefaultCellComparator.INSTANCE; + private static final CellFormatter DEF_FMT = DefaultCellFormatter.INSTANCE; + + private final String[] columnIds; + private final Set<String> idSet; + private final Map<String, CellComparator> comparators = new HashMap<>(); + private final Map<String, CellFormatter> formatters = new HashMap<>(); + private final List<Row> rows = new ArrayList<>(); + + + /** + * Constructs a table (devoid of data) with the given column IDs. + * + * @param columnIds column identifiers + */ + public TableModel(String... columnIds) { + checkNotNull(columnIds, "columnIds cannot be null"); + checkArgument(columnIds.length > 0, "must be at least one column"); + + idSet = Sets.newHashSet(columnIds); + if (idSet.size() != columnIds.length) { + throw new IllegalArgumentException("duplicate column ID(s) detected"); + } + + this.columnIds = Arrays.copyOf(columnIds, columnIds.length); + } + + private void checkId(String id) { + checkNotNull(id, "must provide a column ID"); + if (!idSet.contains(id)) { + throw new IllegalArgumentException("unknown column id: " + id); + } + } + + /** + * Returns the number of rows in this table model. + * + * @return number of rows + */ + public int rowCount() { + return rows.size(); + } + + /** + * Returns the number of columns in this table model. + * + * @return number of columns + */ + public int columnCount() { + return columnIds.length; + } + + /** + * Returns the array of column IDs for this table model. + * <p> + * Implementation note: we are knowingly passing you a reference to + * our internal array to avoid copying. Don't mess with it. It's your + * table you'll break if you do! + * + * @return the column identifiers + */ + public String[] getColumnIds() { + return columnIds; + } + + /** + * Returns the raw {@link Row} representation of the rows in this table. + * + * @return raw table rows + */ + public Row[] getRows() { + return rows.toArray(new Row[rows.size()]); + } + + /** + * Sets a cell comparator for the specified column. + * + * @param columnId column identifier + * @param comparator comparator to use + */ + public void setComparator(String columnId, CellComparator comparator) { + checkNotNull(comparator, "must provide a comparator"); + checkId(columnId); + comparators.put(columnId, comparator); + } + + /** + * Returns the cell comparator to use on values in the specified column. + * + * @param columnId column identifier + * @return an appropriate cell comparator + */ + private CellComparator getComparator(String columnId) { + checkId(columnId); + CellComparator cmp = comparators.get(columnId); + return cmp == null ? DEF_CMP : cmp; + } + + /** + * Sets a cell formatter for the specified column. + * + * @param columnId column identifier + * @param formatter formatter to use + */ + public void setFormatter(String columnId, CellFormatter formatter) { + checkNotNull(formatter, "must provide a formatter"); + checkId(columnId); + formatters.put(columnId, formatter); + } + + /** + * Returns the cell formatter to use on values in the specified column. + * + * @param columnId column identifier + * @return an appropriate cell formatter + */ + public CellFormatter getFormatter(String columnId) { + checkId(columnId); + CellFormatter fmt = formatters.get(columnId); + return fmt == null ? DEF_FMT : fmt; + } + + /** + * Adds a row to the table model. + * + * @return the row, for chaining + */ + public Row addRow() { + Row r = new Row(); + rows.add(r); + return r; + } + + /** + * Sorts the table rows based on the specified column, in the + * specified direction. + * + * @param columnId column identifier + * @param dir sort direction + */ + public void sort(String columnId, SortDir dir) { + Collections.sort(rows, new RowComparator(columnId, dir)); + } + + + /** Designates sorting direction. */ + public enum SortDir { + /** Designates an ascending sort. */ + ASC, + /** Designates a descending sort. */ + DESC + } + + /** + * Row comparator. + */ + private class RowComparator implements Comparator<Row> { + private final String columnId; + private final SortDir dir; + private final CellComparator cellComparator; + + /** + * Constructs a row comparator based on the specified + * column identifier and sort direction. + * + * @param columnId column identifier + * @param dir sort direction + */ + public RowComparator(String columnId, SortDir dir) { + this.columnId = columnId; + this.dir = dir; + cellComparator = getComparator(columnId); + } + + @Override + public int compare(Row a, Row b) { + Object cellA = a.get(columnId); + Object cellB = b.get(columnId); + int result = cellComparator.compare(cellA, cellB); + return dir == SortDir.ASC ? result : -result; + } + } + + /** + * Model of a row. + */ + public class Row { + private final Map<String, Object> cells = new HashMap<>(); + + /** + * Sets the cell value for the given column of this row. + * + * @param columnId column identifier + * @param value value to set + * @return self, for chaining + */ + public Row cell(String columnId, Object value) { + checkId(columnId); + cells.put(columnId, value); + return this; + } + + /** + * Returns the value of the cell in the given column for this row. + * + * @param columnId column identifier + * @return cell value + */ + public Object get(String columnId) { + return cells.get(columnId); + } + + /** + * Returns the value of the cell as a string, using the + * formatter appropriate for the column. + * + * @param columnId column identifier + * @return formatted cell value + */ + String getAsString(String columnId) { + return getFormatter(columnId).format(get(columnId)); + } + + /** + * Returns the row as an array of formatted strings. + * + * @return the formatted row data + */ + public String[] getAsFormattedStrings() { + List<String> formatted = new ArrayList<>(columnCount()); + for (String c : columnIds) { + formatted.add(getAsString(c)); + } + return formatted.toArray(new String[formatted.size()]); + } + } + + private static final String DESC = "desc"; + + /** + * Returns the appropriate sort direction for the given string. + * <p> + * The expected strings are "asc" for {@link SortDir#ASC ascending} and + * "desc" for {@link SortDir#DESC descending}. Any other value will + * default to ascending. + * + * @param s sort direction string encoding + * @return sort direction + */ + public static SortDir sortDir(String s) { + return !DESC.equals(s) ? SortDir.ASC : SortDir.DESC; + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java new file mode 100644 index 00000000..b8d48575 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java @@ -0,0 +1,111 @@ +/* + * 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.ui.table; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onosproject.ui.JsonUtils; +import org.onosproject.ui.RequestHandler; + +/** + * Message handler specifically for table views. + */ +public abstract class TableRequestHandler extends RequestHandler { + + private final String respType; + private final String nodeName; + + /** + * Constructs a table request handler for a specific table view. When + * table requests come in, the handler will generate the appropriate + * table rows, sort them according the the request sort parameters, and + * send back the response to the client. + * + * @param reqType type of the request event + * @param respType type of the response event + * @param nodeName name of JSON node holding row data + */ + public TableRequestHandler(String reqType, String respType, String nodeName) { + super(reqType); + this.respType = respType; + this.nodeName = nodeName; + } + + @Override + public void process(long sid, ObjectNode payload) { + TableModel tm = createTableModel(); + populateTable(tm, payload); + + String sortCol = JsonUtils.string(payload, "sortCol", defaultColumnId()); + String sortDir = JsonUtils.string(payload, "sortDir", "asc"); + tm.sort(sortCol, TableModel.sortDir(sortDir)); + + ObjectNode rootNode = MAPPER.createObjectNode(); + rootNode.set(nodeName, TableUtils.generateArrayNode(tm)); + sendMessage(respType, 0, rootNode); + } + + /** + * Creates the table model (devoid of data) using {@link #getColumnIds()} + * to initialize it, ready to be populated. + * <p> + * This default implementation returns a table model with default + * formatters and comparators for all columns. + * + * @return an empty table model + */ + protected TableModel createTableModel() { + return new TableModel(getColumnIds()); + } + + /** + * Returns the default column ID to be used when one is not supplied in + * the payload as the column on which to sort. + * <p> + * This default implementation returns "id". + * + * @return default sort column identifier + */ + protected String defaultColumnId() { + return "id"; + } + + /** + * Subclasses should return the array of column IDs with which + * to initialize their table model. + * + * @return the column IDs + */ + protected abstract String[] getColumnIds(); + + /** + * Subclasses should populate the table model by adding + * {@link TableModel.Row rows}. + * <pre> + * tm.addRow() + * .cell(COL_ONE, ...) + * .cell(COL_TWO, ...) + * ... ; + * </pre> + * The request payload is provided in case there are request filtering + * parameters (other than sort column and sort direction) that are required + * to generate the appropriate data. + * + * @param tm the table model + * @param payload request payload + */ + protected abstract void populateTable(TableModel tm, ObjectNode payload); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableUtils.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableUtils.java new file mode 100644 index 00000000..eb2dff78 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/TableUtils.java @@ -0,0 +1,58 @@ +/* + * 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.ui.table; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Provides static utility methods for dealing with tables. + */ +public final class TableUtils { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + // non-instantiable + private TableUtils() { } + + /** + * Generates a JSON array node from a table model. + * + * @param tm the table model + * @return the array node representation + */ + public static ArrayNode generateArrayNode(TableModel tm) { + ArrayNode array = MAPPER.createArrayNode(); + for (TableModel.Row r : tm.getRows()) { + array.add(toJsonNode(r, tm)); + } + return array; + } + + private static JsonNode toJsonNode(TableModel.Row row, TableModel tm) { + ObjectNode result = MAPPER.createObjectNode(); + String[] keys = tm.getColumnIds(); + String[] cells = row.getAsFormattedStrings(); + int n = keys.length; + for (int i = 0; i < n; i++) { + result.put(keys[i], cells[i]); + } + return result; + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellComparator.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellComparator.java new file mode 100644 index 00000000..6113fc3f --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellComparator.java @@ -0,0 +1,61 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.ui.table.CellComparator; + +/** + * Base implementation of a {@link CellComparator}. This class takes care + * of dealing with null inputs; subclasses should implement their comparison + * knowing that both inputs are guaranteed to be non-null. + */ +public abstract class AbstractCellComparator implements CellComparator { + + @Override + public int compare(Object o1, Object o2) { + if (o1 == null && o2 == null) { + return 0; // o1 == o2 + } + if (o1 == null) { + return -1; // o1 < o2 + } + if (o2 == null) { + return 1; // o1 > o2 + } + return nonNullCompare(o1, o2); + } + + /** + * Compares its two arguments for order. Returns a negative integer, + * zero, or a positive integer as the first argument is less than, equal + * to, or greater than the second.<p> + * + * Note that both objects are guaranteed to be non-null. + * + * @see java.util.Comparator#compare(Object, Object) + * + * @param o1 the first object to be compared. + * @param o2 the second object to be compared. + * @return a negative integer, zero, or a positive integer as the + * first argument is less than, equal to, or greater than the + * second. + * @throws ClassCastException if the arguments' types prevent them from + * being compared by this comparator. + */ + protected abstract int nonNullCompare(Object o1, Object o2); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellFormatter.java new file mode 100644 index 00000000..33ce2ab5 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AbstractCellFormatter.java @@ -0,0 +1,42 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.ui.table.CellFormatter; + +/** + * Base implementation of a {@link CellFormatter}. This class takes care of + * dealing with null inputs; subclasses should implement their format method + * knowing that the input is guaranteed to be non-null. + */ +public abstract class AbstractCellFormatter implements CellFormatter { + + @Override + public String format(Object value) { + return value == null ? "" : nonNullFormat(value); + } + + /** + * Formats the specified value into a string appropriate for displaying + * in a table cell. Note that value is guaranteed to be non-null. + * + * @param value the value + * @return the formatted string + */ + protected abstract String nonNullFormat(Object value); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java new file mode 100644 index 00000000..42d684b6 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java @@ -0,0 +1,41 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.core.ApplicationId; +import org.onosproject.ui.table.CellFormatter; + +/** + * Formats an application identifier as "(app-id) : (app-name)". + */ +public final class AppIdFormatter extends AbstractCellFormatter { + + // non-instantiable + private AppIdFormatter() { } + + @Override + protected String nonNullFormat(Object value) { + ApplicationId appId = (ApplicationId) value; + return appId.id() + " : " + appId.name(); + } + + /** + * An instance of this class. + */ + public static final CellFormatter INSTANCE = new AppIdFormatter(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/ConnectPointFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/ConnectPointFormatter.java new file mode 100644 index 00000000..fee26154 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/ConnectPointFormatter.java @@ -0,0 +1,41 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.net.ConnectPoint; +import org.onosproject.ui.table.CellFormatter; + +/** + * Formats a connect point as "(element-id)/(port)". + */ +public final class ConnectPointFormatter extends AbstractCellFormatter { + + // non-instantiable + private ConnectPointFormatter() { } + + @Override + protected String nonNullFormat(Object value) { + ConnectPoint cp = (ConnectPoint) value; + return cp.elementId() + "/" + cp.port(); + } + + /** + * An instance of this class. + */ + public static final CellFormatter INSTANCE = new ConnectPointFormatter(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellComparator.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellComparator.java new file mode 100644 index 00000000..093a20d3 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellComparator.java @@ -0,0 +1,52 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.ui.table.CellComparator; + +/** + * A default cell comparator. + * <p> + * Verifies that the objects being compared are the same class. + * Looks to see if the objects being compared implement comparable and, if so, + * delegates to that; otherwise, implements a lexicographical compare function + * (i.e. string sorting). Uses the objects' toString() method and then + * compares the resulting strings. Note that null values are acceptable and + * are considered "smaller" than any non-null value. + */ +public final class DefaultCellComparator extends AbstractCellComparator { + + // non-instantiable + private DefaultCellComparator() { } + + @Override + @SuppressWarnings("unchecked") + protected int nonNullCompare(Object o1, Object o2) { + if (o1 instanceof Comparable) { + // if o2 is not the same class as o1, then compareTo will + // throw ClassCastException for us + return ((Comparable) o1).compareTo(o2); + } + return o1.toString().compareTo(o2.toString()); + } + + /** + * An instance of this class. + */ + public static final CellComparator INSTANCE = new DefaultCellComparator(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellFormatter.java new file mode 100644 index 00000000..0efa2ebd --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/DefaultCellFormatter.java @@ -0,0 +1,39 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.ui.table.CellFormatter; + +/** + * A default cell formatter. Uses the object's toString() method. + */ +public final class DefaultCellFormatter extends AbstractCellFormatter { + + // non-instantiable + private DefaultCellFormatter() { } + + @Override + public String nonNullFormat(Object value) { + return value.toString(); + } + + /** + * An instance of this class. + */ + public static final CellFormatter INSTANCE = new DefaultCellFormatter(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/EnumFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/EnumFormatter.java new file mode 100644 index 00000000..5b89a0b7 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/EnumFormatter.java @@ -0,0 +1,40 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.ui.table.CellFormatter; + +import static org.apache.commons.lang.WordUtils.capitalizeFully; + +/** + * Formats enum types to be readable strings. + */ +public final class EnumFormatter extends AbstractCellFormatter { + + // non-instantiable + private EnumFormatter() { } + + @Override + protected String nonNullFormat(Object value) { + return capitalizeFully(value.toString().replace("_", " ")); + } + + /** + * An instance of this class. + */ + public static final CellFormatter INSTANCE = new EnumFormatter(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/HexFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/HexFormatter.java new file mode 100644 index 00000000..e09982ea --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/HexFormatter.java @@ -0,0 +1,39 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.ui.table.CellFormatter; + +/** + * Formats integer values as hex strings with a "0x" prefix. + */ +public final class HexFormatter extends AbstractCellFormatter { + + // non-instantiable + private HexFormatter() { } + + @Override + protected String nonNullFormat(Object value) { + return "0x" + Integer.toHexString((Integer) value); + } + + /** + * An instance of this class. + */ + public static final CellFormatter INSTANCE = new HexFormatter(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/HostLocationFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/HostLocationFormatter.java new file mode 100644 index 00000000..fe87c61b --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/HostLocationFormatter.java @@ -0,0 +1,41 @@ +/* + * 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.ui.table.cell; + +import org.onosproject.net.HostLocation; +import org.onosproject.ui.table.CellFormatter; + +/** + * Formats a host location as "(device-id)/(port)". + */ +public final class HostLocationFormatter extends AbstractCellFormatter { + + // non-instantiable + private HostLocationFormatter() { } + + @Override + protected String nonNullFormat(Object value) { + HostLocation loc = (HostLocation) value; + return loc.deviceId() + "/" + loc.port(); + } + + /** + * An instance of this class. + */ + public static final CellFormatter INSTANCE = new HostLocationFormatter(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/TimeFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/TimeFormatter.java new file mode 100644 index 00000000..44dc1940 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/TimeFormatter.java @@ -0,0 +1,72 @@ +/* + * 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.ui.table.cell; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import java.util.Locale; + +/** + * Formats time values using {@link DateTimeFormatter}. + */ +public final class TimeFormatter extends AbstractCellFormatter { + + private DateTimeFormatter dtf; + + // NOTE: Unlike other formatters in this package, this one is not + // implemented as a Singleton, because instances may be + // decorated with alternate locale and/or timezone. + + /** + * Constructs a time formatter that uses the default locale and timezone. + */ + public TimeFormatter() { + dtf = DateTimeFormat.longTime(); + } + + /** + * Sets the locale to use for formatting the time. + * + * @param locale locale to use for formatting + * @return self, for chaining + */ + public TimeFormatter withLocale(Locale locale) { + dtf = dtf.withLocale(locale); + return this; + } + + /** + * Sets the time zone to use for formatting the time. + * + * @param zone time zone to use + * @return self, for chaining + */ + public TimeFormatter withZone(DateTimeZone zone) { + dtf = dtf.withZone(zone); + return this; + } + + @Override + protected String nonNullFormat(Object value) { + return dtf.print((DateTime) value); + } + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/package-info.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/package-info.java new file mode 100644 index 00000000..c25bcb06 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Set of table cell renderers and comparators for use by GUI apps. + */ +package org.onosproject.ui.table.cell;
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/package-info.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/package-info.java new file mode 100644 index 00000000..ee975d11 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Facilities for creating tabular models of data for the GUI. + */ +package org.onosproject.ui.table;
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/AbstractHighlight.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/AbstractHighlight.java new file mode 100644 index 00000000..ab2ced36 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/AbstractHighlight.java @@ -0,0 +1,75 @@ +/* + * 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.ui.topo; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Partial implementation of the highlighting to apply to topology + * view elements. + */ +public abstract class AbstractHighlight { + private final TopoElementType type; + private final String elementId; + private boolean keepSubdued = false; + + /** + * Constructs the highlight. + * + * @param type highlight element type + * @param elementId element identifier + */ + public AbstractHighlight(TopoElementType type, String elementId) { + this.type = checkNotNull(type); + this.elementId = checkNotNull(elementId); + } + + /** + * Sets a flag to tell the renderer to keep this element subdued. + */ + public void keepSubdued() { + keepSubdued = true; + } + + /** + * Returns the element type. + * + * @return element type + */ + public TopoElementType type() { + return type; + } + + /** + * Returns the element identifier. + * + * @return element identifier + */ + public String elementId() { + return elementId; + } + + /** + * Returns the subdued flag. + * + * @return subdued flag + */ + public boolean subdued() { + return keepSubdued; + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BaseLink.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BaseLink.java new file mode 100644 index 00000000..c37c129b --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BaseLink.java @@ -0,0 +1,43 @@ +/* + * 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.ui.topo; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; + +/** + * A simple concrete implementation of a {@link BiLink}. + * Note that this implementation does not generate any link highlights. + */ +public class BaseLink extends BiLink { + + /** + * Constructs a base link for the given key and initial link. + * + * @param key canonical key for this base link + * @param link first link + */ + public BaseLink(LinkKey key, Link link) { + super(key, link); + } + + @Override + public LinkHighlight highlight(Enum<?> type) { + return null; + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BaseLinkMap.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BaseLinkMap.java new file mode 100644 index 00000000..720eca49 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BaseLinkMap.java @@ -0,0 +1,31 @@ +/* + * 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.ui.topo; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; + +/** + * Collection of {@link BaseLink}s. + */ +public class BaseLinkMap extends BiLinkMap<BaseLink> { + @Override + public BaseLink create(LinkKey key, Link link) { + return new BaseLink(key, link); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BiLink.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BiLink.java new file mode 100644 index 00000000..8c95e15d --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BiLink.java @@ -0,0 +1,104 @@ +/* + * 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.ui.topo; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Representation of a link and its inverse, as a partial implementation. + * <p> + * Subclasses will decide how to generate the link highlighting (coloring + * and labeling) for the topology view. + */ +public abstract class BiLink { + + private final LinkKey key; + private final Link one; + private Link two; + + /** + * Constructs a bi-link for the given key and initial link. It is expected + * that the caller will have used {@link TopoUtils#canonicalLinkKey(Link)} + * to generate the key. + * + * @param key canonical key for this bi-link + * @param link first link + */ + public BiLink(LinkKey key, Link link) { + this.key = checkNotNull(key); + this.one = checkNotNull(link); + } + + /** + * Sets the second link for this bi-link. + * + * @param link second link + */ + public void setOther(Link link) { + this.two = checkNotNull(link); + } + + /** + * Returns the link identifier in the form expected on the Topology View + * in the web client. + * + * @return link identifier + */ + public String linkId() { + return TopoUtils.compactLinkString(one); + } + + /** + * Returns the key for this bi-link. + * + * @return the key + */ + public LinkKey key() { + return key; + } + + /** + * Returns the first link in this bi-link. + * + * @return the first link + */ + public Link one() { + return one; + } + + /** + * Returns the second link in this bi-link. + * + * @return the second link + */ + public Link two() { + return two; + } + + /** + * Returns the link highlighting to use, based on this bi-link's current + * state. + * + * @param type optional highlighting type parameter + * @return link highlighting model + */ + public abstract LinkHighlight highlight(Enum<?> type); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BiLinkMap.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BiLinkMap.java new file mode 100644 index 00000000..7bc0e65d --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/BiLinkMap.java @@ -0,0 +1,90 @@ +/* + * 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.ui.topo; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Represents a collection of {@link BiLink} concrete classes. These maps + * are used to collate a set of unidirectional {@link Link}s into a smaller + * set of bi-directional {@link BiLink} derivatives. + * <p> + * @param <B> the type of bi-link subclass + */ +public abstract class BiLinkMap<B extends BiLink> { + + private final Map<LinkKey, B> map = new HashMap<>(); + + /** + * Creates a new instance of a bi-link. Concrete subclasses should + * instantiate and return the appropriate bi-link subclass. + * + * @param key the link key + * @param link the initial link + * @return a new instance + */ + protected abstract B create(LinkKey key, Link link); + + /** + * Adds the given link to our collection, returning the corresponding + * bi-link (creating one if needed necessary). + * + * @param link the link to add to the collection + * @return the corresponding bi-link wrapper + */ + public B add(Link link) { + LinkKey key = TopoUtils.canonicalLinkKey(checkNotNull(link)); + B blink = map.get(key); + if (blink == null) { + // no bi-link yet exists for this link + blink = create(key, link); + map.put(key, blink); + } else { + // we have a bi-link for this link. + if (!blink.one().equals(link)) { + blink.setOther(link); + } + } + return blink; + } + + /** + * Returns the bi-link instances in the collection. + * + * @return the bi-links in this map + */ + public Collection<B> biLinks() { + return map.values(); + } + + /** + * Returns the number of bi-links in the collection. + * + * @return number of bi-links + */ + public int size() { + return map.size(); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/ButtonId.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/ButtonId.java new file mode 100644 index 00000000..ca2ecccd --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/ButtonId.java @@ -0,0 +1,70 @@ +/* + * 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.ui.topo; + +import com.google.common.base.MoreObjects; + +/** + * Designates the identity of a button on the topology view panels. + */ +public class ButtonId { + + private final String id; + + /** + * Creates a button ID with the given identifier. + * + * @param id identifier for the button + */ + public ButtonId(String id) { + this.id = id; + } + + /** + * Returns the identifier for this button. + * + * @return identifier + */ + public String id() { + return id; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("id", id()).toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ButtonId that = (ButtonId) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/DeviceHighlight.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/DeviceHighlight.java new file mode 100644 index 00000000..2985d3d4 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/DeviceHighlight.java @@ -0,0 +1,33 @@ +/* + * 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.ui.topo; + +/** + * Denotes the highlighting to apply to a device. + */ +public class DeviceHighlight extends NodeHighlight { + + public DeviceHighlight(String deviceId) { + super(TopoElementType.DEVICE, deviceId); + } + + // TODO: implement device highlighting: + // - visual highlight + // - badging + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/Highlights.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/Highlights.java new file mode 100644 index 00000000..be59c26b --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/Highlights.java @@ -0,0 +1,190 @@ +/* + * 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.ui.topo; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Encapsulates highlights to be applied to the topology view, such as + * highlighting links, displaying link labels, perhaps even decorating + * nodes with badges, etc. + */ +public class Highlights { + + private static final String EMPTY = ""; + private static final String MIN = "min"; + private static final String MAX = "max"; + + /** + * A notion of amount. + */ + public enum Amount { + ZERO(EMPTY), + MINIMALLY(MIN), + MAXIMALLY(MAX); + + private final String s; + Amount(String str) { + s = str; + } + + @Override + public String toString() { + return s; + } + } + + private final Map<String, DeviceHighlight> devices = new HashMap<>(); + private final Map<String, HostHighlight> hosts = new HashMap<>(); + private final Map<String, LinkHighlight> links = new HashMap<>(); + + private Amount subdueLevel = Amount.ZERO; + + + /** + * Adds highlighting information for a device. + * + * @param dh device highlight + * @return self, for chaining + */ + public Highlights add(DeviceHighlight dh) { + devices.put(dh.elementId(), dh); + return this; + } + + /** + * Adds highlighting information for a host. + * + * @param hh host highlight + * @return self, for chaining + */ + public Highlights add(HostHighlight hh) { + hosts.put(hh.elementId(), hh); + return this; + } + + /** + * Adds highlighting information for a link. + * + * @param lh link highlight + * @return self, for chaining + */ + public Highlights add(LinkHighlight lh) { + links.put(lh.elementId(), lh); + return this; + } + + /** + * Marks the amount by which all other elements (devices, hosts, links) + * not explicitly referenced here will be "subdued" visually. + * + * @param amount amount to subdue other elements + * @return self, for chaining + */ + public Highlights subdueAllElse(Amount amount) { + subdueLevel = checkNotNull(amount); + return this; + } + + /** + * Returns the collection of device highlights. + * + * @return device highlights + */ + public Collection<DeviceHighlight> devices() { + return Collections.unmodifiableCollection(devices.values()); + } + + /** + * Returns the collection of host highlights. + * + * @return host highlights + */ + public Collection<HostHighlight> hosts() { + return Collections.unmodifiableCollection(hosts.values()); + } + + /** + * Returns the collection of link highlights. + * + * @return link highlights + */ + public Collection<LinkHighlight> links() { + return Collections.unmodifiableCollection(links.values()); + } + + /** + * Returns the amount by which all other elements not explicitly + * referenced here should be "subdued". + * + * @return amount to subdue other elements + */ + public Amount subdueLevel() { + return subdueLevel; + } + + /** + * Returns the node highlight (device or host) for the given element + * identifier, or null if no match. + * + * @param id element identifier + * @return corresponding node highlight + */ + public NodeHighlight getNode(String id) { + NodeHighlight nh = devices.get(id); + return nh != null ? nh : hosts.get(id); + } + + /** + * Returns the device highlight for the given device identifier, + * or null if no match. + * + * @param id device identifier + * @return corresponding device highlight + */ + public DeviceHighlight getDevice(String id) { + return devices.get(id); + } + + /** + * Returns the host highlight for the given host identifier, + * or null if no match. + * + * @param id host identifier + * @return corresponding host highlight + */ + public HostHighlight getHost(String id) { + return hosts.get(id); + } + + /** + * Returns the link highlight for the given link identifier, + * or null if no match. + * + * @param id link identifier + * @return corresponding link highlight + */ + public LinkHighlight getLink(String id) { + return links.get(id); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/HostHighlight.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/HostHighlight.java new file mode 100644 index 00000000..76669a84 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/HostHighlight.java @@ -0,0 +1,33 @@ +/* + * 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.ui.topo; + +/** + * Denotes the highlighting to apply to a host. + */ +public class HostHighlight extends NodeHighlight { + + public HostHighlight(String hostId) { + super(TopoElementType.HOST, hostId); + } + + // TODO: implement host highlighting: + // - visual highlight + // - badging + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/LinkHighlight.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/LinkHighlight.java new file mode 100644 index 00000000..b4e43304 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/LinkHighlight.java @@ -0,0 +1,147 @@ +/* + * 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.ui.topo; + +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Denotes the highlighting to be applied to a link. + * {@link Flavor} is a closed set of NO-, PRIMARY-, or SECONDARY- highlighting. + * {@link Mod} is an open ended set of additional modifications (CSS classes) + * that may also be applied. + * Note that {@link #MOD_OPTICAL} and {@link #MOD_ANIMATED} are pre-defined mods. + * Label text may be set, which will also be displayed on the link. + */ +public class LinkHighlight extends AbstractHighlight { + + private static final String PLAIN = "plain"; + private static final String PRIMARY = "primary"; + private static final String SECONDARY = "secondary"; + private static final String EMPTY = ""; + private static final String SPACE = " "; + + private final Flavor flavor; + private final Set<Mod> mods = new TreeSet<>(); + private String label = EMPTY; + + /** + * Constructs a link highlight entity. + * + * @param linkId the link identifier + * @param flavor the highlight flavor + */ + public LinkHighlight(String linkId, Flavor flavor) { + super(TopoElementType.LINK, linkId); + this.flavor = checkNotNull(flavor); + } + + /** + * Adds a highlighting modification to this link highlight. + * + * @param mod mod to be added + * @return self, for chaining + */ + public LinkHighlight addMod(Mod mod) { + mods.add(checkNotNull(mod)); + return this; + } + + /** + * Adds a label to be displayed on the link. + * + * @param label the label text + * @return self, for chaining + */ + public LinkHighlight setLabel(String label) { + this.label = label == null ? EMPTY : label; + return this; + } + + /** + * Returns the highlight flavor. + * + * @return highlight flavor + */ + public Flavor flavor() { + return flavor; + } + + /** + * Returns the highlight modifications. + * + * @return highlight modifications + */ + public Set<Mod> mods() { + return Collections.unmodifiableSet(mods); + } + + /** + * Generates the CSS classes string from the {@link #flavor} and + * any optional {@link #mods}. + * + * @return CSS classes string + */ + public String cssClasses() { + StringBuilder sb = new StringBuilder(flavor.toString()); + mods.forEach(m -> sb.append(SPACE).append(m)); + return sb.toString(); + } + + /** + * Returns the label text. + * + * @return label text + */ + public String label() { + return label; + } + + /** + * Link highlighting flavor. + */ + public enum Flavor { + NO_HIGHLIGHT(PLAIN), + PRIMARY_HIGHLIGHT(PRIMARY), + SECONDARY_HIGHLIGHT(SECONDARY); + + private String cssName; + + Flavor(String s) { + cssName = s; + } + + @Override + public String toString() { + return cssName; + } + } + + /** + * Denotes a link to be tagged as an optical link. + */ + public static final Mod MOD_OPTICAL = new Mod("optical"); + + /** + * Denotes a link to be tagged with animated traffic ("marching ants"). + */ + public static final Mod MOD_ANIMATED = new Mod("animated"); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/Mod.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/Mod.java new file mode 100644 index 00000000..d21a8724 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/Mod.java @@ -0,0 +1,66 @@ +/* + * 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.ui.topo; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Highlighting modification. + * <p> + * Note that (for link highlights) this translates to a CSS class name + * that is applied to the link in the Topology UI. + */ +public final class Mod implements Comparable<Mod> { + private final String modId; + + /** + * Constructs a mod with the given identifier. + * + * @param modId modification identifier + */ + public Mod(String modId) { + this.modId = checkNotNull(modId); + } + + @Override + public String toString() { + return modId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Mod mod = (Mod) o; + return modId.equals(mod.modId); + } + + @Override + public int hashCode() { + return modId.hashCode(); + } + + @Override + public int compareTo(Mod o) { + return this.modId.compareTo(o.modId); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeHighlight.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeHighlight.java new file mode 100644 index 00000000..735f8166 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeHighlight.java @@ -0,0 +1,27 @@ +/* + * 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.ui.topo; + +/** + * Parent class of {@link DeviceHighlight} and {@link HostHighlight}. + */ +public abstract class NodeHighlight extends AbstractHighlight { + public NodeHighlight(TopoElementType type, String elementId) { + super(type, elementId); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeSelection.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeSelection.java new file mode 100644 index 00000000..b284de1b --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeSelection.java @@ -0,0 +1,252 @@ +/* + * 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.ui.topo; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onosproject.net.Device; +import org.onosproject.net.Element; +import org.onosproject.net.Host; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.host.HostService; +import org.onosproject.ui.JsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.HostId.hostId; + +/** + * Encapsulates a selection of devices and/or hosts from the topology view. + */ +public class NodeSelection { + + private static final Logger log = + LoggerFactory.getLogger(NodeSelection.class); + + private static final String IDS = "ids"; + private static final String HOVER = "hover"; + + private final DeviceService deviceService; + private final HostService hostService; + + private final Set<String> ids; + private final String hover; + + private final Set<Device> devices = new HashSet<>(); + private final Set<Host> hosts = new HashSet<>(); + private Element hovered; + + /** + * Creates a node selection entity, from the given payload, using the + * supplied device and host services. Note that if a device or host was + * hovered over by the mouse, it is available via {@link #hovered()}. + * + * @param payload message payload + * @param deviceService device service + * @param hostService host service + */ + public NodeSelection(ObjectNode payload, + DeviceService deviceService, + HostService hostService) { + this.deviceService = deviceService; + this.hostService = hostService; + + ids = extractIds(payload); + hover = extractHover(payload); + + // start by extracting the hovered element if any + if (isNullOrEmpty(hover)) { + hovered = null; + } else { + setHoveredElement(); + } + + // now go find the devices and hosts that are in the selection list + Set<String> unmatched = findDevices(ids); + unmatched = findHosts(unmatched); + if (unmatched.size() > 0) { + log.debug("Skipping unmatched IDs {}", unmatched); + } + + } + + /** + * Returns a view of the selected devices (hover not included). + * + * @return selected devices + */ + public Set<Device> devices() { + return Collections.unmodifiableSet(devices); + } + + /** + * Returns a view of the selected devices, including the hovered device + * if there was one. + * + * @return selected (plus hovered) devices + */ + public Set<Device> devicesWithHover() { + Set<Device> withHover; + if (hovered != null && hovered instanceof Device) { + withHover = new HashSet<>(devices); + withHover.add((Device) hovered); + } else { + withHover = devices; + } + return Collections.unmodifiableSet(withHover); + } + + /** + * Returns a view of the selected hosts (hover not included). + * + * @return selected hosts + */ + public Set<Host> hosts() { + return Collections.unmodifiableSet(hosts); + } + + /** + * Returns a view of the selected hosts, including the hovered host + * if thee was one. + * + * @return selected (plus hovered) hosts + */ + public Set<Host> hostsWithHover() { + Set<Host> withHover; + if (hovered != null && hovered instanceof Host) { + withHover = new HashSet<>(hosts); + withHover.add((Host) hovered); + } else { + withHover = hosts; + } + return Collections.unmodifiableSet(withHover); + } + + /** + * Returns the element (host or device) over which the mouse was hovering, + * or null. + * + * @return element hovered over + */ + public Element hovered() { + return hovered; + } + + /** + * Returns true if nothing is selected. + * + * @return true if nothing selected + */ + public boolean none() { + return devices().size() == 0 && hosts().size() == 0; + } + + @Override + public String toString() { + return "NodeSelection{" + + "ids=" + ids + + ", hover='" + hover + '\'' + + ", #devices=" + devices.size() + + ", #hosts=" + hosts.size() + + '}'; + } + + // == helper methods + + private Set<String> extractIds(ObjectNode payload) { + ArrayNode array = (ArrayNode) payload.path(IDS); + if (array == null || array.size() == 0) { + return Collections.emptySet(); + } + + Set<String> ids = new HashSet<>(); + for (JsonNode node : array) { + ids.add(node.asText()); + } + return ids; + } + + private String extractHover(ObjectNode payload) { + return JsonUtils.string(payload, HOVER); + } + + private void setHoveredElement() { + Set<String> unmatched; + unmatched = new HashSet<>(); + unmatched.add(hover); + unmatched = findDevices(unmatched); + if (devices.size() == 1) { + hovered = devices.iterator().next(); + devices.clear(); + } else { + unmatched = findHosts(unmatched); + if (hosts.size() == 1) { + hovered = hosts.iterator().next(); + hosts.clear(); + } else { + hovered = null; + log.debug("Skipping unmatched HOVER {}", unmatched); + } + } + } + + private Set<String> findDevices(Set<String> ids) { + Set<String> unmatched = new HashSet<>(); + Device device; + + for (String id : ids) { + try { + device = deviceService.getDevice(deviceId(id)); + if (device != null) { + devices.add(device); + } else { + unmatched.add(id); + } + } catch (Exception e) { + unmatched.add(id); + } + } + return unmatched; + } + + private Set<String> findHosts(Set<String> ids) { + Set<String> unmatched = new HashSet<>(); + Host host; + + for (String id : ids) { + try { + host = hostService.getHost(hostId(id)); + if (host != null) { + hosts.add(host); + } else { + unmatched.add(id); + } + } catch (Exception e) { + unmatched.add(id); + } + } + return unmatched; + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java new file mode 100644 index 00000000..121e0834 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java @@ -0,0 +1,353 @@ +/* + * 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.ui.topo; + +import com.google.common.collect.Sets; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Models a panel displayed on the Topology View. + */ +public class PropertyPanel { + + private static final DecimalFormat DF0 = new DecimalFormat("#,###"); + + private String title; + private String typeId; + private String id; + private List<Prop> properties = new ArrayList<>(); + private List<ButtonId> buttons = new ArrayList<>(); + + /** + * Constructs a property panel model with the given title and + * type identifier (icon to display). + * + * @param title title text + * @param typeId type (icon) ID + */ + public PropertyPanel(String title, String typeId) { + this.title = title; + this.typeId = typeId; + } + + /** + * Adds an ID field to the panel data, to be included in + * the returned JSON data to the client. + * + * @param id the identifier + * @return self, for chaining + */ + public PropertyPanel id(String id) { + this.id = id; + return this; + } + + /** + * Adds a property to the panel data. + * + * @param key property key + * @param value property value + * @return self, for chaining + */ + public PropertyPanel addProp(String key, String value) { + properties.add(new Prop(key, value)); + return this; + } + + /** + * Adds a property to the panel data, using a decimal formatter. + * + * @param key property key + * @param value property value + * @return self, for chaining + */ + public PropertyPanel addProp(String key, int value) { + properties.add(new Prop(key, DF0.format(value))); + return this; + } + + /** + * Adds a property to the panel data, using a decimal formatter. + * + * @param key property key + * @param value property value + * @return self, for chaining + */ + public PropertyPanel addProp(String key, long value) { + properties.add(new Prop(key, DF0.format(value))); + return this; + } + + /** + * Adds a property to the panel data. Note that the value's + * {@link Object#toString toString()} method is used to convert the + * value to a string. + * + * @param key property key + * @param value property value + * @return self, for chaining + */ + public PropertyPanel addProp(String key, Object value) { + properties.add(new Prop(key, value.toString())); + return this; + } + + /** + * Adds a property to the panel data. Note that the value's + * {@link Object#toString toString()} method is used to convert the + * value to a string, from which the characters defined in the given + * regular expression string are stripped. + * + * @param key property key + * @param value property value + * @param reStrip regexp characters to strip from value string + * @return self, for chaining + */ + public PropertyPanel addProp(String key, Object value, String reStrip) { + String val = value.toString().replaceAll(reStrip, ""); + properties.add(new Prop(key, val)); + return this; + } + + /** + * Adds a separator to the panel data. + * + * @return self, for chaining + */ + public PropertyPanel addSeparator() { + properties.add(new Separator()); + return this; + } + + /** + * Returns the title text. + * + * @return title text + */ + public String title() { + return title; + } + + /** + * Returns the type identifier. + * + * @return type identifier + */ + public String typeId() { + return typeId; + } + + /** + * Returns the internal ID. + * + * @return the ID + */ + public String id() { + return id; + } + + /** + * Returns the list of properties to be displayed. + * + * @return the property list + */ + // TODO: consider protecting this? + public List<Prop> properties() { + return properties; + } + + /** + * Returns the list of button descriptors. + * + * @return the button list + */ + // TODO: consider protecting this? + public List<ButtonId> buttons() { + return buttons; + } + + // == MUTATORS + + /** + * Sets the title text. + * + * @param title title text + * @return self, for chaining + */ + public PropertyPanel title(String title) { + this.title = title; + return this; + } + + /** + * Sets the type identifier (icon ID). + * + * @param typeId type identifier + * @return self, for chaining + */ + public PropertyPanel typeId(String typeId) { + this.typeId = typeId; + return this; + } + + /** + * Removes properties with the given keys from the list. + * + * @param keys keys of properties to remove + * @return self, for chaining + */ + public PropertyPanel removeProps(String... keys) { + Set<String> forRemoval = Sets.newHashSet(keys); + List<Prop> toKeep = new ArrayList<>(); + for (Prop p: properties) { + if (!forRemoval.contains(p.key())) { + toKeep.add(p); + } + } + properties = toKeep; + return this; + } + + /** + * Removes all currently defined properties. + * + * @return self, for chaining + */ + public PropertyPanel removeAllProps() { + properties.clear(); + return this; + } + + /** + * Adds the given button descriptor to the panel data. + * + * @param button button descriptor + * @return self, for chaining + */ + public PropertyPanel addButton(ButtonId button) { + buttons.add(button); + return this; + } + + /** + * Removes buttons with the given descriptors from the list. + * + * @param descriptors descriptors to remove + * @return self, for chaining + */ + public PropertyPanel removeButtons(ButtonId... descriptors) { + Set<ButtonId> forRemoval = Sets.newHashSet(descriptors); + List<ButtonId> toKeep = new ArrayList<>(); + for (ButtonId bd: buttons) { + if (!forRemoval.contains(bd)) { + toKeep.add(bd); + } + } + buttons = toKeep; + return this; + } + + /** + * Removes all currently defined buttons. + * + * @return self, for chaining + */ + public PropertyPanel removeAllButtons() { + buttons.clear(); + return this; + } + + // ==================== + + + /** + * Simple data carrier for a property, composed of a key/value pair. + */ + public static class Prop { + private final String key; + private final String value; + + /** + * Constructs a property data value. + * + * @param key property key + * @param value property value + */ + public Prop(String key, String value) { + this.key = key; + this.value = value; + } + + /** + * Returns the property's key. + * + * @return the key + */ + public String key() { + return key; + } + + /** + * Returns the property's value. + * + * @return the value + */ + public String value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Prop prop = (Prop) o; + return key.equals(prop.key) && value.equals(prop.value); + } + + @Override + public int hashCode() { + int result = key.hashCode(); + result = 31 * result + value.hashCode(); + return result; + } + + @Override + public String toString() { + return "{" + key + " -> " + value + "}"; + } + } + + /** + * Auxiliary class representing a separator property. + */ + public static class Separator extends Prop { + public Separator() { + super("-", ""); + } + } + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java new file mode 100644 index 00000000..38a8f036 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java @@ -0,0 +1,129 @@ +/* + * 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.ui.topo; + +/** + * Defines string constants used in the Topology View of the ONOS GUI. + * <p> + * See also: + * <ul> + * <li> https://wiki.onosproject.org/display/ONOS/UI+Service+-+GlyphService </li> + * </ul> + */ +public final class TopoConstants { + + /** + * Defines constants for standard glyph identifiers. + */ + public static final class Glyphs { + public static final String UNKNOWN = "unknown"; + public static final String BIRD = "bird"; + public static final String NODE = "node"; + public static final String SWITCH = "switch"; + public static final String ROADM = "roadm"; + public static final String ENDSTATION = "endstation"; + public static final String ROUTER = "router"; + public static final String BGP_SPEAKER = "bgpSpeaker"; + public static final String CHAIN = "chain"; + public static final String CROWN = "crown"; + public static final String TOPO = "topo"; + public static final String REFRESH = "refresh"; + public static final String GARBAGE = "garbage"; + public static final String FLOW_TABLE = "flowTable"; + public static final String PORT_TABLE = "portTable"; + public static final String GROUP_TABLE = "groupTable"; + public static final String SUMMARY = "summary"; + public static final String DETAILS = "details"; + public static final String PORTS = "ports"; + public static final String MAP = "map"; + public static final String CYCLE_LABELS = "cycleLabels"; + public static final String OBLIQUE = "oblique"; + public static final String FILTERS = "filters"; + public static final String RESET_ZOOM = "resetZoom"; + public static final String RELATED_INTENTS = "relatedIntents"; + public static final String NEXT_INTENT = "nextIntent"; + public static final String PREV_INTENT = "prevIntent"; + public static final String INTENT_TRAFFIC = "intentTraffic"; + public static final String ALL_TRAFFIC = "allTraffic"; + public static final String FLOWS = "flows"; + public static final String EQ_MASTER = "eqMaster"; + public static final String UI_ATTACHED = "uiAttached"; + public static final String CHECK_MARK = "checkMark"; + public static final String X_MARK = "xMark"; + public static final String TRIANGLE_UP = "triangleUp"; + public static final String TRIANGLE_DOWN = "triangleDown"; + public static final String PLUS = "plus"; + public static final String MINUS = "minus"; + public static final String PLAY = "play"; + public static final String STOP = "stop"; + public static final String CLOUD = "cloud"; + } + + /** + * Defines constants for property names on the default summary and + * details panels. + */ + public static final class Properties { + public static final String SEPARATOR = "-"; + + // summary panel + public static final String DEVICES = "Devices"; + public static final String LINKS = "Links"; + public static final String HOSTS = "Hosts"; + public static final String TOPOLOGY_SSCS = "Topology SCCs"; + public static final String INTENTS = "Intents"; + public static final String TUNNELS = "Tunnels"; + public static final String FLOWS = "Flows"; + public static final String VERSION = "Version"; + + // device details + public static final String URI = "URI"; + public static final String VENDOR = "Vendor"; + public static final String HW_VERSION = "H/W Version"; + public static final String SW_VERSION = "S/W Version"; + public static final String SERIAL_NUMBER = "Serial Number"; + public static final String PROTOCOL = "Protocol"; + public static final String LATITUDE = "Latitude"; + public static final String LONGITUDE = "Longitude"; + public static final String PORTS = "Ports"; + + // host details + public static final String MAC = "MAC"; + public static final String IP = "IP"; + public static final String VLAN = "VLAN"; + } + + /** + * Defines identities of core buttons that appear on the topology + * details panel. + */ + public static final class CoreButtons { + public static final ButtonId SHOW_DEVICE_VIEW = + new ButtonId("showDeviceView"); + + public static final ButtonId SHOW_FLOW_VIEW = + new ButtonId("showFlowView"); + + public static final ButtonId SHOW_PORT_VIEW = + new ButtonId("showPortView"); + + public static final ButtonId SHOW_GROUP_VIEW = + new ButtonId("showGroupView"); + } + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoElementType.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoElementType.java new file mode 100644 index 00000000..dc327464 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoElementType.java @@ -0,0 +1,25 @@ +/* + * 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.ui.topo; + +/** + * The topology element types to which a highlight can be applied. + */ +public enum TopoElementType { + DEVICE, HOST, LINK +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java new file mode 100644 index 00000000..a94068ee --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java @@ -0,0 +1,160 @@ +/* + * 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.ui.topo; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import static org.onosproject.ui.JsonUtils.envelope; + +/** + * JSON utilities for the Topology View. + */ +public final class TopoJson { + // package-private for unit test access + static final String SHOW_HIGHLIGHTS = "showHighlights"; + + static final String DEVICES = "devices"; + static final String HOSTS = "hosts"; + static final String LINKS = "links"; + static final String SUBDUE = "subdue"; + + static final String ID = "id"; + static final String LABEL = "label"; + static final String CSS = "css"; + + static final String TITLE = "title"; + static final String TYPE = "type"; + static final String PROP_ORDER = "propOrder"; + static final String PROPS = "props"; + static final String BUTTONS = "buttons"; + + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private static ObjectNode objectNode() { + return MAPPER.createObjectNode(); + } + + private static ArrayNode arrayNode() { + return MAPPER.createArrayNode(); + } + + // non-instantiable + private TopoJson() { } + + /** + * Returns a formatted message ready to send to the topology view + * to render highlights. + * + * @param highlights highlights model to transform + * @return fully formatted "show highlights" message + */ + public static ObjectNode highlightsMessage(Highlights highlights) { + return envelope(SHOW_HIGHLIGHTS, json(highlights)); + } + + /** + * Transforms the given highlights model into a JSON message payload. + * + * @param highlights the model to transform + * @return JSON payload + */ + public static ObjectNode json(Highlights highlights) { + ObjectNode payload = objectNode(); + + ArrayNode devices = arrayNode(); + ArrayNode hosts = arrayNode(); + ArrayNode links = arrayNode(); + + payload.set(DEVICES, devices); + payload.set(HOSTS, hosts); + payload.set(LINKS, links); + + highlights.devices().forEach(dh -> devices.add(json(dh))); + highlights.hosts().forEach(hh -> hosts.add(json(hh))); + highlights.links().forEach(lh -> links.add(json(lh))); + + Highlights.Amount toSubdue = highlights.subdueLevel(); + if (!toSubdue.equals(Highlights.Amount.ZERO)) { + payload.put(SUBDUE, toSubdue.toString()); + } + return payload; + } + + private static ObjectNode json(DeviceHighlight dh) { + ObjectNode n = objectNode() + .put(ID, dh.elementId()); + if (dh.subdued()) { + n.put(SUBDUE, true); + } + return n; + } + + private static ObjectNode json(HostHighlight hh) { + ObjectNode n = objectNode() + .put(ID, hh.elementId()); + if (hh.subdued()) { + n.put(SUBDUE, true); + } + return n; + } + + private static ObjectNode json(LinkHighlight lh) { + ObjectNode n = objectNode() + .put(ID, lh.elementId()) + .put(LABEL, lh.label()) + .put(CSS, lh.cssClasses()); + if (lh.subdued()) { + n.put(SUBDUE, true); + } + return n; + } + + /** + * Translates the given property panel into JSON, for returning + * to the client. + * + * @param pp the property panel model + * @return JSON payload + */ + public static ObjectNode json(PropertyPanel pp) { + ObjectNode result = objectNode() + .put(TITLE, pp.title()) + .put(TYPE, pp.typeId()) + .put(ID, pp.id()); + + ObjectNode pnode = objectNode(); + ArrayNode porder = arrayNode(); + for (PropertyPanel.Prop p : pp.properties()) { + porder.add(p.key()); + pnode.put(p.key(), p.value()); + } + result.set(PROP_ORDER, porder); + result.set(PROPS, pnode); + + ArrayNode buttons = arrayNode(); + for (ButtonId b : pp.buttons()) { + buttons.add(b.id()); + } + result.set(BUTTONS, buttons); + return result; + } + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoUtils.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoUtils.java new file mode 100644 index 00000000..f92d5798 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoUtils.java @@ -0,0 +1,159 @@ +/* + * 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.ui.topo; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; + +import java.text.DecimalFormat; + +import static org.onosproject.net.LinkKey.linkKey; + +/** + * Utility methods for helping out with formatting data for the Topology View + * in the web client. + */ +public final class TopoUtils { + + // explicit decision made to not 'javadoc' these self explanatory constants + public static final double KILO = 1024; + public static final double MEGA = 1024 * KILO; + public static final double GIGA = 1024 * MEGA; + + public static final String GBITS_UNIT = "Gb"; + public static final String MBITS_UNIT = "Mb"; + public static final String KBITS_UNIT = "Kb"; + public static final String BITS_UNIT = "b"; + public static final String GBYTES_UNIT = "GB"; + public static final String MBYTES_UNIT = "MB"; + public static final String KBYTES_UNIT = "KB"; + public static final String BYTES_UNIT = "B"; + + + private static final DecimalFormat DF2 = new DecimalFormat("#,###.##"); + + private static final String COMPACT = "%s/%s-%s/%s"; + private static final String EMPTY = ""; + private static final String SPACE = " "; + private static final String PER_SEC = "ps"; + private static final String FLOW = "flow"; + private static final String FLOWS = "flows"; + + // non-instantiable + private TopoUtils() { } + + /** + * Returns a compact identity for the given link, in the form + * used to identify links in the Topology View on the client. + * + * @param link link + * @return compact link identity + */ + public static String compactLinkString(Link link) { + return String.format(COMPACT, link.src().elementId(), link.src().port(), + link.dst().elementId(), link.dst().port()); + } + + /** + * Produces a canonical link key, that is, one that will match both a link + * and its inverse. + * + * @param link the link + * @return canonical key + */ + public static LinkKey canonicalLinkKey(Link link) { + String sn = link.src().elementId().toString(); + String dn = link.dst().elementId().toString(); + return sn.compareTo(dn) < 0 ? + linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src()); + } + + /** + * Returns human readable count of bytes, to be displayed as a label. + * + * @param bytes number of bytes + * @return formatted byte count + */ + public static String formatBytes(long bytes) { + String unit; + double value; + if (bytes > GIGA) { + value = bytes / GIGA; + unit = GBYTES_UNIT; + } else if (bytes > MEGA) { + value = bytes / MEGA; + unit = MBYTES_UNIT; + } else if (bytes > KILO) { + value = bytes / KILO; + unit = KBYTES_UNIT; + } else { + value = bytes; + unit = BYTES_UNIT; + } + return DF2.format(value) + SPACE + unit; + } + + /** + * Returns human readable bit rate, to be displayed as a label. + * + * @param bytes bytes per second + * @return formatted bits per second + */ + public static String formatBitRate(long bytes) { + String unit; + double value; + + //Convert to bits + long bits = bytes * 8; + if (bits > GIGA) { + value = bits / GIGA; + unit = GBITS_UNIT; + + // NOTE: temporary hack to clip rate at 10.0 Gbps + // Added for the CORD Fabric demo at ONS 2015 + // TODO: provide a more elegant solution to this issue + if (value > 10.0) { + value = 10.0; + } + + } else if (bits > MEGA) { + value = bits / MEGA; + unit = MBITS_UNIT; + } else if (bits > KILO) { + value = bits / KILO; + unit = KBITS_UNIT; + } else { + value = bits; + unit = BITS_UNIT; + } + return DF2.format(value) + SPACE + unit + PER_SEC; + } + + /** + * Returns human readable flow count, to be displayed as a label. + * + * @param flows number of flows + * @return formatted flow count + */ + public static String formatFlows(long flows) { + if (flows < 1) { + return EMPTY; + } + return String.valueOf(flows) + SPACE + (flows > 1 ? FLOWS : FLOW); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/package-info.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/package-info.java new file mode 100644 index 00000000..85ac7fea --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +/** + * Mechanism for dynamically extending topology view with information and + * behaviour overlays. + */ +package org.onosproject.ui.topo;
\ No newline at end of file |