You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2007/05/25 17:07:06 UTC

svn commit: r541682 - in /tapestry/tapestry5/trunk/tapestry-core/src: images/ main/java/org/apache/tapestry/ main/java/org/apache/tapestry/corelib/components/ main/java/org/apache/tapestry/internal/ main/java/org/apache/tapestry/internal/services/ main...

Author: hlship
Date: Fri May 25 08:07:05 2007
New Revision: 541682

URL: http://svn.apache.org/viewvc?view=rev&rev=541682
Log:
TAPESTRY-1373: Recreate T4's Palette component for T5
Partial support for controlling whether the Palette supports reordering or not.
Improve the L&F of the Palette control images (select, deselect, etc.)
Introduce SelectModelVisitor interface, to navigate a SelectModel in proper order.
Move common rendering logic in Select and Palette to a new SelectModelRenderer class.
New TransformationException catches errors when transforming a class, or loading a transformed class, plus hooks to properly format the transformation inside the exception report page.

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModelVisitor.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/util/SelectModelRenderer.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ioc/internal/
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ioc/internal/TransformationException.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ioc/internal/util/
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/AbstractSelectModel.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/images/deselect.psd
    tapestry/tapestry5/trunk/tapestry-core/src/images/move_down.psd
    tapestry/tapestry5/trunk/tapestry-core/src/images/move_up.psd
    tapestry/tapestry5/trunk/tapestry-core/src/images/select.psd
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModel.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Palette.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/SelectModelImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/EnumSelectModel.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Palette.html
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/deselect.png
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/move_down.png
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/move_up.png
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/palette.js
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/select.png
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/PaletteDemo.html

Modified: tapestry/tapestry5/trunk/tapestry-core/src/images/deselect.psd
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/images/deselect.psd?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
Binary files - no diff available.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/images/move_down.psd
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/images/move_down.psd?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
Binary files - no diff available.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/images/move_up.psd
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/images/move_up.psd?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
Binary files - no diff available.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/images/select.psd
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/images/select.psd?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
Binary files - no diff available.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModel.java?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModel.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModel.java Fri May 25 08:07:05 2007
@@ -41,4 +41,10 @@
      * @return the ungrouped options, or null
      */
     List<OptionModel> getOptions();
+
+    /**
+     * Allows access to all the {@link OptionGroupModel}s and {@link OptionModel}s within the
+     * SelectModel.
+     */
+    void visit(SelectModelVisitor visitor);
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModelVisitor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModelVisitor.java?view=auto&rev=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModelVisitor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/SelectModelVisitor.java Fri May 25 08:07:05 2007
@@ -0,0 +1,40 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry;
+
+/**
+ * Callback interface that allows for visiting the option groups and option models of a select model
+ * in correct render order.
+ */
+public interface SelectModelVisitor
+{
+    /**
+     * Invoked once for each {@link OptionGroupModel}, just before invoking
+     * {@link #option(OptionModel)} for each embedded option within the group.
+     */
+    void beginOptionGroup(OptionGroupModel groupModel);
+
+    /**
+     * Invoked for each option within a group, and at the end, for each ungrouped option.
+     * 
+     * @param optionModel
+     */
+    void option(OptionModel optionModel);
+
+    /**
+     * Invoked after all options within the group have been visited.
+     */
+    void endOptionGroup(OptionGroupModel groupModel);
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Palette.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Palette.java?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Palette.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Palette.java Fri May 25 08:07:05 2007
@@ -31,12 +31,14 @@
 import org.apache.tapestry.PageRenderSupport;
 import org.apache.tapestry.Renderable;
 import org.apache.tapestry.SelectModel;
+import org.apache.tapestry.SelectModelVisitor;
 import org.apache.tapestry.ValueEncoder;
 import org.apache.tapestry.annotations.Environmental;
 import org.apache.tapestry.annotations.Inject;
 import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.annotations.Path;
 import org.apache.tapestry.corelib.base.AbstractField;
+import org.apache.tapestry.internal.util.SelectModelRenderer;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.services.FormSupport;
 import org.apache.tapestry.services.Request;
@@ -56,6 +58,10 @@
  * DIV.t-palette SELECT { width: 300px; }
  * &lt;/style&gt;
  * </pre>
+ * 
+ * <p>
+ * Option groups within the {@link SelectModel} will be rendered, but are not supported by the many
+ * browsers, and are not fully handled on the client side.
  */
 public class Palette extends AbstractField
 {
@@ -79,22 +85,29 @@
 
             writeDisabled(writer, isDisabled());
 
-            for (Renderable r : _availableOptions)
-                r.render(writer);
+            for (Runnable r : _availableOptions)
+                r.run();
 
             writer.end();
         }
     }
 
