From d5ef217841d863e4a040ac07b631b19b3202b9d8 Mon Sep 17 00:00:00 2001
From: Simon Barner <barner@fortiss.org>
Date: Mon, 1 Sep 2014 11:43:02 +0000
Subject: [PATCH] - Sort columns alphabetically using a TreeSet of the newly
 introduced ColumnHandles. - Ensure that "Create" columns for dynamic
 multi-instance annotations are right to the corresponding instances. -
 Simplify column handling. Merge updateSingleInstanceAnnotationColumns() /
 updateMultiInstanceAnnotationColumns() into update() refs 1841

---
 .../view/GenericAnnotationView.java           | 281 ++++++++++++------
 1 file changed, 187 insertions(+), 94 deletions(-)

diff --git a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/view/GenericAnnotationView.java b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/view/GenericAnnotationView.java
index 4f186af7f..a6cc52461 100644
--- a/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/view/GenericAnnotationView.java
+++ b/org.fortiss.tooling.base.ui/trunk/src/org/fortiss/tooling/base/ui/annotation/view/GenericAnnotationView.java
@@ -19,9 +19,9 @@ package org.fortiss.tooling.base.ui.annotation.view;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
 
 import org.eclipse.jface.viewers.ArrayContentProvider;
 import org.eclipse.jface.viewers.EditingSupport;
