Enhance extension point for editing Dependencies

Change-Id: I17a01cb736cfc70beaf5a4c7d1dd32f7a2af3765
diff --git a/plugins/org.eclipse.tigerstripe.workbench.base/.classpath b/plugins/org.eclipse.tigerstripe.workbench.base/.classpath
index fd511b1..20f7922 100644
--- a/plugins/org.eclipse.tigerstripe.workbench.base/.classpath
+++ b/plugins/org.eclipse.tigerstripe.workbench.base/.classpath
@@ -1,14 +1,12 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-	<classpathentry kind="src" path="src/java"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.tigerstripe.api"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.tigerstripe.core"/>
-	<classpathentry exported="true" kind="lib" path="lib/tigerstripe-api.jar"/>
-	<classpathentry exported="true" kind="lib" path="lib/tigerstripe-core.jar"/>
-	<classpathentry exported="true" kind="lib" path="lib/velocity-1.5.jar"/>
-	<classpathentry exported="true" kind="lib" path="lib/dom4j-1.6.1.jar"/>
-	<classpathentry exported="true" kind="lib" path="lib/qdox-1.6.3.jar"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>

+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>

+	<classpathentry kind="src" path="src/java"/>

+	<classpathentry exported="true" kind="lib" path="lib/tigerstripe-api.jar"/>

+	<classpathentry exported="true" kind="lib" path="lib/tigerstripe-core.jar"/>

+	<classpathentry exported="true" kind="lib" path="lib/velocity-1.5.jar"/>

+	<classpathentry exported="true" kind="lib" path="lib/dom4j-1.6.1.jar"/>

+	<classpathentry exported="true" kind="lib" path="lib/qdox-1.6.3.jar"/>

+	<classpathentry kind="output" path="bin"/>

+</classpath>

diff --git a/plugins/org.eclipse.tigerstripe.workbench.base/schema/customTigerstripeDependencies.exsd b/plugins/org.eclipse.tigerstripe.workbench.base/schema/customTigerstripeDependencies.exsd
index 09f9f9b..0947460 100644
--- a/plugins/org.eclipse.tigerstripe.workbench.base/schema/customTigerstripeDependencies.exsd
+++ b/plugins/org.eclipse.tigerstripe.workbench.base/schema/customTigerstripeDependencies.exsd
@@ -21,6 +21,8 @@
             <element ref="dialog_element"/>
             <element ref="listener"/>
             <element ref="transitiveSupported" minOccurs="0" maxOccurs="1"/>
+            <element ref="label_provider" minOccurs="0" maxOccurs="1"/>
+            <element ref="details_page" minOccurs="0" maxOccurs="1"/>
          </sequence>
          <attribute name="point" type="string" use="required">
             <annotation>
@@ -91,6 +93,36 @@
       </complexType>
    </element>
 
+   <element name="label_provider">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.eclipse.jface.viewers.ITableLabelProvider"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="details_page">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn="org.eclipse.tigerstripe.workbench.ui.internal.editors.descriptor.dependencies.ReferenceDetails:"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
    <annotation>
       <appInfo>
          <meta.section type="since"/>
diff --git a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/CustomTigerstripeDependenciesManager.java b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/CustomTigerstripeDependenciesManager.java
index ba1dc62..56e8e98 100644
--- a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/CustomTigerstripeDependenciesManager.java
+++ b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/CustomTigerstripeDependenciesManager.java
@@ -1,159 +1,261 @@
-/*******************************************************************************

- * Copyright (c) 2017-2019 Cisco Systems, Inc.

- * All rights reserved. This program and the accompanying materials

- * are made available under the terms of the Eclipse Public License v1.0

- * which accompanies this distribution, and is available at

- * http://www.eclipse.org/legal/epl-v10.html

- *******************************************************************************/

-package org.eclipse.tigerstripe.workbench.ui.internal.dialogs;

-

-import java.lang.reflect.Constructor;

-import java.util.ArrayList;

-import java.util.List;

-import java.util.Map;

-import java.util.Set;

-

-import org.eclipse.core.runtime.IConfigurationElement;

-import org.eclipse.core.runtime.Platform;

-import org.eclipse.core.runtime.preferences.ConfigurationScope;

-import org.eclipse.jface.preference.IPreferenceStore;

-import org.eclipse.swt.widgets.Shell;

-import org.eclipse.tigerstripe.workbench.TigerstripeException;

-import org.eclipse.tigerstripe.workbench.internal.BasePlugin;

-import org.eclipse.tigerstripe.workbench.model.IModelSynchronizationListener;

-import org.eclipse.tigerstripe.workbench.project.IModelReference;

-import org.eclipse.tigerstripe.workbench.project.ITigerstripeModelProject;

-import org.eclipse.tigerstripe.workbench.project.ITigerstripeModelProjectInternal;

-import org.eclipse.tigerstripe.workbench.ui.EclipsePlugin;

-import org.eclipse.tigerstripe.workbench.ui.internal.preferences.ContributionsPreferencePage;

