001// Copyright 2004, 2005 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry.form; 016 017import java.util.HashSet; 018import java.util.Set; 019 020import org.apache.hivemind.ApplicationRuntimeException; 021import org.apache.tapestry.IMarkupWriter; 022import org.apache.tapestry.IRequestCycle; 023import org.apache.tapestry.Tapestry; 024import org.apache.tapestry.valid.ValidatorException; 025 026/** 027 * Implements a component that manages an HTML <select> form element. The most common 028 * situation, using a <select> to set a specific property of some object, is best handled 029 * using a {@link PropertySelection}component. [ <a 030 * href="../../../../../ComponentReference/Select.html">Component Reference </a>] 031 * <p> 032 * Otherwise, this component is very similar to {@link RadioGroup}. 033 * <p> 034 * As of 4.0, this component can be validated. 035 * 036 * @author Howard Lewis Ship 037 * @author Paul Ferraro 038 */ 039public abstract class Select extends AbstractFormComponent implements ValidatableField 040{ 041 private boolean _rewinding; 042 043 private boolean _rendering; 044 045 private Set _selections; 046 047 private int _nextOptionId; 048 049 /** 050 * Used by the <code>Select</code> to record itself as a {@link IRequestCycle}attribute, so 051 * that the {@link Option}components it wraps can have access to it. 052 */ 053 054 private final static String ATTRIBUTE_NAME = "org.apache.tapestry.active.Select"; 055 056 public static Select get(IRequestCycle cycle) 057 { 058 return (Select) cycle.getAttribute(ATTRIBUTE_NAME); 059 } 060 061 public abstract boolean isMultiple(); 062 063 public boolean isRewinding() 064 { 065 if (!_rendering) 066 throw Tapestry.createRenderOnlyPropertyException(this, "rewinding"); 067 068 return _rewinding; 069 } 070 071 public String getNextOptionId() 072 { 073 if (!_rendering) 074 throw Tapestry.createRenderOnlyPropertyException(this, "nextOptionId"); 075 076 // Return it as a hex value. 077 078 return Integer.toString(_nextOptionId++); 079 } 080 081 public boolean isSelected(String value) 082 { 083 if (_selections == null) 084 return false; 085 086 return _selections.contains(value); 087 } 088 089 /** 090 * @see org.apache.tapestry.AbstractComponent#prepareForRender(org.apache.tapestry.IRequestCycle) 091 */ 092 protected void prepareForRender(IRequestCycle cycle) 093 { 094 if (cycle.getAttribute(ATTRIBUTE_NAME) != null) 095 throw new ApplicationRuntimeException(Tapestry.getMessage("Select.may-not-nest"), this, 096 null, null); 097 098 cycle.setAttribute(ATTRIBUTE_NAME, this); 099 100 _rendering = true; 101 _nextOptionId = 0; 102 } 103 104 /** 105 * @see org.apache.tapestry.AbstractComponent#cleanupAfterRender(org.apache.tapestry.IRequestCycle) 106 */ 107 protected void cleanupAfterRender(IRequestCycle cycle) 108 { 109 _rendering = false; 110 _selections = null; 111 112 cycle.removeAttribute(ATTRIBUTE_NAME); 113 } 114 115 /** 116 * @see org.apache.tapestry.form.AbstractFormComponent#renderFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle) 117 */ 118 protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) 119 { 120 _rewinding = false; 121 122 renderDelegatePrefix(writer, cycle); 123 124 writer.begin("select"); 125 126 writer.attribute("name", getName()); 127 128 if (isMultiple()) 129 writer.attribute("multiple", "multiple"); 130 131 if (isDisabled()) 132 writer.attribute("disabled", "disabled"); 133 134 renderIdAttribute(writer, cycle); 135 136 renderDelegateAttributes(writer, cycle); 137 138 getValidatableFieldSupport().renderContributions(this, writer, cycle); 139 140 renderInformalParameters(writer, cycle); 141 142 renderBody(writer, cycle); 143 144 writer.end(); 145 146 renderDelegateSuffix(writer, cycle); 147 } 148 149 /** 150 * @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle) 151 */ 152 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) 153 { 154 _selections = null; 155 _rewinding = true; 156 157 String[] parameters = cycle.getParameters(getName()); 158 159 try 160 { 161 if (parameters != null) 162 { 163 int length = parameters.length; 164 165 _selections = new HashSet((length > 30) ? 101 : 7); 166 167 for (int i = 0; i < length; i++) 168 _selections.add(parameters[i]); 169 } 170 171 renderBody(writer, cycle); 172 173 // This is atypical validation - since this component does not explicitly bind to an object 174 getValidatableFieldSupport().validate(this, writer, cycle, parameters); 175 } 176 catch (ValidatorException e) 177 { 178 getForm().getDelegate().record(e); 179 } 180 } 181 182 /** 183 * Injected. 184 */ 185 public abstract ValidatableFieldSupport getValidatableFieldSupport(); 186 187 /** 188 * @see org.apache.tapestry.form.AbstractFormComponent#isRequired() 189 */ 190 public boolean isRequired() 191 { 192 return getValidatableFieldSupport().isRequired(this); 193 } 194}