In progress: [ Issue 40 ] Add support for import resolution across multiple
folders
https://code.google.com/p/protobuf-dt/issues/detail?id=40

List of directories does not take empty spaces anymore. Code cleanup.
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/PreferenceAndPropertyPage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/PreferenceAndPropertyPage.java
index 6625380..6c354ed 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/PreferenceAndPropertyPage.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/PreferenceAndPropertyPage.java
@@ -15,12 +15,10 @@
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.preference.*;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.*;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.*;
 import org.eclipse.swt.widgets.*;
 import org.eclipse.ui.*;
 import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
@@ -29,7 +27,7 @@
 
 /**
  * Base class for pages that set up both "Workspace Preferences" and "Project Properties."
- * 
+ *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public abstract class PreferenceAndPropertyPage extends PreferencePage implements IWorkbenchPreferencePage, IWorkbenchPropertyPage {
@@ -47,7 +45,7 @@
   }
 
   /**
-   * Creates the <code>{@link Composite}</code> to be used as the base container in implementations of 
+   * Creates the <code>{@link Composite}</code> to be used as the base container in implementations of
    * <code>{@link #createContents(Composite)}</code>.
    * @param parent the parent {@code Composite}.
    * @return the created {@code Composite}.
@@ -71,7 +69,7 @@
     addEventListeners();
     return contents;
   }
-  
+
   private void addEventListeners() {
     if (isPropertyPage()) {
       btnEnableProjectSettings.addSelectionListener(new SelectionAdapter() {
@@ -89,7 +87,7 @@
   }
 
   /**
-   * Notification that the "Enable project specific settings" check button has been selected. 
+   * Notification that the "Enable project specific settings" check button has been selected.
    * @param projectSettingsActive indicates the selection of the "Enable project specific settings" check button.
    */
   protected abstract void onProjectSettingsActivation(boolean projectSettingsActive);
@@ -104,7 +102,7 @@
    * @return the id of this preference page.
    */
   protected abstract String preferencePageId();
-  
+
   /**
    * Returns the <code>{@link IProject}</code> that owns the properties shown in this page.
    * @return the project that owns the properties shown in this page.
@@ -132,7 +130,7 @@
 
   /**
    * Indicates whether this page is a "Project Properties" page or not.
-   * @return {@code true} if this page is a "Project Properties" page, or {@code false} if this page is a 
+   * @return {@code true} if this page is a "Project Properties" page, or {@code false} if this page is a
    * "Worspace Settings" page.
    */
   protected final boolean isPropertyPage() {
@@ -146,24 +144,24 @@
   }
 
   /** {@inheritDoc} */
-  @SuppressWarnings("unchecked") 
+  @SuppressWarnings("unchecked")
   @Override public final void applyData(Object data) {
     if (data instanceof Map) this.dataMap = (Map<String, Object>) data;
   }
-  
+
   /**
-   * Activates or deactivates the project-specific settings. 
+   * Activates or deactivates the project-specific settings.
    * @param active indicates whether the project-specific settings should be active or not.
    */
   protected final void activateProjectSettings(boolean active) {
     btnEnableProjectSettings.setSelection(active);
     updateEnableWorkspaceSettingsLink();
   }
-  
+
   private void updateEnableWorkspaceSettingsLink() {
     lnkEnableWorkspaceSettings.setEnabled(!areProjectSettingsActive());
   }
-  
+
   /**
    * Indicates if the project-specific settings are active or not.
    * @return {@code true} if the project-specific settings are active; {@code false} otherwise.
@@ -184,24 +182,6 @@
    * Saves the current settings.
    */
   protected abstract void savePreferences();
