001    /*
002     * This file is part of the Echo Point Project.  This project is a
003     * collection of Components that have extended the Echo Web Application
004     * Framework Version 3.
005     *
006     * Version: MPL 1.1
007     *
008     * The contents of this file are subject to the Mozilla Public License Version
009     * 1.1 (the "License"); you may not use this file except in compliance with
010     * the License. You may obtain a copy of the License at
011     * http://www.mozilla.org/MPL/
012     *
013     * Software distributed under the License is distributed on an "AS IS" basis,
014     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
015     * for the specific language governing rights and limitations under the
016     * License.
017     */
018    
019    package echopoint;
020    
021    import echopoint.event.TagEvent;
022    import echopoint.internal.AbstractContainer;
023    import echopoint.model.Tag;
024    import nextapp.echo.app.Color;
025    import nextapp.echo.app.Component;
026    import nextapp.echo.app.event.ActionListener;
027    
028    import java.util.ArrayList;
029    import java.util.Collection;
030    import java.util.Collections;
031    import java.util.LinkedHashMap;
032    import java.util.Map;
033    
034    /**
035     * A <a href='http://en.wikipedia.org/wiki/Tag_cloud'>tag cloud</a> component
036     * based on client-side code contribued by Tod.  Please note that the tag
037     * model objects specified for this component should have a unique name
038     * property.  This component supports the
039     * following style properties in addition to those defined in {@link
040     * nextapp.echo.app.Component}:
041     *
042     * <ul>
043     *   <li><code>rolloverEnabled</code> - A flag used to indicate that the tag
044     *     components should support change of style on mouse rollover.</li>
045     *   <li><code>rolloverBackground</code> - The colour to use as the background
046     *     of the tag element over which the mouse hovers.</li>
047     *   <li><code>rolloverForeground</code> - The colour to use for the title
048     *     of the tag element over which the mouse hovers.</li>
049     * </ul>
050     *
051     * <p><b>Note:</b> There is a bug in how updates to the tags properties are
052     * handled by the update manager at present.  For the time-being, please
053     * remove and re-add the tag cloud to its parent if the tags have been
054     * updated during an event cycle (see {@code TagCloudTest} for example).</p>
055     *
056     * <p>The following shows sample usage of this component:</p>
057     * <pre>
058     *   import echopoint.TagCloud;
059     *   import echopoint.event.TagEvent;
060     *   import echopoint.model.Tag;
061     *   import nextapp.echo.app.Row;
062     *
063     *     ...
064     *     final Collection&lt;Tag&gt; tags = new LinkedHashSet&lt;Tag&gt;();
065     *     tags.add( new Tag( "Java", 50 ) );
066     *     tags.add( new Tag( "Objective-C", 25 ) );
067     *     tags.add( new Tag( "Groovy", 10 ) );
068     *     tags.add( new Tag( "JavaScript", 15 ) );
069     *
070     *     final Row row = new Row();
071     *     final TagCloud tc = new TagCloud();
072     *     tc.setRolloverEnabled( true );
073     *     tc.setRolloverBackground( new Color( 0xa1a1a1 ) );
074     *     tc.setRolloverForeground( new Color( 0xc1c1c1 ) );
075     *     tc.setTags( tags );
076     *     row.add( tc );
077     *
078     *      tc.addActionListener( new ActionListener()
079     *      {
080     *        public void actionPerformed( final ActionEvent event )
081     *        {
082     *          final TagEvent te = (TagEvent) event;
083     *          if ( te.getTag() != null )
084     *          {
085     *            final Component content = Application.getContent().getTestArea();
086     *            content.add( new Label( "Clicked Tag: " + te.getTag().getName() ) );
087     *          }
088     *        }
089     *      });
090     * </pre>
091     *
092     * @author Rakesh 2008-07-20
093     * @version $Id: TagCloud.java 204 2009-05-20 18:50:57Z sptrakesh $
094     */
095    public class TagCloud extends AbstractContainer
096    {
097      private static final long serialVersionUID = 1l;
098    
099      /** A flag indicating whether rollover is enabled for a tag. */
100      public static final String PROPERTY_ROLLOVER_ENABLED = "rolloverEnabled";
101    
102      /** The rollover background for the tag element. */
103      public static final String PROPERTY_ROLLOVER_BACKGROUND = "rolloverBackground";
104    
105      /** The rollover foreground for the tag element. */
106      public static final String PROPERTY_ROLLOVER_FOREGROUND = "rolloverForeground";
107    
108      /**
109       * The collection of {@link echopoint.model.Tag} instances to be
110       * represented in the component.
111       */
112      public static final String PROPERTY_TAGS = "tags";
113    
114      /**
115       * <b>TagCloud</b> is <i>NOT</i> allowed to have any children.
116       *
117       * @see nextapp.echo.app.Component#isValidChild(nextapp.echo.app.Component)
118       */
119      @Override
120      public boolean isValidChild( final Component child )
121      {
122        return false;
123      }
124    
125      /**
126       * Return the value of the {@link #PROPERTY_ROLLOVER_ENABLED} property.
127       *
128       * @return The flag indicating whether rollover behaviour is enabled or not.
129       */
130      public boolean getRolloverEnabled()
131      {
132        final Boolean enabled =  (Boolean) get( PROPERTY_ROLLOVER_ENABLED );
133        return ( enabled != null ) ? enabled : false;
134      }
135    
136      /**
137       * Set the value of the {@link #PROPERTY_ROLLOVER_ENABLED} property.
138       *
139       * @param enabled The flag indicating whether rollover behaviour is to be
140       *   enabled or not.
141       */
142      public void setRolloverEnabled( final boolean enabled )
143      {
144        set( PROPERTY_ROLLOVER_ENABLED, enabled );
145      }
146    
147      /**
148       * Return the value of the {@link #PROPERTY_ROLLOVER_BACKGROUND} property.
149       *
150       * @return The colour used as the background while rolling over the tag.
151       */
152      public Color getRolloverBackground()
153      {
154        return (Color) get( PROPERTY_ROLLOVER_BACKGROUND );
155      }
156    
157      /**
158       * Set the value of the {@link #PROPERTY_ROLLOVER_BACKGROUND} property.
159       *
160       * @param background The colour used as the background while rolling over.
161       */
162      public void setRolloverBackground( final Color background )
163      {
164        set( PROPERTY_ROLLOVER_BACKGROUND, background );
165      }
166    
167      /**
168       * Return the value of the {@link #PROPERTY_ROLLOVER_BACKGROUND} property.
169       *
170       * @return The colour used as the foreground while rolling over the tag.
171       */
172      public Color getRolloverForeground()
173      {
174        return (Color) get( PROPERTY_ROLLOVER_FOREGROUND );
175      }
176    
177      /**
178       * Set the value of the {@link #PROPERTY_ROLLOVER_BACKGROUND} property.
179       *
180       * @param foreground The colour used as the foreground while rolling over.
181       */
182      public void setRolloverForeground( final Color foreground )
183      {
184        set( PROPERTY_ROLLOVER_FOREGROUND, foreground );
185      }
186    
187      /**
188       * Return the value of the {@link #PROPERTY_TAGS} property.
189       *
190       * @return A read-only view of the collection of tags instances.
191       */
192      @SuppressWarnings( {"unchecked"} )
193      public Collection<Tag> getTags()
194      {
195        final Map<String,Tag> map = (Map<String,Tag>) get( PROPERTY_TAGS );
196        return Collections.unmodifiableCollection( map.values() );
197      }
198    
199      /**
200       * Return the collection of tags that represent the model for this component.
201       *
202       * @return The collection of tag instances.
203       */
204      @SuppressWarnings( {"unchecked"} )
205      protected Collection<Tag> getData()
206      {
207        final Map<String,Tag> map = (Map<String,Tag>) get( PROPERTY_TAGS );
208        return new ArrayList<Tag>( map.values() );
209      }
210    
211      /**
212       * Return the tag identified by its name.
213       *
214       * @param name The unique name for the tag.
215       * @return The tag model object or {@code null} if no such tag exists.
216       */
217      @SuppressWarnings( {"unchecked"} )
218      public Tag getTag( final String name )
219      {
220        final Map<String,Tag> map = (Map<String,Tag>) get( PROPERTY_TAGS );
221        return map.get( name );
222      }
223    
224      /**
225       * Set the value of the {@link #PROPERTY_TAGS} property.  Note that there
226       * is not way to individually add/delete tag instances other than setting
227       * a new collection (or a modified version of the original collection you
228       * used to set the property).
229       *
230       * @param tags The collection of tag instances to be represented in this
231       *   component.  Please note that a {@link java.util.Set} implementation
232       *   is preferred since this implementation requires unique tag names.
233       */
234      public void setTags( final Collection<Tag> tags )
235      {
236        final Map<String,Tag> map = new LinkedHashMap<String,Tag>();
237    
238        for ( Tag tag : tags )
239        {
240          map.put( tag.getName(), tag );
241        }
242    
243        set( PROPERTY_TAGS, map );
244      }
245    
246      /** {@inheritDoc} */
247      @Override
248      public void addActionListener( final ActionListener listener )
249      {
250        super.addActionListener( listener );
251      }
252    
253      /** {@inheritDoc} */
254      public void removeActionListener( final ActionListener listener )
255      {
256        super.removeActionListener( listener );
257      }
258    
259      /** {@inheritDoc} */
260      @Override
261      public boolean hasActionListeners()
262      {
263        return super.hasActionListeners();
264      }
265    
266      /** {@inheritDoc} */
267      @Override
268      public void processInput( String name, Object value )
269      {
270        super.processInput( name, value );
271        if ( INPUT_ACTION.equals( name ) )
272        {
273          final Tag tag = getTag( (String) value );
274          fireActionPerformed( new TagEvent( this, tag.getName(), tag ) );
275        }
276      }
277    }