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 java.util.HashSet; 018 import java.util.Set; 019 020 import org.apache.hivemind.ApplicationRuntimeException; 021 import org.apache.tapestry.IMarkupWriter; 022 import org.apache.tapestry.IRequestCycle; 023 import org.apache.tapestry.Tapestry; 024 import 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 */ 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 if (cycle.getAttribute(ATTRIBUTE_NAME) != null) 096 throw new ApplicationRuntimeException(Tapestry.getMessage("Select.may-not-nest"), this, 097 null, null); 098 099 cycle.setAttribute(ATTRIBUTE_NAME, this); 100 101 _rendering = true; 102 _nextOptionId = 0; 103 } 104 105 /** 106 * @see org.apache.tapestry.AbstractComponent#cleanupAfterRender(org.apache.tapestry.IRequestCycle) 107 */ 108 protected void cleanupAfterRender(IRequestCycle cycle) 109 { 110 _rendering = false; 111 _selections = null; 112 113 cycle.removeAttribute(ATTRIBUTE_NAME); 114 } 115 116 /** 117 * @see org.apache.tapestry.form.AbstractFormComponent#renderFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle) 118 */ 119 protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) 120 { 121 _rewinding = false; 122 123 renderDelegatePrefix(writer, cycle); 124 125 writer.begin("select"); 126 127 writer.attribute("name", getName()); 128 129 if (isMultiple()) 130 writer.attribute("multiple", "multiple"); 131 132 if (isDisabled()) 133 writer.attribute("disabled", "disabled"); 134 135 renderIdAttribute(writer, cycle); 136 137 renderDelegateAttributes(writer, cycle); 138 139 getValidatableFieldSupport().renderContributions(this, writer, cycle); 140 141 renderInformalParameters(writer, cycle); 142 143 renderBody(writer, cycle); 144 145 writer.end(); 146 147 renderDelegateSuffix(writer, cycle); 148 } 149 150 /** 151 * @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle) 152 */ 153 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) 154 { 155 _selections = null; 156 _rewinding = true; 157 158 String[] parameters = cycle.getParameters(getName()); 159 160 try 161 { 162 if (parameters != null) 163 { 164 int length = parameters.length; 165 166 _selections = new HashSet((length > 30) ? 101 : 7); 167 168 for (int i = 0; i < length; i++) 169 _selections.add(parameters[i]); 170 } 171 172 renderBody(writer, cycle); 173 174 // This is atypical validation - since this component does not explicitly bind to an object 175 getValidatableFieldSupport().validate(this, writer, cycle, parameters); 176 } 177 catch (ValidatorException e) 178 { 179 getForm().getDelegate().record(e); 180 } 181 } 182 183 /** 184 * Injected. 185 */ 186 public abstract ValidatableFieldSupport getValidatableFieldSupport(); 187 188 /** 189 * @see org.apache.tapestry.form.AbstractFormComponent#isRequired() 190 */ 191 public boolean isRequired() 192 { 193 return getValidatableFieldSupport().isRequired(this); 194 } 195 }