Refactor construction of application objects

- Use a builder and make the constructors private for DefaultApplication
- Make DefaultApplication immutable
- Use a builder and make the constructors private for DefaultApplicationDescription
- Make DefaultApplicationDescription immutable

Change-Id: I9499981bd2c0f48aede40682260d51eeae2cab98
diff --git a/core/api/src/main/java/org/onosproject/core/DefaultApplication.java b/core/api/src/main/java/org/onosproject/core/DefaultApplication.java
index f780623..1eb83b4 100644
--- a/core/api/src/main/java/org/onosproject/core/DefaultApplication.java
+++ b/core/api/src/main/java/org/onosproject/core/DefaultApplication.java
@@ -17,6 +17,8 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+
+import org.onosproject.app.ApplicationDescription;
 import org.onosproject.security.Permission;
 
 import java.net.URI;
@@ -32,7 +34,7 @@
 /**
  * Default implementation of network control/management application descriptor.
  */
-public class DefaultApplication implements Application {
+public final class DefaultApplication implements Application {
 
     private final ApplicationId appId;
     private final Version version;
@@ -50,6 +52,14 @@
     private final List<String> requiredApps;
 
     /**
+     * Default constructor is hidden to prevent calls to new.
+     */
+    private DefaultApplication() {
+        // should never happen
+        throw new UnsupportedOperationException();
+    }
+
+    /**
      * Creates a new application descriptor using the supplied data.
      *
      * @param appId        application identifier
@@ -67,33 +77,26 @@
      * @param features     application features
      * @param requiredApps list of required application names
      */
-    public DefaultApplication(ApplicationId appId, Version version, String title,
+    private DefaultApplication(ApplicationId appId, Version version, String title,
                               String description, String origin, String category,
                               String url, String readme, byte[] icon,
                               ApplicationRole role, Set<Permission> permissions,
                               Optional<URI> featuresRepo, List<String> features,
                               List<String> requiredApps) {
-        this.appId = checkNotNull(appId, "ID cannot be null");
-        this.version = checkNotNull(version, "Version cannot be null");
-        this.title = checkNotNull(title, "Title cannot be null");
-        this.description = checkNotNull(description, "Description cannot be null");
-        this.origin = checkNotNull(origin, "Origin cannot be null");
-        this.category = checkNotNull(category, "Category cannot be null");
+        this.appId = appId;
+        this.version = version;
+        this.title = title;
+        this.description = description;
+        this.origin = origin;
+        this.category = category;
         this.url = url;
-        this.readme = checkNotNull(readme, "Readme cannot be null");
+        this.readme = readme;
         this.icon = icon == null ? new byte[0] : icon.clone();
-        this.role = checkNotNull(role, "Role cannot be null");
-        this.permissions = ImmutableSet.copyOf(
-                checkNotNull(permissions, "Permissions cannot be null")
-        );
-        this.featuresRepo = checkNotNull(featuresRepo, "Features repo cannot be null");
-        this.features = ImmutableList.copyOf(
-                checkNotNull(features, "Features cannot be null")
-        );
-        this.requiredApps = ImmutableList.copyOf(
-                checkNotNull(requiredApps, "Required apps cannot be null")
-        );
-        checkArgument(!features.isEmpty(), "There must be at least one feature");
+        this.role = role;
+        this.permissions = ImmutableSet.copyOf(permissions);
+        this.featuresRepo = featuresRepo;
+        this.features = ImmutableList.copyOf(features);
+        this.requiredApps = ImmutableList.copyOf(requiredApps);
     }
 
     @Override
@@ -217,4 +220,317 @@
                 .add("requiredApps", requiredApps)
                 .toString();
     }
+
+    /**
+     * Returns a default application builder.
+     *
+     * @return builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Creates a new builder as a copy of an existing builder.
+     *
+     * @param builder existing builder to copy
+     * @return new builder
+     */
+    public static Builder builder(Builder builder) {
+        return new Builder(builder);
+    }
+
+    /**
+     * Creates a new builder as a copy of an existing application.
+     *
+     * @param application existing application to copy
+     * @return new builder
+     */
+    public static Builder builder(Application application) {
+        return new Builder(application);
+    }
+
+    /**
+     * Creates a new builder as a copy of an existing application description.
+     *
+     * @param appDesc existing application description
+     * @return new builder
+     */
+    public static Builder builder(ApplicationDescription appDesc) {
+        return new Builder(appDesc);
+    }
+
+
+    /**
+     * Default application builder.
+     */
+    public static final class Builder {
+
+        private ApplicationId appId;
+        private Version version;
+        private String title;
+        private String description;
+        private String category;
+        private String url;
+        private String readme;
+        private byte[] icon;
+        private String origin;
+        private ApplicationRole role;
+        private Set<Permission> permissions;
+        private Optional<URI> featuresRepo;
+        private List<String> features;
+        private List<String> requiredApps;
+
+        /**
+         * Default constructor for the builder.
+         */
+        public Builder() {}
+
+        /**
+         * Updates the builder to be a copy of an existing builder.
+         *
+         * @param builder existing builder to copy
+         */
+        public Builder(Builder builder) {
+            this.appId = builder.appId;
+            this.version = builder.version;
+            this.title = builder.title;
+            this.description = builder.description;
+            this.category = builder.category;
+            this.url = builder.url;
+            this.readme = builder.readme;
+            this.icon = builder.icon;
+            this.origin = builder.origin;
+            this.role = builder.role;
+            this.permissions = builder.permissions;
+            this.featuresRepo = builder.featuresRepo;
+            this.features = builder.features;
+            this.requiredApps = builder.requiredApps;
+        }
+
+        /**
+         * Updates the builder to be a copy of an existing application.
+         *
+         * @param application existing application to copy
+         */
+        public Builder(Application application) {
+            this.appId = application.id();
+            this.version = application.version();
+            this.title = application.title();
+            this.description = application.description();
+            this.category = application.category();
+            this.url = application.url();
+            this.readme = application.readme();
+            this.icon = application.icon();
+            this.origin = application.origin();
+            this.role = application.role();
+            this.permissions = application.permissions();
+            this.featuresRepo = application.featuresRepo();
+            this.features = application.features();
+            this.requiredApps = application.requiredApps();
+        }
+
+        /**
+         * Updates the builder to be a copy of an existing application description.
+         *
+         * @param appDesc existing application description
+         */
+        public Builder(ApplicationDescription appDesc) {
+            this.version = appDesc.version();
+            this.title = appDesc.title();
+            this.description = appDesc.description();
+            this.category = appDesc.category();
+            this.url = appDesc.url();
+            this.readme = appDesc.readme();
+            this.icon = appDesc.icon();
+            this.origin = appDesc.origin();
+            this.role = appDesc.role();
+            this.permissions = appDesc.permissions();
+            this.featuresRepo = appDesc.featuresRepo();
+            this.features = appDesc.features();
+            this.requiredApps = appDesc.requiredApps();
+        }
+
+        /**
+         * Adds an application id.
+         *
+         * @param appId application id
+         * @return builder
+         */
+        public Builder withAppId(ApplicationId appId) {
+            this.appId = appId;
+            return this;
+        }
+
+        /**
+         * Adds a version string.
+         *
+         * @param version version string
+         * @return builder
+         */
+        public Builder withVersion(Version version) {
+            this.version = version;
+            return this;
+        }
+
+        /**
+         * Adds a title string.
+         *
+         * @param title title string
+         * @return builder
+         */
+        public Builder withTitle(String title) {
+            this.title = title;
+            return this;
+        }
+
+        /**
+         * Adds a description string.
+         *
+         * @param description description string
+         * @return builder
+         */
+        public Builder withDescription(String description) {
+            this.description = description;
+            return this;
+        }
+
+        /**
+         * Adds a category string.
+         *
+         * @param category category string
+         * @return builder
+         */
+        public Builder withCategory(String category) {
+            this.category = category;
+            return this;
+        }
+
+        /**
+         * Adds a URL string.
+         *
+         * @param url url string
+         * @return builder
+         */
+        public Builder withUrl(String url) {
+            this.url = url;
+            return this;
+        }
+
+        /**
+         * Adds a readme string.
+         *
+         * @param readme readme string
+         * @return builder
+         */
+        public Builder withReadme(String readme) {
+            this.readme = readme;
+            return this;
+        }
+
+        /**
+         * Adds an icon.
+         *
+         * @param icon icon data
+         * @return builder
+         */
+        public Builder withIcon(byte[] icon) {
+            this.icon = icon;
+            return this;
+        }
+
+        /**
+         * Adds an origin string.
+         *
+         * @param origin origin string
+         * @return builder
+         */
+        public Builder withOrigin(String origin) {
+            this.origin = origin;
+            return this;
+        }
+
+        /**
+         * Adds an application role.
+         *
+         * @param role application role
+         * @return builder
+         */
+        public Builder withRole(ApplicationRole role) {
+            this.role = role;
+            return this;
+        }
+
+        /**
+         * Adds a permissions set.
+         *
+         * @param permissions permissions set
+         * @return builder
+         */
+        public Builder withPermissions(Set<Permission> permissions) {
+            this.permissions = permissions;
+            return this;
+        }
+
+        /**
+         * Adds a URI for a features repository.
+         *
+         * @param featuresRepo Optional URI for a features repository
+         * @return builder
+         */
+        public Builder withFeaturesRepo(Optional<URI> featuresRepo) {
+            this.featuresRepo = featuresRepo;
+            return this;
+        }
+
+        /**
+         * Adds a features list.
+         *
+         * @param features features list
+         * @return builder
+         */
+        public Builder withFeatures(List<String> features) {
+            this.features = features;
+            return this;
+        }
+
+        /**
+         * Adds a list of required applications.
+         *
+         * @param requiredApps List of name strings of required applications
+         * @return builder
+         */
+        public Builder withRequiredApps(List<String> requiredApps) {
+            this.requiredApps = requiredApps;
+            return this;
+        }
+
+        /**
+         * Builds a default application object from the gathered parameters.
+         *
+         * @return new default application
+         */
+        public DefaultApplication build() {
+            checkNotNull(appId, "ID cannot be null");
+            checkNotNull(version, "Version cannot be null");
+            checkNotNull(title, "Title cannot be null");
+            checkNotNull(description, "Description cannot be null");
+            checkNotNull(origin, "Origin cannot be null");
+            checkNotNull(category, "Category cannot be null");
+            checkNotNull(readme, "Readme cannot be null");
+            checkNotNull(role, "Role cannot be null");
+            checkNotNull(permissions, "Permissions cannot be null");
+            checkNotNull(featuresRepo, "Features repo cannot be null");
+            checkNotNull(features, "Features cannot be null");
+            checkNotNull(requiredApps, "Required apps cannot be null");
+            checkArgument(!features.isEmpty(), "There must be at least one feature");
+
+            return new DefaultApplication(appId, version, title,
+                                          description, origin, category,
+                                          url, readme, icon,
+                                          role, permissions,
+                                          featuresRepo, features,
+                                          requiredApps);
+        }
+    }
 }