-    private final class OptionGroupEnd implements Renderable
+    private final class OptionGroupEnd implements Runnable
     {
-        public void render(MarkupWriter writer)
+        private final OptionGroupModel _model;
+
+        private OptionGroupEnd(OptionGroupModel model)
         {
-            writer.end();
+            _model = model;
+        }
+
+        public void run()
+        {
+            _renderer.endOptionGroup(_model);
         }
     }
 
-    private final class OptionGroupStart implements Renderable
+    private final class OptionGroupStart implements Runnable
     {
         private final OptionGroupModel _model;
 
@@ -103,16 +116,13 @@
             _model = model;
         }
 
-        public void render(MarkupWriter writer)
+        public void run()
         {
-            writer.element("optgroup", "label", _model.getLabel());
-            writeDisabled(writer, _model.isDisabled());
-
-            writeAttributes(writer, _model.getAttributes());
+            _renderer.beginOptionGroup(_model);
         }
     }
 
-    private final class RenderOption implements Renderable
+    private final class RenderOption implements Runnable
     {
         private final OptionModel _model;
 
@@ -121,9 +131,9 @@
             _model = model;
         }
 
-        public void render(MarkupWriter writer)
+        public void run()
         {
-            renderOption(writer, _model);
+            _renderer.option(_model);
         }
     }
 