-import org.eclipse.ui.preferences.ScopedPreferenceStore;

-import org.osgi.framework.Bundle;

-

-public class CustomTigerstripeDependenciesManager implements ICustomTigerstripeDependenciesManager {

-    private static String EXT_POINT = "org.eclipse.tigerstripe.workbench.base.customTigerstripeDependencies";

-    private static final String ATTR_CLASS = "class";

-    private static String DIALOG_CLASS = "dialog_class";

-    private static String TRANSITIVE = "transitiveSupported";

-

-    private IModelSynchronizationListener[] syncListeners;

-    private static IConfigurationElement dialogElement;

-    private static boolean transitivesChecked = false;

-    private static boolean transitiveValue = true;

-

-    public static boolean hasCustomDialog() {

-        IPreferenceStore store = new ScopedPreferenceStore(ConfigurationScope.INSTANCE, BasePlugin.PLUGIN_ID);

-        if (store.getBoolean(ContributionsPreferencePage.P_USE_CUSTOM_DEPENDENCIES_DIALOG)) {

-            if (dialogElement == null) {

-                getRegisteredDependenciesDialog();

-            }

-            return dialogElement != null;

-        }

-        return false;

-    }

-

-

-    public static boolean isTransitiveSupported() {

-        IPreferenceStore store = new ScopedPreferenceStore(ConfigurationScope.INSTANCE, BasePlugin.PLUGIN_ID);

-        if (store.getBoolean(ContributionsPreferencePage.P_USE_CUSTOM_DEPENDENCIES_DIALOG)) {

-            if (! transitivesChecked) {

-                IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXT_POINT); // $NON-NLS-1$

-                // set initial value in case there is no config!

-                transitiveValue = (config.length == 0);

-

-                for (IConfigurationElement configElement : config) {

-                    // The presence of the element means it does

-                    if (configElement.getName().equals(TRANSITIVE)) {

-                        transitiveValue = true;

-                        break;

-                    }

-                }

-                transitivesChecked = true;

-            }

-        }

-        return transitiveValue;    

-    }

-

-    public static synchronized IProjectSelectionDialog make(Shell shell,

-            final Set<String> filteredOutProjects, final ITigerstripeModelProject currentProject)

-                    throws TigerstripeException {

-

-        // Note this assumes that the check to see if it is provided has been

-        // used

-        // as that is where we check for a preference

-        if (dialogElement == null) {

-            getRegisteredDependenciesDialog();

-        }

-

-        IConfigurationElement element = dialogElement;

-        if (element != null) {

-            try {

-                Bundle bundle = Platform.getBundle(element.getContributor().getName());

-                String className = element.getAttribute(DIALOG_CLASS);

-                Class<?> extensionClass = bundle.loadClass(className);

-

-                for (Constructor<?> constructor : extensionClass.getDeclaredConstructors()) {

-                    Class<?>[] paramTypes = constructor.getParameterTypes();

-                    if (paramTypes.length == 3 && paramTypes[0].isAssignableFrom(Shell.class)) {

-

-                        return (IProjectSelectionDialog) constructor.newInstance(shell, filteredOutProjects,

-                                currentProject);

-                    }

-                }

-                return null;

-            } catch (Exception e) {

-                EclipsePlugin.log(e);

-            }

-        }

-        throw new TigerstripeException("Couldn't instantiate custom dependencies dialog");

-

-    }

-

-    private static void getRegisteredDependenciesDialog() {

-        IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXT_POINT); // $NON-NLS-1$

-        for (IConfigurationElement configElement : config) {

-            if (configElement.getAttribute(DIALOG_CLASS) != null) {

-                dialogElement = configElement;

-                break;

-            }

-        }

-    }

-

-    public void fireSaved(final ITigerstripeModelProjectInternal project) {

-        for (IModelSynchronizationListener listener : getSyncListeners()) {

-            listener.saved(project);

-        }

-    }

-

-    public void fireDisposed(final ITigerstripeModelProjectInternal project) {

-        for (IModelSynchronizationListener listener : getSyncListeners()) {

-            listener.disposed(project);

-        }

-    }

-

-    public void fireRemoveDependencies(final ITigerstripeModelProjectInternal project,

-            final Map<String, IModelReference> modelRefMap) {

-        for (IModelSynchronizationListener listener : getSyncListeners()) {

-            listener.removed(project, modelRefMap);

-        }

-    }

-

-    private IModelSynchronizationListener[] getSyncListeners() {

-        if (syncListeners == null) {

-            List<IModelSynchronizationListener> list = new ArrayList<>();

-            IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(EXT_POINT);

-            for (IConfigurationElement config : configs) {

-                try {

-                    if (config.getAttribute(ATTR_CLASS) != null) {

-                        IModelSynchronizationListener listener = (IModelSynchronizationListener) config

-                                .createExecutableExtension(ATTR_CLASS);

-                        list.add(listener);

-                    }

-                } catch (Exception e) {

-                    EclipsePlugin.log(e);

-                }

-            }

-            syncListeners = list.toArray(new IModelSynchronizationListener[list.size()]);

-        }

-        return syncListeners;

-    }

