Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/scopemvc/view/servlet/xml/XSLPage.java


1   /*
2    * Scope: a generic MVC framework.
3    * Copyright (c) 2000-2002, Steve Meyfroidt
4    * All rights reserved.
5    * Email: smeyfroi@users.sourceforge.net
6    * 
7    * 
8    * Redistribution and use in source and binary forms, with or without
9    * modification, are permitted provided that the following conditions
10   * are met:
11   * 
12   * Redistributions of source code must retain the above copyright
13   * notice, this list of conditions and the following disclaimer.
14   * 
15   * Redistributions in binary form must reproduce the above copyright
16   * notice, this list of conditions and the following disclaimer in the
17   * documentation and/or other materials provided with the distribution.
18   * 
19   * Neither the name "Scope" nor the names of its contributors
20   * may be used to endorse or promote products derived from this software
21   * without specific prior written permission.
22   * 
23   * 
24   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27   * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
28   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35   * 
36   * 
37   * $Id: XSLPage.java,v 1.5 2002/01/26 09:46:20 smeyfroi Exp $
38   */
39  
40  
41  package org.scopemvc.view.servlet.xml;
42  
43  
44  import java.util.HashMap;
45  import java.util.Iterator;
46  import java.util.LinkedList;
47  import java.util.List;
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogSource;
50  import org.scopemvc.core.PropertyManager;
51  import org.scopemvc.core.Selector;
52  import org.scopemvc.util.Debug;
53  import org.scopemvc.util.ScopeConfig;
54  import org.scopemvc.util.convertor.StringConvertor;
55  import org.scopemvc.util.convertor.StringConvertors;
56  import org.scopemvc.view.servlet.ValidationFailure;
57  import org.xml.sax.ContentHandler;
58  
59  
60  
61  /**
62   * <P>
63   * A concrete {@link AbstractXSLPage} 
64   * that uses Scope's ModelManager implementations
65   * to serialise its entire bound model object to an XML
66   * document. A better strategy would be to use a 
67   * more intelligent view that selectively serialises
68   * relevant parts of the model object.
69   * </P>
70   * <P>
71   * Handles circular references using the "ID" and "IDREF" pattern.
72   * </P>
73   * 
74   * @author <A HREF="mailto:smeyfroi@users.sourceforge.net">Steve Meyfroidt</A>
75   * @version $Revision: 1.5 $ $Date: 2002/01/26 09:46:20 $
76   */
77  public class XSLPage extends AbstractXSLPage {
78  
79  
80      private static final Log LOG = LogSource.getInstance(XSLPage.class);
81  
82  
83      /**
84       * The prefix on form parameters to identify a property/value
85       * pair used to repopulate the View's bound model.
86      * A property/value pair looks like:
87      * <CODE>
88      * &lt;PROPERTY_ID_PREFIX&gt;ViewID&lt;VIEW_ID_SUFFIX&gt;SelectorDescription
89      * </CODE>
90       * 
91       * @see #populateModel
92       */
93      static char PROPERTY_ID_PREFIX;
94  
95  
96      /**
97       * Optional setting for whether the SAX 
98       * convertor should write all the property "paths"
99       * out when this view is streamed. Some views might
100      * want this if they send back form parameters like
101      * <CODE>{path}=newValue</CODE> for
102      * automatic repopulation back into the bound model object.
103      */
104     protected boolean requiresPropertyDescriptions;
105     
106     
107     protected ModelToXML xmlGenerator = new ModelToXML();
108 
109 
110     /**
111      * 
112      * @param inViewID unique View ID for
113      *                 routing incoming Controls
114      * @param inXslURI the XSLT this View uses to transform
115      *                 its model objects after they convert
116      *                 to XML
117      */
118     public XSLPage(String inViewID, String inXslURI) {
119         this(inViewID, inXslURI, false);
120     }
121 
122 
123     /**
124      * 
125      * @param inViewID unique View ID for
126      *                 routing incoming Controls
127      * @param inXslURI the XSLT this View uses to transform
128      *                 its model objects after they convert
129      *                 to XML
130      * @param inRequiresModelIds
131      *                 Does this view need the SAX
132      *                 convertor to write out property description
133      *                 attributes for properties? For repopulation
134      *                 back into the bound model via
135      *                 {@link #populateModel}
136      */
137     public XSLPage(String inViewID, String inXslURI, boolean inRequiresModelIds) {
138         super(inViewID, inXslURI);
139         requiresPropertyDescriptions = inRequiresModelIds;
140         init();
141     }
142 
143 
144   /**
145    * Do it like this so that we can pick up application-specific
146    * ScopeConfig... static initializers would happen before user
147    * got a chance to setup the custom config properties.
148    */
149     protected void init() {
150         PROPERTY_ID_PREFIX = ScopeConfig.getChar("ServletFormParameter.propertyIDPrefix");
151         if (PROPERTY_ID_PREFIX == 0) LOG.fatal("No propertyIDPrefix in config.");
152     }
153 
154     
155     /**
156      * 
157      * @param inContentHandler
158      *                 Drive this ContentHandler with the Model's SAX events.
159      * @param inModel  Model object to write. null generates no SAX.
160      * @param inWritePropertyIds
161      *                 Write the property id as the selector chain giving the full path from the top-level model
162      *                 to this model. null for the top-level model.
163      * @param inViewID ViewID to include in property id attributes
164      */
165     protected void generateXMLDocument(ContentHandler inContentHandler)
166     throws Exception {
167       if (requiresPropertyDescriptions) {
168         xmlGenerator.modelToXML(getBoundModel(), inContentHandler, 
169               new FullIDGenerator());
170       } else {
171         xmlGenerator.modelToXML(getBoundModel(), inContentHandler, 
172               new NoIDGenerator());
173       }
174     }
175     
176 
177     /**
178      * <P>
179      * Interprets any form parameters like
180      * <CODE>
181      * &lt;PROPERTY_ID_PREFIX&gt;SelectorDescription
182      * </CODE>
183      * as [property_description, property_value] pairs
184      * </P>
185      * <P>
186      * Extracts the property_description
187      * from the form parameter key, then 
188      * populates its model object using
189      * the String value. Any parameters treated this
190      * way are removed from the HashMap.
191      * </P>
192      * 
193      * @param ioParameters
194      *               form parameters to parse for [property_description, property_value]
195      *               pairs, removing any processed pairs from the parameters
196      *               before return
197      * @return PopulateModelFailedException that will be handled as a validation failure.
198      */
199     public List populateModel(HashMap ioParameters) {
200         if (LOG.isDebugEnabled()) LOG.debug("populateModel: " + ioParameters);
201 
202         List errors = new LinkedList(); // collect ValidationFailures in here
203         List toRemove = new LinkedList();
204         for (Iterator i = ioParameters.keySet().iterator(); i.hasNext(); ) {
205             Object o = i.next();
206             if (Debug.ON) Debug.assertTrue(o instanceof String);
207             String parameterKey = (String)o;
208             if (LOG.isDebugEnabled()) LOG.debug("populateModel: " + parameterKey);
209 
210             // Recognise prefixed form parameters for model population
211             if (parameterKey.charAt(0) == PROPERTY_ID_PREFIX) {
212 
213                 // get the form parameter value and remove from the parameter list
214                 o = ioParameters.get(parameterKey);
215                 // ... if multiple values take the first
216                 if (o instanceof Object[]) {
217                   if (Debug.ON) Debug.assertTrue( ((Object[])o).length > 0);
218                   o = ((Object[])o)[0];
219                 }
220                 if (Debug.ON) Debug.assertTrue(o instanceof String);
221                 String stringValue = (String)o;
222 
223                 // Mark it for removal (avoid concurrency problem with Iterator)
224                 toRemove.add(o);
225 
226                 // find the property description
227                 String propertyDescription = parameterKey.substring(1);
228                 try {
229                     populateBoundModelProperty(propertyDescription, stringValue);
230                 } catch (Exception e) {
231                     if (LOG.isDebugEnabled()) LOG.debug("populateModel: got an exception: " + e);
232                     errors.add(new ValidationFailure(propertyDescription, stringValue, e));
233                 }
234             }
235         }
236 
237         for (Iterator i = toRemove.iterator(); i.hasNext(); ) {
238             ioParameters.remove(i.next());
239         }
240 
241     if (errors.size() == 0) {
242       errors = null;
243     }
244     return errors;
245     }
246     
247     
248   /**
249    * Use the property_description, property_value pair
250    * passed to set a property in the bound model to 
251    * a new value. Use StringConvertor if available to 
252    * convert from String to the property's native datatype.
253    * 
254    * @throws Exception on any failure
255    */
256     protected void populateBoundModelProperty(String inPropertyDescription, String inValue) 
257     throws Exception {
258         if (LOG.isDebugEnabled()) LOG.debug("populateBoundModelProperty: " + inPropertyDescription + ", " + inValue);
259         
260         if (getBoundModel() == null) {
261             LOG.error("No bound model for: " + this);
262             return;
263         }
264 
265         // Get the PropertyManager for the bound model
266         Object model = getBoundModel();
267         PropertyManager manager = PropertyManager.getInstance(model);
268         if (Debug.ON) Debug.assertTrue(manager != null, "null manager");
269 
270         // Get the Selector from the property description
271         Selector selector = Selector.fromString(inPropertyDescription);
272 
273         // Use StringConvertors if available, else just set(Selector, String)
274         StringConvertor convertor = StringConvertors.forClass(manager.getPropertyClass(model, selector));
275         if (convertor != null) {
276             if (LOG.isDebugEnabled()) LOG.debug("populateProperty: got " + convertor.getClass());
277             Object value = convertor.stringAsValue(inValue);
278             manager.set(model, selector, value);
279         } else {
280             if (LOG.isDebugEnabled()) LOG.debug("populateProperty: no StringConvertor ");
281             manager.set(model, selector, inValue);
282         }
283     }
284 }
285 
286 
287 final class NoIDGenerator extends PropertyIDGenerator {
288     public String getPropertyID() {
289         return null;
290     }
291 }
292 
293 
294 final class FullIDGenerator extends PropertyIDGenerator {
295     public String getPropertyID() {
296       if (currentPropertySelector == null) {
297         return null;
298       }
299         return XSLPage.PROPERTY_ID_PREFIX 
300                 + Selector.asString(currentPropertySelector);
301     }
302 }