Provide protected helper to attach ClientListner in AutoSuggest

This helps with using custom scripts:
- No longer depending on how listener for Model work
- No longer need to handle dispose
- ClientListener implicitly re-used
diff --git a/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/AutoSuggest.java b/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/AutoSuggest.java
index 6549f43..669e3fb 100644
--- a/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/AutoSuggest.java
+++ b/bundles/org.eclipse.rap.addons.autosuggest/src/org/eclipse/rap/addons/autosuggest/AutoSuggest.java
@@ -22,6 +22,7 @@
 import org.eclipse.rap.addons.autosuggest.internal.resources.DataBindingScript;
 import org.eclipse.rap.addons.dropdown.DropDown;
 import org.eclipse.rap.clientscripting.ClientListener;
+import org.eclipse.rap.clientscripting.Script;
 import org.eclipse.rap.clientscripting.WidgetDataWhiteList;
 import org.eclipse.rap.json.JsonObject;
 import org.eclipse.swt.SWT;
@@ -41,7 +42,8 @@
   private final Model model;
   private final List<SuggestionSelectedListener> selectionListeners;
   private final ModelListener modelListener;
-  private final ClientListener[] clientListeners;
+  private ClientListener textClientListener;
+  private int[] textClientListenerTypes;
   private boolean isDisposed;
 
   public AutoSuggest( Text text ) {
@@ -61,7 +63,7 @@
       }
     };
     connectClientObjects();
