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 015 package org.apache.tapestry.form; 016 017 import org.apache.hivemind.ApplicationRuntimeException; 018 import org.apache.tapestry.IMarkupWriter; 019 import org.apache.tapestry.IRequestCycle; 020 import org.apache.tapestry.Tapestry; 021 import org.apache.tapestry.valid.ValidatorException; 022 023 import java.util.HashSet; 024 import java.util.Set; 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 */ 039 public abstract class Select extends AbstractFormComponent implements ValidatableField 040 { 041 042 /** 043 * Used by the <code>Select</code> to record itself as a {@link IRequestCycle}attribute, so 044 * that the {@link Option}components it wraps can have access to it. 045 */ 046 047 private static final String ATTRIBUTE_NAME = "org.apache.tapestry.active.Select"; 048 049 private boolean _rewinding; 050 051 private boolean _rendering; 052 053 private Set _selections; 054 055 private int _nextOptionId; 056 057 public static Select get(IRequestCycle cycle) 058 { 059 return (Select) cycle.getAttribute(ATTRIBUTE_NAME); 060 } 061 062 public abstract boolean isMultiple(); 063 064 public boolean isRewinding() 065 { 066 if (!_rendering) 067 throw Tapestry.createRenderOnlyPropertyException(this, "rewinding"); 068 069 return _rewinding; 070 } 071 072 public String getNextOptionId() 073 { 074 if (!_rendering) 075 throw Tapestry.createRenderOnlyPropertyException(this, "nextOptionId"); 076 077 // Return it as a hex value. 078 079 return Integer.toString(_nextOptionId++); 080 } 081 082 public boolean isSelected(String value) 083 { 084 if (_selections == null) 085 return false; 086 087 return _selections.contains(value); 088 } 089 090 /** 091 * @see org.apache.tapestry.AbstractComponent#prepareForRender(org.apache.tapestry.IRequestCycle) 092 */ 093 protected void prepareForRender(IRequestCycle cycle) 094 { 095 super.prepareForRender(cycle); 096 097 if (cycle.getAttribute(ATTRIBUTE_NAME) != null) 098 throw new ApplicationRuntimeException(Tapestry.getMessage("Select.may-not-nest"), this, null, null); 099 100 cycle.setAttribute(ATTRIBUTE_NAME, this); 101 102 _rendering = true; 103 _nextOptionId = 0; 104 } 105 106 /** 107 * @see org.apache.tapestry.AbstractComponent#cleanupAfterRender(org.apache.tapestry.IRequestCycle) 108 */ 109 protected void cleanupAfterRender(IRequestCycle cycle) 110 { 111 super.cleanupAfterRender(cycle); 112 113 _rendering = false; 114 _selections = null; 115 116 cycle.removeAttribute(ATTRIBUTE_NAME); 117 } 118 119 /** 120 * @see org.apache.tapestry.form.AbstractFormComponent#renderFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle) 121 */ 122 protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) 123 { 124 _rewinding = false; 125 126 renderDelegatePrefix(writer, cycle); 127 128 writer.begin("select"); 129 130 writer.attribute("name", getName()); 131 132 if (isMultiple()) 133 writer.attribute("multiple", "multiple"); 134 135 if (isDisabled()) 136 writer.attribute("disabled", "disabled"); 137 138 renderIdAttribute(writer, cycle); 139 140 renderDelegateAttributes(writer, cycle); 141 142 getValidatableFieldSupport().renderContributions(this, writer, cycle); 143 144 renderInformalParameters(writer, cycle); 145 146 renderBody(writer, cycle); 147 148 writer.end(); 149 150 renderDelegateSuffix(writer, cycle); 151 } 152 153 /** 154 * @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle) 155 */ 156 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) 157 { 158 _selections = null; 159 _rewinding = true; 160 161 String[] parameters = cycle.getParameters(getName()); 162 163 try 164 { 165 if (parameters != null) 166 { 167 int length = parameters.length; 168 169 _selections = new HashSet((length > 30) ? 101 : 7); 170 171 for (int i = 0; i < length; i++) 172 _selections.add(parameters[i]); 173 } 174 175 renderBody(writer, cycle); 176 177 // This is atypical validation - since this component does not explicitly bind to an object 178 getValidatableFieldSupport().validate(this, writer, cycle, parameters); 179 } 180 catch (ValidatorException e) 181 { 182 getForm().getDelegate().record(e); 183 } 184 } 185 186 /** 187 * Injected. 188 */ 189 public abstract ValidatableFieldSupport getValidatableFieldSupport(); 190 191 /** 192 * @see org.apache.tapestry.form.AbstractFormComponent#isRequired() 193 */ 194 public boolean isRequired() 195 { 196 return getValidatableFieldSupport().isRequired(this); 197 } 198 }