001: /*
002: * Copyright 2004, 2005, 2006 Odysseus Software GmbH
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package de.odysseus.calyxo.forms;
017:
018: import java.util.HashMap;
019: import java.util.Iterator;
020: import java.util.Locale;
021:
022: import javax.servlet.http.HttpServletRequest;
023: import javax.servlet.http.HttpSession;
024: import javax.servlet.http.HttpSessionActivationListener;
025: import javax.servlet.http.HttpSessionEvent;
026: import javax.servlet.jsp.PageContext;
027:
028: import de.odysseus.calyxo.base.I18nSupport;
029: import de.odysseus.calyxo.base.ModuleContext;
030: import de.odysseus.calyxo.base.ModuleSupport;
031: import de.odysseus.calyxo.base.conf.ConfigException;
032: import de.odysseus.calyxo.forms.conf.FieldConfig;
033: import de.odysseus.calyxo.forms.conf.FormConfig;
034: import de.odysseus.calyxo.forms.conf.FormsRootConfig;
035:
036: /**
037: *
038: * @author Christoph Beck
039: */
040: public abstract class FormsSupport {
041: /**
042: * Module scope key where the support instance is stored.
043: */
044: public static final String FORMS_SUPPORT_KEY = "de.odysseus.calyxo.forms.support";
045:
046: /**
047: * Session scope key prefix used to cache form validators.
048: * This prefix will be suffixed by <code>"/" + context.getName()</code>.
049: */
050: static final String FORMS_KEY_PREFIX = "de.odysseus.calyxo.forms.forms";
051:
052: /**
053: * Request scope key used to save form validator result.
054: */
055: public static final String FORM_RESULT_KEY = "de.odysseus.calyxo.forms.result";
056:
057: private class FormsMap extends HashMap implements
058: HttpSessionActivationListener {
059: public void sessionDidActivate(HttpSessionEvent arg0) {
060: }
061:
062: public void sessionWillPassivate(HttpSessionEvent event) {
063: event.getSession().removeAttribute(formsKey);
064: }
065: };
066:
067: /**
068: * Lookup forms support for module determined by specified request
069: */
070: public static FormsSupport getInstance(HttpServletRequest request) {
071: ModuleContext context = ModuleSupport.getInstance(request)
072: .getModuleContext(request);
073: return (FormsSupport) context.getAttribute(FORMS_SUPPORT_KEY);
074: }
075:
076: /**
077: * Convenience method.
078: * @see #getInstance(HttpServletRequest)
079: */
080: public static final FormsSupport getInstance(PageContext pageContext) {
081: return getInstance((HttpServletRequest) pageContext
082: .getRequest());
083: }
084:
085: /**
086: * Lookup forms support for specified module
087: */
088: public static FormsSupport getInstance(ModuleContext context) {
089: return (FormsSupport) context.getAttribute(FORMS_SUPPORT_KEY);
090: }
091:
092: private ModuleContext context;
093: private FormsRootConfig config;
094: private String formsKey;
095: private I18nSupport i18n;
096: private FormFactory factory;
097: private HashMap formNames = new HashMap();
098:
099: /**
100: * Initialze by setting the module context and configuration element.
101: */
102: protected void init(ModuleContext context, FormsRootConfig config,
103: FormFactory factory) throws ConfigException {
104: this .context = context;
105: this .config = config;
106: this .factory = factory;
107:
108: formsKey = FORMS_KEY_PREFIX + "/" + context.getName();
109: i18n = I18nSupport.getInstance(context);
110: }
111:
112: /**
113: * Get the module context we belong to
114: */
115: protected ModuleContext getContext() {
116: return context;
117: }
118:
119: /**
120: * Get our configuration
121: */
122: protected FormsRootConfig getConfig() {
123: return config;
124: }
125:
126: /**
127: * Get the form name for the specified action path identifier.
128: * This method delegates to {@link #lookupFormName(String)}
129: * and caches results in a map.
130: * @param action (module-relative) action path
131: * @return form name
132: */
133: protected final String getFormName(String action) {
134: String result = null;
135: if (formNames.containsKey(action)) {
136: result = (String) formNames.get(action);
137: } else {
138: formNames.put(action, result = lookupFormName(action));
139: }
140: return result;
141: }
142:
143: /**
144: * Lookup the form name for the specified action path identifier.
145: * To be implemented by subclasses.
146: * @param action (module-relative) action path
147: * @return form name
148: */
149: protected abstract String lookupFormName(String action);
150:
151: /**
152: * Lookup form data for the specified action path identifier.
153: * @param request the request we're in
154: * @param action (module-relative) action path
155: * @param create lazily create form data instance if there's none
156: * @return form data
157: * @throws Exception
158: */
159: public abstract FormData getFormData(HttpServletRequest request,
160: String action, boolean create) throws Exception;
161:
162: /**
163: * Remove existing form data for the specified action path identifier.
164: * @param request the request we're in
165: * @param action (module-relative) action path
166: * @throws Exception
167: */
168: public abstract void removeFormData(HttpServletRequest request,
169: String action) throws Exception;
170:
171: /**
172: * Answer form for given action and locale.
173: * <p/>
174: * A map holding <code>Form</code> instances by action path is
175: * stored per session. The map is created lazily by this method.
176: * <code>Form</code> instances are created lazily by this method
177: * if necessary (that is, if there's no instance for the given action,
178: * or if the locale of the stored instance does not match the desired locale).
179: * <p/>
180: * This means, for a given action, there's one <code>Form</code> instance
181: * per session.
182: * So, there's a need for synchronization to prevent concurrent use
183: * of the same instance by parallel requests.
184: *
185: * @param request the request we're in
186: * @param locale desired locale
187: * @param action (module-relative) action path
188: * @return form validator
189: */
190: public Form getForm(HttpServletRequest request, Locale locale,
191: String action) {
192: HttpSession session = request.getSession();
193: FormsMap map = (FormsMap) session.getAttribute(formsKey);
194: if (map == null) {
195: session.setAttribute(formsKey, map = new FormsMap());
196: }
197: Form form = (Form) map.get(action);
198: if (form == null || !locale.equals(form.getLocale())) {
199: String name = getFormName(action);
200: if (name == null) {
201: return null;
202: }
203: FormConfig formConfig = config.findFormConfig(name, locale);
204: if (formConfig == null)
205: return null;
206: form = factory.createForm(formConfig, locale);
207: map.put(action, form);
208: }
209: return form;
210: }
211:
212: /**
213: * Convenience method.
214: * Answer form for given form name. Use <code>I18nSupport</code>
215: * to lookup the locale to use.
216: *
217: * @see FormsSupport#getForm(HttpServletRequest, Locale, String)
218: */
219: public Form getForm(HttpServletRequest request, String action) {
220: return getForm(request, i18n.getLocale(request), action);
221: }
222:
223: /**
224: * Get the form validation result from the given request.
225: * If the validation result corresponds to the form definition for the
226: * specified action, answer the result. Otherwise, answer <code>null</code>.
227: * @param request the request we're in
228: * @param action (module-relative) action path
229: * @return for result
230: */
231: public FormResult getFormResult(HttpServletRequest request,
232: String action) {
233: String name = getFormName(action);
234: if (name == null) {
235: return null;
236: }
237: FormResult result = (FormResult) request
238: .getAttribute(FORM_RESULT_KEY);
239: if (result != null
240: && result.getFormConfig().getName().equals(name)) {
241: return result;
242: }
243: return null;
244: }
245:
246: public FormProperties getFormProperties(
247: final HttpServletRequest request, final String action) {
248: final FormResult result = getFormResult(request, action);
249: if (result == null) {
250: return null;
251: }
252: return new FormProperties() {
253: /* (non-Javadoc)
254: * @see de.odysseus.calyxo.forms.FormData#_getProperty(java.lang.String)
255: */
256: public Object getProperty(String name) {
257: FieldConfig field = result.getFormConfig()
258: .findFieldConfig(name);
259: if (field == null) {
260: return null;
261: }
262: FormInputResult inputResult = result
263: .getFormInputResult(field.getName());
264: if (!inputResult.getProperty().equals(name)) {
265: return null;
266: }
267: return inputResult.getValue();
268: }
269:
270: public void commit() throws Exception {
271: if (!result.isValid()) {
272: throw new IllegalStateException(
273: "Cannot populate from invalid result");
274: }
275: FormData data = getFormData(request, action, true);
276: if (data != null) {
277: Iterator inputs = result.getFormInputResults();
278: while (inputs.hasNext()) {
279: ((FormInputResult) inputs.next())
280: .populate(data);
281: }
282: }
283: }
284: };
285: }
286: }
|