You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ja...@apache.org on 2013/10/25 17:31:42 UTC

svn commit: r1535767 - in /ace/trunk/org.apache.ace.webui.vaadin: resources/VAADIN/themes/reindeer/ src/org/apache/ace/webui/domain/ src/org/apache/ace/webui/vaadin/ src/org/apache/ace/webui/vaadin/component/

Author: jawi
Date: Fri Oct 25 15:31:42 2013
New Revision: 1535767

URL: http://svn.apache.org/r1535767
Log:
Some UI improvements:

- made the artifacts table a tree-table to group on bundles with equal bundle name;
- ensure that the unlink-button is only enabled when a valid association is selected.


Added:
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java   (with props)
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/AssociationHelper.java
      - copied, changed from r1535328, ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java
Removed:
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java
Modified:
    ace/trunk/org.apache.ace.webui.vaadin/resources/VAADIN/themes/reindeer/styles.css
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/VaadinClient.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/ArtifactsPanel.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/BaseObjectPanel.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/DistributionsPanel.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/FeaturesPanel.java
    ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/TargetsPanel.java

Modified: ace/trunk/org.apache.ace.webui.vaadin/resources/VAADIN/themes/reindeer/styles.css
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/resources/VAADIN/themes/reindeer/styles.css?rev=1535767&r1=1535766&r2=1535767&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/resources/VAADIN/themes/reindeer/styles.css (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/resources/VAADIN/themes/reindeer/styles.css Fri Oct 25 15:31:42 2013
@@ -1,5 +1,5 @@
-.v-theme-version:after {content:"6_8_4";}
-.v-theme-version-6_8_4 {display: none;}
+.v-theme-version:after {content:"6_8_13";}
+.v-theme-version-6_8_13 {display: none;}
 /* Automatically compiled css file from subdirectories. */
 
 .v-absolutelayout-wrapper {
@@ -51,11 +51,13 @@
 	padding: .2em 1em;
 	color: inherit;
 	font: inherit;
-	line-height: normal;
+	line-height: normal;		
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
 	-khtml-user-select: none;
 	-moz-user-select: none;
-	-ie-user-select: none;
-	user-select: none;
+	-ms-user-select: none;
+	user-select: none;	
 	-webkit-box-sizing: border-box;
 	-moz-box-sizing: border-box;
 	-ms-box-sizing: border-box;
@@ -495,10 +497,13 @@ div.v-app-loading {
 .v-scrollable {
 	overflow: auto;
 }
-/* Enable kinetic scrolling on Mobile Safari 6 */ 
-.v-ios.v-sa6 .v-scrollable { 
-        -webkit-overflow-scrolling: touch; 
+.v-ios.v-webkit & .v-scrollable {
+    -webkit-overflow-scrolling: touch; 
 } 
+/* Disable native scrolling on iOS 5 due to #8792 */ 
+.v-ios5.v-webkit & .v-scrollable {
+    -webkit-overflow-scrolling: none; 
+}    
 
 .v-csslayout {
 	overflow: hidden;
@@ -2275,7 +2280,7 @@ div.v-tree-node-leaf {
     background: transparent;
     height: 10px;
     /* defines the amount of indent per level */
-    width: 18px;
+    width: 10px;
     position: absolute;
     left: 0;
     top: 5px;
@@ -4071,6 +4076,10 @@ div.v-table-full-height-fields .v-datefi
 	background: #fff;
 	min-height: 100%;
 }
+.v-panel-content > .v-ddwrapper {
+	/* A hack to make the padding in .v-ddwrapper work correctly */
+	min-height: 0;
+}
 .v-ie6 .v-panel-content {
 	background: #fff;
 }
@@ -5986,14 +5995,27 @@ textarea.v-textarea-readonly,
 
 /* extra ace styles */
 .v-table-row-associated {
-    background: #88aacc;
+    background: #8ac;
 }
 .v-table-row-odd.v-table-row-associated {
-    background: #7799bb;
+    background: #79b;
 }
 .v-table-row-related {
-    background: #99aabb;
+    background: #9ab;
 }
 .v-table-row-odd.v-table-row-related {
-    background: #8899aa;
+    background: #89a;
+}
+/* makes the description less prominent */
+.v-table-cell-content-description {
+	color: #9ab;
+}
+.v-table-row-odd.vtable-cell-content-description {
+	color: #89a;
+}
+.v-selected .v-table-cell-content-description {
+	color: white;
+}
+.v-selected .v-table-row-odd.vtable-cell-content-description {
+	color: white;
 }

Added: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java?rev=1535767&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java (added)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java Fri Oct 25 15:31:42 2013
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.ace.webui.domain;
+
+import org.apache.ace.client.repository.RepositoryObject;
+import org.apache.ace.client.repository.object.ArtifactObject;
+import org.apache.ace.client.repository.object.DistributionObject;
+import org.apache.ace.client.repository.object.FeatureObject;
+import org.apache.ace.client.repository.object.TargetObject;
+import org.apache.ace.client.repository.stateful.StatefulTargetObject;
+import org.apache.ace.webui.NamedObject;
+
+/**
+ * Provides a small factory for creating {@link NamedObject}s for any given {@link RepositoryObject}.
+ */
+public final class NamedObjectFactory {
+
+    public static NamedObject getNamedObject(RepositoryObject object) {
+        if (object instanceof ArtifactObject) {
+            return new NamedArtifactObject((ArtifactObject) object);
+        }
+        else if (object instanceof FeatureObject) {
+            return new NamedFeatureObject((FeatureObject) object);
+        }
+        else if (object instanceof DistributionObject) {
+            return new NamedDistributionObject((DistributionObject) object);
+        }
+        else if (object instanceof StatefulTargetObject) {
+            return new NamedTargetObject((StatefulTargetObject) object);
+        }
+        else if (object instanceof TargetObject) {
+            return new NamedTargetObject((TargetObject) object);
+        }
+        return null;
+    }
+}

Propchange: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/domain/NamedObjectFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/VaadinClient.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/VaadinClient.java?rev=1535767&r1=1535766&r2=1535767&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/VaadinClient.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/VaadinClient.java Fri Oct 25 15:31:42 2013
@@ -57,6 +57,7 @@ import org.apache.ace.webui.NamedObject;
 import org.apache.ace.webui.UIExtensionFactory;
 import org.apache.ace.webui.vaadin.LoginWindow.LoginFunction;
 import org.apache.ace.webui.vaadin.component.ArtifactsPanel;
+import org.apache.ace.webui.vaadin.component.AssociationHelper;
 import org.apache.ace.webui.vaadin.component.DistributionsPanel;
 import org.apache.ace.webui.vaadin.component.FeaturesPanel;
 import org.apache.ace.webui.vaadin.component.MainActionToolbar;
@@ -71,7 +72,6 @@ import org.osgi.service.useradmin.Author
 import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
 
-import com.sun.imageio.spi.RAFImageInputStreamSpi;
 import com.vaadin.event.Transferable;
 import com.vaadin.event.dd.DragAndDropEvent;
 import com.vaadin.event.dd.DropHandler;
@@ -155,15 +155,15 @@ public class VaadinClient extends com.va
     private final boolean m_useAuth;
     private final String m_userName;
 
-    private final Associations m_associations = new Associations();
+    private final AssociationHelper m_associations = new AssociationHelper();
     private final AtomicBoolean m_dependenciesResolved = new AtomicBoolean(false);
 
     private ProgressIndicator m_progress;
     private DependencyManager m_manager;
-	private Component m_component;
-	private final List<Component> m_eventHandlers = new ArrayList<Component>();
+    private Component m_component;
+    private final List<Component> m_eventHandlers = new ArrayList<Component>();
 
-	private GridLayout m_mainToolbar;
+    private GridLayout m_mainToolbar;
 
     // basic session ID generator
     private static long generateSessionID() {
@@ -172,14 +172,16 @@ public class VaadinClient extends com.va
 
     /**
      * Remove the given directory and all it's files and subdirectories
-     * @param directory the name of the directory to remove
+     * 
+     * @param directory
+     *            the name of the directory to remove
      */
     private static void removeDirectoryWithContent(File directory) {
         if ((directory == null) || !directory.exists()) {
             return;
         }
         File[] filesAndSubDirs = directory.listFiles();
-        for (int i=0; i < filesAndSubDirs.length; i++) {
+        for (int i = 0; i < filesAndSubDirs.length; i++) {
             File file = filesAndSubDirs[i];
             if (file.isDirectory()) {
                 removeDirectoryWithContent(file);
@@ -192,15 +194,20 @@ public class VaadinClient extends com.va
 
     /**
      * Creates a new {@link VaadinClient} instance.
-     * @param m_manager2 
      * 
-     * @param aceHost the hostname where the management service can be reached;
-     * @param obrUrl the URL of the OBR to use;
-     * @param useAuth <code>true</code> to use authentication, <code>false</code> to disable authentication;
-     * @param userName the hardcoded username to use when authentication is disabled.
+     * @param m_manager2
+     * 
+     * @param aceHost
+     *            the hostname where the management service can be reached;
+     * @param obrUrl
+     *            the URL of the OBR to use;
+     * @param useAuth
+     *            <code>true</code> to use authentication, <code>false</code> to disable authentication;
+     * @param userName
+     *            the hardcoded username to use when authentication is disabled.
      */
     public VaadinClient(DependencyManager manager, URL aceHost, URL obrUrl, String repositoryXML, boolean useAuth, String userName) {
-    	m_manager = manager;
+        m_manager = manager;
         try {
             m_repository = new URL(aceHost, endpoint);
         }
@@ -216,7 +223,7 @@ public class VaadinClient extends com.va
             throw new IllegalArgumentException("Need a valid user name when no authentication is used!");
         }
     }
-    
+
     @Override
     public void start(URL applicationUrl, Properties applicationProperties, ApplicationContext context) {
         m_component = m_manager.createComponent()
@@ -238,18 +245,18 @@ public class VaadinClient extends com.va
                 .setService(LogService.class)
                 .setRequired(false)
             );
-		m_manager.add(m_component);
-    	super.start(applicationUrl, applicationProperties, context);
+        m_manager.add(m_component);
+        super.start(applicationUrl, applicationProperties, context);
     }
-    
+
     @Override
     public void close() {
-    	if (isRunning()) {
-	        m_admin.deleteLocal();
-	        cleanupListeners();
-	        m_manager.remove(m_component);
-	        super.close();
-    	}
+        if (isRunning()) {
+            m_admin.deleteLocal();
+            cleanupListeners();
+            m_manager.remove(m_component);
+            super.close();
+        }
     }
 
     public void setupDependencies(Component component) {
@@ -274,9 +281,22 @@ public class VaadinClient extends com.va
         m_dependenciesResolved.set(true);
     }
 
-    public void stop() {
+    public void stop() throws Exception {
         m_log.log(LogService.LOG_INFO, "Stopping session #" + m_sessionID);
-        m_dependenciesResolved.set(false);
+
+        try {
+            close();
+
+            try {
+                m_admin.logout(true /* force */);
+            }
+            catch (IllegalStateException exception) {
+                // Ignore, we're already logged out...
+            }
+        }
+        finally {
+            m_dependenciesResolved.set(false);
+        }
     }
 
     public void destroyDependencies() {
@@ -314,7 +334,7 @@ public class VaadinClient extends com.va
         // Authenticate the user either by showing a login window; or by another means...
         authenticate();
     }
-    
+
     /**
      * Shows the login window on the center of the main window.
      */
@@ -339,7 +359,7 @@ public class VaadinClient extends com.va
         m_grid.setSizeFull();
 
         m_mainToolbar = createToolbar(user);
-		m_grid.addComponent(m_mainToolbar, 0, 0, count - 1, 0);
+        m_grid.addComponent(m_mainToolbar, 0, 0, count - 1, 0);
 
         m_artifactsPanel = createArtifactsPanel(user);
 
@@ -570,22 +590,22 @@ public class VaadinClient extends com.va
         props.put(EventConstants.EVENT_TOPIC, topics);
         props.put(EventConstants.EVENT_FILTER, "(" + SessionFactory.SERVICE_SID + "=" + m_sessionID + ")");
         Component component = m_manager.createComponent()
-		    .setInterface(EventHandler.class.getName(), props)
-		    .setImplementation(implementation);
+            .setInterface(EventHandler.class.getName(), props)
+            .setImplementation(implementation);
         synchronized (m_eventHandlers) {
-        	m_eventHandlers.add(component);
+            m_eventHandlers.add(component);
         }
-		m_manager.add(component);
+        m_manager.add(component);
     }
-    
+
     private void cleanupListeners() {
-    	Component[] components;
+        Component[] components;
         synchronized (m_eventHandlers) {
-			components = m_eventHandlers.toArray(new Component[m_eventHandlers.size()]);
-			m_eventHandlers.clear();
+            components = m_eventHandlers.toArray(new Component[m_eventHandlers.size()]);
+            m_eventHandlers.clear();
         }
         for (Component component : components) {
-        	m_manager.remove(component);
+            m_manager.remove(component);
         }
     }
 
@@ -831,9 +851,11 @@ public class VaadinClient extends com.va
 
     /**
      * Create a button to show a pop window for adding new features.
-     * @param user 
      * 
-     * @param main Main Window
+     * @param user
+     * 
+     * @param main
+     *            Main Window
      * @return Button
      */
     private Button createAddArtifactButton(User user) {
@@ -847,9 +869,9 @@ public class VaadinClient extends com.va
     }
 
     /***
-     * Create a button to show popup window for adding a new feature. On success
-     * this calls the createFeature() method.
-     * @param user 
+     * Create a button to show popup window for adding a new feature. On success this calls the createFeature() method.
+     * 
+     * @param user
      * 
      * @return the add-feature button instance.
      */
@@ -875,9 +897,10 @@ public class VaadinClient extends com.va
     }
 
     /**
-     * Create a button to show a popup window for adding a new distribution. On
-     * success this calls the createDistribution() method.
-     * @param user 
+     * Create a button to show a popup window for adding a new distribution. On success this calls the
+     * createDistribution() method.
+     * 
+     * @param user
      * 
      * @return the add-distribution button instance.
      */
@@ -904,9 +927,9 @@ public class VaadinClient extends com.va
     }
 
     /**
-     * Create a button to show a popup window for adding a new target. On
-     * success this calls the createTarget() method
-     * @param user 
+     * Create a button to show a popup window for adding a new target. On success this calls the createTarget() method
+     * 
+     * @param user
      * 
      * @return the add-target button instance.
      */
@@ -942,8 +965,10 @@ public class VaadinClient extends com.va
     /**
      * Create a new feature in the feature repository.
      * 
-     * @param name the name of the new feature;
-     * @param description the description of the new feature.
+     * @param name
+     *            the name of the new feature;
+     * @param description
+     *            the description of the new feature.
      */
     private void createFeature(String name, String description) {
         Map<String, String> attributes = new HashMap<String, String>();
@@ -956,7 +981,8 @@ public class VaadinClient extends com.va
     /**
      * Create a new target in the stateful target repository.
      * 
-     * @param name the name of the new target;
+     * @param name
+     *            the name of the new target;
      */
     private void createTarget(String name) {
         Map<String, String> attributes = new HashMap<String, String>();
@@ -969,8 +995,10 @@ public class VaadinClient extends com.va
     /**
      * Create a new distribution in the distribution repository
      * 
-     * @param name the name of the new distribution;
-     * @param description the description of the new distribution.
+     * @param name
+     *            the name of the new distribution;
+     * @param description
+     *            the description of the new distribution.
      */
     private void createDistribution(String name, String description) {
         Map<String, String> attributes = new HashMap<String, String>();
@@ -1002,7 +1030,7 @@ public class VaadinClient extends com.va
             protected ArtifactRepository getArtifactRepository() {
                 return m_artifactRepository;
             }
-            
+
             @Override
             protected URLConnection openConnection(URL url) throws IOException {
                 return m_connectionFactory.createConnection(url);
@@ -1022,12 +1050,13 @@ public class VaadinClient extends com.va
      * Authenticates the given user by creating all dependent services.
      * 
      * @param user
-     * @throws IOException in case of I/O problems.
+     * @throws IOException
+     *             in case of I/O problems.
      */
     private boolean login(final User user) {
         try {
             RepositoryAdminLoginContext context = m_admin.createLoginContext(user);
-            
+
             // @formatter:off
             context
                 .add(context.createShopRepositoryContext()
@@ -1046,7 +1075,7 @@ public class VaadinClient extends com.va
         }
         catch (Exception e) {
             m_log.log(LogService.LOG_WARNING, "Login failed! Destroying session...", e);
-            
+
             try {
                 // Avoid errors when the user tries to login again (due to the stale session)...
                 m_admin.logout(true /* force */);

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/ArtifactsPanel.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/ArtifactsPanel.java?rev=1535767&r1=1535766&r2=1535767&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/ArtifactsPanel.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/ArtifactsPanel.java Fri Oct 25 15:31:42 2013
@@ -29,7 +29,7 @@ import org.apache.ace.client.repository.
 import org.apache.ace.client.repository.repository.ArtifactRepository;
 import org.apache.ace.webui.UIExtensionFactory;
 import org.apache.ace.webui.vaadin.AssociationRemover;
-import org.apache.ace.webui.vaadin.Associations;
+import org.osgi.framework.Constants;
 
 import com.vaadin.data.Item;
 
@@ -41,14 +41,44 @@ public abstract class ArtifactsPanel ext
     /**
      * Creates a new {@link ArtifactsPanel} instance.
      * 
-     * @param associations the assocation-holder object;
-     * @param associationRemover the helper for removing associations.
+     * @param associations
+     *            the assocation-holder object;
+     * @param associationRemover
+     *            the helper for removing associations.
      */
-    public ArtifactsPanel(Associations associations, AssociationRemover associationRemover) {
+    public ArtifactsPanel(AssociationHelper associations, AssociationRemover associationRemover) {
         super(associations, associationRemover, "Artifact", UIExtensionFactory.EXTENSION_POINT_VALUE_ARTIFACT, true);
     }
 
     @Override
+    protected void add(ArtifactObject artifact) {
+        String itemId = artifact.getDefinition();
+        String parentId = getParentId(artifact);
+
+        if (parentId != null && !containsId(parentId)) {
+            Item item = addItem(parentId);
+            item.getItemProperty(OBJECT_NAME).setValue(getParentDisplayName(artifact));
+            item.getItemProperty(OBJECT_DESCRIPTION).setValue("");
+            // we *must* set a non-null icon for the parent as well to ensure that the tree-table open/collapse icon is
+            // rendered properly...
+            setItemIcon(parentId, createIconResource("resource_workingstate_unchanged"));
+        }
+
+        Item item = addItem(itemId);
+        if (item != null) {
+            populateItem(artifact, item);
+        }
+
+        if (parentId != null) {
+            setParent(itemId, parentId);
+            setCollapsed(parentId, false);
+            setItemIcon(artifact);
+        }
+
+        setChildrenAllowed(itemId, false);
+    }
+
+    @Override
     protected boolean doRemoveRightSideAssociation(ArtifactObject object, RepositoryObject other) {
         List<Artifact2FeatureAssociation> associations = object.getAssociationsWith((FeatureObject) other);
         for (Artifact2FeatureAssociation association : associations) {
@@ -77,16 +107,53 @@ public abstract class ArtifactsPanel ext
 
     @Override
     protected void populateItem(ArtifactObject artifact, Item item) {
-        item.getItemProperty(WORKING_STATE_ICON).setValue(getWorkingStateIcon(artifact));
-        item.getItemProperty(OBJECT_NAME).setValue(artifact.getName());
+        item.getItemProperty(OBJECT_NAME).setValue(getDisplayName(artifact));
         item.getItemProperty(OBJECT_DESCRIPTION).setValue(artifact.getDescription());
-        item.getItemProperty(ACTIONS).setValue(createActionButtons(artifact));
+        item.getItemProperty(ACTION_UNLINK).setValue(createUnlinkButton(artifact));
+        item.getItemProperty(ACTION_DELETE).setValue(createRemoveItemButton(artifact));
+    }
+
+    protected String getDisplayName(ArtifactObject artifact) {
+        String bv = artifact.getAttribute(Constants.BUNDLE_VERSION);
+        if (bv != null) {
+            return bv;
+        }
+        return artifact.getName();
+    }
+
+    private String getParentDisplayName(ArtifactObject artifact) {
+        String bn = artifact.getAttribute(Constants.BUNDLE_NAME);
+        if (bn != null) {
+            return bn;
+        }
+        String name = artifact.getName();
+        int idx = name.lastIndexOf('-');
+        if (idx > 0) {
+            name = name.substring(0, idx);
+        }
+        else {
+            idx = name.lastIndexOf('.');
+            if (idx > 0) {
+                name = name.substring(0, idx);
+            }
+        }
+        return name;
+    }
+
+    private String getParentId(ArtifactObject artifact) {
+        String bsn = artifact.getAttribute(Constants.BUNDLE_SYMBOLICNAME);
+        if (bsn != null) {
+            return bsn;
+        }
+        // return getParentDisplayName(artifact);
+        return null;
     }
 
     /**
      * Returns whether or not the given artifact is actually a resource processor.
      * 
-     * @param artifact the artifact to test, cannot be <code>null</code>.
+     * @param artifact
+     *            the artifact to test, cannot be <code>null</code>.
      * @return <code>true</code> if the given artifact is a resource processor, <code>false</code> otherwise.
      */
     private boolean isResourceProcessor(ArtifactObject artifact) {

Copied: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/AssociationHelper.java (from r1535328, ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java)
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/AssociationHelper.java?p2=ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/AssociationHelper.java&p1=ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java&r1=1535328&r2=1535767&rev=1535767&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/Associations.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/AssociationHelper.java Fri Oct 25 15:31:42 2013
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.ace.webui.vaadin;
+package org.apache.ace.webui.vaadin.component;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -24,22 +24,15 @@ import java.util.Set;
 
 import org.apache.ace.client.repository.ObjectRepository;
 import org.apache.ace.client.repository.RepositoryObject;
-import org.apache.ace.client.repository.object.ArtifactObject;
-import org.apache.ace.client.repository.object.TargetObject;
-import org.apache.ace.client.repository.object.FeatureObject;
-import org.apache.ace.client.repository.object.DistributionObject;
 import org.apache.ace.client.repository.stateful.StatefulTargetObject;
-import org.apache.ace.webui.NamedObject;
-import org.apache.ace.webui.domain.NamedArtifactObject;
-import org.apache.ace.webui.domain.NamedDistributionObject;
-import org.apache.ace.webui.domain.NamedFeatureObject;
-import org.apache.ace.webui.domain.NamedTargetObject;
 
+import com.vaadin.data.Item;
 import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.ui.Button;
 import com.vaadin.ui.Table;
 import com.vaadin.ui.Table.CellStyleGenerator;
 
-public class Associations {
+public class AssociationHelper {
     private List<RepositoryObject> m_associatedItems = new ArrayList<RepositoryObject>();
     private List<RepositoryObject> m_relatedItems = new ArrayList<RepositoryObject>();
     private Table m_activeTable;
@@ -64,20 +57,17 @@ public class Associations {
     }
 
     public RepositoryObject lookupInActiveSelection(Object item) {
+        if (m_activeSelectionListener == null) {
+            return null;
+        }
         return m_activeSelectionListener.lookup(item);
     }
 
-    public void addAssociatedItems(List items) {
-        m_associatedItems.addAll(items);
-    }
-
-    public void addRelatedItems(List items) {
-        m_relatedItems.addAll(items);
-    }
-
-    public CellStyleGenerator createCellStyleGenerator() {
+    public CellStyleGenerator createCellStyleGenerator(final BaseObjectPanel parent) {
         return new CellStyleGenerator() {
             public String getStyle(Object itemId, Object propertyId) {
+                Item item = parent.getItem(itemId);
+
                 if (propertyId == null) {
                     // no propertyId, styling row
                     for (RepositoryObject o : m_associatedItems) {
@@ -90,33 +80,37 @@ public class Associations {
                             return "related";
                         }
                     }
+
+                    parent.updateItemIcon(itemId);
+                }
+                else if (BaseObjectPanel.OBJECT_DESCRIPTION.equals(propertyId)) {
+                    return "description";
+                }
+                else if (BaseObjectPanel.ACTION_UNLINK.equals(propertyId)) {
+                    Button unlinkButton = (Button) item.getItemProperty(propertyId).getValue();
+
+                    boolean enabled = false;
+                    for (RepositoryObject o : m_associatedItems) {
+                        if (equals(itemId, o)) {
+                            enabled = true;
+                        }
+                    }
+
+                    if (unlinkButton != null) {
+                        unlinkButton.setEnabled(enabled);
+                    }
                 }
                 return null;
             }
 
-            public boolean equals(Object itemId, RepositoryObject object) {
-                return (getNamedObject(object).getDefinition().equals(itemId));
+            private boolean equals(Object itemId, RepositoryObject object) {
+                return object.getDefinition().equals(itemId);
             }
         };
     }
 
-    public NamedObject getNamedObject(RepositoryObject object) {
-        if (object instanceof ArtifactObject) {
-            return new NamedArtifactObject((ArtifactObject) object);
-        }
-        else if (object instanceof FeatureObject) {
-            return new NamedFeatureObject((FeatureObject) object);
-        }
-        else if (object instanceof DistributionObject) {
-            return new NamedDistributionObject((DistributionObject) object);
-        }
-        else if (object instanceof StatefulTargetObject) {
-            return new NamedTargetObject((StatefulTargetObject) object);
-        }
-        else if (object instanceof TargetObject) {
-            return new NamedTargetObject((TargetObject) object);
-        }
-        return null;
+    public SelectionListener createSelectionListener(Table table, ObjectRepository<? extends RepositoryObject> repository, Class[] left, Class[] right, Table[] tablesToRefresh) {
+        return new SelectionListener(table, repository, left, right, tablesToRefresh);
     }
 
     /**
@@ -129,7 +123,8 @@ public class Associations {
     }
 
     /**
-     * Helper method to find all related {@link RepositoryObject}s in a given 'direction', starting with a list of objects
+     * Helper method to find all related {@link RepositoryObject}s in a given 'direction', starting with a list of
+     * objects
      */
     private <FROM extends RepositoryObject, TO extends RepositoryObject> List<TO> getRelated(List<FROM> from,
         Class<TO> toClass) {
@@ -140,7 +135,7 @@ public class Associations {
         return result;
     }
 
-    public class SelectionListener implements Table.ValueChangeListener {
+    private class SelectionListener implements Table.ValueChangeListener {
         private final Table m_table;
         private final Table[] m_tablesToRefresh;
         private final ObjectRepository<? extends RepositoryObject> m_repository;
@@ -156,8 +151,8 @@ public class Associations {
             m_tablesToRefresh = tablesToRefresh;
         }
 
+        @SuppressWarnings("unchecked")
         public void valueChange(ValueChangeEvent event) {
-
             if (m_activeSelection != null && m_activeTable != null) {
                 if (!m_activeTable.equals(m_table)) {
                     for (Object val : m_activeSelection) {
@@ -181,6 +176,7 @@ public class Associations {
 
             if (value != null) {
                 clear();
+
                 for (Object val : value) {
                     RepositoryObject lo = lookup(val);
                     if (lo != null) {
@@ -233,10 +229,4 @@ public class Associations {
         }
 
     }
-
-    public SelectionListener createSelectionListener(final Table table,
-        final ObjectRepository<? extends RepositoryObject> repository, final Class[] left, final Class[] right,
-        final Table[] tablesToRefresh) {
-        return new SelectionListener(table, repository, left, right, tablesToRefresh);
-    }
 }

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/BaseObjectPanel.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/BaseObjectPanel.java?rev=1535767&r1=1535766&r2=1535767&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/BaseObjectPanel.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/BaseObjectPanel.java Fri Oct 25 15:31:42 2013
@@ -30,8 +30,8 @@ import org.apache.ace.client.repository.
 import org.apache.ace.client.repository.RepositoryObject.WorkingState;
 import org.apache.ace.webui.NamedObject;
 import org.apache.ace.webui.UIExtensionFactory;
+import org.apache.ace.webui.domain.NamedObjectFactory;
 import org.apache.ace.webui.vaadin.AssociationRemover;
-import org.apache.ace.webui.vaadin.Associations;
 import org.apache.ace.webui.vaadin.EditWindow;
 import org.apache.felix.dm.Component;
 import org.apache.felix.dm.DependencyManager;
@@ -45,22 +45,24 @@ import com.vaadin.terminal.Resource;
 import com.vaadin.terminal.ThemeResource;
 import com.vaadin.ui.Button;
 import com.vaadin.ui.Embedded;
-import com.vaadin.ui.HorizontalLayout;
 import com.vaadin.ui.Table;
+import com.vaadin.ui.TreeTable;
 import com.vaadin.ui.Window.Notification;
+import com.vaadin.ui.themes.Reindeer;
 
 /**
  * Provides a custom table for displaying artifacts, features and so on.
  */
-abstract class BaseObjectPanel<REPO_OBJ extends RepositoryObject, REPO extends ObjectRepository<REPO_OBJ>> extends Table implements EventHandler {
+abstract class BaseObjectPanel<REPO_OBJ extends RepositoryObject, REPO extends ObjectRepository<REPO_OBJ>> extends TreeTable implements EventHandler {
 
     /**
      * Provides a generic remove item button.
      */
-    private static class RemoveItemButton extends Button {
-        public RemoveItemButton(final RepositoryObject object, final ObjectRepository repository) {
+    private class RemoveItemButton extends Button {
+        public RemoveItemButton(final REPO repository, final REPO_OBJ object) {
             super("x");
-            setStyleName("small");
+            setStyleName(Reindeer.BUTTON_SMALL);
+            setDescription("Delete " + getDisplayName(object));
 
             addListener(new Button.ClickListener() {
                 public void buttonClick(ClickEvent event) {
@@ -80,11 +82,14 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Provides a generic remove-link (or association) button.
      */
-    private class RemoveLinkButton<REPO_OBJECT extends RepositoryObject> extends Button {
-        public RemoveLinkButton(final REPO_OBJECT object, final Table toLeft, final Table toRight,
-            final BaseObjectPanel removeHandler) {
+    private class RemoveLinkButton extends Button {
+        public RemoveLinkButton(final REPO_OBJ object, final Table toLeft, final Table toRight) {
             super("-");
-            setStyleName("small");
+            setStyleName(Reindeer.BUTTON_SMALL);
+            setData(object.getDefinition());
+            setDescription("Unlink " + getDisplayName(object));
+            // Only enable this button when actually selected...
+            setEnabled(false);
 
             addListener(new Button.ClickListener() {
                 public void buttonClick(ClickEvent event) {
@@ -93,13 +98,13 @@ abstract class BaseObjectPanel<REPO_OBJ 
                         if (m_associations.isActiveTable(toLeft)) {
                             for (Object item : selection) {
                                 RepositoryObject selected = m_associations.lookupInActiveSelection(item);
-                                removeHandler.removeLeftSideAssociation(object, selected);
+                                removeLeftSideAssociation(object, selected);
                             }
                         }
                         else if (m_associations.isActiveTable(toRight)) {
                             for (Object item : selection) {
                                 RepositoryObject selected = m_associations.lookupInActiveSelection(item);
-                                removeHandler.removeRightSideAssocation(object, selected);
+                                removeRightSideAssocation(object, selected);
                             }
                         }
                     }
@@ -164,36 +169,42 @@ abstract class BaseObjectPanel<REPO_OBJ 
         }
     }
 
-    protected static final String WORKING_STATE_ICON = "workStateIcon";
+    protected static final String ICON = "icon";
     protected static final String OBJECT_NAME = "name";
     protected static final String OBJECT_DESCRIPTION = "description";
-    protected static final String ACTIONS = "actions";
+    protected static final String ACTION_UNLINK = "unlink";
+    protected static final String ACTION_DELETE = "delete";
 
     protected static final int ICON_HEIGHT = 16;
     protected static final int ICON_WIDTH = 16;
+    /** Empirically determined (most common width appears to be 36px). */
+    protected static final int FIXED_COLUMN_WIDTH = 36;
 
-    private final Associations m_associations;
+    private final AssociationHelper m_associations;
     protected final AssociationRemover m_associationRemover;
 
     private final List<UIExtensionFactoryHolder> m_extensionFactories;
     private final String m_extensionPoint;
 
-    private Button m_removeLinkButton;
-    private Button m_deleteButton;
-
     private Table m_leftTable;
     private Table m_rightTable;
 
     /**
      * Creates a new {@link BaseObjectPanel} instance.
      * 
-     * @param associations the associations for this panel;
-     * @param associationRemover the association remove to use for removing associations;
-     * @param name the name of this panel;
-     * @param extensionPoint the extension point to listen for;
-     * @param hasEdit <code>true</code> if double clicking an row in this table should show an editor, <code>false</code> to disallow editing.
+     * @param associations
+     *            the associations for this panel;
+     * @param associationRemover
+     *            the association remove to use for removing associations;
+     * @param name
+     *            the name of this panel;
+     * @param extensionPoint
+     *            the extension point to listen for;
+     * @param hasEdit
+     *            <code>true</code> if double clicking an row in this table should show an editor, <code>false</code> to
+     *            disallow editing.
      */
-    public BaseObjectPanel(final Associations associations, final AssociationRemover associationRemover,
+    public BaseObjectPanel(final AssociationHelper associations, final AssociationRemover associationRemover,
         final String name, final String extensionPoint, final boolean hasEdit) {
         super(name + "s");
 
@@ -205,20 +216,26 @@ abstract class BaseObjectPanel<REPO_OBJ 
         defineTableColumns();
 
         setSizeFull();
-        setCellStyleGenerator(m_associations.createCellStyleGenerator());
+        setCellStyleGenerator(m_associations.createCellStyleGenerator(this));
         setSelectable(true);
         setMultiSelect(true);
         setImmediate(true);
         setDragMode(TableDragMode.MULTIROW);
+        setColumnCollapsingAllowed(true);
+
+        setItemIconPropertyId(ICON);
+        setHierarchyColumn(ICON);
 
         if (hasEdit) {
             addListener(new ItemClickListener() {
                 public void itemClick(ItemClickEvent event) {
                     if (event.isDoubleClick()) {
-                        String itemId = (String) event.getItemId();
-                        RepositoryObject object = getFromId(itemId);
-                        NamedObject namedObject = m_associations.getNamedObject(object);
-                        showEditWindow(namedObject);
+                        RepositoryObject object = getFromId((String) event.getItemId());
+
+                        NamedObject namedObject = NamedObjectFactory.getNamedObject(object);
+                        if (namedObject != null) {
+                            showEditWindow(namedObject);
+                        }
                     }
                 }
             });
@@ -228,8 +245,10 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Called by the dependency manager in case a new {@link UIExtensionFactory} is registered.
      * 
-     * @param ref the service reference of the new extension;
-     * @param factory the extension instance itself.
+     * @param ref
+     *            the service reference of the new extension;
+     * @param factory
+     *            the extension instance itself.
      */
     public final void addExtension(ServiceReference ref, UIExtensionFactory factory) {
         synchronized (m_extensionFactories) {
@@ -263,7 +282,8 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Called by the dependency manager upon initialization of this component.
      * 
-     * @param component the component representing this object.
+     * @param component
+     *            the component representing this object.
      */
     public void init(Component component) {
         populate();
@@ -289,8 +309,10 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Called by the dependency manager in case a {@link UIExtensionFactory} is unregistered.
      * 
-     * @param ref the service reference of the extension;
-     * @param factory the extension instance itself.
+     * @param ref
+     *            the service reference of the extension;
+     * @param factory
+     *            the extension instance itself.
      */
     public final void removeExtension(ServiceReference ref, UIExtensionFactory factory) {
         synchronized (m_extensionFactories) {
@@ -302,7 +324,8 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Sets the left-side table, that defines the left-hand side of the assocations of the entities.
      * 
-     * @param leftTable the table to set, can be <code>null</code>.
+     * @param leftTable
+     *            the table to set, can be <code>null</code>.
      */
     public final void setLeftTable(Table leftTable) {
         m_leftTable = leftTable;
@@ -311,7 +334,8 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Sets the right-side table, that defines the right-hand side of the assocations of the entities.
      * 
-     * @param rightTable the table to set, can be <code>null</code>.
+     * @param rightTable
+     *            the table to set, can be <code>null</code>.
      */
     public final void setRightTable(Table rightTable) {
         m_rightTable = rightTable;
@@ -320,8 +344,10 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Removes the left-hand side associations for a given repository object.
      * 
-     * @param object the repository object to remove the left-hand side associations;
-     * @param other the (left-hand side) repository object to remove the associations for.
+     * @param object
+     *            the repository object to remove the left-hand side associations;
+     * @param other
+     *            the (left-hand side) repository object to remove the associations for.
      */
     final void removeLeftSideAssociation(REPO_OBJ object, RepositoryObject other) {
         if (doRemoveLeftSideAssociation(object, other)) {
@@ -336,8 +362,10 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Removes the right-hand side associations for a given repository object.
      * 
-     * @param object the repository object to remove the right-hand side associations;
-     * @param other the (right-hand side) repository object to remove the associations for.
+     * @param object
+     *            the repository object to remove the right-hand side associations;
+     * @param other
+     *            the (right-hand side) repository object to remove the associations for.
      */
     final void removeRightSideAssocation(REPO_OBJ object, RepositoryObject other) {
         if (doRemoveRightSideAssociation(object, other)) {
@@ -352,42 +380,40 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Adds a given repository object to this table.
      * 
-     * @param object the repository object to add, cannot be <code>null</code>.
+     * @param object
+     *            the repository object to add, cannot be <code>null</code>.
      */
     protected void add(REPO_OBJ object) {
         Item item = addItem(object.getDefinition());
         if (item != null) {
+            setChildrenAllowed(object.getDefinition(), false);
+
             populateItem(object, item);
+            setItemIcon(object);
         }
     }
 
-    /**
-     * Creates the action buttons.
-     * 
-     * @param statefulTarget the target to create the action buttons for, cannot be <code>null</code>.
-     * @return a HorizontalLayout instance with action buttons.
-     */
-    protected HorizontalLayout createActionButtons(REPO_OBJ object) {
-        m_removeLinkButton = createRemoveLinkButton(object);
-        m_deleteButton = createRemoveItemButton(object);
+    protected void updateItemIcon(Object itemId) {
+        REPO_OBJ obj = getFromId((String) itemId);
+        setItemIcon(obj);
+    }
 
-        HorizontalLayout buttons = new HorizontalLayout();
-        if (m_removeLinkButton != null) {
-            buttons.addComponent(m_removeLinkButton);
-        }
-        if (m_deleteButton != null) {
-            buttons.addComponent(m_deleteButton);
+    protected void setItemIcon(REPO_OBJ object) {
+        if (object != null) {
+            Resource icon = getWorkingStateIcon(object);
+            setItemIcon(object.getDefinition(), icon);
         }
-        return buttons;
     }
-    
+
     protected abstract EditWindow createEditor(NamedObject object, List<UIExtensionFactory> extensions);
 
     /**
      * Factory method to create an embeddable icon.
      * 
-     * @param name the name of the icon to use (is also used as tooltip text);
-     * @param res the resource denoting the actual icon.
+     * @param name
+     *            the name of the icon to use (is also used as tooltip text);
+     * @param res
+     *            the resource denoting the actual icon.
      * @return an embeddable icon, never <code>null</code>.
      */
     protected Embedded createIcon(String name, Resource res) {
@@ -402,50 +428,62 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Factory method to create an icon resource.
      * 
-     * @param iconName the base name of the icon to use, it will be appended with '.png'.
+     * @param iconName
+     *            the base name of the icon to use, it will be appended with '.png'.
      * @return a {@link Resource} denoting the icon.
      */
-    protected Resource createIconResource(String iconName) {
+    protected ThemeResource createIconResource(String iconName) {
         return new ThemeResource("icons/" + iconName.toLowerCase() + ".png");
     }
 
     /**
      * Factory method to create a remove-item button.
      * 
-     * @param object the repository object to create the remove-item button for, cannot be <code>null</code>.
+     * @param object
+     *            the repository object to create the remove-item button for, cannot be <code>null</code>.
      * @return a button, can be <code>null</code> if removal of this repository object is not supported.
      */
     protected Button createRemoveItemButton(REPO_OBJ object) {
-        return new RemoveItemButton(object, getRepository());
+        return new RemoveItemButton(getRepository(), object);
     }
 
     /**
      * Factory method to create a remove-link button.
      * 
-     * @param object the repository object to create the remove-link button for, cannot be <code>null</code>.
+     * @param object
+     *            the repository object to create the remove-link button for, cannot be <code>null</code>.
      * @return a button, can be <code>null</code> if remove-link is not supported.
      */
-    protected Button createRemoveLinkButton(REPO_OBJ object) {
-        return new RemoveLinkButton<REPO_OBJ>(object, m_leftTable, m_rightTable, this);
+    protected Button createUnlinkButton(REPO_OBJ object) {
+        return new RemoveLinkButton(object, m_leftTable, m_rightTable);
     }
 
     /**
      * Defines the table columns for this panel.
      */
     protected void defineTableColumns() {
-        addContainerProperty(WORKING_STATE_ICON, Embedded.class, null, "", null, ALIGN_CENTER);
+        addContainerProperty(ICON, Resource.class, null, "", null, ALIGN_CENTER);
         addContainerProperty(OBJECT_NAME, String.class, null);
         addContainerProperty(OBJECT_DESCRIPTION, String.class, null);
-        addContainerProperty(ACTIONS, HorizontalLayout.class, null);
+        addContainerProperty(ACTION_UNLINK, Button.class, null, "", null, ALIGN_CENTER);
+        addContainerProperty(ACTION_DELETE, Button.class, null, "", null, ALIGN_CENTER);
 
-        setColumnWidth(WORKING_STATE_ICON, ICON_WIDTH);
+        setColumnWidth(ACTION_UNLINK, FIXED_COLUMN_WIDTH);
+        setColumnWidth(ACTION_DELETE, FIXED_COLUMN_WIDTH);
+        setColumnWidth(ICON, FIXED_COLUMN_WIDTH);
+
+        setColumnCollapsible(ICON, false);
+        setColumnCollapsible(ACTION_UNLINK, false);
+        setColumnCollapsible(ACTION_DELETE, false);
     }
 
     /**
      * Does the actual removal of the left-hand side associations for a given repository object.
      * 
-     * @param object the repository object to remove the left-hand side associations;
-     * @param other the (left-hand side) repository object to remove the associations for.
+     * @param object
+     *            the repository object to remove the left-hand side associations;
+     * @param other
+     *            the (left-hand side) repository object to remove the associations for.
      * @return <code>true</code> if the associations were removed, <code>false</code> if not.
      */
     protected boolean doRemoveLeftSideAssociation(REPO_OBJ object, RepositoryObject other) {
@@ -455,8 +493,10 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Does the actual removal of the right-hand side associations for a given repository object.
      * 
-     * @param object the repository object to remove the right-hand side associations;
-     * @param other the (right-hand side) repository object to remove the associations for.
+     * @param object
+     *            the repository object to remove the right-hand side associations;
+     * @param other
+     *            the (right-hand side) repository object to remove the associations for.
      * @return <code>true</code> if the associations were removed, <code>false</code> if not.
      */
     protected boolean doRemoveRightSideAssociation(REPO_OBJ object, RepositoryObject other) {
@@ -464,10 +504,21 @@ abstract class BaseObjectPanel<REPO_OBJ 
     }
 
     /**
+     * Returns a user-friendly name for a given repository object.
+     * 
+     * @param object
+     *            the repository object to get the display name for, cannot be <code>null</code>.
+     * @return the display name, never <code>null</code>.
+     */
+    protected abstract String getDisplayName(REPO_OBJ object);
+
+    /**
      * Converts a table-id back to a concrete {@link RepositoryObject}.
      * 
-     * @param id the identifier of the {@link RepositoryObject}, cannot be <code>null</code>.
-     * @return a {@link RepositoryObject} instance for the given ID, can be <code>null</code> in case no such object is found.
+     * @param id
+     *            the identifier of the {@link RepositoryObject}, cannot be <code>null</code>.
+     * @return a {@link RepositoryObject} instance for the given ID, can be <code>null</code> in case no such object is
+     *         found.
      */
     protected final REPO_OBJ getFromId(String id) {
         return getRepository().get(id);
@@ -490,7 +541,8 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Determines the working state for the given repository object.
      * 
-     * @param object the repository object to determine the working state for, cannot be <code>null</code>.
+     * @param object
+     *            the repository object to determine the working state for, cannot be <code>null</code>.
      * @return the working state for the given repository object, never <code>null</code>.
      */
     protected WorkingState getWorkingState(RepositoryObject object) {
@@ -500,19 +552,22 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Helper method to return the working state icon for the given repository object.
      * 
-     * @param object the repository object to get the icon for, cannot be <code>null</code>.
+     * @param object
+     *            the repository object to get the icon for, cannot be <code>null</code>.
      * @return an icon representing the working state of the given repository object, never <code>null</code>.
      */
-    protected Embedded getWorkingStateIcon(RepositoryObject object) {
+    protected Resource getWorkingStateIcon(RepositoryObject object) {
         String name = getWorkingState(object).name();
-        Resource res = createIconResource("resource_workingstate_" + name);
-        return createIcon(name, res);
+        return createIconResource("resource_workingstate_" + name);
     }
 
     /**
-     * @param topic the topic of the event;
-     * @param entity the entity of the event;
-     * @param event the original event.
+     * @param topic
+     *            the topic of the event;
+     * @param entity
+     *            the entity of the event;
+     * @param event
+     *            the original event.
      * 
      * @see org.osgi.service.event.EventHandler#handleEvent(org.osgi.service.event.Event)
      */
@@ -521,7 +576,8 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Returns whether the given {@link RepositoryObject} can be handled by this panel.
      * 
-     * @param entity the entity to test, cannot be <code>null</code>.
+     * @param entity
+     *            the entity to test, cannot be <code>null</code>.
      * @return <code>true</code> if the entity is supported by this panel, <code>false</code> if not.
      */
     protected abstract boolean isSupportedEntity(RepositoryObject entity);
@@ -529,15 +585,18 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Populates the given table item with information from the given repository object.
      * 
-     * @param object the repository object to take the information from, cannot be <code>null</code>;
-     * @param item the table item to populate, cannot be <code>null</code>.
+     * @param object
+     *            the repository object to take the information from, cannot be <code>null</code>;
+     * @param item
+     *            the table item to populate, cannot be <code>null</code>.
      */
     protected abstract void populateItem(REPO_OBJ object, Item item);
 
     /**
      * Removes a given repository object from this table.
      * 
-     * @param object the repository object to remove, cannot be <code>null</code>.
+     * @param object
+     *            the repository object to remove, cannot be <code>null</code>.
      */
     protected void remove(REPO_OBJ object) {
         removeItem(object.getDefinition());
@@ -546,7 +605,8 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Updates a given repository object in this table.
      * 
-     * @param object the repository object to update, cannot be <code>null</code>.
+     * @param object
+     *            the repository object to update, cannot be <code>null</code>.
      */
     protected void update(REPO_OBJ object) {
         if (object != null) {
@@ -594,11 +654,13 @@ abstract class BaseObjectPanel<REPO_OBJ 
     /**
      * Shows an edit window for the given named object.
      * 
-     * @param object the named object to edit;
-     * @param main the main window to use.
+     * @param object
+     *            the named object to edit;
+     * @param main
+     *            the main window to use.
      */
     private void showEditWindow(NamedObject object) {
         List<UIExtensionFactory> extensions = getExtensionFactories();
         createEditor(object, extensions).show(getParent().getWindow());
     }
-}
\ No newline at end of file
+}

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/DistributionsPanel.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/DistributionsPanel.java?rev=1535767&r1=1535766&r2=1535767&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/DistributionsPanel.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/DistributionsPanel.java Fri Oct 25 15:31:42 2013
@@ -30,7 +30,6 @@ import org.apache.ace.client.repository.
 import org.apache.ace.client.repository.repository.DistributionRepository;
 import org.apache.ace.webui.UIExtensionFactory;
 import org.apache.ace.webui.vaadin.AssociationRemover;
-import org.apache.ace.webui.vaadin.Associations;
 
 import com.vaadin.data.Item;
 
@@ -42,10 +41,12 @@ public abstract class DistributionsPanel
     /**
      * Creates a new {@link DistributionsPanel} instance.
      * 
-     * @param associations the assocation-holder object;
-     * @param associationRemover the helper for removing associations.
+     * @param associations
+     *            the assocation-holder object;
+     * @param associationRemover
+     *            the helper for removing associations.
      */
-    public DistributionsPanel(Associations associations, AssociationRemover associationRemover) {
+    public DistributionsPanel(AssociationHelper associations, AssociationRemover associationRemover) {
         super(associations, associationRemover, "Distribution", UIExtensionFactory.EXTENSION_POINT_VALUE_DISTRIBUTION,
             true /* hasEdit */);
     }
@@ -68,6 +69,12 @@ public abstract class DistributionsPanel
         return true;
     }
 
+    @Override
+    protected String getDisplayName(DistributionObject object) {
+        return object.getName();
+    }
+
+    @Override
     protected void handleEvent(String topic, RepositoryObject entity, org.osgi.service.event.Event event) {
         DistributionObject distribution = (DistributionObject) entity;
         if (DistributionObject.TOPIC_ADDED.equals(topic)) {
@@ -88,9 +95,9 @@ public abstract class DistributionsPanel
 
     @Override
     protected void populateItem(DistributionObject distribution, Item item) {
-        item.getItemProperty(WORKING_STATE_ICON).setValue(getWorkingStateIcon(distribution));
         item.getItemProperty(OBJECT_NAME).setValue(distribution.getName());
         item.getItemProperty(OBJECT_DESCRIPTION).setValue(distribution.getDescription());
-        item.getItemProperty(ACTIONS).setValue(createActionButtons(distribution));
+        item.getItemProperty(ACTION_UNLINK).setValue(createUnlinkButton(distribution));
+        item.getItemProperty(ACTION_DELETE).setValue(createRemoveItemButton(distribution));
     }
 }

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/FeaturesPanel.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/FeaturesPanel.java?rev=1535767&r1=1535766&r2=1535767&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/FeaturesPanel.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/FeaturesPanel.java Fri Oct 25 15:31:42 2013
@@ -30,7 +30,6 @@ import org.apache.ace.client.repository.
 import org.apache.ace.client.repository.repository.FeatureRepository;
 import org.apache.ace.webui.UIExtensionFactory;
 import org.apache.ace.webui.vaadin.AssociationRemover;
-import org.apache.ace.webui.vaadin.Associations;
 
 import com.vaadin.data.Item;
 
@@ -42,10 +41,12 @@ public abstract class FeaturesPanel exte
     /**
      * Creates a new {@link FeaturesPanel} instance.
      * 
-     * @param associations the assocation-holder object;
-     * @param associationRemover the helper for removing associations.
+     * @param associations
+     *            the assocation-holder object;
+     * @param associationRemover
+     *            the helper for removing associations.
      */
-    public FeaturesPanel(Associations associations, AssociationRemover associationRemover) {
+    public FeaturesPanel(AssociationHelper associations, AssociationRemover associationRemover) {
         super(associations, associationRemover, "Feature", UIExtensionFactory.EXTENSION_POINT_VALUE_FEATURE, true);
     }
 
@@ -57,7 +58,7 @@ public abstract class FeaturesPanel exte
         }
         return true;
     }
-    
+
     @Override
     protected boolean doRemoveRightSideAssociation(FeatureObject object, RepositoryObject other) {
         List<Feature2DistributionAssociation> associations = object.getAssociationsWith((DistributionObject) other);
@@ -67,6 +68,12 @@ public abstract class FeaturesPanel exte
         return true;
     }
     
+    @Override
+    protected String getDisplayName(FeatureObject object) {
+        return object.getName();
+    }
+
+    @Override
     protected void handleEvent(String topic, RepositoryObject entity, org.osgi.service.event.Event event) {
         FeatureObject feature = (FeatureObject) entity;
         if (FeatureObject.TOPIC_ADDED.equals(topic)) {
@@ -79,7 +86,7 @@ public abstract class FeaturesPanel exte
             update(feature);
         }
     }
-    
+
     @Override
     protected boolean isSupportedEntity(RepositoryObject entity) {
         return entity instanceof FeatureObject;
@@ -87,10 +94,9 @@ public abstract class FeaturesPanel exte
 
     @Override
     protected void populateItem(FeatureObject feature, Item item) {
-        item.getItemProperty(WORKING_STATE_ICON).setValue(getWorkingStateIcon(feature));
         item.getItemProperty(OBJECT_NAME).setValue(feature.getName());
         item.getItemProperty(OBJECT_DESCRIPTION).setValue(feature.getDescription());
-        item.getItemProperty(ACTIONS).setValue(createActionButtons(feature));
+        item.getItemProperty(ACTION_UNLINK).setValue(createUnlinkButton(feature));
+        item.getItemProperty(ACTION_DELETE).setValue(createRemoveItemButton(feature));
     }
 }
-

Modified: ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/TargetsPanel.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/TargetsPanel.java?rev=1535767&r1=1535766&r2=1535767&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/TargetsPanel.java (original)
+++ ace/trunk/org.apache.ace.webui.vaadin/src/org/apache/ace/webui/vaadin/component/TargetsPanel.java Fri Oct 25 15:31:42 2013
@@ -20,7 +20,6 @@ package org.apache.ace.webui.vaadin.comp
 
 import java.util.List;
 
-import org.apache.ace.client.repository.RepositoryAdmin;
 import org.apache.ace.client.repository.RepositoryObject;
 import org.apache.ace.client.repository.RepositoryObject.WorkingState;
 import org.apache.ace.client.repository.object.Distribution2TargetAssociation;
@@ -30,13 +29,11 @@ import org.apache.ace.client.repository.
 import org.apache.ace.client.repository.stateful.StatefulTargetRepository;
 import org.apache.ace.webui.UIExtensionFactory;
 import org.apache.ace.webui.vaadin.AssociationRemover;
-import org.apache.ace.webui.vaadin.Associations;
 
 import com.vaadin.data.Item;
 import com.vaadin.terminal.Resource;
 import com.vaadin.ui.Button;
 import com.vaadin.ui.Embedded;
-import com.vaadin.ui.HorizontalLayout;
 
 /**
  * Provides an object panel for displaying (stateful) targets.
@@ -50,10 +47,12 @@ public abstract class TargetsPanel exten
     /**
      * Creates a new {@link TargetsPanel} instance.
      * 
-     * @param associations the assocation-holder object;
-     * @param associationRemover the helper for removing associations.
+     * @param associations
+     *            the assocation-holder object;
+     * @param associationRemover
+     *            the helper for removing associations.
      */
-    public TargetsPanel(Associations associations, AssociationRemover associationRemover) {
+    public TargetsPanel(AssociationHelper associations, AssociationRemover associationRemover) {
         super(associations, associationRemover, "Target", UIExtensionFactory.EXTENSION_POINT_VALUE_TARGET, true /* hasEdit */);
     }
 
@@ -65,20 +64,24 @@ public abstract class TargetsPanel exten
     }
 
     protected void defineTableColumns() {
-        addContainerProperty(WORKING_STATE_ICON, Embedded.class, null, "", null, ALIGN_CENTER);
-
+        addContainerProperty(ICON, Resource.class, null, "", null, ALIGN_CENTER);
         addContainerProperty(OBJECT_NAME, String.class, null);
-
         addContainerProperty(REGISTRATION_STATE_ICON, Embedded.class, null, "", null, ALIGN_CENTER);
         addContainerProperty(STORE_STATE_ICON, Embedded.class, null, "", null, ALIGN_CENTER);
         addContainerProperty(PROVISIONING_STATE_ICON, Embedded.class, null, "", null, ALIGN_CENTER);
+        addContainerProperty(ACTION_UNLINK, Button.class, null, "", null, ALIGN_CENTER);
+        addContainerProperty(ACTION_DELETE, Button.class, null, "", null, ALIGN_CENTER);
 
-        addContainerProperty(ACTIONS, HorizontalLayout.class, null);
-
-        setColumnWidth(WORKING_STATE_ICON, ICON_WIDTH);
+        setColumnWidth(ICON, FIXED_COLUMN_WIDTH);
+        setColumnWidth(ACTION_UNLINK, FIXED_COLUMN_WIDTH);
+        setColumnWidth(ACTION_DELETE, FIXED_COLUMN_WIDTH);
         setColumnWidth(REGISTRATION_STATE_ICON, ICON_WIDTH);
         setColumnWidth(STORE_STATE_ICON, ICON_WIDTH);
         setColumnWidth(PROVISIONING_STATE_ICON, ICON_WIDTH);
+        
+        setColumnCollapsible(ICON, false);
+        setColumnCollapsible(ACTION_UNLINK, false);
+        setColumnCollapsible(ACTION_DELETE, false);
     }
 
     @Override
@@ -91,6 +94,11 @@ public abstract class TargetsPanel exten
     }
 
     @Override
+    protected String getDisplayName(StatefulTargetObject object) {
+        return object.getID();
+    }
+
+    @Override
     protected WorkingState getWorkingState(RepositoryObject object) {
         final StatefulTargetObject statefulTarget = (StatefulTargetObject) object;
         if (statefulTarget.isRegistered()) {
@@ -99,6 +107,7 @@ public abstract class TargetsPanel exten
         return WorkingState.Unchanged;
     }
 
+    @Override
     protected void handleEvent(String topic, RepositoryObject entity, org.osgi.service.event.Event event) {
         StatefulTargetObject statefulTarget = asStatefulTargetObject(entity);
         if (StatefulTargetObject.TOPIC_ADDED.equals(topic)) {
@@ -117,16 +126,13 @@ public abstract class TargetsPanel exten
         return (entity instanceof StatefulTargetObject) || (entity instanceof TargetObject);
     }
 
-    protected void populateItem(StatefulTargetObject object, Item item) {
-        item.getItemProperty(WORKING_STATE_ICON).setValue(getWorkingStateIcon(object));
-
-        item.getItemProperty(OBJECT_NAME).setValue(object.getID());
-
-        item.getItemProperty(REGISTRATION_STATE_ICON).setValue(getRegistrationStateIcon(object));
-        item.getItemProperty(STORE_STATE_ICON).setValue(getStoreStateIcon(object));
-        item.getItemProperty(PROVISIONING_STATE_ICON).setValue(getProvisioningStateIcon(object));
-
-        item.getItemProperty(ACTIONS).setValue(createActionButtons(object));
+    protected void populateItem(StatefulTargetObject target, Item item) {
+        item.getItemProperty(OBJECT_NAME).setValue(target.getID());
+        item.getItemProperty(REGISTRATION_STATE_ICON).setValue(getRegistrationStateIcon(target));
+        item.getItemProperty(STORE_STATE_ICON).setValue(getStoreStateIcon(target));
+        item.getItemProperty(PROVISIONING_STATE_ICON).setValue(getProvisioningStateIcon(target));
+        item.getItemProperty(ACTION_UNLINK).setValue(createUnlinkButton(target));
+        item.getItemProperty(ACTION_DELETE).setValue(createRemoveItemButton(target));
     }
 
     private Embedded getProvisioningStateIcon(StatefulTargetObject object) {