1 package org.apache.turbine.services.intake.model;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import org.apache.commons.pool.BaseKeyedPoolableObjectFactory;
29
30 import org.apache.turbine.om.Retrievable;
31 import org.apache.turbine.services.intake.IntakeException;
32 import org.apache.turbine.services.intake.TurbineIntake;
33 import org.apache.turbine.services.intake.xmlmodel.AppData;
34 import org.apache.turbine.services.intake.xmlmodel.XmlField;
35 import org.apache.turbine.services.intake.xmlmodel.XmlGroup;
36 import org.apache.turbine.util.TurbineException;
37 import org.apache.turbine.util.parser.ValueParser;
38
39 /***
40 * Holds a group of Fields
41 *
42 * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
43 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
44 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
45 * @version $Id: Group.java 264148 2005-08-29 14:21:04Z henning $
46 */
47 public class Group
48 {
49 public static final String EMPTY = "";
50
51
52
53
54 public static final String NEW = "_0";
55
56 private static final Log log;
57 private static final boolean isDebugEnabled;
58
59 static
60 {
61 log = LogFactory.getLog(Group.class);
62 isDebugEnabled = log.isDebugEnabled();
63 }
64
65 /***
66 * The key used to represent this group in a parameter.
67 * This key is usually a prefix as part of a field key.
68 */
69 protected final String gid;
70
71 /***
72 * The name used in templates and java code to refer to this group.
73 */
74 protected final String name;
75
76 /***
77 * The number of Groups with the same name that will be pooled.
78 */
79 private final int poolCapacity;
80
81 /***
82 * A map of the fields in this group mapped by field name.
83 */
84 protected Map fields;
85
86 /***
87 * Map of the fields by mapToObject
88 */
89 protected Map mapToObjectFields;
90
91 /***
92 * An array of fields in this group.
93 */
94 protected Field[] fieldsArray;
95
96 /***
97 * The object id used to associate this group to a bean
98 * for one request cycle
99 */
100 protected String oid;
101
102 /***
103 * The object containing the request data
104 */
105 protected ValueParser pp;
106
107 /***
108 * A flag to help prevent duplicate hidden fields declaring this group.
109 */
110 protected boolean isDeclared;
111
112 /***
113 * Constructs a new Group based on the xml specification. Groups are
114 * instantiated and pooled by the IntakeService and should not
115 * be instantiated otherwise.
116 *
117 * @param group a <code>XmlGroup</code> value
118 * @exception IntakeException if an error occurs in other classes
119 */
120 public Group(XmlGroup group) throws IntakeException
121 {
122 gid = group.getKey();
123 name = group.getName();
124 poolCapacity = Integer.parseInt(group.getPoolCapacity());
125
126 List inputFields = group.getFields();
127 int size = inputFields.size();
128 fields = new HashMap((int) (1.25 * size + 1));
129 mapToObjectFields = new HashMap((int) (1.25 * size + 1));
130 fieldsArray = new Field[size];
131 for (int i = size - 1; i >= 0; i--)
132 {
133 XmlField f = (XmlField) inputFields.get(i);
134 Field field = FieldFactory.getInstance(f, this);
135 fieldsArray[i] = field;
136 fields.put(f.getName(), field);
137
138
139 List tmpFields = (List) mapToObjectFields.get(f.getMapToObject());
140 if (tmpFields == null)
141 {
142 tmpFields = new ArrayList(size);
143 mapToObjectFields.put(f.getMapToObject(), tmpFields);
144 }
145 tmpFields.add(field);
146 }
147
148
149 for (Iterator keys = mapToObjectFields.keySet().iterator(); keys.hasNext();)
150 {
151 Object key = keys.next();
152 List tmpFields = (List) mapToObjectFields.get(key);
153 mapToObjectFields.put(key,
154 tmpFields.toArray(new Field[tmpFields.size()]));
155 }
156 }
157
158 /***
159 * Initializes the default Group using parameters.
160 *
161 * @param pp a <code>ValueParser</code> value
162 * @return this Group
163 */
164 public Group init(ValueParser pp) throws TurbineException
165 {
166 return init(NEW, pp);
167 }
168
169 /***
170 * Initializes the Group with parameters from RunData
171 * corresponding to key.
172 *
173 * @param pp a <code>ValueParser</code> value
174 * @return this Group
175 */
176 public Group init(String key, ValueParser pp) throws IntakeException
177 {
178 this.oid = key;
179 this.pp = pp;
180 for (int i = fieldsArray.length - 1; i >= 0; i--)
181 {
182 fieldsArray[i].init(pp);
183 }
184 return this;
185 }
186
187 /***
188 * Initializes the group with properties from an object.
189 *
190 * @param obj a <code>Persistent</code> value
191 * @return a <code>Group</code> value
192 */
193 public Group init(Retrievable obj)
194 {
195 this.oid = obj.getQueryKey();
196
197 Class cls = obj.getClass();
198 while (cls != null)
199 {
200 Field[] flds = (Field[]) mapToObjectFields.get(cls.getName());
201 if (flds != null)
202 {
203 for (int i = flds.length - 1; i >= 0; i--)
204 {
205 flds[i].init(obj);
206 }
207 }
208
209 cls = cls.getSuperclass();
210 }
211
212 return this;
213 }
214
215 /***
216 * Gets a list of the names of the fields stored in this object.
217 *
218 * @return A String array containing the list of names.
219 */
220 public String[] getFieldNames()
221 {
222 String nameList[] = new String[fieldsArray.length];
223 for (int i = 0; i < nameList.length; i++)
224 {
225 nameList[i] = fieldsArray[i].name;
226 }
227 return nameList;
228 }
229
230 /***
231 * Return the name given to this group. The long name is to
232 * avoid conflicts with the get(String key) method.
233 *
234 * @return a <code>String</code> value
235 */
236 public String getIntakeGroupName()
237 {
238 return name;
239 }
240
241 /***
242 * Get the number of Group objects that will be pooled.
243 *
244 * @return an <code>int</code> value
245 */
246 public int getPoolCapacity()
247 {
248 return poolCapacity;
249 }
250
251 /***
252 * Get the part of the key used to specify the group.
253 * This is specified in the key attribute in the xml file.
254 *
255 * @return a <code>String</code> value
256 */
257 public String getGID()
258 {
259 return gid;
260 }
261
262 /***
263 * Get the part of the key that distinguishes a group
264 * from others of the same name.
265 *
266 * @return a <code>String</code> value
267 */
268 public String getOID()
269 {
270 return oid;
271 }
272
273 /***
274 * Concatenation of gid and oid.
275 *
276 * @return a <code>String</code> value
277 */
278 public String getObjectKey()
279 {
280 return gid + oid;
281 }
282
283 /***
284 * Describe <code>getObjects</code> method here.
285 *
286 * @param pp a <code>ValueParser</code> value
287 * @return an <code>ArrayList</code> value
288 * @exception IntakeException if an error occurs
289 */
290 public ArrayList getObjects(ValueParser pp) throws IntakeException
291 {
292 ArrayList objs = null;
293 String[] oids = pp.getStrings(gid);
294 if (oids != null)
295 {
296 objs = new ArrayList(oids.length);
297 for (int i = oids.length - 1; i >= 0; i--)
298 {
299 objs.add(TurbineIntake.getGroup(name).init(oids[i], pp));
300 }
301 }
302 return objs;
303 }
304
305 /***
306 * Get the Field .
307 * @return Field.
308 * @throws IntakeException indicates the field could not be found.
309 */
310 public Field get(String fieldName)
311 throws IntakeException
312 {
313 if (fields.containsKey(fieldName))
314 {
315 return (Field) fields.get(fieldName);
316 }
317 else
318 {
319 throw new IntakeException("Intake Field name: " + fieldName +
320 " not found!");
321 }
322 }
323
324 /***
325 * Performs an AND between all the fields in this group.
326 *
327 * @return a <code>boolean</code> value
328 */
329 public boolean isAllValid()
330 {
331 boolean valid = true;
332 for (int i = fieldsArray.length - 1; i >= 0; i--)
333 {
334 valid &= fieldsArray[i].isValid();
335 if (isDebugEnabled && !fieldsArray[i].isValid())
336 {
337 log.debug("Group(" + oid + "): " + name + "; Field: "
338 + fieldsArray[i].name + "; value=" +
339 fieldsArray[i].getValue() + " is invalid!");
340 }
341 }
342 return valid;
343 }
344
345 /***
346 * Calls a setter methods on obj, for fields which have been set.
347 *
348 * @param obj Object to be set with the values from the group.
349 * @throws IntakeException indicates that a failure occurred while
350 * executing the setter methods of the mapped object.
351 */
352 public void setProperties(Object obj) throws IntakeException
353 {
354 Class cls = obj.getClass();
355
356 while (cls != null)
357 {
358 if (isDebugEnabled)
359 {
360 log.debug("setProperties(" + cls.getName() + ")");
361 }
362
363 Field[] flds = (Field[]) mapToObjectFields.get(cls.getName());
364 if (flds != null)
365 {
366 for (int i = flds.length - 1; i >= 0; i--)
367 {
368 flds[i].setProperty(obj);
369 }
370 }
371
372 cls = cls.getSuperclass();
373 }
374 log.debug("setProperties() finished");
375 }
376
377 /***
378 * Calls a setter methods on obj, for fields which pass validity tests.
379 * In most cases one should call Intake.isAllValid() and then if that
380 * test passes call setProperties. Use this method when some data is
381 * known to be invalid, but you still want to set the object properties
382 * that are valid.
383 */
384 public void setValidProperties(Object obj)
385 {
386 Class cls = obj.getClass();
387 while (cls != null)
388 {
389 Field[] flds = (Field[]) mapToObjectFields.get(cls.getName());
390 if (flds != null)
391 {
392 for (int i = flds.length - 1; i >= 0; i--)
393 {
394 try
395 {
396 flds[i].setProperty(obj);
397 }
398 catch (Exception e)
399 {
400
401 }
402 }
403 }
404
405 cls = cls.getSuperclass();
406 }
407 }
408
409 /***
410 * Calls getter methods on objects that are known to Intake
411 * so that field values in forms can be initialized from
412 * the values contained in the intake tool.
413 *
414 * @param obj Object that will be used to as a source of data for
415 * setting the values of the fields within the group.
416 * @throws IntakeException indicates that a failure occurred while
417 * executing the setter methods of the mapped object.
418 */
419 public void getProperties(Object obj) throws IntakeException
420 {
421 Class cls = obj.getClass();
422 while (cls != null)
423 {
424 Field[] flds = (Field[]) mapToObjectFields.get(cls.getName());
425 if (flds != null)
426 {
427 for (int i = flds.length - 1; i >= 0; i--)
428 {
429 flds[i].getProperty(obj);
430 }
431 }
432
433 cls = cls.getSuperclass();
434 }
435 }
436
437 /***
438 * Removes references to this group and its fields from the
439 * query parameters
440 */
441 public void removeFromRequest()
442 {
443 if (pp != null)
444 {
445 String[] groups = pp.getStrings(gid);
446 if (groups != null)
447 {
448 pp.remove(gid);
449 for (int i = 0; i < groups.length; i++)
450 {
451 if (groups[i] != null && !groups[i].equals(oid))
452 {
453 pp.add(gid, groups[i]);
454 }
455 }
456 for (int i = fieldsArray.length - 1; i >= 0; i--)
457 {
458 fieldsArray[i].removeFromRequest();
459 }
460 }
461 }
462 }
463
464 /***
465 * To be used in the event this group is used within multiple
466 * forms within the same template.
467 */
468 public void resetDeclared()
469 {
470 isDeclared = false;
471 }
472
473 /***
474 * A xhtml valid hidden input field that notifies intake of the
475 * group's presence.
476 *
477 * @return a <code>String</code> value
478 */
479 public String getHtmlFormInput()
480 {
481 StringBuffer sb = new StringBuffer(64);
482 appendHtmlFormInput(sb);
483 return sb.toString();
484 }
485
486 /***
487 * A xhtml valid hidden input field that notifies intake of the
488 * group's presence.
489 */
490 public void appendHtmlFormInput(StringBuffer sb)
491 {
492 if (!isDeclared)
493 {
494 isDeclared = true;
495 sb.append("<input type=\"hidden\" name=\"")
496 .append(gid)
497 .append("\" value=\"")
498 .append(oid)
499 .append("\"/>\n");
500 }
501 }
502
503
504
505 public static class GroupFactory
506 extends BaseKeyedPoolableObjectFactory
507 {
508 private AppData appData;
509
510 public GroupFactory(AppData appData)
511 {
512 this.appData = appData;
513 }
514
515 /***
516 * Creates an instance that can be returned by the pool.
517 * @return an instance that can be returned by the pool.
518 * @throws IntakeException indicates that the group could not be retreived
519 */
520 public Object makeObject(Object key) throws IntakeException
521 {
522 return new Group(appData.getGroup((String) key));
523 }
524
525 /***
526 * Uninitialize an instance to be returned to the pool.
527 * @param obj the instance to be passivated
528 */
529 public void passivateObject(Object key, Object obj)
530 {
531 Group group = (Group) obj;
532 group.oid = null;
533 group.pp = null;
534 for (int i = group.fieldsArray.length - 1; i >= 0; i--)
535 {
536 group.fieldsArray[i].dispose();
537 }
538 group.isDeclared = false;
539 }
540 }
541 }
542
543