-  
-  /**
-   * Adds the given <code>{@link SelectionListener}</code> to the given <code>{@link Button}</code>s.
-   * @param listener the listener to add.
-   * @param buttons the buttons to add the listener to.
-   */
-  protected static void addSelectionListener(SelectionListener listener, Button...buttons) {
-    for (Button button : buttons) button.addSelectionListener(listener);
-  }
-  
-  /**
-   * Adds the given <code>{@link ModifyListener}</code> to the given <code>{@link Text}</code> widgets.
-   * @param listener the listener to add.
-   * @param texts the text widgets to add the listener to.
-   */
-  protected static void addModifyListener(ModifyListener listener, Text...texts) {
-    for (Text text : texts) text.addModifyListener(listener);
-  }
 
   /**
    * Marks this page as "valid."
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/compiler/CompilerPreferencePage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/compiler/CompilerPreferencePage.java
index 8f12479..b7501fe 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/compiler/CompilerPreferencePage.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/compiler/CompilerPreferencePage.java
@@ -10,6 +10,7 @@
 
 import static com.google.eclipse.protobuf.ui.preferences.compiler.CompilerPreferenceNames.*;
 import static com.google.eclipse.protobuf.ui.preferences.compiler.Messages.*;
+import static java.util.Arrays.asList;
 import static org.eclipse.xtext.util.Strings.isEmpty;
 
 import java.io.File;
@@ -22,7 +23,7 @@
 import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
 
 import com.google.eclipse.protobuf.ui.preferences.PreferenceAndPropertyPage;
-import com.google.eclipse.protobuf.ui.util.DirectoryNameValidator;
+import com.google.eclipse.protobuf.ui.util.*;
 import com.google.inject.Inject;
 
 /**
@@ -57,6 +58,7 @@
   private Label lblOutputFolderRelative;
 
   @Inject private DirectoryNameValidator directoryNameValidator;
+  @Inject private SwtEventListeners eventListeners;
 
   @Inject public CompilerPreferencePage(IPreferenceStoreAccess preferenceStoreAccess) {
     super(preferenceStoreAccess);
@@ -196,13 +198,13 @@
         checkState();
       }
     });
-    addSelectionListener(new SelectionAdapter() {
+    eventListeners.addSelectionListener(new SelectionAdapter() {
       @Override public void widgetSelected(SelectionEvent e) {
         boolean selected = btnUseProtocInCustomPath.getSelection();
         enableCompilerCustomPathOptions(!selected);
         checkState();
       }
-    }, btnUseProtocInCustomPath, btnUseProtocInSystemPath);
+    }, asList(btnUseProtocInCustomPath, btnUseProtocInSystemPath));
     btnProtocPathBrowse.addSelectionListener(new SelectionAdapter() {
       @Override public void widgetSelected(SelectionEvent e) {
         FileDialog dialog = new FileDialog(getShell(), SWT.OPEN | SWT.SHEET);
@@ -215,11 +217,11 @@
         refreshResourcesOptionsEnabled(btnRefreshResources.getSelection());
       }
     });
-    addModifyListener(new ModifyListener() {
+    eventListeners.addModifyListener(new ModifyListener() {
       public void modifyText(ModifyEvent e) {
         checkState();
       }
-    }, txtProtocFilePath, txtOutputFolderName);
+    }, asList(txtProtocFilePath, txtOutputFolderName));
   }
 
   private void checkState() {
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/DirectoryNamesEditor.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/DirectoryNamesEditor.java
index f056d89..a829057 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/DirectoryNamesEditor.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/DirectoryNamesEditor.java
@@ -22,7 +22,7 @@
 import org.eclipse.swt.layout.*;
 import org.eclipse.swt.widgets.*;
 
-import com.google.eclipse.protobuf.ui.util.DirectoryNameValidator;
+import com.google.eclipse.protobuf.ui.util.*;
 
 /**
  * Editor where users can add/remove the directories to be used for URI resolution.
@@ -32,6 +32,7 @@
 public class DirectoryNamesEditor extends Composite {
 
   private final DirectoryNameValidator directoryNameValidator;
+  private final SwtEventListeners eventListeners;
 
   private List lstDirectoryNames;
   private final Button btnAdd;
@@ -39,16 +40,15 @@
   private final Button btnUp;
   private final Button btnDown;
 
-  private SelectionListener onRemoveListener;
+  private SelectionListener onChangeListener;
 
-  /**
-   * Creates a new </code>{@link DirectoryNamesEditor}</code>.
-   * @param parent a widget which will be the parent of the new instance (cannot be {@code null}.)
-   * @param directoryNameValidator validates that a {@code String} is a valid directory name.
-   */
-  public DirectoryNamesEditor(Composite parent, DirectoryNameValidator directoryNameValidator) {
+  public DirectoryNamesEditor(Composite parent, DirectoryNameValidator directoryNameValidator,
+      SwtEventListeners eventListeners) {
     super(parent, SWT.NONE);
+
+    // generated by WindowBuilder
     this.directoryNameValidator = directoryNameValidator;
+    this.eventListeners = eventListeners;
     setLayout(new GridLayout(3, false));
 
     lstDirectoryNames = new List(this, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI);
@@ -95,13 +95,14 @@
       @Override public void widgetSelected(SelectionEvent e) {
         IInputValidator validator = new IInputValidator() {
           public String isValid(String newText) {
-            if (isEmpty(newText)) return Messages.errorEmptyDirectoryName;
-            return directoryNameValidator.validateDirectoryName(newText);
+            String text = (newText == null) ? null : newText.trim();
+            if (isEmpty(text)) return errorEmptyDirectoryName;
+            return directoryNameValidator.validateDirectoryName(text);
           }
         };
         InputDialog input = new InputDialog(getShell(), directoryNameInputTitle, directoryNameInputMessage, null, validator);
         if (input.open() == OK) {
-          lstDirectoryNames.add(input.getValue());
+          lstDirectoryNames.add(input.getValue().trim());
         }
       }
     });
