001: package org.apache.turbine.services.intake.model;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.util.ArrayList;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030:
031: import org.apache.commons.pool.BaseKeyedPoolableObjectFactory;
032:
033: import org.apache.turbine.om.Retrievable;
034: import org.apache.turbine.services.intake.IntakeException;
035: import org.apache.turbine.services.intake.TurbineIntake;
036: import org.apache.turbine.services.intake.xmlmodel.AppData;
037: import org.apache.turbine.services.intake.xmlmodel.XmlField;
038: import org.apache.turbine.services.intake.xmlmodel.XmlGroup;
039: import org.apache.turbine.util.TurbineException;
040: import org.apache.turbine.util.parser.ValueParser;
041:
042: /**
043: * Holds a group of Fields
044: *
045: * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
046: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
047: * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
048: * @author <a href="jh@byteaction.de">Jürgen Hoffmann</a>
049: * @version $Id: Group.java 538811 2007-05-17 05:55:42Z seade $
050: */
051: public class Group {
052: public static final String EMPTY = "";
053:
054: /*
055: * An id representing a new object.
056: */
057: public static final String NEW = "_0";
058:
059: private static final Log log;
060: private static final boolean isDebugEnabled;
061:
062: static {
063: log = LogFactory.getLog(Group.class);
064: isDebugEnabled = log.isDebugEnabled();
065: }
066:
067: /**
068: * The key used to represent this group in a parameter.
069: * This key is usually a prefix as part of a field key.
070: */
071: protected final String gid;
072:
073: /**
074: * The name used in templates and java code to refer to this group.
075: */
076: protected final String name;
077:
078: /**
079: * The number of Groups with the same name that will be pooled.
080: */
081: private final int poolCapacity;
082:
083: /**
084: * A map of the fields in this group mapped by field name.
085: */
086: protected Map fields;
087:
088: /**
089: * Map of the fields by mapToObject
090: */
091: protected Map mapToObjectFields;
092:
093: /**
094: * An array of fields in this group.
095: */
096: protected Field[] fieldsArray;
097:
098: /**
099: * The object id used to associate this group to a bean
100: * for one request cycle
101: */
102: protected String oid;
103:
104: /**
105: * The object containing the request data
106: */
107: protected ValueParser pp;
108:
109: /**
110: * A flag to help prevent duplicate hidden fields declaring this group.
111: */
112: protected boolean isDeclared;
113:
114: /**
115: * Constructs a new Group based on the xml specification. Groups are
116: * instantiated and pooled by the IntakeService and should not
117: * be instantiated otherwise.
118: *
119: * @param group a <code>XmlGroup</code> value
120: * @exception IntakeException if an error occurs in other classes
121: */
122: public Group(XmlGroup group) throws IntakeException {
123: gid = group.getKey();
124: name = group.getName();
125: poolCapacity = Integer.parseInt(group.getPoolCapacity());
126:
127: List inputFields = group.getFields();
128: int size = inputFields.size();
129: fields = new HashMap((int) (1.25 * size + 1));
130: mapToObjectFields = new HashMap((int) (1.25 * size + 1));
131: fieldsArray = new Field[size];
132: for (int i = size - 1; i >= 0; i--) {
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: // map fields by their mapToObject
139: List tmpFields = (List) mapToObjectFields.get(f
140: .getMapToObject());
141: if (tmpFields == null) {
142: tmpFields = new ArrayList(size);
143: mapToObjectFields.put(f.getMapToObject(), tmpFields);
144: }
145: tmpFields.add(field);
146: }
147:
148: // Change the mapToObjectFields values to Field[]
149: for (Iterator keys = mapToObjectFields.keySet().iterator(); keys
150: .hasNext();) {
151: Object key = keys.next();
152: List tmpFields = (List) mapToObjectFields.get(key);
153: mapToObjectFields.put(key, tmpFields
154: .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: return init(NEW, pp);
166: }
167:
168: /**
169: * Initializes the Group with parameters from RunData
170: * corresponding to key.
171: *
172: * @param pp a <code>ValueParser</code> value
173: * @return this Group
174: */
175: public Group init(String key, ValueParser pp)
176: throws IntakeException {
177: this .oid = key;
178: this .pp = pp;
179: for (int i = fieldsArray.length - 1; i >= 0; i--) {
180: fieldsArray[i].init(pp);
181: }
182: for (int i = fieldsArray.length - 1; i >= 0; i--) {
183: if (fieldsArray[i].isSet() && !fieldsArray[i].isValidated()) {
184: fieldsArray[i].validate();
185: }
186: }
187: return this ;
188: }
189:
190: /**
191: * Initializes the group with properties from an object.
192: *
193: * @param obj a <code>Persistent</code> value
194: * @return a <code>Group</code> value
195: */
196: public Group init(Retrievable obj) {
197: this .oid = obj.getQueryKey();
198:
199: Class cls = obj.getClass();
200: while (cls != null) {
201: Field[] flds = (Field[]) mapToObjectFields.get(cls
202: .getName());
203: if (flds != null) {
204: for (int i = flds.length - 1; i >= 0; i--) {
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: String nameList[] = new String[fieldsArray.length];
222: for (int i = 0; i < nameList.length; i++) {
223: nameList[i] = fieldsArray[i].name;
224: }
225: return nameList;
226: }
227:
228: /**
229: * Return the name given to this group. The long name is to
230: * avoid conflicts with the get(String key) method.
231: *
232: * @return a <code>String</code> value
233: */
234: public String getIntakeGroupName() {
235: return name;
236: }
237:
238: /**
239: * Get the number of Group objects that will be pooled.
240: *
241: * @return an <code>int</code> value
242: */
243: public int getPoolCapacity() {
244: return poolCapacity;
245: }
246:
247: /**
248: * Get the part of the key used to specify the group.
249: * This is specified in the key attribute in the xml file.
250: *
251: * @return a <code>String</code> value
252: */
253: public String getGID() {
254: return gid;
255: }
256:
257: /**
258: * Get the part of the key that distinguishes a group
259: * from others of the same name.
260: *
261: * @return a <code>String</code> value
262: */
263: public String getOID() {
264: return oid;
265: }
266:
267: /**
268: * Concatenation of gid and oid.
269: *
270: * @return a <code>String</code> value
271: */
272: public String getObjectKey() {
273: return gid + oid;
274: }
275:
276: /**
277: * Describe <code>getObjects</code> method here.
278: *
279: * @param pp a <code>ValueParser</code> value
280: * @return an <code>ArrayList</code> value
281: * @exception IntakeException if an error occurs
282: */
283: public ArrayList getObjects(ValueParser pp) throws IntakeException {
284: ArrayList objs = null;
285: String[] oids = pp.getStrings(gid);
286: if (oids != null) {
287: objs = new ArrayList(oids.length);
288: for (int i = oids.length - 1; i >= 0; i--) {
289: objs
290: .add(TurbineIntake.getGroup(name).init(oids[i],
291: pp));
292: }
293: }
294: return objs;
295: }
296:
297: /**
298: * Get the Field .
299: * @return Field.
300: * @throws IntakeException indicates the field could not be found.
301: */
302: public Field get(String fieldName) throws IntakeException {
303: if (fields.containsKey(fieldName)) {
304: return (Field) fields.get(fieldName);
305: } else {
306: throw new IntakeException("Intake Field name: " + fieldName
307: + " not found in Group " + name);
308: }
309: }
310:
311: /**
312: * Performs an AND between all the fields in this group.
313: *
314: * @return a <code>boolean</code> value
315: */
316: public boolean isAllValid() {
317: boolean valid = true;
318: for (int i = fieldsArray.length - 1; i >= 0; i--) {
319: valid &= fieldsArray[i].isValid();
320: if (isDebugEnabled && !fieldsArray[i].isValid()) {
321: log.debug("Group(" + oid + "): " + name + "; Field: "
322: + fieldsArray[i].name + "; value="
323: + fieldsArray[i].getValue() + " is invalid!");
324: }
325: }
326: return valid;
327: }
328:
329: /**
330: * Calls a setter methods on obj, for fields which have been set.
331: *
332: * @param obj Object to be set with the values from the group.
333: * @throws IntakeException indicates that a failure occurred while
334: * executing the setter methods of the mapped object.
335: */
336: public void setProperties(Object obj) throws IntakeException {
337: Class cls = obj.getClass();
338:
339: while (cls != null) {
340: if (isDebugEnabled) {
341: log.debug("setProperties(" + cls.getName() + ")");
342: }
343:
344: Field[] flds = (Field[]) mapToObjectFields.get(cls
345: .getName());
346: if (flds != null) {
347: for (int i = flds.length - 1; i >= 0; i--) {
348: flds[i].setProperty(obj);
349: }
350: }
351:
352: cls = cls.getSuperclass();
353: }
354: log.debug("setProperties() finished");
355: }
356:
357: /**
358: * Calls a setter methods on obj, for fields which pass validity tests.
359: * In most cases one should call Intake.isAllValid() and then if that
360: * test passes call setProperties. Use this method when some data is
361: * known to be invalid, but you still want to set the object properties
362: * that are valid.
363: */
364: public void setValidProperties(Object obj) {
365: Class cls = obj.getClass();
366: while (cls != null) {
367: Field[] flds = (Field[]) mapToObjectFields.get(cls
368: .getName());
369: if (flds != null) {
370: for (int i = flds.length - 1; i >= 0; i--) {
371: try {
372: flds[i].setProperty(obj);
373: } catch (Exception e) {
374: // just move on to next field
375: }
376: }
377: }
378:
379: cls = cls.getSuperclass();
380: }
381: }
382:
383: /**
384: * Calls getter methods on objects that are known to Intake
385: * so that field values in forms can be initialized from
386: * the values contained in the intake tool.
387: *
388: * @param obj Object that will be used to as a source of data for
389: * setting the values of the fields within the group.
390: * @throws IntakeException indicates that a failure occurred while
391: * executing the setter methods of the mapped object.
392: */
393: public void getProperties(Object obj) throws IntakeException {
394: Class cls = obj.getClass();
395: while (cls != null) {
396: Field[] flds = (Field[]) mapToObjectFields.get(cls
397: .getName());
398: if (flds != null) {
399: for (int i = flds.length - 1; i >= 0; i--) {
400: flds[i].getProperty(obj);
401: }
402: }
403:
404: cls = cls.getSuperclass();
405: }
406: }
407:
408: /**
409: * Removes references to this group and its fields from the
410: * query parameters
411: */
412: public void removeFromRequest() {
413: if (pp != null) {
414: String[] groups = pp.getStrings(gid);
415: if (groups != null) {
416: pp.remove(gid);
417: for (int i = 0; i < groups.length; i++) {
418: if (groups[i] != null && !groups[i].equals(oid)) {
419: pp.add(gid, groups[i]);
420: }
421: }
422: for (int i = fieldsArray.length - 1; i >= 0; i--) {
423: fieldsArray[i].removeFromRequest();
424: }
425: }
426: }
427: }
428:
429: /**
430: * To be used in the event this group is used within multiple
431: * forms within the same template.
432: */
433: public void resetDeclared() {
434: isDeclared = false;
435: }
436:
437: /**
438: * A xhtml valid hidden input field that notifies intake of the
439: * group's presence.
440: *
441: * @return a <code>String</code> value
442: */
443: public String getHtmlFormInput() {
444: StringBuffer sb = new StringBuffer(64);
445: appendHtmlFormInput(sb);
446: return sb.toString();
447: }
448:
449: /**
450: * A xhtml valid hidden input field that notifies intake of the
451: * group's presence.
452: */
453: public void appendHtmlFormInput(StringBuffer sb) {
454: if (!isDeclared) {
455: isDeclared = true;
456: sb.append("<input type=\"hidden\" name=\"").append(gid)
457: .append("\" value=\"").append(oid).append("\"/>\n");
458: }
459: }
460:
461: // ********** PoolableObjectFactory implementation ******************
462:
463: public static class GroupFactory extends
464: BaseKeyedPoolableObjectFactory {
465: private AppData appData;
466:
467: public GroupFactory(AppData appData) {
468: this .appData = appData;
469: }
470:
471: /**
472: * Creates an instance that can be returned by the pool.
473: * @return an instance that can be returned by the pool.
474: * @throws IntakeException indicates that the group could not be retreived
475: */
476: public Object makeObject(Object key) throws IntakeException {
477: return new Group(appData.getGroup((String) key));
478: }
479:
480: /**
481: * Uninitialize an instance to be returned to the pool.
482: * @param obj the instance to be passivated
483: */
484: public void passivateObject(Object key, Object obj) {
485: Group group = (Group) obj;
486: group.oid = null;
487: group.pp = null;
488: for (int i = group.fieldsArray.length - 1; i >= 0; i--) {
489: group.fieldsArray[i].dispose();
490: }
491: group.isDeclared = false;
492: }
493: }
494: }
|