-    clientListeners = attachClientListeners( text, dropDown, model );
+    attachClientListeners();
     text.addListener( SWT.Dispose, new Listener() {
       public void handleEvent( Event event ) {
         dispose();
@@ -123,7 +125,7 @@
     isDisposed = true;
     dropDown.dispose();
     model.dispose();
-    removeTextClientListeners( text, clientListeners );
+    removeTextClientListeners();
   }
 
   public boolean isDisposed() {
@@ -146,19 +148,37 @@
     }
   }
 
-  protected ClientListener[] attachClientListeners( Text text, DropDown dropDown, Model model ) {
-    ClientListener clientListener = new ClientListener( DataBindingScript.getInstance() );
-    text.addListener( SWT.Modify, clientListener );
-    text.addListener( SWT.Verify, clientListener );
-    dropDown.addListener( SWT.Show, clientListener );
-    dropDown.addListener( SWT.Hide, clientListener );
-    dropDown.addListener( SWT.Selection, clientListener );
-    dropDown.addListener( SWT.DefaultSelection, clientListener );
-    model.addListener( "change", new ClientModelListener( DataBindingScript.getInstance() ) );
-    ClientModelListener modelListener = new ClientModelListener( AutoSuggestScript.getInstance() );
-    model.addListener( "change", modelListener );
-    model.addListener( "accept", modelListener );
-    return new ClientListener[] { clientListener };
+  protected void attachClientListeners() {
+    int[] dropDownEventTypes = new int[] { SWT.Show, SWT.Hide, SWT.Selection, SWT.DefaultSelection };
+    attachClientListenerToDropDown( DataBindingScript.getInstance(), dropDownEventTypes );
+    attachClientListenerToText( DataBindingScript.getInstance(), SWT.Modify, SWT.Verify );
+    attachClientListenerToModel( DataBindingScript.getInstance(), "change" );
+    attachClientListenerToModel( AutoSuggestScript.getInstance(), "change", "accept" );
+  }
+
+  protected void attachClientListenerToText( Script script, int... types ) {
+    if( textClientListener != null ) {
+      throw new IllegalStateException( "AutoSuggest: Can not add listener to Text twice." );
+    }
+    textClientListenerTypes = types;
+    textClientListener = new ClientListener( script );
+    for( int type : types ) {
+      text.addListener( type, textClientListener );
+    }
+  }
+
+  protected void attachClientListenerToDropDown( Script script, int... types ) {
+    ClientListener clientListener = new ClientListener( script );
+    for( int type : types ) {
+      dropDown.addListener( type, clientListener );
+    }
+  }
+
+  protected void attachClientListenerToModel( Script script, String... types ) {
+    ClientModelListener clientModelListener = new ClientModelListener( script );
+    for( String type : types ) {
+      model.addListener( type, clientModelListener );
+    }
   }
 
   private void connectClientObjects() {
@@ -169,9 +189,10 @@
     text.setData( MODEL_ID_KEY, model.getId() );
   }
 
-  protected void removeTextClientListeners( Text text, ClientListener[] clientListeners ) {
-    text.removeListener( SWT.Verify, clientListeners[ 0 ] );
-    text.removeListener( SWT.Modify, clientListeners[ 0 ] );
+  private void removeTextClientListeners() {
+    for( int type : textClientListenerTypes ) {
+      text.removeListener( type, textClientListener );
+    }
   }
 
 }
diff --git a/examples/org.eclipse.rap.addons.dropdown.demo/src/org/eclipse/rap/addons/dropdown/demo/AutoSuggestDemo.java b/examples/org.eclipse.rap.addons.dropdown.demo/src/org/eclipse/rap/addons/dropdown/demo/AutoSuggestDemo.java
index 7b99808..85b5fc8 100644
--- a/examples/org.eclipse.rap.addons.dropdown.demo/src/org/eclipse/rap/addons/dropdown/demo/AutoSuggestDemo.java
+++ b/examples/org.eclipse.rap.addons.dropdown.demo/src/org/eclipse/rap/addons/dropdown/demo/AutoSuggestDemo.java
@@ -16,13 +16,9 @@
 import org.eclipse.rap.addons.autosuggest.DataProvider;
 import org.eclipse.rap.addons.autosuggest.DataSource;
 import org.eclipse.rap.addons.autosuggest.SuggestionSelectedListener;
-import org.eclipse.rap.addons.autosuggest.internal.ClientModelListener;
-import org.eclipse.rap.addons.autosuggest.internal.Model;
-import org.eclipse.rap.addons.dropdown.DropDown;
 import org.eclipse.rap.addons.dropdown.demo.data.KFZ;
 import org.eclipse.rap.addons.dropdown.demo.scripts.CustomAutoSuggestScript;
 import org.eclipse.rap.addons.dropdown.demo.scripts.CustomDataBindingScript;
-import org.eclipse.rap.clientscripting.ClientListener;
 import org.eclipse.rap.rwt.RWT;
 import org.eclipse.rap.rwt.application.AbstractEntryPoint;
 import org.eclipse.rap.rwt.internal.client.WidgetDataWhiteList;
@@ -52,25 +48,12 @@
     }
 
     @Override
-    protected ClientListener[] attachClientListeners( Text text, DropDown dropDown, Model model ) {
-      ClientListener clientListener = new ClientListener( CustomDataBindingScript.getInstance() );
-      text.addListener( SWT.Modify, clientListener );
-      text.addListener( SWT.Verify, clientListener );
-      dropDown.addListener( SWT.Show, clientListener );
-      dropDown.addListener( SWT.Hide, clientListener );
-      dropDown.addListener( SWT.Selection, clientListener );
-      dropDown.addListener( SWT.DefaultSelection, clientListener );
-      model.addListener( "change", new ClientModelListener( CustomDataBindingScript.getInstance() ) );
-      ClientModelListener modelListener = new ClientModelListener( CustomAutoSuggestScript.getInstance() );
-      model.addListener( "change", modelListener );
-      model.addListener( "accept", modelListener );
-      return new ClientListener[] { clientListener };
-    }
-
-    @Override
-    protected void removeTextClientListeners( Text text, ClientListener[] clientListeners ) {
-      text.removeListener( SWT.Verify, clientListeners[ 0 ] );
-      text.removeListener( SWT.Modify, clientListeners[ 0 ] );
+    protected void attachClientListeners() {
+      int[] dropDownEventTypes = new int[] { SWT.Show, SWT.Hide, SWT.Selection, SWT.DefaultSelection };
+      attachClientListenerToDropDown( CustomDataBindingScript.getInstance(), dropDownEventTypes );
+      attachClientListenerToText( CustomDataBindingScript.getInstance(), SWT.Modify, SWT.Verify );
+      attachClientListenerToModel( CustomDataBindingScript.getInstance(), "change" );
+      attachClientListenerToModel( CustomAutoSuggestScript.getInstance(), "change", "accept" );
     }
 
   }
diff --git a/tests/org.eclipse.rap.addons.autosuggest.test/META-INF/MANIFEST.MF b/tests/org.eclipse.rap.addons.autosuggest.test/META-INF/MANIFEST.MF
index a4f0cf4..1c62b5e 100644
--- a/tests/org.eclipse.rap.addons.autosuggest.test/META-INF/MANIFEST.MF
+++ b/tests/org.eclipse.rap.addons.autosuggest.test/META-INF/MANIFEST.MF
@@ -9,4 +9,5 @@
 Require-Bundle: org.junit;bundle-version="4.11.0",
  org.mockito.mockito-all;bundle-version="1.9.5",
  org.eclipse.rap.jstestrunner;bundle-version="0.1.0"
-Import-Package: org.eclipse.rap.rwt.testfixture;version="2.1.0"
+Import-Package: org.eclipse.rap.clientscripting;version="0.2.0",
+ org.eclipse.rap.rwt.testfixture;version="2.1.0"
diff --git a/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/AutoSuggest_Test.java b/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/AutoSuggest_Test.java
index 238cf2f..69ce0cd 100644
--- a/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/AutoSuggest_Test.java
+++ b/tests/org.eclipse.rap.addons.autosuggest.test/src/org/eclipse/rap/addons/autosuggest/AutoSuggest_Test.java
@@ -32,9 +32,9 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.eclipse.rap.addons.autosuggest.internal.Model;
 import org.eclipse.rap.addons.dropdown.DropDown;
 import org.eclipse.rap.clientscripting.ClientListener;
+import org.eclipse.rap.clientscripting.Script;
 import org.eclipse.rap.rwt.RWT;
 import org.eclipse.rap.rwt.internal.client.WidgetDataWhiteList;
 import org.eclipse.rap.rwt.internal.client.WidgetDataWhiteListImpl;
@@ -192,25 +192,65 @@
   }
 
   @Test
