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