-}

+/*******************************************************************************
+ * Copyright (c) 2017-2019 Cisco Systems, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.tigerstripe.workbench.ui.internal.dialogs;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.InvalidRegistryObjectException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.ConfigurationScope;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tigerstripe.workbench.TigerstripeException;
+import org.eclipse.tigerstripe.workbench.internal.BasePlugin;
+import org.eclipse.tigerstripe.workbench.model.IModelSynchronizationListener;
+import org.eclipse.tigerstripe.workbench.project.IModelReference;
+import org.eclipse.tigerstripe.workbench.project.ITigerstripeModelProject;
+import org.eclipse.tigerstripe.workbench.project.ITigerstripeModelProjectInternal;
+import org.eclipse.tigerstripe.workbench.ui.EclipsePlugin;
+import org.eclipse.tigerstripe.workbench.ui.internal.editors.descriptor.dependencies.ReferenceDetails;
+import org.eclipse.tigerstripe.workbench.ui.internal.preferences.ContributionsPreferencePage;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.preferences.ScopedPreferenceStore;
+import org.osgi.framework.Bundle;
+
+public class CustomTigerstripeDependenciesManager implements ICustomTigerstripeDependenciesManager {
+    private static String EXT_POINT = "org.eclipse.tigerstripe.workbench.base.customTigerstripeDependencies";
+    private static final String ATTR_CLASS = "class";
+    private static String DIALOG_CLASS = "dialog_class";
+    private static String TRANSITIVE = "transitiveSupported";
+    
+    private static String LISTENER = "listener";
+    private static String DETAILS_PAGE = "details_page";
+    private static String LABEL_PROVIDER = "label_provider";
+
+    private IModelSynchronizationListener[] syncListeners;
+    private static IConfigurationElement dialogElement;
+    private static IConfigurationElement labelProviderElement;
+    private static IConfigurationElement detailsPageElement;
+    private static boolean transitivesChecked = false;
+    private static boolean transitiveValue = true;
+
+    public static boolean hasCustomDialog() {
+        IPreferenceStore store = new ScopedPreferenceStore(ConfigurationScope.INSTANCE, BasePlugin.PLUGIN_ID);
+        if (store.getBoolean(ContributionsPreferencePage.P_USE_CUSTOM_DEPENDENCIES_DIALOG)) {
+            if (dialogElement == null) {
+                getRegisteredDependenciesDialog();
+            }
+            return dialogElement != null;
+        }
+        return false;
+    }
+
+    public static boolean hasCustomLabelProvider() {
+        IPreferenceStore store = new ScopedPreferenceStore(ConfigurationScope.INSTANCE, BasePlugin.PLUGIN_ID);
+        if (store.getBoolean(ContributionsPreferencePage.P_USE_CUSTOM_DEPENDENCIES_LABELPROVIDER)) {
+            if (labelProviderElement == null) {
+                getRegisteredDependenciesLabelProvider();
+            }
+            return labelProviderElement != null;
+        }
+        return false;
+    }
+    
+    public static boolean hasCustomDetailsPage() {
+        IPreferenceStore store = new ScopedPreferenceStore(ConfigurationScope.INSTANCE, BasePlugin.PLUGIN_ID);
+        if (store.getBoolean(ContributionsPreferencePage.P_USE_CUSTOM_DEPENDENCIES_DETAILSPAGE)) {
+            if (detailsPageElement == null) {
+                getRegisteredDependenciesDetailsPage();
+            }
+            return detailsPageElement != null;
+        }
+        return false;
+    }
+    
+
+    public static boolean isTransitiveSupported() {
+        IPreferenceStore store = new ScopedPreferenceStore(ConfigurationScope.INSTANCE, BasePlugin.PLUGIN_ID);
+        if (store.getBoolean(ContributionsPreferencePage.P_USE_CUSTOM_DEPENDENCIES_DIALOG)) {
+            if (! transitivesChecked) {
+                IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXT_POINT); // $NON-NLS-1$
+                // set initial value in case there is no config!
+                transitiveValue = (config.length == 0);
+
+                for (IConfigurationElement configElement : config) {
+                    // The presence of the element means it does
+                    if (configElement.getName().equals(TRANSITIVE)) {
+                        transitiveValue = true;
+                        break;
+                    }
+                }
+                transitivesChecked = true;
+            }
+        }
+        return transitiveValue;    
+    }
+
+    public static synchronized IProjectSelectionDialog makeDialog(Shell shell,
+            final Set<String> filteredOutProjects, final ITigerstripeModelProject currentProject)
+                    throws TigerstripeException {
+
+        // Note this assumes that the check to see if it is provided has been
+        // used
+        // as that is where we check for a preference
+        if (dialogElement == null) {
+            getRegisteredDependenciesDialog();
+        }
+
+        IConfigurationElement element = dialogElement;
+        if (element != null) {
+            try {
+                Bundle bundle = Platform.getBundle(element.getContributor().getName());
+                String className = element.getAttribute(DIALOG_CLASS);
+                Class<?> extensionClass = bundle.loadClass(className);
+
+                for (Constructor<?> constructor : extensionClass.getDeclaredConstructors()) {
+                    Class<?>[] paramTypes = constructor.getParameterTypes();
+                    if (paramTypes.length == 3 && paramTypes[0].isAssignableFrom(Shell.class)) {
+
+                        return (IProjectSelectionDialog) constructor.newInstance(shell, filteredOutProjects,
+                                currentProject);
+                    }
+                }
+                return null;
+            } catch (Exception e) {
+                EclipsePlugin.log(e);
+            }
+        }
+        throw new TigerstripeException("Couldn't instantiate custom dependencies dialog");
+
+    }
+
+    public static ITableLabelProvider makeLabelProvider() {
+        if (labelProviderElement == null) {
+            getRegisteredDependenciesLabelProvider();
+        }
+        
+        IConfigurationElement element = labelProviderElement;
+        if (element != null) {
+                Bundle bundle = Platform.getBundle(element.getContributor().getName());
+                try {
+                    return (ITableLabelProvider) element.createExecutableExtension(ATTR_CLASS);
+                } catch (InvalidRegistryObjectException | CoreException e) {
+                    EclipsePlugin.log(e);
+                }
+        }
+        return null;
+                
+    }
+    
+    public static ReferenceDetails makeReferenceDetailsPage(FormToolkit toolkit, Composite container) 
+            throws TigerstripeException {
+        if (detailsPageElement == null) {
+            getRegisteredDependenciesDetailsPage();;
+        }
+        
+        IConfigurationElement element = detailsPageElement;
+        if (element != null) {
+            try {
+                Bundle bundle = Platform.getBundle(element.getContributor().getName());
+                String className = element.getAttribute(ATTR_CLASS);
+                Class<?> extensionClass = bundle.loadClass(className);
+
+                for (Constructor<?> constructor : extensionClass.getDeclaredConstructors()) {
+                    Class<?>[] paramTypes = constructor.getParameterTypes();
+                    if (paramTypes.length == 2 && paramTypes[0].isAssignableFrom(FormToolkit.class) &&
+                            paramTypes[1].isAssignableFrom(Composite.class)) {
+                        return (ReferenceDetails) constructor.newInstance(toolkit,container);
+                    }
+                }
+                return null;
+            } catch (Exception e) {
+                EclipsePlugin.log(e);
+            }
+        }
+        throw new TigerstripeException("Couldn't instantiate custom Details Page");
+    }
+    
+    private static void getRegisteredDependenciesDialog() {
+        IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXT_POINT); // $NON-NLS-1$
+        for (IConfigurationElement configElement : config) {
+            if (configElement.getAttribute(DIALOG_CLASS) != null) {
+                dialogElement = configElement;
+                break;
+            }
+        }
+    }
+    
+    private static void getRegisteredDependenciesLabelProvider() {
+        IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXT_POINT); // $NON-NLS-1$
+        for (IConfigurationElement configElement : config) {
+            if (configElement.getName().equals(LABEL_PROVIDER) && configElement.getAttribute(ATTR_CLASS) != null) {
+                labelProviderElement = configElement;
+                break;
+            }
+        }
+    }
+    
+    private static void getRegisteredDependenciesDetailsPage() {
+        IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXT_POINT); // $NON-NLS-1$
+        for (IConfigurationElement configElement : config) {
+            if (configElement.getName().equals(DETAILS_PAGE) && configElement.getAttribute(ATTR_CLASS) != null) {
+                detailsPageElement = configElement;
+                break;
+            }
+        }
+    }
+
+    public void fireSaved(final ITigerstripeModelProjectInternal project) {
+        for (IModelSynchronizationListener listener : getSyncListeners()) {
+            listener.saved(project);
+        }
+    }
+
+    public void fireDisposed(final ITigerstripeModelProjectInternal project) {
+        for (IModelSynchronizationListener listener : getSyncListeners()) {
+            listener.disposed(project);
+        }
+    }
+
+    public void fireRemoveDependencies(final ITigerstripeModelProjectInternal project,
+            final Map<String, IModelReference> modelRefMap) {
+        for (IModelSynchronizationListener listener : getSyncListeners()) {
+            listener.removed(project, modelRefMap);
+        }
+    }
+
+    private IModelSynchronizationListener[] getSyncListeners() {
+        if (syncListeners == null) {
+            List<IModelSynchronizationListener> list = new ArrayList<>();
+            IConfigurationElement[] configs = Platform.getExtensionRegistry().getConfigurationElementsFor(EXT_POINT);
+            for (IConfigurationElement configElement : configs) {
+                try {
+                    if (configElement.getName().equals(LISTENER) && configElement.getAttribute(ATTR_CLASS) != null) {
+                        IModelSynchronizationListener listener = (IModelSynchronizationListener) configElement
+                                .createExecutableExtension(ATTR_CLASS);
+                        list.add(listener);
+                    }
+                } catch (Exception e) {
+                    EclipsePlugin.log(e);
+                }
+            }
+            syncListeners = list.toArray(new IModelSynchronizationListener[list.size()]);
+        }
+        return syncListeners;
+    }
+
+
+}
diff --git a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/IProjectSelectionDialog.java b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/IProjectSelectionDialog.java
index ba62300..0e474a7 100644
--- a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/IProjectSelectionDialog.java
+++ b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/IProjectSelectionDialog.java
@@ -7,6 +7,8 @@
 *******************************************************************************/

 package org.eclipse.tigerstripe.workbench.ui.internal.dialogs;

 