@@ -55,10 +55,6 @@ import org.fortiss.tooling.base.ui.annotation.valueprovider.IAnnotationValueProv
  */
 public class GenericAnnotationView extends AnnotationViewPartBase {
 
-	/** mapping of {@link IAnnotatedSpecification} -> TableViewerColumn */
-	protected HashMap<Class<? extends IAnnotatedSpecification>, TableViewerColumn> annotationSpecColumns =
-			new HashMap<Class<? extends IAnnotatedSpecification>, TableViewerColumn>();
-
 	/**
 	 * List containing the columns that are responsible for creating new keys. Required for
 	 * disposing when updating the view.
@@ -66,104 +62,185 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
 	private List<CreateAnnotationInstanceColumn> createColumns =
 			new ArrayList<CreateAnnotationInstanceColumn>();
 
+	/** The table viewer */
+	protected TableViewer tableViewer;
+
 	/**
-	 * Map for associating those specifications that allow multiple values with their according
-	 * AnnotationEntries.
+	 * Character used to separte the annotation name from the instance name for multi-instance
+	 * annotations.
 	 */
-	private Map<IAnnotatedSpecification, AnnotationEntry> specsAllowingMultipleValues =
-			new HashMap<IAnnotatedSpecification, AnnotationEntry>();
+	private final static String MULTIINSTANCE_NAME_SEPARATOR = ":";
 
-	/** The table viewer */
-	protected TableViewer tableViewer;
+	/**
+	 * Data required to identify a column displaying a particular {@link IAnnotatedSpecification} in
+	 * a column of the {@link GenericAnnotationView}. Used to sort columns (see
+	 * {@link #compareTo(ColumnHandle)}).
+	 */
+	private class ColumnHandle implements Comparable<ColumnHandle> {
+		/**
+		 * {@link AnnotationEntry} from which a particular {@link IAnnotatedSpecification} is to be
+		 * displayed in the column represented by this {@link ColumnHandle}.
+		 */
+		private AnnotationEntry entry;
+
+		/**
+		 * {@link IAnnotatedSpecification} from {@code entry} to be displayed in the column
+		 * represented by this {@link ColumnHandle}.
+		 */
+		private IAnnotatedSpecification annotatedSpecification;
+
+		/**
+		 * Instance key identifying the particular instance of an {@link IAnnotatedSpecification}
+		 * from {@code entry} to be displayed in the column represented by this {@link ColumnHandle}
+		 * .
+		 */
+		private String instanceKey;
+
+		/** Flag if this {@link ColumnHandle} represents a {@link CreateAnnotationInstanceColumn}. */
+		private boolean isCreateColumn;
+
+		/** Constructs a new {@link ColumnHandle}. */
+		public ColumnHandle(AnnotationEntry entry, IAnnotatedSpecification annotatedSpecification,
+				String instanceKey, boolean isCreateColumn) {
+
+			this.entry = entry;
+			this.annotatedSpecification = annotatedSpecification;
+			this.instanceKey = instanceKey;
+			this.isCreateColumn = isCreateColumn;
+		}
+
+		/**
+		 * Returns the {@link AnnotationEntry} from which a particular
+		 * {@link IAnnotatedSpecification} is to be
+		 * displayed in the column represented by this {@link ColumnHandle}.
+		 */
+		public AnnotationEntry getEntry() {
+			return entry;
+		}
+
+		/**
+		 * Returns the {@link IAnnotatedSpecification} from {@code entry} to be displayed in the
+		 * column represented by this {@link ColumnHandle}.
+		 */
+		public IAnnotatedSpecification getAnnotatedSpecification() {
+			return annotatedSpecification;
+		}
+
+		/**
+		 * Returns the instance key identifying the particular instance of an
+		 * {@link IAnnotatedSpecification} from {@code entry} to be displayed in the column
+		 * represented by this {@link ColumnHandle} .
+		 */
+		public String getInstanceKey() {
+			return instanceKey;
+		}
+
+		/**
+		 * Returns a flag if this {@link ColumnHandle} represents a
+		 * {@link CreateAnnotationInstanceColumn}.
+		 */
+		public boolean isCreateColumn() {
+			return isCreateColumn;
+		}
+
+		/** {@inheritDoc} */
+		@Override
+		public int compareTo(ColumnHandle other) {
+			String columnName = getColumnName(this);
+			String annotationName;
+
+			int columnNameSeparatorIndex = columnName.indexOf(MULTIINSTANCE_NAME_SEPARATOR);
+			if(columnNameSeparatorIndex != -1) {
+				annotationName = columnName.substring(0, columnNameSeparatorIndex);
+			} else {
+				annotationName = columnName;
+			}
+
+			String otherColumnName = getColumnName(other);
+			String otherAnnotationName;
+			int otherColumnNameSeparatorIndex =
+					otherColumnName.indexOf(MULTIINSTANCE_NAME_SEPARATOR);
+			if(otherColumnNameSeparatorIndex != -1) {
+				otherAnnotationName = otherColumnName.substring(0, otherColumnNameSeparatorIndex);
+			} else {
+				otherAnnotationName = otherColumnName;
+			}
+
+			// Place "create" columns (label: "<specification name>") after their instances
+			// (which are labeled "<specification name>: <instance name>").
+			if(annotationName.equals(otherAnnotationName)) {
+				// Ensure uniqueness of "create" columns"
+				if(isCreateColumn && other.isCreateColumn) {
+					return 0;
+				}
+				if(isCreateColumn) {
+					return 1;
+				}
+				if(other.isCreateColumn) {
+					return -1;
+				}
+			}
+
+			// Sort alphabetically by specification column label
+			return columnName.compareTo(otherColumnName);
+		}
+	}
 
 	/** {@inheritDoc} */
 	@Override
 	protected void update(Collection<AnnotationEntry> annotationEntries) {
-		updateSingleInstanceAnnotationColumns(annotationEntries);
-		updateMultiInstanceAnnotationColumns(annotationEntries);
-	}
+		/*
+		 * Sorted set of {@link ColumnHandle} used to instantiate this {@link
+		 * GenericAnnotationView}'s columns in the right order.
+		 */
+		Set<ColumnHandle> sortedColumnHandles = new TreeSet<ColumnHandle>();
 
-	/** Create columns for ordinary (single instance) {@link IAnnotatedSpecification}s */
-	private void
-			updateSingleInstanceAnnotationColumns(Collection<AnnotationEntry> annotationEntries) {
 		/*
-		 * disposes all columns of the table viewer, except the first column, which holds the names
+		 * Dispose all columns of the table viewer, except the first column, which holds the names
 		 * of the model elements
 		 */
 		while(tableViewer.getTable().getColumnCount() > 1) {
 			tableViewer.getTable().getColumn(1).dispose();
 		}
-		annotationSpecColumns.clear();
-
-		/*
-		 * Create a column for each annotation specification provided by any AnnotationEntry. There
-		 * is no additional columns created for those specification which already have an associated
-		 * column or provide complex values.
-		 */
-		for(AnnotationEntry entry : annotationEntries) {
-			for(IAnnotatedSpecification spec : entry.getSpecificationsList()) {
-				if(!isExistingColumn(spec) &&
-						!entry.allowsDynamicAnnotationInstances(spec.getClass()) &&
-						entry.getInstanceKeys(spec.getClass()).isEmpty()) {
-					createAnnotationColumn(entry, spec, null);
-				}
-			}
-		}
 
-		if(!tableViewer.getTable().isDisposed())
-			tableViewer.setInput(annotationEntries);
-	}
-
-	/** Create columns for multi-instance {@link IAnnotatedSpecification}s */
-	private void
-			updateMultiInstanceAnnotationColumns(Collection<AnnotationEntry> annotationEntries) {
 		for(CreateAnnotationInstanceColumn col : createColumns) {
 			col.dispose();
 		}
 
-		specsAllowingMultipleValues.clear();
-
-		/*
-		 * Iterate over all available specifications in the model. Columns for specifications
-		 * with simple values have already been created by the superclass 'GenericAnnotationView',
-		 * others are stored in the map 'specsAllowingMultipleValues'.
-		 * Each annotation specification type may exist only once in the table view or the map
-		 * 'code specsAllowingMultipleValues'.
-		 */
-		List<String> specClassNamesMult = new ArrayList<String>();
+		// Aggregate required columns. Column order is defined by ColumnHandle.compareTo().
 		for(AnnotationEntry entry : annotationEntries) {
 			for(IAnnotatedSpecification spec : entry.getSpecificationsList()) {
-				if((!entry.getInstanceKeys(spec.getClass()).isEmpty() || entry
-						.allowsDynamicAnnotationInstances(spec.getClass())) &&
-						!specClassNamesMult.contains(spec.getClass().getSimpleName())) {
-					specsAllowingMultipleValues.put(spec, entry);
-					specClassNamesMult.add(spec.getClass().getSimpleName());
-				}
-			}
-		}
+				// Create new column for each instance of the current multi-instance annotation
+				if((entry.allowsDynamicAnnotationInstances(spec.getClass())) ||
+						!entry.getInstanceKeys(spec.getClass()).isEmpty()) {
 
-		// Iterate over all specification supporting multiple values.
-		for(IAnnotatedSpecification spec : specsAllowingMultipleValues.keySet().toArray(
-				new IAnnotatedSpecification[specsAllowingMultipleValues.keySet().size()])) {
-			AnnotationEntry entry = specsAllowingMultipleValues.get(spec);
+					for(String instanceKey : entry.getInstanceKeys(spec.getClass())) {
+						sortedColumnHandles.add(new ColumnHandle(entry, spec, instanceKey, false));
+					}
+				}
 
-			// Create new column for each instance of the current multi-instance annotation
-			for(String key : entry.getInstanceKeys(spec.getClass())) {
-				createAnnotationColumn(entry, spec, key);
-			}
+				// Create a new column for
+				// - ordinary (single-instance) annotations
+				// - "create" column for multi-instance annotations for which instances can be
+				// created dynamically
+				if(entry.getInstanceKeys(spec.getClass()).isEmpty() ||
+						entry.allowsDynamicAnnotationInstances(spec.getClass())) {
+					sortedColumnHandles.add(new ColumnHandle(entry, spec, null, entry
+							.allowsDynamicAnnotationInstances(spec.getClass())));
 
-			if(entry.allowsDynamicAnnotationInstances(spec.getClass())) {
-				// Create column that contains a button for adding new instances of the
-				// current annotation type.
-				createColumns.add(new CreateAnnotationInstanceColumn(tableViewer, SWT.NONE, spec,
-						entry));
-				(createColumns.get(createColumns.size() - 1)).getColumn().setWidth(125);
+				}
 			}
+		}
 
+		// Instantiate columns
+		for(ColumnHandle columnHandle : sortedColumnHandles) {
+			createAnnotationColumn(columnHandle);
 		}
 
-		// Refresh table to trigger the update method of the create columns in order to actually
-		// instantiate the "Create" buttons
+		// Register content provider
+		if(!tableViewer.getTable().isDisposed()) {
+			tableViewer.setInput(annotationEntries);
+		}
 		tableViewer.refresh();
 	}
 
@@ -195,42 +272,58 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
 	}
 
 	/** Creates a column in tableViewer that displays the annotation given in spec */
