001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.forms.transformation;
018:
019: import org.apache.avalon.framework.parameters.Parameters;
020:
021: import org.apache.cocoon.components.flow.FlowHelper;
022: import org.apache.cocoon.components.flow.WebContinuation;
023: import org.apache.cocoon.environment.ObjectModelHelper;
024: import org.apache.cocoon.environment.Request;
025: import org.apache.cocoon.environment.Session;
026: import org.apache.cocoon.forms.formmodel.Form;
027: import org.apache.cocoon.i18n.I18nUtils;
028: import org.apache.cocoon.util.Deprecation;
029:
030: import org.apache.commons.jxpath.JXPathContext;
031: import org.apache.commons.jxpath.JXPathException;
032: import org.apache.commons.jxpath.Variables;
033: import org.xml.sax.Attributes;
034: import org.xml.sax.SAXException;
035: import org.xml.sax.helpers.AttributesImpl;
036:
037: import java.io.IOException;
038: import java.io.StringReader;
039: import java.util.ArrayList;
040: import java.util.HashMap;
041: import java.util.List;
042: import java.util.Locale;
043: import java.util.Map;
044:
045: /**
046: * @version $Id: FormsPipelineConfig.java 449149 2006-09-23 03:58:05Z crossley $
047: */
048: public class FormsPipelineConfig {
049:
050: /**
051: * Default key under which the Cocoon Forms form instance is stored in the JXPath context.
052: */
053: public static final String CFORMSKEY = "CocoonFormsInstance";
054:
055: /**
056: * Name of the request attribute under which the Cocoon Form is stored (optional). */
057: private final String attributeName;
058:
059: /**
060: * Pointer to the current request object.
061: */
062: private final Request request;
063:
064: /**
065: * Initialized jxpathcontext to evaluate passed expressions with.
066: */
067: private final JXPathContext jxpathContext;
068:
069: /**
070: * Containts locale specified as a parameter to the transformer, if any.
071: */
072: private final Locale localeParameter;
073:
074: /**
075: * The locale currently used by the transformer.
076: */
077: private Locale locale;
078:
079: /**
080: * Value for the action attribute of the form.
081: */
082: private String formAction;
083:
084: /**
085: * Value for the method attribute of the form.
086: */
087: private String formMethod;
088:
089: private FormsPipelineConfig(JXPathContext jxpc, Request req,
090: Locale localeParam, String attName,
091: String actionExpression, String method) {
092: this .attributeName = attName;
093: this .request = req;
094: this .jxpathContext = jxpc;
095: this .localeParameter = localeParam;
096: this .formAction = translateText(actionExpression);
097: this .formMethod = method;
098: }
099:
100: /**
101: * Creates and initializes a FormsPipelineConfig object based on the passed
102: * arguments of the setup() of the specific Pipeline-component.
103: *
104: * @param objectModel the objectmodel as passed in the setup()
105: * @param parameters the parameters as passed in the setup()
106: * @return an instance of FormsPipelineConfig initialized according to the
107: * settings in the sitemap.
108: */
109: public static FormsPipelineConfig createConfig(Map objectModel,
110: Parameters parameters) {
111: // create and set the jxpathContext...
112: Object flowContext = FlowHelper.getContextObject(objectModel);
113: WebContinuation wk = FlowHelper.getWebContinuation(objectModel);
114: JXPathContext jxpc = JXPathContext.newContext(flowContext);
115: // We manually create a cocoon object here to provide the same way
116: // of accessing things as in the jxtg
117: // as soon as we have our unified om, we should use that
118: Request request = ObjectModelHelper.getRequest(objectModel);
119: Session session = request.getSession(false);
120: final Map cocoonOM = new HashMap();
121: cocoonOM.put("continuation", wk);
122: cocoonOM.put("request", request);
123: if (session != null) {
124: cocoonOM.put("session", session);
125: }
126: cocoonOM.put("parameters", parameters);
127:
128: FormsVariables vars = new FormsVariables();
129: vars.declareVariable("cocoon", cocoonOM);
130: // These four are deprecated!
131: vars.declareVariable("continuation", wk);
132: vars.declareVariable("request", request);
133: vars.declareVariable("session", session);
134: vars.declareVariable("parameters", parameters);
135: vars.addDeprecatedVariable("continuation");
136: vars.addDeprecatedVariable("request");
137: vars.addDeprecatedVariable("session");
138: vars.addDeprecatedVariable("parameters");
139: jxpc.setVariables(vars);
140:
141: Locale localeParameter = null;
142: String localeStr = parameters.getParameter("locale", null);
143: if (localeStr != null) {
144: localeParameter = I18nUtils.parseLocale(localeStr);
145: }
146:
147: String attributeName = parameters.getParameter(
148: "attribute-name", null);
149: String actionExpression = parameters.getParameter(
150: "form-action", null);
151: String formMethod = parameters
152: .getParameter("form-method", null);
153: //TODO (20031223 mpo)think about adding form-encoding for the Generator.
154: // Note generator will also need some text to go on the submit-button?
155: // Alternative to adding more here is to apply xinclude ?
156:
157: return new FormsPipelineConfig(jxpc, request, localeParameter,
158: attributeName, actionExpression, formMethod);
159: }
160:
161: /**
162: * Overloads {@link #findForm(String)} by setting the jxpath-expression to null
163: */
164: public Form findForm() throws SAXException {
165: return this .findForm(null);
166: }
167:
168: /**
169: * Finds the form from the current request-context based on the settings of
170: * this configuration object. The fall-back search-procedure is as follows:
171: * <ol><li>Use the provided jxpathExpression (if not null)</li>
172: * <li>Use the setting of the 'attribute-name' parameter on the request</li>
173: * <li>Obtain the form from it's default location in the flow context</li>
174: * </ol>
175: *
176: * @param jxpathExpression that should be pointing to the form
177: * @return the found form if found
178: * @throws SAXException in any of the following cases:
179: * <ul><li>The provided jxpathExpression (if not null) not point to a
180: * {@link Form} instance.</li>
181: * <li>The request is not holding a {@link Form} instance under the key
182: * specified by 'attribute-name' (if specified)</li>
183: * <li>Both jxpathExpression and 'attribute-name' were not specified AND
184: * also the default location was not holding a valid {@link Form} instance.</li>
185: * </ol>
186: */
187: public Form findForm(String jxpathExpression) throws SAXException {
188: Object form = null;
189: if (jxpathExpression != null) {
190: form = this .jxpathContext.getValue(jxpathExpression);
191: if (form == null) {
192: throw new SAXException(
193: "No Cocoon Form found at location \""
194: + jxpathExpression + "\".");
195: } else if (!(form instanceof Form)) {
196: throw new SAXException(
197: "Object returned by expression \""
198: + jxpathExpression
199: + "\" is not a Cocoon Form.");
200: }
201: } else if (this .attributeName != null) { // then see if an attribute-name was specified
202: form = this .request.getAttribute(this .attributeName);
203: if (form == null) {
204: throw new SAXException(
205: "No Cocoon Form found in request attribute with name \""
206: + this .attributeName + "\"");
207: } else if (!(form instanceof Form)) {
208: throw new SAXException(
209: "Object found in request (attribute = '"
210: + this .attributeName
211: + "') is not a Cocoon Form.");
212: }
213: } else { // and then see if we got a form from the flow
214: jxpathExpression = "/" + FormsPipelineConfig.CFORMSKEY;
215: try {
216: form = this .jxpathContext.getValue(jxpathExpression);
217: } catch (JXPathException e) { /* do nothing */
218: }
219: if (form == null) {
220: throw new SAXException("No Cocoon Form found.");
221: }
222: }
223: return (Form) form;
224: }
225:
226: /**
227: * Replaces JXPath expressions embedded inside #{ and } by their value.
228: * This will parse the passed String looking for #{} occurences and then
229: * uses the {@link #evaluateExpression(String)} to evaluate the found expression.
230: *
231: * @return the original String with it's #{}-parts replaced by the evaulated results.
232: */
233: public String translateText(String original) {
234: if (original == null) {
235: return null;
236: }
237:
238: StringBuffer expression;
239: StringBuffer translated = new StringBuffer();
240: StringReader in = new StringReader(original);
241: int chr;
242: try {
243: while ((chr = in.read()) != -1) {
244: char c = (char) chr;
245: if (c == '#') {
246: chr = in.read();
247: if (chr != -1) {
248: c = (char) chr;
249: if (c == '{') {
250: expression = new StringBuffer();
251: boolean more = true;
252: while (more) {
253: more = false;
254: if ((chr = in.read()) != -1) {
255: c = (char) chr;
256: if (c != '}') {
257: expression.append(c);
258: more = true;
259: } else {
260: translated
261: .append(evaluateExpression(
262: expression
263: .toString())
264: .toString());
265: }
266: } else {
267: translated.append('#').append('{')
268: .append(expression);
269: }
270: }
271: }
272: } else {
273: translated.append((char) chr);
274: }
275: } else {
276: translated.append(c);
277: }
278: }
279: } catch (IOException ignored) {
280: ignored.printStackTrace();
281: }
282: return translated.toString();
283: }
284:
285: /**
286: * Evaluates the passed xpath expression using the internal jxpath context
287: * holding the declared variables:
288: * <ol><li>continuation: as made available by flowscript</li>
289: * <li>request: as present in the cocoon processing environment</li>
290: * <li>session: as present in the cocoon processing environment</li>
291: * <li>parameters: as present in the cocoon sitemap node of the pipeline component</li></ol>
292: *
293: * @param expression
294: * @return the object-value resulting the expression evaluation.
295: */
296: public Object evaluateExpression(String expression) {
297: return this .jxpathContext.getValue(expression);
298: }
299:
300: public Locale getLocale() {
301: return locale;
302: }
303:
304: public void setLocale(Locale locale) {
305: this .locale = locale;
306: }
307:
308: public Locale getLocaleParameter() {
309: return localeParameter;
310: }
311:
312: /**
313: * The value for the wi:form-generated/@action.
314: * Note: wi:form-template copies this from its wt:form-template counterpart.
315: *
316: * @return the {@link #translateText(String)} result of the 'form-action' sitemap
317: * parameter to the pipeline component, or null if that parameter was not set.
318: */
319: public String getFormAction() {
320: return formAction;
321: }
322:
323: /**
324: * The value for the wi:form-generated/@method.
325: * Note: wi:form-template copies this from its wt:form-template counterpart.
326: *
327: * @return the value of the 'form-method' sitemap parameter to the pipeline
328: * component. (or 'null' if it was not set.)
329: */
330: public String getFormMethod() {
331: return formMethod;
332: }
333:
334: /**
335: * Sets the form method to use in the generator/transformer that uses this.
336: *
337: * @param method to use in the generated form should be "POST", "GET" or null
338: */
339: public void setFormMethod(String method) {
340: this .formMethod = method;
341: }
342:
343: /**
344: * The grouped attributes to set on the wi:form-generated element.
345: * Note: wi:form-template copies this from its wt:form-template counterpart.
346: *
347: * @see #getFormAction()
348: * @see #getFormMethod()
349: */
350: public Attributes getFormAttributes() {
351: AttributesImpl attrs = new org.apache.cocoon.xml.AttributesImpl();
352: addFormAttributes(attrs);
353: return attrs;
354: }
355:
356: public void addFormAttributes(AttributesImpl attrs) {
357: if (getFormAction() != null) {
358: attrs.addAttribute("", "action", "action", "CDATA",
359: getFormAction());
360: }
361: if (getFormMethod() != null) {
362: attrs.addAttribute("", "method", "method", "CDATA",
363: getFormMethod());
364: }
365: }
366:
367: public static final class FormsVariables implements Variables {
368:
369: final Map vars = new HashMap();
370: final List deprecatedNames = new ArrayList();
371:
372: public void addDeprecatedVariable(String name) {
373: this .deprecatedNames.add(name);
374: }
375:
376: /* (non-Javadoc)
377: * @see org.apache.commons.jxpath.Variables#declareVariable(java.lang.String, java.lang.Object)
378: */
379: public void declareVariable(String name, Object value) {
380: this .vars.put(name, value);
381: }
382:
383: /* (non-Javadoc)
384: * @see org.apache.commons.jxpath.Variables#getVariable(java.lang.String)
385: */
386: public Object getVariable(String name) {
387: Object value = this .vars.get(name);
388: if (deprecatedNames.contains(name)) {
389: Deprecation.logger
390: .warn("CForms: usage of the variable '" + name
391: + "' is deprecated."
392: + "Please use 'cocoon/" + name
393: + "' instead. The usage of just '"
394: + name
395: + "' will be removed in Cocoon 2.2.");
396: }
397: return value;
398: }
399:
400: /* (non-Javadoc)
401: * @see org.apache.commons.jxpath.Variables#isDeclaredVariable(java.lang.String)
402: */
403: public boolean isDeclaredVariable(String name) {
404: return this .vars.containsKey(name);
405: }
406:
407: /* (non-Javadoc)
408: * @see org.apache.commons.jxpath.Variables#undeclareVariable(java.lang.String)
409: */
410: public void undeclareVariable(String name) {
411: this.vars.remove(name);
412: }
413: }
414: }
|