+import org.eclipse.tigerstripe.workbench.project.IModelReference;

+

 public interface IProjectSelectionDialog {

 

     int open();

@@ -15,6 +17,12 @@
 

     boolean isIncludeTransitiveDependencies();

 

-    void addMavenDependency();

+    default void addDependency() {

+        // Do nothing by default

+    }

+

+    default void setInitialProjectSelection(IModelReference initialSelection) {

+     // Do nothing by default - "built-in" dialog does not support this 

+    }

     

 }

diff --git a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/TigerstripeProjectSelectionDialog.java b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/TigerstripeProjectSelectionDialog.java
index de87d92..2c3176e 100644
--- a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/TigerstripeProjectSelectionDialog.java
+++ b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/dialogs/TigerstripeProjectSelectionDialog.java
@@ -373,7 +373,4 @@
     protected void computeResult() {

     }

 

-    @Override

-    public void addMavenDependency() {

-    }

 }

diff --git a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/editors/descriptor/dependencies/ReferenceDetails.java b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/editors/descriptor/dependencies/ReferenceDetails.java
index 9bc4550..425bcec 100644
--- a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/editors/descriptor/dependencies/ReferenceDetails.java
+++ b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/editors/descriptor/dependencies/ReferenceDetails.java
@@ -33,7 +33,7 @@
 
 	private final Composite externalContainer;
 