-	protected void createAnnotationColumn(AnnotationEntry entry, IAnnotatedSpecification spec,
-			String instanceKey) {
-
-		// Determine column name
-		String specName = entry.getSpecificationAnnotationName(spec.getClass());
-		if(specName == null || specName.isEmpty()) {
-			specName = "<Unnamed Annotation>";
+	protected void createAnnotationColumn(ColumnHandle columnHandle) {
+
+		if(columnHandle.isCreateColumn()) {
+			// Create column that contains a button for adding new instances of the
+			// current annotation type.
+			createColumns.add(new CreateAnnotationInstanceColumn(tableViewer, SWT.NONE,
+					columnHandle.getAnnotatedSpecification(), columnHandle.getEntry()));
+			(createColumns.get(createColumns.size() - 1)).getColumn().setWidth(125);
+			return;
 		}
 
-		if(instanceKey != null) {
-			specName += ": " + instanceKey;
-		}
+		String specName = getColumnName(columnHandle);
 
 		// Add new new column
 		TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE);
 		column.getColumn().setText(specName);
 
+		Class<? extends IAnnotatedSpecification> annotationClass =
+				columnHandle.getAnnotatedSpecification().getClass();
+
 		column.getColumn().setWidth(125);
-		annotationSpecColumns.put(spec.getClass(), column);
 
 		// Have the matching EditingSupport created for the current annotated specification
 		// (work is delegated to respective IAnnotationValueProvider implementation)
 		EditingSupport editingSupport =
-				entry.createSpecificationEditElement(tableViewer, spec.getClass(), instanceKey);
+				columnHandle.getEntry().createSpecificationEditElement(tableViewer,
+						annotationClass, columnHandle.getInstanceKey());
 
 		// Add column label provider, and set EditingSupport
-		column.setLabelProvider(new AnnotationLabelProvider(spec.getClass(), this, instanceKey));
+		column.setLabelProvider(new AnnotationLabelProvider(annotationClass, this, columnHandle
+				.getInstanceKey()));
 		column.setEditingSupport(editingSupport);
 
 	}
 
-	/** Checks if column for given specification already exists */
-	protected boolean isExistingColumn(IAnnotatedSpecification specification) {
-		if(annotationSpecColumns.containsKey(specification.getClass()))
-			return true;
-		return false;
+	/**
+	 * Returns the name of a column for a given {@link IAnnotatedSpecification} represented by the
+	 * given {@link ColumnHandle}.
+	 */
+	protected String getColumnName(ColumnHandle columnHandle) {
+		// Determine column name
+		String specName =
+				columnHandle.getEntry().getSpecificationAnnotationName(
+						columnHandle.getAnnotatedSpecification().getClass());
+		if(specName == null || specName.isEmpty()) {
+			specName = "<Unnamed Annotation>";
+		}
+
+		if(columnHandle.getInstanceKey() != null) {
+			specName += MULTIINSTANCE_NAME_SEPARATOR + " " + columnHandle.getInstanceKey();
+		}
+		return specName;
 	}
 
 	/** {@inheritDoc} */
-- 
GitLab