View Javadoc
1   package org.vaadin.tokenfield;
2   
3   import java.util.Collection;
4   import java.util.HashSet;
5   import java.util.LinkedHashMap;
6   import java.util.LinkedHashSet;
7   import java.util.Set;
8   
9   import com.vaadin.v7.data.Container;
10  import com.vaadin.v7.data.Property;
11  import com.vaadin.server.Resource;
12  import com.vaadin.v7.shared.ui.combobox.FilteringMode;
13  import com.vaadin.v7.ui.AbstractSelect.ItemCaptionMode;
14  import com.vaadin.v7.ui.AbstractSelect.NewItemHandler;
15  import com.vaadin.ui.Button;
16  import com.vaadin.ui.Button.ClickEvent;
17  import com.vaadin.v7.ui.ComboBox;
18  import com.vaadin.ui.Component;
19  import com.vaadin.ui.CssLayout;
20  import com.vaadin.v7.ui.CustomField;
21  import com.vaadin.v7.ui.HorizontalLayout;
22  import com.vaadin.ui.Layout;
23  import com.vaadin.v7.ui.themes.Reindeer;
24  
25  /**
26   * 
27   * A kind of multiselect ComboBox. When the user selects a token (or inputs a
28   * new token, TokenField defaults to allowing new tokens), the value is added as
29   * a clickable "token button" before or after the input box. Duplicate
30   * selections are not allowed.
31   * 
32   * <p>
33   * TokenField defaults to using CssLayout, but virtually any Layout can be used.
34   * </p>
35   * 
36   * <p>
37   * Can be customized in several ways by overriding certain methods. When the
38   * user select or enters a new token, the following happens:
39   * <ul>
40   * <li>If the token is new (not in the container) and new tokens are not allowed
41   * ( {@link #setNewTokensAllowed(boolean) }), nothing happens - otherwise</li>
42   * <li>{@link #onTokenInput(Object)} is called, by default it just calls</li>
43   * <li>{@link #addToken(Object)} which will eventually cause a call to</li>
44   * <li>{@link #configureTokenButton(Object, Button)}</li>
45   * <li>finally, if the token is new, it's added to the container if
46   * {@link #setRememberNewTokens(boolean)} is on - this means previous method
47   * calls can know whether or not the token is new by examining the container.</li>
48   * </ul>
49   * Custom functionality when adding and removing tokens, such as showing a
50   * notification for duplicates or confirming removal, is done by overriding
51   * {@link #onTokenInput(Object)} and {@link #onTokenClick(Object)} respectively.
52   * In much the same way, {@link #onTokenDelete()} is called when the user
53   * presses delete or backspace when the input is empty, and can be customized.<br/>
54   * The token buttons can be styled customized by overriding
55   * {@link #configureTokenButton(TokenField, Object, Button).
56   * </p>
57   * <p>
58   * The content of the input (ComboBox) can be bound to a Container datasource,
59   * and filtering can be used. Note that the TokenField can select values that
60   * are not present in the ComboBox.<br/>
61   * <p>
62   * The content of the input (ComboBox) can be bound to a Container datasource,
63   * and filtering can be used. Note that the TokenField can select values that
64   * are not present in the ComboBox.<br/>
65   * <p>
66   * The content of the input (ComboBox) can be bound to a Container datasource,
67   * and filtering can be used. Note that the TokenField can select values that
68   * are not present in the ComboBox.<br/>
69   * <p>
70   * The content of the input (ComboBox) can be bound to a Container datasource,
71   * and filtering can be used. Note that the TokenField can select values that
72   * are not present in the ComboBox.<br/>
73   * <p>
74   * The content of the input (ComboBox) can be bound to a Container datasource,
75   * and filtering can be used. Note that the TokenField can select values that
76   * are not present in the ComboBox.<br/>
77   * <p>
78   * The content of the input (ComboBox) can be bound to a Container datasource,
79   * and filtering can be used. Note that the TokenField can select values that
80   * are not present in the ComboBox.<br/>
81   * <p>
82   * The content of the input (ComboBox) can be bound to a Container datasource,
83   * and filtering can be used. Note that the TokenField can select values that
84   * are not present in the ComboBox.<br/><p>
85   * The content of the input (ComboBox) can be bound to a Container datasource,
86   * and filtering can be used. Note that the TokenField can select values that
87   * are not present in the ComboBox.<br/>
88   * 
89   * 
90   * 
91   * 
92   * 
93   * 
94   * 
95   * 
96   * 
97   * 
98   * 
99   * 
100  * 
101  * 
102  * 
103  * 
104  * 
105  * 
106  * 
107  * 
108  * 
109  * 
110  * 
111  * 
112  * 
113  * 
114  * 
115  * 
116  * 
117  * 
118  * 
119  * 
120  * 
121  * 
122  * 
123  * 
124  * 
125  * 
126  * 
127  * 
128  * 
129  * 
130  * 
131  * 
132  * 
133  * 
134  * 
135  * 
136  * 
137  * 
138  * 
139  * 
140  * 
141  * 
142  * 
143  * 
144  * 
145  * 
146  * 
147  * 
148  * 
149  * 
150  * 
151  * 
152  * 
153  * 
154  * 
155  * 
156  * 
157  * 
158  * 
159  * 
160  * 
161  * 
162  * 
163  * 
164  * 
165  * 
166  * 
167  * 
168  * 
169  * 
170  * 
171  * 
172  * 
173  * 
174  * 
175  * 
176  * 
177  * 
178  * 
179  * 
180  * 
181  * 
182  * 
183  * 
184  * 
185  * 
186  * 
187  * 
188  * 
189  * 
190  * 
191  * 
192  * 
193  * 
194  * 
195  * 
196  * 
197  * 
198  * 
199  * 
200  * 
201  * Also note that if you use {@link #setTokenCaptionPropertyId(Object)} (to use
202  * a specific property as token caption) AND allow new tokens to be input (
203  * {@link #setNewTokensAllowed(boolean)}, you should probably use a custom
204  * {@link NewItemHandler) ({@link #setNewTokenHandler(NewItemHandler)}) in
205  * order to provide a sensible caption for the new token.
206  * </p>
207  * 
208  * <p>
209  * TokenField is a full-fledged field - it can be bound to a Property
210  * datasource, and supports buffering.
211  * </p>
212  * 
213  * <p>
214  * Note "token" and "tokenId" is often used interchangeably in the documentation
215  * - usually the token is just a string that is the id as well. The term
216  * <i>Token</i> as used in the method names is often interchangeable with the
217  * term <i>item</i> seen elsewhere in the Vaadin API; e.g
218  * {@link #setTokenCaption(Object, String)} works exactly as
219  * {@link ComboBox#setItemCaption(Object, String)}, and <code>tokenId</code> is
220  * the same as <code>itemId</code>.
221  * </p>
222  * 
223  */
224 public class TokenField extends CustomField implements Container.Editor {
225 
226     private static final long serialVersionUID = -4718188396491718742L;
227 
228     public enum InsertPosition {
229         /**
230          * Tokens will be added after the input
231          */
232         AFTER,
233         /**
234          * Add tokens before the input
235          */
236         BEFORE
237     }
238 
239     public static final String STYLE_TOKENFIELD = "tokenfield";
240     public static final String STYLE_TOKENTEXTFIELD = "tokentextfield";
241 
242     public static final String STYLE_BUTTON_EMPHAZISED = "emphasize";
243 
244     /**
245      * The layout currently in use
246      */
247     protected Layout layout;
248 
249     /**
250      * Current insert position
251      */
252     protected InsertPosition insertPosition = InsertPosition.BEFORE;
253 
254     /**
255      * The ComboBox used for input - should probably not be touched.
256      */
257     protected TokenComboBoxx.html#TokenComboBox">TokenComboBox cb = new TokenComboBox(insertPosition) {
258 
259         private static final long serialVersionUID = -5550767105896319355L;
260 
261         protected void onDelete() {
262             if (!buttons.isEmpty()) {
263                 Object[] keys = buttons.keySet().toArray();
264                 onTokenDelete(keys[keys.length - 1]);
265                 cb.focus();
266             }
267         }
268     };
269 
270     /**
271      * Maps the tokenId (itemId) to the token button
272      */
273     protected LinkedHashMap<Object, Button> buttons = new LinkedHashMap<Object, Button>();
274 
275     protected boolean rememberNewTokens = true;
276 
277     /**
278      * Create a new TokenField with a caption and a {@link InsertPosition}.
279      * 
280      * @param caption
281      *            the desired caption
282      * @param insertPosition
283      *            the desired insert position
284      */
285     public TokenField(String caption, InsertPosition insertPosition) {
286         this();
287         this.insertPosition = insertPosition;
288         setCaption(caption);
289     }
290 
291     /**
292      * Create a new TokenField with a caption.
293      * 
294      * @param caption
295      *            the desired caption
296      */
297     public TokenField(String caption) {
298         this();
299         setCaption(caption);
300     }
301 
302     /**
303      * Create a new TokenField.
304      * 
305      */
306     public TokenField() {
307         this(new CssLayout());
308     }
309 
310     /**
311      * Create a new TokenField with a caption and a given layout.
312      * 
313      * @param caption
314      *            the desired caption
315      * @param lo
316      *            the desired layout
317      */
318     public TokenField(String caption, Layout lo) {
319         this(lo);
320         setCaption(caption);
321     }
322 
323     /**
324      * Create a new TokenField with a caption, a given layout, and the specified
325      * token insert position.
326      * 
327      * @param caption
328      *            the desired caption
329      * @param lo
330      *            the desired layout
331      * @param insertPosition
332      *            the desired token insert position
333      */
334     public TokenField(String caption, Layout lo, InsertPosition insertPosition) {
335         this(lo);
336         setCaption(caption);
337         this.insertPosition = insertPosition;
338     }
339 
340     /**
341      * Create a new TokenField with the given layout, and the specified token
342      * insert position.
343      * 
344      * @param lo
345      *            the desired layout
346      * @param insertPosition
347      *            the desired token insert position
348      */
349     public TokenField(Layout lo, InsertPosition insertPosition) {
350         this(lo);
351         this.insertPosition = insertPosition;
352     }
353 
354     /**
355      * Create a new TokenField with the given layout.
356      * 
357      * @param lo
358      *            the desired layout
359      */
360     public TokenField(Layout lo) {
361         setStyleName(STYLE_TOKENFIELD + " " + STYLE_TOKENTEXTFIELD);
362 
363         cb.setImmediate(true);
364         cb.setNewItemsAllowed(true);
365         cb.setNullSelectionAllowed(false);
366         cb.addListener(new ComboBox.ValueChangeListener() {
367 
368             private static final long serialVersionUID = 4370326413130922134L;
369 
370             public void valueChange(
371                     com.vaadin.v7.data.Property.ValueChangeEvent event) {
372                 final Object tokenId = event.getProperty().getValue();
373                 if (tokenId != null) {
374                     onTokenInput(tokenId);
375                     cb.setValue(null);
376                     cb.focus();
377                 }
378             }
379         });
380 
381         cb.setNewItemHandler(new NewItemHandler() {
382 
383             private static final long serialVersionUID = 1L;
384 
385             // This is essentially what the ComboBox.DefaultNewItemHandler does,
386             // but we'll first delegate adding token button, then add to
387             // container.
388             public void addNewItem(String tokenId) {
389                 if (isReadOnly()) {
390                     throw new Property.ReadOnlyException();
391                 }
392                 onTokenInput(tokenId);
393                 if (rememberNewTokens) {
394                     rememberToken(tokenId);
395                 }
396                 cb.focus();
397             }
398 
399         });
400 
401         setLayout(lo);
402 
403     }
404 
405     protected void rememberToken(String tokenId) {
406         if (cb.addItem(getTokenCaption(tokenId)) != null) {
407             // Sets the caption property, if used
408             if (getTokenCaptionPropertyId() != null) {
409 
410                 cb.getContainerProperty(tokenId, getTokenCaptionPropertyId())
411                         .setValue(tokenId);
412 
413             }
414         }
415     }
416 
417     /*
418      * Rebuilds from scratch
419      */
420     private void rebuild() {
421         layout.removeAllComponents();
422         if (!isReadOnly() && insertPosition == InsertPosition.AFTER) {
423             layout.addComponent(cb);
424         }
425         for (Button b2 : buttons.values()) {
426             layout.addComponent(b2);
427         }
428         if (!isReadOnly() && insertPosition == InsertPosition.BEFORE) {
429             layout.addComponent(cb);
430         }
431         if (layout instanceof HorizontalLayout) {
432             ((HorizontalLayout) layout).setExpandRatio(cb, 1.0f);
433         }
434     }
435 
436     /*
437      * Might create a HashSet or two unnecessarily from time to time, but seems
438      * clearer that way.
439      * 
440      * @see org.vaadin.tokenfield.CustomField#setInternalValue(java.lang.Object)
441      */
442     protected void setInternalValue(Object newValue) {
443 
444         Set<Object> vals = (Set<Object>) newValue;
445         Set<Object> old = buttons.keySet();
446 
447         super.setInternalValue(newValue);
448 
449         if (old == null) {
450             old = new HashSet<Object>();
451         }
452 
453         if (vals == null) {
454             vals = new HashSet<Object>();
455         }
456 
457         Set<Object> remove = new HashSet<Object>(old);
458         Set<Object> add = new HashSet<Object>(vals);
459         remove.removeAll(vals);
460         add.removeAll(old);
461 
462         for (Object tokenId : remove) {
463             removeTokenButton(tokenId);
464         }
465         for (Object tokenId : add) {
466             addTokenButton(tokenId);
467         }
468     }
469 
470     /**
471      * Called when the user is adding a new token via the UI; called after the
472      * newItemHandler. Can be used to make customize the adding process; e.g to
473      * notify that the token was not added because it's duplicate, to ask for
474      * additional information, or to disallow addition due to some heuristics
475      * (not both A and Q).<br/>
476      * The default is to call {@link #addToken(Object)} which will add the token
477      * if it's not a duplicate.
478      * 
479      * @param tokenId
480      *            the token id selected (or input)
481      */
482     protected void onTokenInput(Object tokenId) {
483         addToken(tokenId);
484     }
485 
486     /**
487      * Called when the token button is clicked, which by default removes the
488      * token by calling {@link #removeToken(Object)}. The behavior can be
489      * customized, e.g present a confirmation dialog.
490      * 
491      * @param tokenId
492      *            the id of the token that was clicked
493      */
494     protected void onTokenClick(Object tokenId) {
495         removeToken(tokenId);
496     }
497 
498     /**
499      * Called with the last added token when the delete or backspace -key
500      * (depending in insert position) is pressed in an empty input. The default
501      * is to call {@link #onTokenClick(Object)} with the last added token, i.e
502      * remove last. The behavior can be customized, e.g present a confirmation
503      * dialog.
504      * 
505      */
506     protected void onTokenDelete(Object tokenId) {
507         onTokenClick(tokenId);
508     }
509 
510     private void addTokenButton(final Object val) {
511         Button b = new Button();
512         configureTokenButton(val, b);
513         b.addClickListener(new Button.ClickListener() {
514             private static final long serialVersionUID = -1943432188848347317L;
515 
516             public void buttonClick(ClickEvent event) {
517                 onTokenClick(val);
518             }
519         });
520         buttons.put(val, b);
521 
522         if (insertPosition == InsertPosition.BEFORE) {
523             layout.replaceComponent(cb, b);
524             layout.addComponent(cb);
525         } else {
526             layout.addComponent(b);
527         }
528         if (layout instanceof HorizontalLayout) {
529             ((HorizontalLayout) layout).setExpandRatio(cb, 1.0f);
530         }
531 
532     }
533 
534     /**
535      * Adds a token if that token does not already exist.
536      * <p>
537      * Note that tokens are not automatically added to the token container. This
538      * means you can add tokens without adding them to the container (that might
539      * be bound to some data store), and without making them available to the
540      * user in the suggestion dropdown. <br/>
541      * This also means that when new tokens are disallowed (
542      * {@link #setNewTokensAllowed(boolean)}) you can programmatically add
543      * tokens that the user can not add him/herself. <br/>
544      * Consider adding the token to the container before calling
545      * {@link #addToken(Object)} if you're using a custom captions based on
546      * container/item properties, or if you want the token to be available to
547      * the user as a suggestion later.
548      * </p>
549      * 
550      * @param tokenId
551      *            the token to add
552      */
553     public void addToken(Object tokenId) {
554         Set<Object> set = (Set<Object>) getValue();
555         if (set == null) {
556             set = new LinkedHashSet<Object>();
557         }
558         if (set.contains(tokenId)) {
559             return;
560         }
561         HashSet<Object> newSet = new LinkedHashSet<Object>(set);
562         newSet.add(tokenId);
563         setValue(newSet);
564     }
565 
566     /**
567      * Removes the given token.
568      * <p>
569      * Note that the token is not removed from the container, so if it exists in
570      * the container, the token will still be available to the user.
571      * </p>
572      * 
573      * @param tokenId
574      *            the token to remove
575      */
576     public void removeToken(Object tokenId) {
577         Set<Object> set = (Set<Object>) getValue();
578         LinkedHashSet<Object> newSet = new LinkedHashSet<Object>(set);
579         newSet.remove(tokenId);
580 
581         setValue(newSet);
582 
583     }
584 
585     private void removeTokenButton(Object tokenId) {
586         Button button = buttons.get(tokenId);
587         layout.removeComponent(button);
588         buttons.remove(tokenId);
589 
590     }
591 
592     /**
593      * Configures the token button.
594      * <p>
595      * By default, the caption, icon, description, and style is set. Override to
596      * customize.<br/>
597      * Note that the default click-listener is added elsewhere and can not be
598      * changed here.
599      * </p>
600      * 
601      * @param tokenId
602      *            the token this button pertains to
603      * @param button
604      *            the button to be configured
605      */
606     protected void configureTokenButton(Object tokenId, Button button) {
607         button.setCaption(getTokenCaption(tokenId) + " ×");
608         button.setIcon(getTokenIcon(tokenId));
609         button.setDescription("Click to remove");
610         button.setStyleName(Reindeer.BUTTON_LINK);
611     }
612 
613     /**
614      * Gets the layout currently in use.
615      * 
616      * @return the current layout
617      */
618     public Layout getLayout() {
619         return layout;
620     }
621 
622     /**
623      * Sets layout used for laying out the tokens and the input.
624      * 
625      * @param newLayout
626      *            the layout to use
627      */
628     protected void setLayout(Layout newLayout) {
629         if (layout != null) {
630             layout.removeAllComponents();
631         }
632         layout = newLayout;
633         // TODO
634         // setCompositionRoot(layout);
635         rebuild();
636     }
637 
638     /**
639      * Gets the current token {@link InsertPosition}.<br/>
640      * The token buttons are be placed at this position, relative to the input
641      * box.
642      * 
643      * @see #setTokenInsertPosition(InsertPosition)
644      * @see InsertPosition
645      * @return the current token insert position
646      */
647     public InsertPosition getTokenInsertPosition() {
648         return insertPosition;
649     }
650 
651     /**
652      * Sets the token {@link InsertPosition}.<br/>
653      * The token buttons will be placed at this position, relative to the input
654      * box.
655      * 
656      * @see #getTokenInsertPosition()
657      * @see InsertPosition
658      */
659     public void setTokenInsertPosition(InsertPosition insertPosition) {
660         if (this.insertPosition != insertPosition) {
661             this.insertPosition = insertPosition;
662             cb.setTokenInsertPosition(insertPosition);
663             rebuild();
664         }
665     }
666 
667     public void setReadOnly(boolean readOnly) {
668         if (readOnly == isReadOnly()) {
669             return;
670         }
671         for (Button b : buttons.values()) {
672 //            b.setReadOnly(readOnly);
673         }
674         super.setReadOnly(readOnly);
675         if (readOnly) {
676             layout.removeComponent(cb);
677         } else {
678             rebuild();
679         }
680     }
681 
682     /**
683      * Sets the Container data source used for the input box. This works exactly
684      * as {@link ComboBox#setContainerDataSource(Container)}.
685      * 
686      * @see ComboBox#setContainerDataSource(Container)
687      * @param c
688      *            the token container data source
689      */
690     public void setContainerDataSource(Container c) {
691         cb.setContainerDataSource(c);
692     }
693 
694     /**
695      * Gets the Container data source currently used for the input box. This
696      * works exactly as {@link ComboBox#getContainerDataSource()}.
697      * 
698      * @see ComboBox#getContainerDataSource()
699      * @return the container used to tokens
700      */
701     public Container getContainerDataSource() {
702         return cb.getContainerDataSource();
703     }
704 
705     /**
706      * Sets whether or not tokens entered by the user that not present in the
707      * container are allowed. When true, the token is added, and if
708      * {@link #setRememberNewTokens(boolean)} is true, the new token will be
709      * added to the container as well.
710      * 
711      * @see #setNewTokenHandler(NewItemHandler)
712      * @param allowNewTokens
713      *            true to allow tokens that are not in the container
714      */
715     public void setNewTokensAllowed(boolean allowNewTokens) {
716         cb.setNewItemsAllowed(allowNewTokens);
717     }
718 
719     /**
720      * Checks whether or not new tokens are allowed
721      * 
722      * @see #setNewTokensAllowed(boolean)
723      * @return
724      */
725     public boolean isNewTokensAllowed() {
726         return cb.isNewItemsAllowed();
727     }
728 
729     /**
730      * If true, new tokens entered by the user are automatically added to the
731      * container.
732      * 
733      * @return true if tokens are automatically added
734      */
735     public boolean isRememberNewTokens() {
736         return rememberNewTokens;
737     }
738 
739     /**
740      * Provided new tokens are allowed ({@link #setNewTokensAllowed(boolean)}),
741      * this sets whether or not new tokens entered by the user are automatically
742      * added to the container.
743      * 
744      * @param rememberNewTokens
745      *            true to add new tokens automatically
746      */
747     public void setRememberNewTokens(boolean rememberNewTokens) {
748         this.rememberNewTokens = rememberNewTokens;
749     }
750 
751     /**
752      * Works as {@link ComboBox#setFilteringMode(int)}.
753      * 
754      * @see ComboBox#setFilteringMode(int)
755      * @param filteringMode
756      *            the desired filtering mode
757      */
758     public void setFilteringMode(FilteringMode filteringMode) {
759         cb.setFilteringMode(filteringMode);
760     }
761 
762     /**
763      * Works as {@link ComboBox#getFilteringMode()}.
764      * 
765      * @see ComboBox#getFilteringMode()
766      * @param filteringMode
767      *            the desired filtering mode
768      */
769     public FilteringMode getFilteringMode() {
770         return cb.getFilteringMode();
771     }
772 
773     /*
774      * (non-Javadoc)
775      * 
776      * @see org.vaadin.tokenfield.CustomField#focus()
777      */
778     public void focus() {
779         cb.focus();
780     }
781 
782     /**
783      * Gets the input prompt; works as {@link ComboBox#getInputPrompt()}.
784      * 
785      * @see ComboBox#getInputPrompt()
786      * @return the current input prompt
787      */
788     public String getInputPrompt() {
789         return cb.getInputPrompt();
790     }
791 
792     /**
793      * Gets the caption for the given token; the caption can be based on a
794      * property, just as in a ComboBox. Note that the string representation of
795      * the tokenId itself is always used if the container does not contain the
796      * id.
797      * 
798      * @param tokenId
799      *            the id of the token
800      * @return the caption
801      */
802     public String getTokenCaption(Object tokenId) {
803         if (cb.containsId(tokenId)) {
804             return cb.getItemCaption(tokenId);
805         } else {
806             return "" + tokenId;
807         }
808     }
809 
810     /**
811      * @see ComboBox#getItemCaptionMode()
812      * @return the current caption mode
813      */
814     public ItemCaptionMode getTokenCaptionMode() {
815         return cb.getItemCaptionMode();
816     }
817 
818     /**
819      * @see ComboBox#getItemCaptionPropertyId()
820      * @return the current caption property id
821      */
822     public Object getTokenCaptionPropertyId() {
823         return cb.getItemCaptionPropertyId();
824     }
825 
826     /**
827      * @see ComboBox#getItemIcon(Object)
828      * @return the icon for the given resource
829      */
830 
831     public Resource getTokenIcon(Object tokenId) {
832         return cb.getItemIcon(tokenId);
833     }
834 
835     /**
836      * @see ComboBox#getItemIconPropertyId()
837      * @return the current item icon property id
838      */
839 
840     public Object getTokenIconPropertyId() {
841         return cb.getItemIconPropertyId();
842     }
843 
844     /**
845      * Gets all tokenIds currently in the token container.
846      * 
847      * @return a collection of all tokenIds in the container
848      */
849     public Collection getTokenIds() {
850         return cb.getItemIds();
851     }
852 
853     /*
854      * (non-Javadoc)
855      * 
856      * @see org.vaadin.tokenfield.CustomField#getTabIndex()
857      */
858     public int getTabIndex() {
859         return cb.getTabIndex();
860     }
861 
862     /*-
863     @Override
864     public void setHeight(String height) {
865         this.layout.setHeight(height);
866         super.setHeight(height);
867     }
868 
869     @Override
870     public void setWidth(String width) {
871         this.layout.setWidth(width);
872         super.setWidth(width);
873     }
874     -*/
875 
876     @Override
877     public void setHeight(float height, Unit unit) {
878         if (this.layout != null) {
879             this.layout.setHeight(height, unit);
880         }
881         super.setHeight(height, unit);
882     }
883 
884     @Override
885     public void setWidth(float width, Unit unit) {
886         if (this.layout != null) {
887             this.layout.setWidth(width, unit);
888         }
889         super.setWidth(width, unit);
890     }
891 
892     @Override
893     public void setSizeFull() {
894         if (this.layout != null) {
895             this.layout.setSizeFull();
896         }
897         super.setSizeFull();
898     }
899 
900     @Override
901     public void setSizeUndefined() {
902         if (this.layout != null) {
903             this.layout.setSizeUndefined();
904         }
905         super.setSizeUndefined();
906     }
907 
908     public void setInputHeight(String height) {
909         this.cb.setHeight(height);
910     }
911 
912     public void setInputWidth(String width) {
913         this.cb.setWidth(width);
914     }
915 
916     public void setInputHeight(float height, Unit unit) {
917         this.cb.setHeight(height, unit);
918     }
919 
920     public void setInputWidth(float width, Unit unit) {
921         this.cb.setWidth(width, unit);
922     }
923 
924     public void setInputSizeFull() {
925         this.cb.setSizeFull();
926     }
927 
928     public void setInputSizeUndefined() {
929         this.cb.setSizeUndefined();
930     }
931 
932     /**
933      * Sets the input prompt; works as {@link ComboBox#setInputPrompt(String)}.
934      * 
935      * @see ComboBox#setInputPrompt(String)
936      * @return the current input prompt
937      */
938     public void setInputPrompt(String inputPrompt) {
939         cb.setInputPrompt(inputPrompt);
940     }
941 
942     /**
943      * sets the caption for the given token.
944      * 
945      * @see ComboBox#setItemCaption(Object, String)
946      * @param tokenId
947      *            token whose caption to set
948      * @param caption
949      *            the desired caption
950      */
951     public void setTokenCaption(Object tokenId, String caption) {
952         cb.setItemCaption(tokenId, caption);
953     }
954 
955     /**
956      * @see ComboBox#setItemCaptionMode(int)
957      * @param mode
958      */
959     public void setTokenCaptionMode(ItemCaptionMode mode) {
960         cb.setItemCaptionMode(mode);
961     }
962 
963     /**
964      * @see ComboBox#setItemCaptionPropertyId(Object)
965      * @param propertyId
966      */
967     public void setTokenCaptionPropertyId(Object propertyId) {
968         cb.setItemCaptionPropertyId(propertyId);
969     }
970 
971     /**
972      * @see ComboBox#setItemIcon(Object, Resource)
973      * @param tokenId
974      * @param icon
975      */
976     public void setTokenIcon(Object tokenId, Resource icon) {
977         cb.setItemIcon(tokenId, icon);
978     }
979 
980     /**
981      * @see ComboBox#setItemIconPropertyId(Object)
982      * @param propertyId
983      */
984     public void setTokenIconPropertyId(Object propertyId) {
985         cb.setItemIconPropertyId(propertyId);
986     }
987 
988     /*
989      * (non-Javadoc)
990      * 
991      * @see org.vaadin.tokenfield.CustomField#setTabIndex(int)
992      */
993     public void setTabIndex(int tabIndex) {
994         cb.setTabIndex(tabIndex);
995     }
996 
997     /*
998      * (non-Javadoc)
999      * 
1000      * @see org.vaadin.tokenfield.CustomField#getType()
1001      */
1002     @Override
1003     public Class<?> getType() {
1004         return Set.class;
1005     }
1006 
1007     @Override
1008     protected Component initContent() {
1009         return layout;
1010     }
1011 
1012 }