-  public void testConstructor_callAttachClientListenerWithTargets() {
-    final AtomicReference<Text> textArg = new AtomicReference<Text>();
-    final AtomicReference<DropDown> dropDownArg = new AtomicReference<DropDown>();
-    final AtomicReference<Model> modelArg = new AtomicReference<Model>();
-
+  public void testConstructor_soesNotAttachListenersWithOverwrittenAttachClientListeners() {
     AutoSuggest autoSuggest = new AutoSuggest( text ) {
       @Override
-      protected ClientListener[] attachClientListeners( Text text, DropDown dropDown, Model model )
-      {
-        textArg.set( text );
-        modelArg.set( model );
-        dropDownArg.set( dropDown );
-        return super.attachClientListeners( text, dropDown, model );
+      protected void attachClientListeners() {
       }
     };
 
-    assertSame( autoSuggest.getDropDown(), dropDownArg.get() );
-    assertSame( text, textArg.get() );
-    assertEquals( "foo", modelArg.get().getId() );
+    DropDown dropDown = autoSuggest.getDropDown();
+    assertFalse( text.isListening( SWT.Verify ) );
+    assertFalse( text.isListening( SWT.Modify ) );
+    assertFalse( dropDown.isListening( SWT.Show ) );
+    assertFalse( dropDown.isListening( SWT.Hide ) );
+    assertFalse( dropDown.isListening( SWT.Selection ) );
+    assertFalse( dropDown.isListening( SWT.DefaultSelection ) );
+  }
+
+  @Test
+  public void testAttachClientListenerToText() {
+    new AutoSuggest( text ) {
+      @Override
+      protected void attachClientListeners() {
+        attachClientListenerToText( new Script( "" ), SWT.Verify, SWT.Modify );
+      }
+    };
+
+    assertTrue( text.getListeners( SWT.Verify )[ 0 ] instanceof ClientListener );
+    assertTrue( text.getListeners( SWT.Modify )[ 0 ] instanceof ClientListener );
+  }
+
+  @Test
+  public void testAttachClientListenerToText_failsWhenCalledTwice() {
+    final AtomicReference<IllegalStateException> exception = new AtomicReference<IllegalStateException>();
+    new AutoSuggest( text ) {
+      @Override
+      protected void attachClientListeners() {
+        attachClientListenerToText( new Script( "" ), SWT.Verify, SWT.Modify );
+        try {
+          attachClientListenerToText( new Script( "" ), SWT.Verify, SWT.Modify );
+        } catch( IllegalStateException ex ) {
+          exception.set( ex );
+        }
+      }
+    };
+
+    assertTrue( exception.get().getMessage().indexOf( "twice" ) != -1 );
+  }
+
+  @Test
+  public void testAttachClientListenerToDropDown() {
+    AutoSuggest autoSuggest = new AutoSuggest( text ) {
+      @Override
+      protected void attachClientListeners() {
+        attachClientListenerToDropDown( new Script( "" ), SWT.Selection, SWT.Show );
+      }
+    };
+
+    DropDown dropDown = autoSuggest.getDropDown();
+    assertTrue( dropDown.getListeners( SWT.Selection )[ 0 ] instanceof ClientListener );
+    assertTrue( dropDown.getListeners( SWT.Show )[ 0 ] instanceof ClientListener );
   }
 
   @Test
@@ -259,6 +299,22 @@
   }
 
   @Test
+  public void testDispose_removesCustomClientListenersFromText() {
+    AutoSuggest autoSuggest = new AutoSuggest( text ) {
+      @Override
+      protected void attachClientListeners() {
+        attachClientListenerToText( new Script( "" ), SWT.Verify, SWT.FocusIn );
+      }
+    };
+
+    autoSuggest.dispose();
+
+    assertFalse( text.isListening( SWT.Verify ) );
+    assertFalse( text.isListening( SWT.FocusIn ) );
+  }
+
+
+  @Test
   public void testDispose_disposeTwice() {
     AutoSuggest autoSuggest = new AutoSuggest( text );
 
@@ -266,30 +322,6 @@
     autoSuggest.dispose();
   }
 
-  @Test
-  public void testDispose_callRemoveTextClientListenerWithTextAndListeners() {
-    final ClientListener clientListener = new ClientListener( "" );
-    final AtomicReference<ClientListener[]> clientListenerArg = new AtomicReference<ClientListener[]>();
-    final AtomicReference<Text> textArg = new AtomicReference<Text>();
-    AutoSuggest autoSuggest = new AutoSuggest( text ) {
-      @Override
-      protected ClientListener[] attachClientListeners( Text text, DropDown dropDown, Model model )
-      {
-        return new ClientListener[]{ clientListener };
-      }
-      @Override
-      protected void removeTextClientListeners( Text text, ClientListener[] clientListeners ) {
-        textArg.set( text );
-        clientListenerArg.set( clientListeners );
-      }
-    };
-
-    autoSuggest.dispose();
-
-    assertSame( text, textArg.get() );
-    assertEquals( 1, clientListenerArg.get().length );
-    assertSame( clientListener, clientListenerArg.get()[ 0 ] );
-  }
 
   @Test
   public void testDisposedOnTextDispose() {