-	private ModelReference reference;
+	protected ModelReference reference;
 
 	private Text moduleId;
 
@@ -43,7 +43,7 @@
 
 	private Text projectVersion;
 
-	private final FormToolkit toolkit;
+	protected final FormToolkit toolkit;
 
 	public ReferenceDetails(FormToolkit toolkit, Composite externalContainer) {
 		this.toolkit = toolkit;
@@ -88,6 +88,8 @@
 		projectVersion.setEditable(false);
 		projectVersion.setEnabled(false);
 
+		insertExtraFields(sectionClient);
+		
 		label = toolkit.createLabel(sectionClient, "Description: ");
 		label.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
 		projectDescription = toolkit.createText(sectionClient, "", SWT.WRAP
@@ -102,7 +104,11 @@
 		toolkit.paintBordersFor(sectionClient);
 	}
 
-	@Override
+	protected void insertExtraFields(Composite sectionClient) {
+        // For subclasses to extend
+    }
+
+    @Override
 	protected Composite getExternalContainer() {
 		return externalContainer;
 	}
@@ -112,7 +118,7 @@
 		updateForm();
 	}
 
-	private void updateForm() {
+	protected void updateForm() {
 		if (reference == null) {
 			return;
 		}
diff --git a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/editors/descriptor/dependencies/ReferencedProjectsSection.java b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/editors/descriptor/dependencies/ReferencedProjectsSection.java
index 088f020..2c753a1 100644
--- a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/editors/descriptor/dependencies/ReferencedProjectsSection.java
+++ b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/editors/descriptor/dependencies/ReferencedProjectsSection.java
@@ -12,6 +12,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -83,6 +84,8 @@
 
     private CheckboxTableViewer viewer;
 
+    private Button editButton;
+
     private Button removeButton;
 
     private Button selectAllButton;
@@ -279,8 +282,8 @@
         }
     }
 
-
-    // TODO - Why do we need access to the internal project? Can we abstract it behind the interface?
+    // TODO - Why do we need access to the internal project? Can we abstract it
+    // behind the interface?
     private InternalTigerstripeProject getActualTSProject() {
         Object input = viewer.getInput();
 
@@ -356,10 +359,21 @@
         getToolkit().paintBordersFor(masterContainer);
 
         masterDetails = MasterDetailsBuilder.create()
-                .addDetail(ModelReference.class, new ReferenceDetails(getToolkit(), detailsContainer))
-                .addDetail(Dependency.class, new DependencyDetails(getToolkit(), detailsContainer)).build();
+                .addDetail(ModelReference.class, getReferenceDetailsPage(getToolkit(), detailsContainer))
+                .addDetail(Dependency.class,  new DependencyDetails(getToolkit(), detailsContainer)).build();
     }
 
