Clover coverage report - Code Coverage for tapestry-contrib release 4.0-beta-10
Coverage timestamp: Sat Oct 8 2005 19:13:41 EDT
file stats: LOC: 530   Methods: 11
NCLOC: 212   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Palette.java 0% 0% 0% 0%
coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.tapestry.contrib.palette;
 16   
 17    import java.util.ArrayList;
 18    import java.util.Collections;
 19    import java.util.HashMap;
 20    import java.util.Iterator;
 21    import java.util.List;
 22    import java.util.Map;
 23   
 24    import org.apache.tapestry.BaseComponent;
 25    import org.apache.tapestry.IAsset;
 26    import org.apache.tapestry.IForm;
 27    import org.apache.tapestry.IMarkupWriter;
 28    import org.apache.tapestry.IRequestCycle;
 29    import org.apache.tapestry.IScript;
 30    import org.apache.tapestry.PageRenderSupport;
 31    import org.apache.tapestry.Tapestry;
 32    import org.apache.tapestry.TapestryUtils;
 33    import org.apache.tapestry.components.Block;
 34    import org.apache.tapestry.form.IPropertySelectionModel;
 35    import org.apache.tapestry.form.ValidatableField;
 36    import org.apache.tapestry.form.ValidatableFieldSupport;
 37    import org.apache.tapestry.valid.IValidationDelegate;
 38    import org.apache.tapestry.valid.ValidationConstants;
 39    import org.apache.tapestry.valid.ValidatorException;
 40   
 41    /**
 42    * A component used to make a number of selections from a list. The general look is a pair of
 43    * <select> elements. with a pair of buttons between them. The right element is a list of
 44    * values that can be selected. The buttons move values from the right column ("available") to the
 45    * left column ("selected").
 46    * <p>
 47    * This all takes a bit of JavaScript to accomplish (quite a bit), which means a {@link Body}
 48    * component must wrap the Palette. If JavaScript is not enabled in the client browser, then the
 49    * user will be unable to make (or change) any selections.
 50    * <p>
 51    * Cross-browser compatibility is not perfect. In some cases, the
 52    * {@link org.apache.tapestry.contrib.form.MultiplePropertySelection}component may be a better
 53    * choice.
 54    * <p>
 55    * <table border=1>
 56    * <tr>
 57    * <td>Parameter</td>
 58    * <td>Type</td>
 59    * <td>Direction</td>
 60    * <td>Required</td>
 61    * <td>Default</td>
 62    * <td>Description</td>
 63    * </tr>
 64    * <tr>
 65    * <td>selected</td>
 66    * <td>{@link List}</td>
 67    * <td>in</td>
 68    * <td>yes</td>
 69    * <td>&nbsp;</td>
 70    * <td>A List of selected values. Possible selections are defined by the model; this should be a
 71    * subset of the possible values. This may be null when the component is renderred. When the
 72    * containing form is submitted, this parameter is updated with a new List of selected objects.
 73    * <p>
 74    * The order may be set by the user, as well, depending on the sortMode parameter.</td>
 75    * </tr>
 76    * <tr>
 77    * <td>model</td>
 78    * <td>{@link IPropertySelectionModel}</td>
 79    * <td>in</td>
 80    * <td>yes</td>
 81    * <td>&nbsp;</td>
 82    * <td>Works, as with a {@link org.apache.tapestry.form.PropertySelection}component, to define the
 83    * possible values.</td>
 84    * </tr>
 85    * <tr>
 86    * <td>sort</td>
 87    * <td>string</td>
 88    * <td>in</td>
 89    * <td>no</td>
 90    * <td>{@link SortMode#NONE}</td>
 91    * <td>Controls automatic sorting of the options.</td>
 92    * </tr>
 93    * <tr>
 94    * <td>rows</td>
 95    * <td>int</td>
 96    * <td>in</td>
 97    * <td>no</td>
 98    * <td>10</td>
 99    * <td>The number of rows that should be visible in the Pallete's &lt;select&gt; elements.</td>
 100    * </tr>
 101    * <tr>
 102    * <td>tableClass</td>
 103    * <td>{@link String}</td>
 104    * <td>in</td>
 105    * <td>no</td>
 106    * <td>tapestry-palette</td>
 107    * <td>The CSS class for the table which surrounds the other elements of the Palette.</td>
 108    * </tr>
 109    * <tr>
 110    * <td>selectedTitleBlock</td>
 111    * <td>{@link Block}</td>
 112    * <td>in</td>
 113    * <td>no</td>
 114    * <td>"Selected"</td>
 115    * <td>If specified, allows a {@link Block}to be placed within the &lt;th&gt; reserved for the
 116    * title above the selected items &lt;select&gt; (on the right). This allows for images or other
 117    * components to be placed there. By default, the simple word <code>Selected</code> is used.</td>
 118    * </tr>
 119    * <tr>
 120    * <td>availableTitleBlock</td>
 121    * <td>{@link Block}</td>
 122    * <td>in</td>
 123    * <td>no</td>
 124    * <td>"Available"</td>
 125    * <td>As with selectedTitleBlock, but for the left column, of items which are available to be
 126    * selected. The default is the word <code>Available</code>.</td>
 127    * </tr>
 128    * <tr>
 129    * <td>selectImage <br>
 130    * selectDisabledImage <br>
 131    * deselectImage <br>
 132    * deselectDisabledImage <br>
 133    * upImage <br>
 134    * upDisabledImage <br>
 135    * downImage <br>
 136    * downDisabledImage</td>
 137    * <td>{@link IAsset}</td>
 138    * <td>in</td>
 139    * <td>no</td>
 140    * <td>&nbsp;</td>
 141    * <td>If any of these are specified then they override the default images provided with the
 142    * component. This allows the look and feel to be customized relatively easily.
 143    * <p>
 144    * The most common reason to replace the images is to deal with backgrounds. The default images are
 145    * anti-aliased against a white background. If a colored or patterned background is used, the
 146    * default images will have an ugly white fringe. Until all browsers have full support for PNG
 147    * (which has a true alpha channel), it is necessary to customize the images to match the
 148    * background.</td>
 149    * </tr>
 150    * </table>
 151    * <p>
 152    * A Palette requires some CSS entries to render correctly ... especially the middle column, which
 153    * contains the two or four buttons for moving selections between the two columns. The width and
 154    * alignment of this column must be set using CSS. Additionally, CSS is commonly used to give the
 155    * Palette columns a fixed width, and to dress up the titles. Here is an example of some CSS you can
 156    * use to format the palette component:
 157    *
 158    * <pre>
 159    *
 160    *
 161    *
 162    *
 163    *
 164    *
 165    *
 166    * TABLE.tapestry-palette TH
 167    * {
 168    * font-size: 9pt;
 169    * font-weight: bold;
 170    * color: white;
 171    * background-color: #330066;
 172    * text-align: center;
 173    * }
 174    *
 175    * TD.available-cell SELECT
 176    * {
 177    * font-weight: normal;
 178    * background-color: #FFFFFF;
 179    * width: 200px;
 180    * }
 181    *
 182    * TD.selected-cell SELECT
 183    * {
 184    * font-weight: normal;
 185    * background-color: #FFFFFF;
 186    * width: 200px;
 187    * }
 188    *
 189    * TABLE.tapestry-palette TD.controls
 190    * {
 191    * text-align: center;
 192    * vertical-align: middle;
 193    * width: 60px;
 194    * }
 195    *
 196    *
 197    *
 198    *
 199    *
 200    *
 201    *
 202    * </pre>
 203    *
 204    * <p>
 205    * As of 4.0, this component can be validated.
 206    *
 207    * @author Howard Lewis Ship
 208    */
 209   
 210    public abstract class Palette extends BaseComponent implements ValidatableField
 211    {
 212    private static final int MAP_SIZE = 7;
 213   
 214    /**
 215    * A set of symbols produced by the Palette script. This is used to provide proper names for
 216    * some of the HTML elements (&lt;select&gt; and &lt;button&gt; elements, etc.).
 217    */
 218    private Map _symbols;
 219   
 220    /** @since 3.0 * */
 221    public abstract void setAvailableColumn(PaletteColumn column);
 222   
 223    /** @since 3.0 * */
 224    public abstract void setSelectedColumn(PaletteColumn column);
 225   
 226    public abstract void setName(String name);
 227   
 228    public abstract void setForm(IForm form);
 229   
 230    /** @since 4.0 */
 231    public abstract void setRequiredMessage(String message);
 232   
 233    /** @since 4.0 */
 234   
 235    public abstract String getIdParameter();
 236   
 237    /** @since 4.0 */
 238   
 239    public abstract void setClientId(String clientId);
 240   
 241  0 protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
 242    {
 243    // Next few lines of code is similar to AbstractFormComponent (which, alas, extends from
 244    // AbstractComponent, not from BaseComponent).
 245  0 IForm form = TapestryUtils.getForm(cycle, this);
 246   
 247  0 setForm(form);
 248   
 249  0 if (form.wasPrerendered(writer, this))
 250  0 return;
 251   
 252  0 IValidationDelegate delegate = form.getDelegate();
 253   
 254  0 delegate.setFormComponent(this);
 255   
 256  0 form.getElementId(this);
 257   
 258  0 if (form.isRewinding())
 259    {
 260  0 if (!isDisabled())
 261    {
 262  0 rewindFormComponent(writer, cycle);
 263    }
 264    }
 265  0 else if (!cycle.isRewinding())
 266    {
 267  0 if (!isDisabled())
 268  0 delegate.registerForFocus(this, ValidationConstants.NORMAL_FIELD);
 269   
 270  0 renderFormComponent(writer, cycle);
 271   
 272  0 if (delegate.isInError())
 273  0 delegate.registerForFocus(this, ValidationConstants.ERROR_FIELD);
 274    }
 275   
 276  0 super.renderComponent(writer, cycle);
 277    }
 278   
 279  0 protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle)
 280    {
 281  0 String clientId = cycle.getUniqueId(TapestryUtils
 282    .convertTapestryIdToNMToken(getIdParameter()));
 283   
 284  0 setClientId(clientId);
 285   
 286  0 _symbols = new HashMap(MAP_SIZE);
 287   
 288  0 runScript(cycle);
 289   
 290  0 constructColumns();
 291   
 292  0 getValidatableFieldSupport().renderContributions(this, writer, cycle);
 293    }
 294   
 295  0 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle)
 296    {
 297  0 String[] values = cycle.getParameters(getName());
 298   
 299  0 int count = Tapestry.size(values);
 300   
 301  0 List selected = new ArrayList(count);
 302  0 IPropertySelectionModel model = getModel();
 303   
 304  0 for (int i = 0; i < count; i++)
 305    {
 306  0 String value = values[i];
 307  0 Object option = model.translateValue(value);
 308   
 309  0 selected.add(option);
 310    }
 311   
 312  0 setSelected(selected);
 313   
 314  0 try
 315    {
 316  0 getValidatableFieldSupport().validate(this, writer, cycle, selected);
 317    }
 318    catch (ValidatorException e)
 319    {
 320  0 getForm().getDelegate().record(e);
 321    }
 322    }
 323   
 324  0 protected void cleanupAfterRender(IRequestCycle cycle)
 325    {
 326  0 _symbols = null;
 327   
 328  0 setAvailableColumn(null);
 329  0 setSelectedColumn(null);
 330   
 331  0 super.cleanupAfterRender(cycle);
 332    }
 333   
 334    /**
 335    * Executes the associated script, which generates all the JavaScript to support this Palette.
 336    */
 337  0 private void runScript(IRequestCycle cycle)
 338    {
 339  0 PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
 340   
 341  0 setImage(pageRenderSupport, cycle, "selectImage", getSelectImage());
 342  0 setImage(pageRenderSupport, cycle, "selectDisabledImage", getSelectDisabledImage());
 343  0 setImage(pageRenderSupport, cycle, "deselectImage", getDeselectImage());
 344  0 setImage(pageRenderSupport, cycle, "deselectDisabledImage", getDeselectDisabledImage());
 345   
 346  0 if (isSortUser())
 347    {
 348  0 setImage(pageRenderSupport, cycle, "upImage", getUpImage());
 349  0 setImage(pageRenderSupport, cycle, "upDisabledImage", getUpDisabledImage());
 350  0 setImage(pageRenderSupport, cycle, "downImage", getDownImage());
 351  0 setImage(pageRenderSupport, cycle, "downDisabledImage", getDownDisabledImage());
 352    }
 353   
 354  0 _symbols.put("palette", this);
 355   
 356  0 getScript().execute(cycle, pageRenderSupport, _symbols);
 357    }
 358   
 359    /**
 360    * Extracts its asset URL, sets it up for preloading, and assigns the preload reference as a
 361    * script symbol.
 362    */
 363  0 private void setImage(PageRenderSupport pageRenderSupport, IRequestCycle cycle,
 364    String symbolName, IAsset asset)
 365    {
 366  0 String URL = asset.buildURL(cycle);
 367  0 String reference = pageRenderSupport.getPreloadedImageReference(URL);
 368   
 369  0 _symbols.put(symbolName, reference);
 370    }
 371   
 372  0 public Map getSymbols()
 373    {
 374  0 return _symbols;
 375    }
 376   
 377    /**
 378    * Constructs a pair of {@link PaletteColumn}s: the available and selected options.
 379    */
 380  0 private void constructColumns()
 381    {
 382    // Build a Set around the list of selected items.
 383   
 384  0 List selected = getSelected();
 385   
 386  0 if (selected == null)
 387  0 selected = Collections.EMPTY_LIST;
 388   
 389  0 String sortMode = getSort();
 390   
 391  0 boolean sortUser = sortMode.equals(SortMode.USER);
 392   
 393  0 List selectedOptions = null;
 394   
 395  0 if (sortUser)
 396    {
 397  0 int count = selected.size();
 398  0 selectedOptions = new ArrayList(count);
 399   
 400  0 for (int i = 0; i < count; i++)
 401  0 selectedOptions.add(null);
 402    }
 403   
 404  0 PaletteColumn availableColumn = new PaletteColumn((String) _symbols.get("availableName"),
 405    null, getRows());
 406  0 PaletteColumn selectedColumn = new PaletteColumn(getName(), getClientId(), getRows());
 407   
 408    // Each value specified in the model will go into either the selected or available
 409    // lists.
 410   
 411  0 IPropertySelectionModel model = getModel();
 412   
 413  0 int count = model.getOptionCount();
 414   
 415  0 for (int i = 0; i < count; i++)
 416    {
 417  0 Object optionValue = model.getOption(i);
 418   
 419  0 PaletteOption o = new PaletteOption(model.getValue(i), model.getLabel(i));
 420   
 421  0 int index = selected.indexOf(optionValue);
 422  0 boolean isSelected = index >= 0;
 423   
 424  0 if (sortUser && isSelected)
 425    {
 426  0 selectedOptions.set(index, o);
 427  0 continue;
 428    }
 429   
 430  0 PaletteColumn c = isSelected ? selectedColumn : availableColumn;
 431   
 432  0 c.addOption(o);
 433    }
 434   
 435  0 if (sortUser)
 436    {
 437  0 Iterator i = selectedOptions.iterator();
 438  0 while (i.hasNext())
 439    {
 440  0 PaletteOption o = (PaletteOption) i.next();
 441  0 selectedColumn.addOption(o);
 442    }
 443    }
 444   
 445  0 if (sortMode.equals(SortMode.VALUE))
 446    {
 447  0 availableColumn.sortByValue();
 448  0 selectedColumn.sortByValue();
 449    }
 450  0 else if (sortMode.equals(SortMode.LABEL))
 451    {
 452  0 availableColumn.sortByLabel();
 453  0 selectedColumn.sortByLabel();
 454    }
 455   
 456  0 setAvailableColumn(availableColumn);
 457  0 setSelectedColumn(selectedColumn);
 458    }
 459   
 460  0 public boolean isSortUser()
 461    {
 462  0 return getSort().equals(SortMode.USER);
 463    }
 464   
 465    public abstract Block getAvailableTitleBlock();
 466   
 467    public abstract IAsset getDeselectDisabledImage();
 468   
 469    public abstract IAsset getDeselectImage();
 470   
 471    public abstract IAsset getDownDisabledImage();
 472   
 473    public abstract IAsset getDownImage();
 474   
 475    public abstract IAsset getSelectDisabledImage();
 476   
 477    public abstract IPropertySelectionModel getModel();
 478   
 479    public abstract int getRows();
 480   
 481    public abstract Block getSelectedTitleBlock();
 482   
 483    public abstract IAsset getSelectImage();
 484   
 485    public abstract String getSort();
 486   
 487    public abstract IAsset getUpDisabledImage();
 488   
 489    public abstract IAsset getUpImage();
 490   
 491    /**
 492    * Returns false. Palette components are never disabled.
 493    *
 494    * @since 2.2
 495    */
 496  0 public boolean isDisabled()
 497    {
 498  0 return false;
 499    }
 500   
 501    /** @since 2.2 * */
 502   
 503    public abstract List getSelected();
 504   
 505    /** @since 2.2 * */
 506   
 507    public abstract void setSelected(List selected);
 508   
 509    /**
 510    * Injected.
 511    *
 512    * @since 4.0
 513    */
 514    public abstract IScript getScript();
 515   
 516    /**
 517    * Injected.
 518    *
 519    * @since 4.0
 520    */
 521    public abstract ValidatableFieldSupport getValidatableFieldSupport();
 522   
 523    /**
 524    * @see org.apache.tapestry.form.AbstractFormComponent#isRequired()
 525    */
 526  0 public boolean isRequired()
 527    {
 528  0 return getValidatableFieldSupport().isRequired(this);
 529    }
 530    }