@@ -131,8 +141,6 @@
     {
         public void render(MarkupWriter writer)
         {
-            // TODO: Support disabled parameter
-
             writer.element(
                     "select",
                     "id",
@@ -150,14 +158,15 @@
             {
                 OptionModel model = _valueToOptionModel.get(value);
 
-                renderOption(writer, model);
+                _renderer.option(model);
             }
 
             writer.end();
         }
     }
 
-    private List<Renderable> _availableOptions;
+    /** List of Runnable commands to render the available options. */
+    private List<Runnable> _availableOptions;
 
     /**
      * The image to use for the deselect button (the default is a left pointing arrow).
@@ -165,9 +174,15 @@
     @Parameter(value = "asset:deselect.png")
     private Asset _deselect;
 
+    /**
+     * Encoder used to translate between server-side objects and client-side strings.
+     */
     @Parameter(required = true)
     private ValueEncoder<Object> _encoder;
 
+    /**
+     * Model used to define the values and labels used when rendering.
+     */
     @Parameter(required = true)
     private SelectModel _model;
 
@@ -187,12 +202,16 @@
     @Path("palette.js")
     private Asset _paletteLibrary;
 
+    /** Used to include scripting code in the rendered page. */
     @Environmental
     private PageRenderSupport _renderSupport;
 
+    /** Needed to access query parameters when processing form submission. */
     @Inject
     private Request _request;
 
+    private SelectModelRenderer _renderer;
+
     /**
      * The image to use for the select button (the default is a right pointing arrow).
      */
@@ -208,6 +227,17 @@
     @Parameter(required = true)
     private List<Object> _selected;
 
+    /**
+     * If true, then additional buttons are provided on the client-side to allow for re-ordering of
+     * the values.
+     */
+    @Parameter("false")
+    private boolean _reorder;
+
+    /**
+     * Used during rendering to identify the options corresponding to selected values (from the
+     * selected parameter), in the order they should be displayed on the page.
+     */
     private List<OptionModel> _selectedOptions;
 
     private Map<Object, OptionModel> _valueToOptionModel;
@@ -218,9 +248,13 @@
     @Parameter(value = "10")
     private int _size;
 
+    /**
+     * Defaults the selected parameter to a container property whose name matches this component's
+     * id.
+     */
     final Binding defaultSelected()
     {
-        return createDefaultParameterBinding("value");
+        return createDefaultParameterBinding("selected");
     }
 
     public Renderable getAvailableRenderer()
@@ -308,7 +342,7 @@
 
         _renderSupport.addScriptLink(_paletteLibrary);
 
-        _renderSupport.addScript("new Tapestry.Palette('%s');", clientId);
+        _renderSupport.addScript("new Tapestry.Palette('%s', %s);", clientId, _reorder);
 
         writer.element(
                 "input",
@@ -328,73 +362,47 @@
         return false;
     }
 
-    void renderOption(MarkupWriter writer, OptionModel model)
-    {
-        String clientValue = _encoder.toClient(model.getValue());
-
-        writer.element("option", "value", clientValue);
-
-        writeDisabled(writer, model.isDisabled());
-
-        writeAttributes(writer, model.getAttributes());
-
-        writer.write(model.getLabel());
-        writer.end();
-    }
-
     @SuppressWarnings("unchecked")
-    void setupRender()
+    void setupRender(MarkupWriter writer)
     {
         _valueToOptionModel = newMap();
         _availableOptions = newList();
         _selectedOptions = newList();
+        _renderer = new SelectModelRenderer(writer, _encoder);
 
-        Set selectedSet = newSet(getSelected());
-
-        SelectModel model = _model;
+        final Set selectedSet = newSet(getSelected());
 
-        if (model.getOptionGroups() != null)
+        SelectModelVisitor visitor = new SelectModelVisitor()
         {
-            for (final OptionGroupModel groupModel : model.getOptionGroups())
+            public void beginOptionGroup(OptionGroupModel groupModel)
             {
                 _availableOptions.add(new OptionGroupStart(groupModel));
-
-                prerender(groupModel.getOptions(), selectedSet);
-
-                _availableOptions.add(new OptionGroupEnd());
             }
-        }
 
-        prerender(_model.getOptions(), selectedSet);
-    }
+            public void endOptionGroup(OptionGroupModel groupModel)
+            {
+                _availableOptions.add(new OptionGroupEnd(groupModel));
+            }
 
-    private void prerender(List<OptionModel> options, Set<Object> selectedSet)
-    {
-        if (options == null) return;
+            public void option(OptionModel optionModel)
+            {
+                Object value = optionModel.getValue();
 
-        for (final OptionModel model : options)
-        {
-            Object value = model.getValue();
+                boolean isSelected = selectedSet.contains(value);
 
-            boolean isSelected = selectedSet.contains(value);
+                if (isSelected)
+                {
+                    _selectedOptions.add(optionModel);
+                    _valueToOptionModel.put(value, optionModel);
+                    return;
+                }
 
-            if (isSelected)
-            {
-                _selectedOptions.add(model);
-                _valueToOptionModel.put(value, model);
-                continue;
+                _availableOptions.add(new RenderOption(optionModel));
             }
 
-            _availableOptions.add(new RenderOption(model));
-        }
-    }
-
-    private void writeAttributes(MarkupWriter writer, Map<String, String> attributes)
-    {
-        if (attributes == null) return;
+        };
 
-        for (Map.Entry<String, String> e : attributes.entrySet())
-            writer.attributes(e.getKey(), e.getValue());
+        _model.visit(visitor);
     }
 
     // Avoids a strange Javassist bytecode error, c'est lavie!
@@ -408,5 +416,10 @@
         if (_selected == null) return Collections.emptyList();
 
         return _selected;
+    }
+
+    public boolean getReorder()
+    {
+        return _reorder;
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/corelib/components/Select.java Fri May 25 08:07:05 2007
@@ -14,17 +14,15 @@
 
 package org.apache.tapestry.corelib.components;
 
-import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 
 import org.apache.tapestry.Binding;
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.FieldValidator;
 import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.OptionGroupModel;
 import org.apache.tapestry.OptionModel;
 import org.apache.tapestry.SelectModel;
+import org.apache.tapestry.SelectModelVisitor;
 import org.apache.tapestry.ValidationException;
 import org.apache.tapestry.ValidationTracker;
 import org.apache.tapestry.ValueEncoder;
@@ -33,6 +31,7 @@
 import org.apache.tapestry.annotations.Inject;
 import org.apache.tapestry.annotations.Parameter;
 import org.apache.tapestry.corelib.base.AbstractField;
+import org.apache.tapestry.internal.util.SelectModelRenderer;
 import org.apache.tapestry.services.FieldValidatorDefaultSource;
 import org.apache.tapestry.services.FormSupport;
 import org.apache.tapestry.services.Request;
@@ -45,9 +44,32 @@
  */
 public final class Select extends AbstractField
 {
-    /** The value to read or update. */
-    @Parameter(required = true, principal = true)
-    private Object _value;
+    private class Renderer extends SelectModelRenderer
+    {
+
+        public Renderer(MarkupWriter writer)
+        {
+            super(writer, getEncoder());
+        }
+
+        @Override
+        protected boolean isOptionSelected(OptionModel optionModel)
+        {
+            Object value = optionModel.getValue();
+
+            return isOptionValueSelected(value);
+        }
+    }
+
+    private boolean isOptionValueSelected(Object value)
+    {
+        return value == _value || (value != null && value.equals(_value));
+    }
+
+    private ValueEncoder getEncoder()
+    {
+        return _encoder;
+    }
 
     /**
      * The default encoder encodes strings, passing them to the client and back unchanged.
@@ -70,8 +92,13 @@
         }
     };
 
-    // Maybe this should default to property "<componentId>Model"?
+    @Inject
+    private FieldValidatorDefaultSource _fieldValidatorDefaultSource;
 
+    @Inject
+    private Locale _locale;
+
+    // Maybe this should default to property "<componentId>Model"?
     /**
      * The model used to identify the option groups and options to be presented to the user. This
      * can be generated automatically for Enum types.
@@ -79,48 +106,54 @@
     @Parameter(required = true)
     private SelectModel _model;
 
+    @Inject
+    private Request _request;
+
+    @Inject
+    private ComponentResources _resources;
+
+    @Environmental
+    private ValidationTracker _tracker;
+
     /** Performs input validation on the value supplied by the user in the form submission. */
     @Parameter(defaultPrefix = "validate")
     @SuppressWarnings("unchecked")
     private FieldValidator<Object> _validate = NOOP_VALIDATOR;
 
-    @Inject
-    private FieldValidatorDefaultSource _fieldValidatorDefaultSource;
+    /** The value to read or update. */
+    @Parameter(required = true, principal = true)
+    private Object _value;
 
-    @Environmental
-    private ValidationTracker _tracker;
+    @Override
+    protected void processSubmission(FormSupport formSupport, String elementName)
+    {
+        String primaryKey = _request.getParameter(elementName);
 
-    @Inject
-    private ComponentResources _resources;
+        Object selectedValue = _encoder.toValue(primaryKey);
 
-    @Inject
-    private Locale _locale;
+        try
+        {
+            _validate.validate(selectedValue);
 
-    @Inject
-    private Request _request;
+            _value = selectedValue;
+        }
+        catch (ValidationException ex)
+        {
+            _tracker.recordError(this, ex.getMessage());
+            return;
+        }
+    }
 
-    Binding defaultValue()
+    void afterRender(MarkupWriter writer)
     {
-        return createDefaultParameterBinding("value");
+        writer.end();
     }
 
-    /**
-     * Computes a default value for the "validate" parameter using
-     * {@link FieldValidatorDefaultSource}.
-     */
-    FieldValidator defaultValidate()
+    void beginRender(MarkupWriter writer)
     {
-        Class type = _resources.getBoundType("value");
-
-        if (type == null) return null;
+        writer.element("select", "name", getElementName(), "id", getClientId());
 
-        return _fieldValidatorDefaultSource.createDefaultValidator(
-                this,
-                _resources.getId(),
-                _resources.getContainerMessages(),
-                _locale,
-                type,
-                _resources.getAnnotationProvider("value"));
+        // Disabled, informals via mixins
     }
 
     @SuppressWarnings("unchecked")
@@ -148,115 +181,47 @@
         return null;
     }
 
-    void beginRender(MarkupWriter writer)
-    {
-        writer.element("select", "name", getElementName(), "id", getClientId());
-
-        // Disabled, informals via mixins
-    }
-
-    @BeforeRenderTemplate
-    void options(MarkupWriter writer)
-    {
-        if (_model.getOptionGroups() != null)
-        {
-            for (OptionGroupModel group : _model.getOptionGroups())
-            {
-                writeOptionGroup(writer, group);
-            }
-        }
-
-        writeOptions(writer, _model.getOptions());
-    }
-
-    private void writeOptionGroup(MarkupWriter writer, OptionGroupModel model)
-    {
-        writer.element("optgroup", "label", model.getLabel());
-
-        writeDisabled(writer, model.isDisabled());
-        writeAttributes(writer, model.getAttributes());
-
-        writeOptions(writer, model.getOptions());
-
-        writer.end(); // optgroup
-    }
-
-    @SuppressWarnings("unchecked")
-    private void writeOptions(MarkupWriter writer, List<OptionModel> optionModels)
+    /**
+     * Computes a default value for the "validate" parameter using
+     * {@link FieldValidatorDefaultSource}.
+     */
+    FieldValidator defaultValidate()
     {
-        if (optionModels == null) return;
-
-        for (OptionModel model : optionModels)
-        {
-            Object optionValue = model.getValue();
-
-            String clientValue = _encoder.toClient(optionValue);
-
-            writer.element("option", "value", clientValue);
-
-            if (isOptionValueSelected(optionValue)) writer.attributes("selected", "selected");
-
-            writeDisabled(writer, model.isDisabled());
-            writeAttributes(writer, model.getAttributes());
-
-            writer.write(model.getLabel());
+        Class type = _resources.getBoundType("value");
 
-            writer.end(); // option
-        }
-    }
+        if (type == null) return null;
 
-    boolean isOptionValueSelected(Object optionValue)
-    {
-        return _value == optionValue || (_value != null && _value.equals(optionValue));
+        return _fieldValidatorDefaultSource.createDefaultValidator(
+                this,
+                _resources.getId(),
+                _resources.getContainerMessages(),
+                _locale,
+                type,
+                _resources.getAnnotationProvider("value"));
     }
 
-    private void writeDisabled(MarkupWriter writer, boolean disabled)
+    Binding defaultValue()
     {
-        if (disabled) writer.attributes("disabled", "disabled");
+        return createDefaultParameterBinding("value");
     }
 
-    private void writeAttributes(MarkupWriter writer, Map<String, String> attributes)
+    @BeforeRenderTemplate
+    void options(MarkupWriter writer)
     {
-        if (attributes == null) return;
+        SelectModelVisitor renderer = new Renderer(writer);
 
-        for (Map.Entry<String, String> e : attributes.entrySet())
-            writer.attributes(e.getKey(), e.getValue());
+        _model.visit(renderer);
     }
 
-    void afterRender(MarkupWriter writer)
-    {
-        writer.end();
-    }
+    // For testing.
 
-    @Override
-    protected void processSubmission(FormSupport formSupport, String elementName)
+    void setModel(SelectModel model)
     {
-        String primaryKey = _request.getParameter(elementName);
-
-        Object selectedValue = _encoder.toValue(primaryKey);
-
-        try
-        {
-            _validate.validate(selectedValue);
-
-            _value = selectedValue;
-        }
-        catch (ValidationException ex)
-        {
-            _tracker.recordError(this, ex.getMessage());
-            return;
-        }
+        _model = model;
     }
 
-    // For testing.
-
     void setValue(Object value)
     {
         _value = value;
-    }
-
-    void setModel(SelectModel model)
-    {
-        _model = model;
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/SelectModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/SelectModelImpl.java?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/SelectModelImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/SelectModelImpl.java Fri May 25 08:07:05 2007
@@ -19,9 +19,9 @@
 
 import org.apache.tapestry.OptionGroupModel;
 import org.apache.tapestry.OptionModel;
-import org.apache.tapestry.SelectModel;
+import org.apache.tapestry.util.AbstractSelectModel;
 
-public final class SelectModelImpl implements SelectModel
+public final class SelectModelImpl extends AbstractSelectModel
 {
     private final List<OptionGroupModel> _optionGroups;
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java Fri May 25 08:07:05 2007
@@ -28,6 +28,7 @@
 import org.apache.tapestry.internal.model.MutableComponentModelImpl;
 import org.apache.tapestry.ioc.LogSource;
 import org.apache.tapestry.ioc.Resource;
+import org.apache.tapestry.ioc.internal.TransformationException;
 import org.apache.tapestry.ioc.internal.util.ClasspathResource;
 import org.apache.tapestry.model.ComponentModel;
 import org.apache.tapestry.model.MutableComponentModel;
@@ -75,8 +76,7 @@
 
         // Component classes must be public
 
-        if (!Modifier.isPublic(ctClass.getModifiers()))
-            return;
+        if (!Modifier.isPublic(ctClass.getModifiers())) return;
 
         try
         {
@@ -84,8 +84,7 @@
 
             CtConstructor ctor = ctClass.getConstructor("()V");
 
-            if (!Modifier.isPublic(ctor.getModifiers()))
-                return;
+            if (!Modifier.isPublic(ctor.getModifiers())) return;
         }
         catch (NotFoundException ex)
         {
@@ -128,12 +127,18 @@
                 : new InternalClassTransformationImpl(ctClass, parentTransformation, classLoader,
                         log, model);
 
-        _workerChain.transform(transformation, model);
+        try
+        {
+            _workerChain.transform(transformation, model);
 
-        transformation.finish();
+            transformation.finish();
+        }
+        catch (Throwable ex)
+        {
+            throw new TransformationException(transformation, ex);
+        }
 
-        if (log.isDebugEnabled())
-            log.debug("Finished class transformation: " + transformation);
+        if (log.isDebugEnabled()) log.debug("Finished class transformation: " + transformation);
 
         _nameToClassTransformation.put(classname, transformation);
         _nameToComponentModel.put(classname, model);
@@ -148,6 +153,13 @@
         if (ct == null)
             throw new RuntimeException(ServicesMessages.classNotTransformed(className));
 
-        return ct.createInstantiator(componentClass);
+        try
+        {
+            return ct.createInstantiator(componentClass);
+        }
+        catch (Throwable ex)
+        {
+            throw new TransformationException(ct, ex);
+        }
     }
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/util/SelectModelRenderer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/util/SelectModelRenderer.java?view=auto&rev=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/util/SelectModelRenderer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/util/SelectModelRenderer.java Fri May 25 08:07:05 2007
@@ -0,0 +1,91 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.util;
+
+import java.util.Map;
+
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.OptionGroupModel;
+import org.apache.tapestry.OptionModel;
+import org.apache.tapestry.SelectModelVisitor;
+import org.apache.tapestry.ValueEncoder;
+
+public class SelectModelRenderer implements SelectModelVisitor
+{
+    private final MarkupWriter _writer;
+
+    private final ValueEncoder _encoder;
+
+    public SelectModelRenderer(final MarkupWriter writer, ValueEncoder encoder)
+    {
+        _writer = writer;
+        _encoder = encoder;
+    }
+
+    public void beginOptionGroup(OptionGroupModel groupModel)
+    {
+        _writer.element("optgroup", "label", groupModel.getLabel());
+
+        writeDisabled(groupModel.isDisabled());
+        writeAttributes(groupModel.getAttributes());
+    }
+
+    public void endOptionGroup(OptionGroupModel groupModel)
+    {
+        _writer.end(); // select
+    }
+
+    @SuppressWarnings("unchecked")
+    public void option(OptionModel optionModel)
+    {
+        Object optionValue = optionModel.getValue();
+
+        String clientValue = _encoder.toClient(optionValue);
+
+        _writer.element("option", "value", clientValue);
+
+        if (isOptionSelected(optionModel)) _writer.attributes("selected", "selected");
+
+        writeDisabled(optionModel.isDisabled());
+        writeAttributes(optionModel.getAttributes());
+
+        _writer.write(optionModel.getLabel());
+
+        _writer.end();
+    }
+
+    private void writeDisabled(boolean disabled)
+    {
+        if (disabled) _writer.attributes("disabled", "disabled");
+    }
+
+    private void writeAttributes(Map<String, String> attributes)
+    {
+        if (attributes == null) return;
+
+        for (Map.Entry<String, String> e : attributes.entrySet())
+            _writer.attributes(e.getKey(), e.getValue());
+    }
+
+    /**
+     * If true, then the selected attribute will be written. This implementation always returns
+     * false.
+     */
+    protected boolean isOptionSelected(OptionModel optionModel)
+    {
+        return false;
+    }
+
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ioc/internal/TransformationException.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ioc/internal/TransformationException.java?view=auto&rev=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ioc/internal/TransformationException.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/ioc/internal/TransformationException.java Fri May 25 08:07:05 2007
@@ -0,0 +1,42 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.internal;
+
+import org.apache.tapestry.ioc.internal.util.TapestryException;
+import org.apache.tapestry.services.ClassTransformation;
+
+/**
+ * Exception thrown when there is a failure transforming a class, or instantiating a transformed
+ * class. The cause may be an Error. The goal is to get the {@link ClassTransformation} into the
+ * exception report page, properly formatted.
+ */
+public class TransformationException extends TapestryException
+{
+    private static final long serialVersionUID = -7312854113157232961L;
+
+    private final ClassTransformation _transformation;
+
+    public TransformationException(ClassTransformation transformation, Throwable cause)
+    {
+        super(cause.getMessage(), cause);
+
+        _transformation = transformation;
+    }
+
+    public ClassTransformation getTransformation()
+    {
+        return _transformation;
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java Fri May 25 08:07:05 2007
@@ -180,6 +180,7 @@
 import org.apache.tapestry.ioc.services.StrategyBuilder;
 import org.apache.tapestry.ioc.services.SymbolSource;
 import org.apache.tapestry.ioc.services.ThreadLocale;
+import org.apache.tapestry.ioc.services.TypeCoercer;
 import org.apache.tapestry.ioc.util.StrategyRegistry;
 import org.apache.tapestry.runtime.Component;
 import org.apache.tapestry.runtime.RenderCommand;
@@ -1322,7 +1323,9 @@
     public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration,
 
     @InjectService("LocationRenderer")
-    ObjectRenderer locationRenderer)
+    ObjectRenderer locationRenderer,
+
+    final TypeCoercer typeCoercer)
     {
         configuration.add(Object.class, new ObjectRenderer()
         {
@@ -1335,6 +1338,18 @@
         configuration.add(Request.class, new RequestRenderer());
 
         configuration.add(Location.class, locationRenderer);
+
+        ObjectRenderer preformatted = new ObjectRenderer<Object>()
+        {
+            public void render(Object object, MarkupWriter writer)
+            {
+                writer.element("pre");
+                writer.write(typeCoercer.coerce(object, String.class));
+                writer.end();
+            }
+        };
+
+        configuration.add(ClassTransformation.class, preformatted);
     }
 
     public void contributePageRenderInitializer(

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/AbstractSelectModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/AbstractSelectModel.java?view=auto&rev=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/AbstractSelectModel.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/AbstractSelectModel.java Fri May 25 08:07:05 2007
@@ -0,0 +1,58 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.util;
+
+import java.util.List;
+
+import org.apache.tapestry.OptionGroupModel;
+import org.apache.tapestry.OptionModel;
+import org.apache.tapestry.SelectModel;
+import org.apache.tapestry.SelectModelVisitor;
+
+/**
+ * Base class for {@link SelectModel} implementations, whose primary job is to provide the
+ * {@link #visit(SelectModelVisitor)} method.
+ */
+public abstract class AbstractSelectModel implements SelectModel
+{
+    public final void visit(SelectModelVisitor visitor)
+    {
+        List<OptionGroupModel> groups = getOptionGroups();
+
+        if (groups != null)
+        {
+            for (OptionGroupModel groupModel : groups)
+            {
+                visitor.beginOptionGroup(groupModel);
+
+                visitOptions(groupModel.getOptions(), visitor);
+
+                visitor.endOptionGroup(groupModel);
+            }
+        }
+
+        visitOptions(getOptions(), visitor);
+    }
+
+    private void visitOptions(List<OptionModel> options, SelectModelVisitor vistor)
+    {
+        if (options != null)
+        {
+            for (OptionModel optionModel : options)
+                vistor.option(optionModel);
+        }
+    }
+
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/EnumSelectModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/EnumSelectModel.java?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/EnumSelectModel.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/util/EnumSelectModel.java Fri May 25 08:07:05 2007
@@ -15,17 +15,16 @@
 package org.apache.tapestry.util;
 
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
 
 import java.io.Serializable;
 import java.util.List;
 
 import org.apache.tapestry.OptionGroupModel;
 import org.apache.tapestry.OptionModel;
-import org.apache.tapestry.SelectModel;
 import org.apache.tapestry.internal.OptionModelImpl;
 import org.apache.tapestry.internal.TapestryInternalUtils;
 import org.apache.tapestry.ioc.Messages;
-import org.apache.tapestry.ioc.internal.util.Defense;
 
 /**
  * A basic select model for a particular Enum type. The labels for each Enum are drawn from the Enum
@@ -37,7 +36,7 @@
  * <li>As a user-presentable version of the name, i.e., "Local Variable".
  * </ul>
  */
-public final class EnumSelectModel implements SelectModel, Serializable
+public final class EnumSelectModel extends AbstractSelectModel implements Serializable
 {
     private static final long serialVersionUID = -3590412082766899684L;
 
@@ -50,8 +49,8 @@
 
     public <T extends Enum> EnumSelectModel(Class<T> enumClass, Messages messages, T[] values)
     {
-        Defense.notNull(enumClass, "enumClass");
-        Defense.notNull(messages, "messages");
+        notNull(enumClass, "enumClass");
+        notNull(messages, "messages");
 
         String prefix = TapestryInternalUtils.lastTerm(enumClass.getName());
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Palette.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Palette.html?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Palette.html (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/Palette.html Fri May 25 08:07:05 2007
@@ -10,16 +10,18 @@
     <button id="${clientId}:deselect" disabled="disabled">
       <img src="${deselect}" alt="${message:deselect-label}"/>
     </button>
-    <button id="${clientId}:up" disabled="disabled">
-      <img src="${moveUp}" alt="${message:up-label}"/>
-    </button>
-    <button id="${clientId}:down" disabled="disabled">
-      <img src="${moveDown}" alt="${message:down-label}"/>
-    </button>
+    <t:if test="reorder">
+      <button id="${clientId}:up" disabled="disabled">
+        <img src="${moveUp}" alt="${message:up-label}"/>
+      </button>
+      <button id="${clientId}:down" disabled="disabled">
+        <img src="${moveDown}" alt="${message:down-label}"/>
+      </button>
+    </t:if>
   </div>
   <div class="t-palette-selected">
     <div class="t-palette-title">${message:selected-label}</div>
     <t:delegate to="selectedRenderer"/>
   </div>
-  <div class="t-palette-spacer"/>    
+  <div class="t-palette-spacer"/>
 </div>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/deselect.png
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/deselect.png?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
Binary files - no diff available.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/move_down.png
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/move_down.png?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
Binary files - no diff available.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/move_up.png
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/move_up.png?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
Binary files - no diff available.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/palette.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/palette.js?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/palette.js (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/palette.js Fri May 25 08:07:05 2007
@@ -2,10 +2,10 @@
 
 Tapestry.Palette.prototype = {
 
-  // TODO: Make Move Up/Move Down optional (via a subclass?)
-  initialize : function(id) {
-    // The two selects:
-    
+  initialize : function(id, reorder) {
+    this.reorder = reorder;
+    // The SELECT elements
+
 	  this.avail = $(id + ":avail");
 	  this.selected = $(id);
 	  
@@ -14,11 +14,14 @@
 	  // Seperator used for values in the hidden field.
 	  this.sep = ";";
 	  
-	  // The four BUTTON elements:
+	  // The BUTTON elements
 	  this.select = $(id + ":select");
 	  this.deselect = $(id + ":deselect");
-	  this.up = $(id + ":up");
-	  this.down = $(id + ":down");
+	  
+	  if (this.reorder) {
+	    this.up = $(id + ":up");
+	    this.down = $(id + ":down");
+	  }
 	  
 	  this.bindEvents();   
   },  
@@ -31,13 +34,15 @@
     var selectClicked = this.selectClicked.bindAsEventListener(this);
     Event.observe(this.select, "click", selectClicked);
     Event.observe(this.avail, "dblclick", selectClicked);
-          
+    
     var deselectClicked = this.deselectClicked.bindAsEventListener(this);      
     Event.observe(this.deselect, "click", deselectClicked);
     Event.observe(this.selected, "dblclick", deselectClicked);
-    
-    Event.observe(this.up, "click", this.moveUpClicked.bindAsEventListener(this));
-    Event.observe(this.down, "click", this.moveDownClicked.bindAsEventListener(this));
+        
+    if (this.reorder) {          
+      Event.observe(this.up, "click", this.moveUpClicked.bindAsEventListener(this));
+      Event.observe(this.down, "click", this.moveDownClicked.bindAsEventListener(this));
+    }
   },
   
   updateButtons: function() {
@@ -46,8 +51,11 @@
     var nothingSelected = this.selected.selectedIndex < 0;
     
     this.deselect.disabled = nothingSelected;
-    this.up.disabled = nothingSelected || this.allSelectionsAtTop();
-    this.down.disabled = nothingSelected || this.allSelectionsAtBottom();
+    
+    if (this.reorder) {
+      this.up.disabled = nothingSelected || this.allSelectionsAtTop();
+      this.down.disabled = nothingSelected || this.allSelectionsAtBottom();
+    }
   },  
   
   indexOfLastSelection : function(select) {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/select.png
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry/corelib/components/select.png?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
Binary files - no diff available.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/PaletteDemo.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/PaletteDemo.html?view=diff&rev=541682&r1=541681&r2=541682
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/PaletteDemo.html (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/WEB-INF/PaletteDemo.html Fri May 25 08:07:05 2007
@@ -9,7 +9,7 @@
 
   <t:form>
     <div class="t-beaneditor">
-      <t:palette t:id="languages" model="languageModel" encoder="languageEncoder"/>
+      <t:palette t:id="languages" model="languageModel" reorder="true" encoder="languageEncoder"/>
       <br/>
       <input type="submit"/>
     </div>