@@ -165,12 +166,12 @@
   }
 
   public void addDirectoryNames(Collection<String> directoryNames) {
-    for (String name : directoryNames) lstDirectoryNames.add(name);
+    for (String name : directoryNames) lstDirectoryNames.add(name.trim());
   }
 
-  public void onRemove(SelectionListener listener) {
-    if (onRemoveListener != null) lstDirectoryNames.removeSelectionListener(onRemoveListener);
-    onRemoveListener = listener;
-    lstDirectoryNames.addSelectionListener(onRemoveListener);
+  public void onAddOrRemove(SelectionListener listener) {
+    if (onChangeListener != null) eventListeners.removeSelectionListener(onChangeListener, asList(btnAdd, btnRemove));
+    onChangeListener = listener;
+    eventListeners.addSelectionListener(onChangeListener, asList(btnAdd, btnRemove));
   }
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/PathsPreferencePage.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/PathsPreferencePage.java
index c2789e0..d1c3807 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/PathsPreferencePage.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/PathsPreferencePage.java
@@ -10,6 +10,7 @@
 
 import static com.google.eclipse.protobuf.ui.preferences.paths.Messages.*;
 import static com.google.eclipse.protobuf.ui.preferences.paths.PathsPreferenceNames.*;
+import static java.util.Arrays.asList;
 import static org.eclipse.xtext.util.Strings.*;
 
 import org.eclipse.jface.preference.IPreferenceStore;
@@ -20,7 +21,7 @@
 import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
 
 import com.google.eclipse.protobuf.ui.preferences.PreferenceAndPropertyPage;
-import com.google.eclipse.protobuf.ui.util.DirectoryNameValidator;
+import com.google.eclipse.protobuf.ui.util.*;
 import com.google.inject.Inject;
 
 /**
@@ -39,6 +40,7 @@
   private DirectoryNamesEditor directoryNamesEditor;
 
   @Inject private DirectoryNameValidator directoryNameValidator;
+  @Inject private SwtEventListeners eventListeners;
 
   @Inject public PathsPreferencePage(IPreferenceStoreAccess preferenceStoreAccess) {
     super(preferenceStoreAccess);
@@ -62,7 +64,7 @@
     btnMultipleFolders.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
     btnMultipleFolders.setText(filesInMultipleDirectories);
 
-    directoryNamesEditor = new DirectoryNamesEditor(grpResolutionOfImported, directoryNameValidator);
+    directoryNamesEditor = new DirectoryNamesEditor(grpResolutionOfImported, directoryNameValidator, eventListeners);
     directoryNamesEditor.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
     new Label(contents, SWT.NONE);
 
@@ -87,14 +89,14 @@
   }
 
   private void addEventListeners() {
-    addSelectionListener(new SelectionAdapter() {
+    eventListeners.addSelectionListener(new SelectionAdapter() {
       @Override public void widgetSelected(SelectionEvent e) {
         boolean selected = btnMultipleFolders.getSelection();
         directoryNamesEditor.setEnabled(selected);
         checkState();
       }
-    }, btnOneFolderOnly, btnMultipleFolders);
-    directoryNamesEditor.onRemove(new SelectionAdapter() {
+    }, asList(btnOneFolderOnly, btnMultipleFolders));
+    directoryNamesEditor.onAddOrRemove(new SelectionAdapter() {
       @Override public void widgetSelected(SelectionEvent e) {
         checkState();
       }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/SwtEventListeners.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/SwtEventListeners.java
new file mode 100644
index 0000000..4515cac
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/SwtEventListeners.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Google 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 com.google.eclipse.protobuf.ui.util;
+
+import java.util.Collection;
+
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+
+import com.google.inject.Singleton;
+
+/**
+ * Utility methods related to SWT event listeners.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton
+public class SwtEventListeners {
+
+  /**
+   * Adds the given <code>{@link SelectionListener}</code> to the given <code>{@link Button}</code>s.
+   * @param listener the listener to add.
+   * @param buttons the buttons to add the listener to.
+   */
+  public void addSelectionListener(SelectionListener listener, Collection<Button> buttons) {
+    for (Button button : buttons) button.addSelectionListener(listener);
+  }
+
+  /**
+   * Removes the given <code>{@link SelectionListener}</code> to the given <code>{@link Button}</code>s.
+   * @param listener the listener to remove.
+   * @param buttons the buttons to remove the listener to.
+   */
+  public void removeSelectionListener(SelectionListener listener, Collection<Button> buttons) {
+    for (Button button : buttons) button.removeSelectionListener(listener);
+  }
+
+  /**
+   * Adds the given <code>{@link ModifyListener}</code> to the given <code>{@link Text}</code> widgets.
+   * @param listener the listener to add.
+   * @param texts the text widgets to add the listener to.
+   */
+  public void addModifyListener(ModifyListener listener, Collection<Text> texts) {
+    for (Text text : texts) text.addModifyListener(listener);
+  }
+
+}