blob: c105238c4e007aba33e2b90edcd143dfed7c2e61 [file] [log] [blame]
package org.apache.turbine.services.intake.model;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.pool.BaseKeyedPoolableObjectFactory;
import org.apache.turbine.om.Retrievable;
import org.apache.turbine.services.intake.IntakeException;
import org.apache.turbine.services.intake.TurbineIntake;
import org.apache.turbine.services.intake.xmlmodel.AppData;
import org.apache.turbine.services.intake.xmlmodel.XmlField;
import org.apache.turbine.services.intake.xmlmodel.XmlGroup;
import org.apache.turbine.util.TurbineException;
import org.apache.turbine.util.parser.ValueParser;
/**
* Holds a group of Fields
*
* @deprecated Use the Fulcrum Intake component instead.
* @author <a href="mailto:jmcnally@collab.net">John McNally</a>
* @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
* @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
* @version $Id$
*/
public class Group
{
public static final String EMPTY = "";
/*
* An id representing a new object.
*/
public static final String NEW = "_0";
private static final Log log;
private static final boolean isDebugEnabled;
static
{
log = LogFactory.getLog(Group.class);
isDebugEnabled = log.isDebugEnabled();
}
/**
* The key used to represent this group in a parameter.
* This key is usually a prefix as part of a field key.
*/
protected final String gid;
/**
* The name used in templates and java code to refer to this group.
*/
protected final String name;
/**
* The number of Groups with the same name that will be pooled.
*/
private final int poolCapacity;
/**
* A map of the fields in this group mapped by field name.
*/
protected Map fields;
/**
* Map of the fields by mapToObject
*/
protected Map mapToObjectFields;
/**
* An array of fields in this group.
*/
protected Field[] fieldsArray;
/**
* The object id used to associate this group to a bean
* for one request cycle
*/
protected String oid;
/**
* The object containing the request data
*/
protected ValueParser pp;
/**
* A flag to help prevent duplicate hidden fields declaring this group.
*/
protected boolean isDeclared;
/**
* Constructs a new Group based on the xml specification. Groups are
* instantiated and pooled by the IntakeService and should not
* be instantiated otherwise.
*
* @param group a <code>XmlGroup</code> value
* @exception IntakeException if an error occurs in other classes
*/
public Group(XmlGroup group) throws IntakeException
{
gid = group.getKey();
name = group.getName();
poolCapacity = Integer.parseInt(group.getPoolCapacity());
List inputFields = group.getFields();
int size = inputFields.size();
fields = new HashMap((int) (1.25 * size + 1));
mapToObjectFields = new HashMap((int) (1.25 * size + 1));
fieldsArray = new Field[size];
for (int i = size - 1; i >= 0; i--)
{
XmlField f = (XmlField) inputFields.get(i);
Field field = FieldFactory.getInstance(f, this);
fieldsArray[i] = field;
fields.put(f.getName(), field);
// map fields by their mapToObject
List tmpFields = (List) mapToObjectFields.get(f.getMapToObject());
if (tmpFields == null)
{
tmpFields = new ArrayList(size);
mapToObjectFields.put(f.getMapToObject(), tmpFields);
}
tmpFields.add(field);
}
// Change the mapToObjectFields values to Field[]
for (Iterator keys = mapToObjectFields.keySet().iterator(); keys.hasNext();)
{
Object key = keys.next();
List tmpFields = (List) mapToObjectFields.get(key);
mapToObjectFields.put(key,
tmpFields.toArray(new Field[tmpFields.size()]));
}
}
/**
* Initializes the default Group using parameters.
*
* @param pp a <code>ValueParser</code> value
* @return this Group
*/
public Group init(ValueParser pp) throws TurbineException
{
return init(NEW, pp);
}
/**
* Initializes the Group with parameters from RunData
* corresponding to key.
*
* @param pp a <code>ValueParser</code> value
* @return this Group
*/
public Group init(String key, ValueParser pp) throws IntakeException
{
this.oid = key;
this.pp = pp;
for (int i = fieldsArray.length - 1; i >= 0; i--)
{
fieldsArray[i].init(pp);
}
return this;
}
/**
* Initializes the group with properties from an object.
*
* @param obj a <code>Persistent</code> value
* @return a <code>Group</code> value
*/
public Group init(Retrievable obj)
{
this.oid = obj.getQueryKey();
Class cls = obj.getClass();
while (cls != null)
{
Field[] flds = (Field[]) mapToObjectFields.get(cls.getName());
if (flds != null)
{
for (int i = flds.length - 1; i >= 0; i--)
{
flds[i].init(obj);
}
}
cls = cls.getSuperclass();
}
return this;
}
/**
* Gets a list of the names of the fields stored in this object.
*
* @return A String array containing the list of names.
*/
public String[] getFieldNames()
{
String nameList[] = new String[fieldsArray.length];
for (int i = 0; i < nameList.length; i++)
{
nameList[i] = fieldsArray[i].name;
}
return nameList;
}
/**
* Return the name given to this group. The long name is to
* avoid conflicts with the get(String key) method.
*
* @return a <code>String</code> value
*/
public String getIntakeGroupName()
{
return name;
}
/**
* Get the number of Group objects that will be pooled.
*
* @return an <code>int</code> value
*/
public int getPoolCapacity()
{
return poolCapacity;
}
/**
* Get the part of the key used to specify the group.
* This is specified in the key attribute in the xml file.
*
* @return a <code>String</code> value
*/
public String getGID()
{
return gid;
}
/**
* Get the part of the key that distinguishes a group
* from others of the same name.
*
* @return a <code>String</code> value
*/
public String getOID()
{
return oid;
}
/**
* Concatenation of gid and oid.
*
* @return a <code>String</code> value
*/
public String getObjectKey()
{
return gid + oid;
}
/**
* Describe <code>getObjects</code> method here.
*
* @param pp a <code>ValueParser</code> value
* @return an <code>ArrayList</code> value
* @exception IntakeException if an error occurs
*/
public ArrayList getObjects(ValueParser pp) throws IntakeException
{
ArrayList objs = null;
String[] oids = pp.getStrings(gid);
if (oids != null)
{
objs = new ArrayList(oids.length);
for (int i = oids.length - 1; i >= 0; i--)
{
objs.add(TurbineIntake.getGroup(name).init(oids[i], pp));
}
}
return objs;
}
/**
* Get the Field .
* @return Field.
* @throws IntakeException indicates the field could not be found.
*/
public Field get(String fieldName)
throws IntakeException
{
if (fields.containsKey(fieldName))
{
return (Field) fields.get(fieldName);
}
else
{
throw new IntakeException("Intake Field name: " + fieldName +
" not found!");
}
}
/**
* Performs an AND between all the fields in this group.
*
* @return a <code>boolean</code> value
*/
public boolean isAllValid()
{
boolean valid = true;
for (int i = fieldsArray.length - 1; i >= 0; i--)
{
valid &= fieldsArray[i].isValid();
if (isDebugEnabled && !fieldsArray[i].isValid())
{
log.debug("Group(" + oid + "): " + name + "; Field: "
+ fieldsArray[i].name + "; value=" +
fieldsArray[i].getValue() + " is invalid!");
}
}
return valid;
}
/**
* Calls a setter methods on obj, for fields which have been set.
*
* @param obj Object to be set with the values from the group.
* @throws IntakeException indicates that a failure occurred while
* executing the setter methods of the mapped object.
*/
public void setProperties(Object obj) throws IntakeException
{
Class cls = obj.getClass();
while (cls != null)
{
if (isDebugEnabled)
{
log.debug("setProperties(" + cls.getName() + ")");
}
Field[] flds = (Field[]) mapToObjectFields.get(cls.getName());
if (flds != null)
{
for (int i = flds.length - 1; i >= 0; i--)
{
flds[i].setProperty(obj);
}
}
cls = cls.getSuperclass();
}
log.debug("setProperties() finished");
}
/**
* Calls a setter methods on obj, for fields which pass validity tests.
* In most cases one should call Intake.isAllValid() and then if that
* test passes call setProperties. Use this method when some data is
* known to be invalid, but you still want to set the object properties
* that are valid.
*/
public void setValidProperties(Object obj)
{
Class cls = obj.getClass();
while (cls != null)
{
Field[] flds = (Field[]) mapToObjectFields.get(cls.getName());
if (flds != null)
{
for (int i = flds.length - 1; i >= 0; i--)
{
try
{
flds[i].setProperty(obj);
}
catch (Exception e)
{
// just move on to next field
}
}
}
cls = cls.getSuperclass();
}
}
/**
* Calls getter methods on objects that are known to Intake
* so that field values in forms can be initialized from
* the values contained in the intake tool.
*
* @param obj Object that will be used to as a source of data for
* setting the values of the fields within the group.
* @throws IntakeException indicates that a failure occurred while
* executing the setter methods of the mapped object.
*/
public void getProperties(Object obj) throws IntakeException
{
Class cls = obj.getClass();
while (cls != null)
{
Field[] flds = (Field[]) mapToObjectFields.get(cls.getName());
if (flds != null)
{
for (int i = flds.length - 1; i >= 0; i--)
{
flds[i].getProperty(obj);
}
}
cls = cls.getSuperclass();
}
}
/**
* Removes references to this group and its fields from the
* query parameters
*/
public void removeFromRequest()
{
if (pp != null)
{
String[] groups = pp.getStrings(gid);
if (groups != null)
{
pp.remove(gid);
for (int i = 0; i < groups.length; i++)
{
if (groups[i] != null && !groups[i].equals(oid))
{
pp.add(gid, groups[i]);
}
}
for (int i = fieldsArray.length - 1; i >= 0; i--)
{
fieldsArray[i].removeFromRequest();
}
}
}
}
/**
* To be used in the event this group is used within multiple
* forms within the same template.
*/
public void resetDeclared()
{
isDeclared = false;
}
/**
* A xhtml valid hidden input field that notifies intake of the
* group's presence.
*
* @return a <code>String</code> value
*/
public String getHtmlFormInput()
{
StringBuffer sb = new StringBuffer(64);
appendHtmlFormInput(sb);
return sb.toString();
}
/**
* A xhtml valid hidden input field that notifies intake of the
* group's presence.
*/
public void appendHtmlFormInput(StringBuffer sb)
{
if (!isDeclared)
{
isDeclared = true;
sb.append("<input type=\"hidden\" name=\"")
.append(gid)
.append("\" value=\"")
.append(oid)
.append("\"/>\n");
}
}
// ********** PoolableObjectFactory implementation ******************
public static class GroupFactory
extends BaseKeyedPoolableObjectFactory
{
private AppData appData;
public GroupFactory(AppData appData)
{
this.appData = appData;
}
/**
* Creates an instance that can be returned by the pool.
* @return an instance that can be returned by the pool.
* @throws IntakeException indicates that the group could not be retreived
*/
public Object makeObject(Object key) throws IntakeException
{
return new Group(appData.getGroup((String) key));
}
/**
* Uninitialize an instance to be returned to the pool.
* @param obj the instance to be passivated
*/
public void passivateObject(Object key, Object obj)
{
Group group = (Group) obj;
group.oid = null;
group.pp = null;
for (int i = group.fieldsArray.length - 1; i >= 0; i--)
{
group.fieldsArray[i].dispose();
}
group.isDeclared = false;
}
}
}