+    private ReferenceDetails getReferenceDetailsPage(FormToolkit toolkit, Composite container){
+        if (CustomTigerstripeDependenciesManager.hasCustomDetailsPage()) {
+            try {
+                return CustomTigerstripeDependenciesManager.makeReferenceDetailsPage(toolkit, container);
+            } catch (TigerstripeException t) {
+                EclipsePlugin.logErrorMessage("Failure createing Custom details Page",t);
+            }
+        }
+        return new ReferenceDetails(toolkit, container);
+    }
+    
     private void enableAllDependencies(boolean markAsDirty) {
 
         boolean stateChanged = false;
@@ -446,10 +460,24 @@
         });
         addButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 
+        // Edit Dependency button - only if there is a custom Dialog, as the built-in does not need/support this
+        if (CustomTigerstripeDependenciesManager.hasCustomDialog()) {
+            editButton = toolkit.createButton(buttonsClient, "Edit...", SWT.PUSH);
+            editButton.addSelectionListener(new SelectionAdapter() {
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    editButtonSelected();
+                }
+            });
+            editButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        }
+
         // Add Missing Transitive Dependency button
-        // Need to disable this function in the case where the "custom dependency Dialog" is set?
+        // Need to disable this function in the case where the "custom dependency
+        // Dialog" is set?
         if (CustomTigerstripeDependenciesManager.isTransitiveSupported()) {
-            Button addMissingTransitiveDependenciesButton = toolkit.createButton(buttonsClient, "Add Transitive", SWT.PUSH);
+            Button addMissingTransitiveDependenciesButton = toolkit.createButton(buttonsClient, "Add Transitive",
+                    SWT.PUSH);
             addMissingTransitiveDependenciesButton.addSelectionListener(new SelectionAdapter() {
                 @Override
                 public void widgetSelected(SelectionEvent e) {
@@ -493,8 +521,13 @@
             updateDetails();
             updateButtonsEnablement();
         });
+        
         viewer.setContentProvider(new ReferencedProjectsContentProvider());
-        final ITableLabelProvider labelProvider = new ReferencedProjectsLabelProvider();
+        final ITableLabelProvider labelProvider = CustomTigerstripeDependenciesManager.hasCustomLabelProvider() 
+                ? CustomTigerstripeDependenciesManager.makeLabelProvider()
+                : new ReferencedProjectsLabelProvider();
+        
+        
         viewer.setLabelProvider(labelProvider);
         ITextProvider textAdapter = obj -> labelProvider.getColumnText(obj, 0);
         viewer.setComparator(new DependenciesSorter(textAdapter, DEPENDENCY_KIND_RESOLVER));
@@ -529,59 +562,144 @@
 
     }
 
-    protected void addButtonSelected() {
+    protected void editButtonSelected() {
+
+        // Need the dialog with everything except the current project in!
+        // ie INCLUDING current ref, as that's what we want to edit
+
+        TableItem[] selectedItems = viewer.getTable().getSelection();
+        IModelReference selectedReference = null;
+        if (selectedItems.length > 0) {
+            Object data = selectedItems[0].getData();
+            if (data instanceof IModelReference) {
+                selectedReference = (IModelReference) data;
+            }
+        }
+
+        List<IModelReference> refList = new ArrayList<IModelReference>();
+        refList.add(selectedReference);
+        IProjectSelectionDialog dialog = getDialog(refList);
+
+        if (dialog != null) {
+            dialog.setInitialProjectSelection(selectedReference);
+
+            if (selectedReference != null && dialog.open() == Window.OK) {
+
+                Object[] results = dialog.getResult();
+                for (Object res : results) {
+                    String modelId = getModelIdFromResult(res);
+
+                    if (StringUtils.isNotEmpty(modelId)) {
+                        try {
+
+                            // Here we need to replace the prior reference with our new one.
+                            // NOTE this can cause duplicates if we are not careful.
+                            getTSProject().removeModelReference(selectedReference.getToModelId());
+                            Map<String, IModelReference> modelRefMap = new HashMap<>();
+                            modelRefMap.put(selectedReference.getToModelId(), selectedReference);
+
+                            EclipsePlugin.getTigerStripeDependenciesManager().fireRemoveDependencies(getTSProject(),
+                                    modelRefMap);
+
+                            IModelReference ref = null;
+                            for (IModelReference existingRef : getTSProject().getModelReferences()) {
+                                if (StringUtils.equals(existingRef.getToModelId(), modelId)) {
+                                    ref = existingRef;
+                                    break;
+                                }
+                            }
+
+                            if (ref == null) {
+                                ref = getTSProject().addModelReference(modelId);
+                            }
+                            viewer.refresh(true);
+                            viewer.setChecked(ref, true); // NM: Check newly added
+                            // dependency
+                            markPageModified();
+                            if (dialog.isIncludeTransitiveDependencies()) {
+                                addMissingTransitiveDependencies(new IModelReference[] { ref });
+                            }
+                            dialog.addDependency();
+                        } catch (TigerstripeException e) {
+                            EclipsePlugin.log(e);
+                        }
+
+                    }
+
+                }
+
+            }
+            viewer.refresh();
+
+        }
+
+    }
+
+    private String getModelIdFromResult(Object res) {
+        String modelId = null;
+        if (res instanceof IJavaProject) {
+            IJavaProject prj = (IJavaProject) res;
+
+            ITigerstripeModelProject tsPrj = prj.getProject().getAdapter(ITigerstripeModelProject.class);
+            if (tsPrj != null) {
+                try {
+                    modelId = tsPrj.getModelId();
+                } catch (TigerstripeException e) {
+                    EclipsePlugin.logErrorMessage("Failed to parse model ID from project: " + tsPrj.getName(), e);
+                }
+            }
+        } else if (res instanceof InstalledModule) {
+            InstalledModule module = (InstalledModule) res;
+            modelId = module.getModuleID();
+        } else if (res instanceof String) {
+            modelId = (String) res;
+        }
+        return modelId;
+    }
+
+    protected IProjectSelectionDialog getDialog() {
+        return getDialog(Collections.emptyList());
+    }
+
+    protected IProjectSelectionDialog getDialog(List<IModelReference> allowedRefs) {
         Set<String> filteredOutProjects = new HashSet<>();
 
         try {
             // the current project
             filteredOutProjects.add(getTSProject().getModelId());
-            // existing Refs
+            // allowed Refs
+
             for (IModelReference prjRef : getTSProject().getModelReferences()) {
-                filteredOutProjects.add(prjRef.getToModelId());
+                if (!allowedRefs.contains(prjRef)) {
+                    filteredOutProjects.add(prjRef.getToModelId());
+                }
             }
         } catch (TigerstripeException e) {
             // ignore here
         }
 
-        IProjectSelectionDialog dialog = null;
-
         // Here we override the default dialog if appropriate extension point is
         // implemented
         if (CustomTigerstripeDependenciesManager.hasCustomDialog()) {
             try {
-                dialog = CustomTigerstripeDependenciesManager.make(getSection().getShell(), filteredOutProjects,
+                return CustomTigerstripeDependenciesManager.makeDialog(getSection().getShell(), filteredOutProjects,
                         getTSProject());
             } catch (TigerstripeException e) {
                 EclipsePlugin.logErrorMessage("Custom Dependencies Dialog Load Error", e);
             }
         }
 
-        if (dialog == null) {
-            dialog = new TigerstripeProjectSelectionDialog(getSection().getShell(), filteredOutProjects,
-                    getTSProject());
-        }
+        return new TigerstripeProjectSelectionDialog(getSection().getShell(), filteredOutProjects, getTSProject());
+    }
 
-        if (dialog.open() == Window.OK) {
+    protected void addButtonSelected() {
+
+        IProjectSelectionDialog dialog = getDialog();
+
+        if (dialog != null && dialog.open() == Window.OK) {
             Object[] results = dialog.getResult();
             for (Object res : results) {
-                String modelId = null;
-                if (res instanceof IJavaProject) {
-                    IJavaProject prj = (IJavaProject) res;
-
-                    ITigerstripeModelProject tsPrj = prj.getProject().getAdapter(ITigerstripeModelProject.class);
-                    if (tsPrj != null) {
-                        try {
-                            modelId = tsPrj.getModelId();
-                        } catch (TigerstripeException e) {
-                            EclipsePlugin.logErrorMessage("Failed to parse model ID from project: " + tsPrj.getName(), e);
-                        }
-                    }
-                } else if (res instanceof InstalledModule) {
-                    InstalledModule module = (InstalledModule) res;
-                    modelId = module.getModuleID();
-                } else if (res instanceof String) {
-                    modelId = (String)res;
-                }
+                String modelId = getModelIdFromResult(res);
                 if (StringUtils.isNotEmpty(modelId)) {
                     try {
 
@@ -592,7 +710,7 @@
                                 break;
                             }
                         }
-                        
+
                         if (ref == null) {
                             ref = getTSProject().addModelReference(modelId);
                         }
@@ -603,7 +721,7 @@
                         if (dialog.isIncludeTransitiveDependencies()) {
                             addMissingTransitiveDependencies(new IModelReference[] { ref });
                         }
-                        dialog.addMavenDependency();
+                        dialog.addDependency();
                     } catch (TigerstripeException e) {
                         EclipsePlugin.log(e);
                     }
@@ -770,6 +888,9 @@
 
     private void updateButtonsEnablement() {
         TableItem[] selectedItems = viewer.getTable().getSelection();
+        if (editButton != null) {
+            editButton.setEnabled(selectedItems != null && selectedItems.length > 0);
+        }
         removeButton.setEnabled(selectedItems != null && selectedItems.length > 0);
     }
 
@@ -802,6 +923,6 @@
         return DependencyKind.UNKNOWN;
     };
 
-    private static final DependenciesImageProvider IMAGE_PROVIDER = new DependenciesImageProvider(
+    public static final DependenciesImageProvider IMAGE_PROVIDER = new DependenciesImageProvider(
             DEPENDENCY_KIND_RESOLVER);
 }
diff --git a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/preferences/ContributionsPreferencePage.java b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/preferences/ContributionsPreferencePage.java
index 560ba63..306f731 100644
--- a/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/preferences/ContributionsPreferencePage.java
+++ b/plugins/org.eclipse.tigerstripe.workbench.ui.base/src/java/org/eclipse/tigerstripe/workbench/ui/internal/preferences/ContributionsPreferencePage.java
@@ -11,11 +11,14 @@
 import java.util.stream.Stream;

 

 import org.eclipse.core.runtime.preferences.ConfigurationScope;

+import org.eclipse.jface.layout.GridDataFactory;

+import org.eclipse.jface.layout.GridLayoutFactory;

 import org.eclipse.jface.preference.BooleanFieldEditor;

 import org.eclipse.jface.preference.FieldEditorPreferencePage;

 import org.eclipse.jface.preference.IPreferenceStore;

 import org.eclipse.swt.SWT;

 import org.eclipse.swt.layout.GridData;

+import org.eclipse.swt.widgets.Composite;

 import org.eclipse.swt.widgets.Group;

 import org.eclipse.tigerstripe.workbench.internal.BasePlugin;

 import org.eclipse.tigerstripe.workbench.internal.builder.ArtifactAuditorFactory;

@@ -30,6 +33,8 @@
 

     public static final String P_USE_CUSTOM_STEROTYPE_DIALOG_FOR = "p.use_custom_stereotype_dialog_for_"; // $NON-NLS-1$

     public static final String P_USE_CUSTOM_DEPENDENCIES_DIALOG = "p.use_custom_dependencies_dialog"; // $NON-NLS-1$

+    public static final String P_USE_CUSTOM_DEPENDENCIES_LABELPROVIDER = "p.use_custom_dependencies_labelProvider"; // $NON-NLS-1$

+    public static final String P_USE_CUSTOM_DEPENDENCIES_DETAILSPAGE = "p.use_custom_dependencies_detailsPage"; // $NON-NLS-1$

 

     public ContributionsPreferencePage() {

         super(GRID);

@@ -45,18 +50,20 @@
 

     @Override

     protected void createFieldEditors() {

-        Group useGroup = new Group(getFieldEditorParent(), SWT.TITLE);

-        GridData gd = new GridData(GridData.FILL_HORIZONTAL);

-        gd.horizontalSpan = 2;

-        useGroup.setLayoutData(gd);

+        

+        Composite parent = new Composite(getFieldEditorParent(), SWT.NONE);

+        GridLayoutFactory.fillDefaults().numColumns(1).applyTo(parent);

+        GridDataFactory.fillDefaults().grab(true, true).applyTo(parent);

+        

+        Group useGroup = new Group(parent, SWT.TITLE);

+        useGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

         useGroup.setText("Use Custom Stereotype Dialogs");

 

         Stream.of(CustomStereotypeDialogFactory.list()).sorted((d1, d2) -> d1.compareTo(d2)).forEach(stereo -> addField(

                 new BooleanFieldEditor(P_USE_CUSTOM_STEROTYPE_DIALOG_FOR + stereo, stereo, useGroup)));

 

-        Group auditGroup = new Group(getFieldEditorParent(), SWT.TITLE);

-        gd.horizontalSpan = 2;

-        auditGroup.setLayoutData(gd);

+        Group auditGroup = new Group(parent, SWT.TITLE);

+        auditGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

         auditGroup.setText("Use Custom Auditors");

 

         ArtifactAuditorFactory.INSTANCE.getAuditorMap().entrySet().stream()

@@ -64,11 +71,15 @@
                 .forEach(entry -> addField(

                         new BooleanFieldEditor(entry.getKey(), entry.getValue().getName(), auditGroup)));

 

-        Group dependenciesGroup = new Group(getFieldEditorParent(), SWT.TITLE);

+        Group dependenciesGroup = new Group(parent, SWT.TITLE);

         dependenciesGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

         dependenciesGroup.setText("Use Custom Dependencies Editor");

 

-        addField(new BooleanFieldEditor(P_USE_CUSTOM_DEPENDENCIES_DIALOG, "Use Custom Dependencies Editor",

+        addField(new BooleanFieldEditor(P_USE_CUSTOM_DEPENDENCIES_DIALOG, "Use Custom Dependencies Dialog",

+                dependenciesGroup));

+        addField(new BooleanFieldEditor(P_USE_CUSTOM_DEPENDENCIES_LABELPROVIDER, "Use Custom Dependencies Label Provider",

+                dependenciesGroup));

+        addField(new BooleanFieldEditor(P_USE_CUSTOM_DEPENDENCIES_DETAILSPAGE, "Use Custom Dependencies Details Page",

                 dependenciesGroup));

     }

 

@@ -78,6 +89,8 @@
             store.setDefault(P_USE_CUSTOM_STEROTYPE_DIALOG_FOR + stereo, true);

         }

         store.setDefault(P_USE_CUSTOM_DEPENDENCIES_DIALOG, true);

+        store.setDefault(P_USE_CUSTOM_DEPENDENCIES_LABELPROVIDER, true);

+        store.setDefault(P_USE_CUSTOM_DEPENDENCIES_DETAILSPAGE, true);

     }

 

 }