Prelimary stuff for REST error reporting

Change-Id: Ie562498d0a9394c3e839ceb463e9beab9e1ecff2
diff --git a/src/main/java/net/onrc/onos/api/rest/RestError.java b/src/main/java/net/onrc/onos/api/rest/RestError.java
new file mode 100644
index 0000000..8b958de
--- /dev/null
+++ b/src/main/java/net/onrc/onos/api/rest/RestError.java
@@ -0,0 +1,60 @@
+package net.onrc.onos.api.rest;
+
+/**
+ * Describes a REST error.
+ * code is a unique identifier for the error.
+ * summary is indended to be a short description of what happened.
+ * description is a long description of the problem, and can be formatted using
+ * variable replacement.  Variable placeholders are indicated with the string
+ * "{}" in the description.
+ * Objects of this class are immutable.
+ */
+
+public final class RestError {
+
+    private final RestErrorCodes.RestErrorCode code;
+    private final String summary;
+    private final String description;
+
+    /**
+     * Constructs a new RestError object from a code, summary and description.
+     *
+     * @param newCode code for the new error
+     * @param newSummary short summary for the new error
+     * @param newDescription formatable description for the new error
+     */
+    public RestError(final RestErrorCodes.RestErrorCode newCode,
+                     final String newSummary,
+                     final String newDescription) {
+        code = newCode;
+        summary = newSummary;
+        description = newDescription;
+    }
+
+    /**
+     * Gets the summary of the error.
+     *
+     * @return string for the summary
+     */
+    public String getSummary() {
+        return summary;
+    }
+
+    /**
+     * Gets the unique code for this error.
+     *
+     * @return unique code
+     */
+    public RestErrorCodes.RestErrorCode getCode() {
+        return code;
+    }
+
+    /**
+     * Gets the unformatted description string for the error.
+     *
+     * @return the unformatted description string.
+     */
+    public String getDescription() {
+        return description;
+    }
+}
diff --git a/src/main/java/net/onrc/onos/api/rest/RestErrorCatalog.java b/src/main/java/net/onrc/onos/api/rest/RestErrorCatalog.java
new file mode 100644
index 0000000..d81805f
--- /dev/null
+++ b/src/main/java/net/onrc/onos/api/rest/RestErrorCatalog.java
@@ -0,0 +1,84 @@
+package net.onrc.onos.api.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Maintains a catalog of RestErrors and allows lookup by error code.
+ */
+public final class RestErrorCatalog {
+
+    /**
+     * Hide the default constructor of a utility class.
+     */
+    private RestErrorCatalog() { }
+
+    /**
+     * Static list of known errors.  Someday this will be read in from an
+     * external file.
+     */
+    private static final RestError[] ERROR_LIST = {
+        new RestError(RestErrorCodes.RestErrorCode.INTENT_NOT_FOUND,
+                      "Intent not found",
+                      "An intent with the identifier {} was not found."),
+        new RestError(RestErrorCodes.RestErrorCode.INTENT_ALREADY_EXISTS,
+                      "Intent already exists",
+                      "An intent with the identifier {} could not be created " +
+                      "because one already exists."),
+        new RestError(RestErrorCodes.RestErrorCode.INTENT_NO_PATH,
+                      "No path found",
+                      "No path found between {} and {}"),
+
+    };
+
+    /**
+     * Singleton implementation using the demand holder idiom.
+     */
+    private static class RestErrorMapHolder {
+        /**
+         * Load up the error map.
+         *
+         * @return REST error map
+         */
+        private static Map<Integer, RestError> initializeRestErrorMap() {
+            restErrorMap = new HashMap<>();
+            for (final RestError restError : ERROR_LIST) {
+                restErrorMap.put(restError.getCode().ordinal(), restError);
+            }
+            return restErrorMap;
+        }
+
+        /**
+         * Fetch the singleton map.
+         *
+         * @return map of the Rest Errors that was created from the known error
+         * list.
+         */
+        public static Map<Integer, RestError> getRestErrorMap() {
+            return restErrorMap;
+        }
+
+
+        private static Map<Integer, RestError> restErrorMap = initializeRestErrorMap();
+    }
+
+    /**
+     * Fetch the map of REST errors.
+     *
+     * @return map of possible REST errors.
+     */
+    public static Map<Integer, RestError> getRestErrorMap() {
+        return RestErrorMapHolder.getRestErrorMap();
+    }
+
+    /**
+     * Fetch the RestError for the given code.
+     *
+     * @param code the code for the message to look up.
+     * @return the REST error for the code if one exists, null if it does not
+     *         exist.
+     */
+    public static RestError getRestError(final RestErrorCodes.RestErrorCode code) {
+        return getRestErrorMap().get(code.ordinal());
+    }
+}
diff --git a/src/main/java/net/onrc/onos/api/rest/RestErrorCodes.java b/src/main/java/net/onrc/onos/api/rest/RestErrorCodes.java
new file mode 100644
index 0000000..1d34615
--- /dev/null
+++ b/src/main/java/net/onrc/onos/api/rest/RestErrorCodes.java
@@ -0,0 +1,30 @@
+package net.onrc.onos.api.rest;
+
+/**
+ * Holder class for constants that describe the REST error codes.
+ */
+public final class RestErrorCodes {
+
+    /**
+     * Hidden default constructor for utility class.
+     */
+    private RestErrorCodes() { }
+
+    /**
+     * Enumeration of the ONOS defined error codes.
+     */
+    public enum RestErrorCode {
+
+        // TODO: These are made up just for testing purposes.
+
+        /** Intent not found. */
+        INTENT_NOT_FOUND,
+
+        /** Intent already exists. */
+        INTENT_ALREADY_EXISTS,
+
+        /** No path available for the Intent. */
+        INTENT_NO_PATH
+    }
+
+}
diff --git a/src/main/java/net/onrc/onos/api/rest/RestErrorFormatter.java b/src/main/java/net/onrc/onos/api/rest/RestErrorFormatter.java
new file mode 100644
index 0000000..edc39b2
--- /dev/null
+++ b/src/main/java/net/onrc/onos/api/rest/RestErrorFormatter.java
@@ -0,0 +1,32 @@
+package net.onrc.onos.api.rest;
+
+import org.slf4j.helpers.FormattingTuple;
+import org.slf4j.helpers.MessageFormatter;
+
+/**
+ * Utility class used for formatting Rest Error descriptions.
+ */
+public final class RestErrorFormatter {
+
+    /**
+     * Hide default constructor for utility classes.
+     */
+    private RestErrorFormatter() { }
+
+    /**
+     * Takes a RestError template and formats the description using a supplied
+     * list of replacement parameters.
+     *
+     * @param error the RestError to format
+     * @param parameters parameter list to use as positional parameters in the
+     *                   result string
+     *
+     * @return the String object for the formatted message.
+     */
+    static String formatErrorMessage(final RestError error,
+                                     final Object... parameters) {
+        final FormattingTuple formattingResult =
+                MessageFormatter.arrayFormat(error.getDescription(), parameters);
+        return formattingResult.getMessage();
+    }
+}
diff --git a/src/test/java/net/onrc/onos/api/rest/RestErrorTest.java b/src/test/java/net/onrc/onos/api/rest/RestErrorTest.java
new file mode 100644
index 0000000..f505740
--- /dev/null
+++ b/src/test/java/net/onrc/onos/api/rest/RestErrorTest.java
@@ -0,0 +1,81 @@
+package net.onrc.onos.api.rest;
+
+import org.junit.Test;
+
+import static net.onrc.onos.core.util.UtilityClassChecker.assertThatClassIsUtility;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+
+/**
+ * Unit tests for REST error handling classes.
+ */
+public class RestErrorTest {
+
+    /**
+     * Test the formatting of a REST error that contains a single
+     * positional parameter.
+     */
+    @Test
+    public void testRestErrorFormatting1Parameter() {
+        final RestError restError =
+                RestErrorCatalog.getRestError(RestErrorCodes.RestErrorCode.INTENT_ALREADY_EXISTS);
+        assertThat(restError, is(notNullValue()));
+
+        final String formattedError =
+                RestErrorFormatter.formatErrorMessage(restError,
+                                                      "INTENT-ID");
+        assertThat(formattedError, is(notNullValue()));
+
+        final String expectedFormattedString =
+                "An intent with the identifier INTENT-ID could not be created " +
+                "because one already exists.";
+        assertThat(formattedError, is(equalTo(expectedFormattedString)));
+
+    }
+
+    /**
+     * Test the formatting of a REST error that contains two
+     * positional parameters.
+     */
+    @Test
+    public void testRestErrorFormatting2Parameters() {
+        final RestError restError =
+                RestErrorCatalog.getRestError(RestErrorCodes.RestErrorCode.INTENT_NO_PATH);
+        assertThat(restError, is(notNullValue()));
+
+        final String formattedError =
+                RestErrorFormatter.formatErrorMessage(restError,
+                        "Switch1", "Switch2");
+        assertThat(formattedError, is(notNullValue()));
+
+        final String expectedFormattedString =
+                "No path found between Switch1 and Switch2";
+        assertThat(formattedError, is(equalTo(expectedFormattedString)));
+
+    }
+
+    /**
+     * Make sure that the error formatter is a utility class.
+     */
+    @Test
+    public void assureThatErrorFormatterIsUtility() {
+        assertThatClassIsUtility(RestErrorFormatter.class);
+    }
+
+    /**
+     * Make sure that the error catalog is a utility class.
+     */
+    @Test
+    public void assureThatErrorCatalogIsUtility() {
+        assertThatClassIsUtility(RestErrorCatalog.class);
+    }
+
+    /**
+     * Make sure that the error codes is a utility class.
+     */
+    @Test
+    public void assureThatErrorCodesIsUtility() {
+        assertThatClassIsUtility(RestErrorCodes.class);
+    }
+}