org.apache.fulcrum.intake.model.Group Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fulcrum-intake Show documentation
Show all versions of fulcrum-intake Show documentation
This Service provides rule-based input validation
package org.apache.fulcrum.intake.model;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.apache.avalon.framework.logger.LogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.fulcrum.intake.IntakeException;
import org.apache.fulcrum.intake.IntakeServiceFacade;
import org.apache.fulcrum.intake.Retrievable;
import org.apache.fulcrum.parser.ValueParser;
/**
* Holds a group of Fields
*
* @author John McNally
* @author Henning P. Schmiedehausen
* @author Quinton McCombs
* @version $Id: Group.java 1673447 2015-04-14 15:47:06Z tv $
*/
@XmlType(name="group")
@XmlAccessorType(XmlAccessType.NONE)
public class Group implements Serializable, LogEnabled
{
/** Serial version */
private static final long serialVersionUID = -5452725641409669284L;
public static final String EMPTY = "";
/*
* An id representing a new object.
*/
public static final String NEW = "_0";
/** Logging */
private transient Logger log;
/**
* The key used to represent this group in a parameter.
* This key is usually a prefix as part of a field key.
*/
@XmlAttribute(name="key", required=true)
private String gid;
/**
* The name used in templates and java code to refer to this group.
*/
@XmlAttribute(required=true)
private String name;
/**
* The number of Groups with the same name that will be pooled.
*/
@XmlAttribute
private int poolCapacity = 128;
/**
* The default map object for this group
*/
@XmlAttribute(name="mapToObject")
private String defaultMapToObject;
/**
* The parent element in the XML tree
*/
private AppData parent;
/**
* 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;
/**
* Enable Avalon Logging
*/
@Override
public void enableLogging(Logger logger)
{
this.log = logger.getChildLogger(getClass().getSimpleName());
}
/**
* Initializes the default Group using parameters.
*
* @param pp a ValueParser
value
* @return this Group
*/
public Group init(ValueParser pp) throws IntakeException
{
return init(NEW, pp);
}
/**
* Initializes the Group with parameters from RunData
* corresponding to key.
*
* @param pp a ValueParser
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);
}
for (int i = fieldsArray.length - 1; i >= 0; i--)
{
if (fieldsArray[i].isSet() && !fieldsArray[i].isValidated())
{
fieldsArray[i].validate();
}
}
return this;
}
/**
* Initializes the group with properties from an object.
*
* @param obj a Persistent
value
* @return a Group
value
*/
public Group init(Retrievable obj)
{
this.oid = obj.getQueryKey();
Class> cls = obj.getClass();
while (cls != null)
{
Field>[] flds = mapToObjectFields.get(cls.getName());
if (flds != null)
{
for (int i = flds.length - 1; i >= 0; i--)
{
flds[i].init(obj);
}
}
// Also check any interfaces
Class>[] interfaces = cls.getInterfaces();
for (int idx = 0; idx < interfaces.length; idx++)
{
Field>[] interfaceFields =
mapToObjectFields.get(interfaces[idx].getName());
if (interfaceFields != null)
{
for (int i = 0; i < interfaceFields.length; i++)
{
interfaceFields[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 String
value
*/
public String getIntakeGroupName()
{
return name;
}
/**
* Get the number of Group objects that will be pooled.
*
* @return an int
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 String
value
*/
public String getGID()
{
return gid;
}
/**
* Get the part of the key that distinguishes a group
* from others of the same name.
*
* @return a String
value
*/
public String getOID()
{
return oid;
}
/**
* Concatenation of gid and oid.
*
* @return a String
value
*/
public String getObjectKey()
{
return gid + oid;
}
/**
* Default object to map this group to.
*
* @return a String
value
*/
public String getDefaultMapToObject()
{
return defaultMapToObject;
}
/**
* Describe getObjects
method here.
*
* @param pp a ValueParser
value
* @return an ArrayList
value
* @exception IntakeException if an error occurs
*/
public List 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(IntakeServiceFacade.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 fields.get(fieldName);
}
else
{
throw new IntakeException("Intake Field name: " + fieldName +
" not found in Group " + name);
}
}
/**
* Get the list of Fields .
* @return list of Fields
*/
public List> getFields()
{
if (fieldsArray == null)
{
return new ArrayList>();
}
return Arrays.asList(fieldsArray);
}
/**
* Set a collection of fields for this group
*
* @param fields the fields to set
*/
@XmlElement(name="field")
@XmlJavaTypeAdapter(FieldAdapter.class)
protected void setFields(List> inputFields)
{
int size = inputFields.size();
fields = new HashMap>((int) (1.25 * size + 1));
fieldsArray = new Field[size];
for (int i = size - 1; i >= 0; i--)
{
Field> field = inputFields.get(i);
fieldsArray[i] = field;
fields.put(field.getName(), field);
}
}
/**
* Performs an AND between all the fields in this group.
*
* @return a boolean
value
*/
public boolean isAllValid()
{
boolean valid = true;
for (int i = fieldsArray.length - 1; i >= 0; i--)
{
valid &= fieldsArray[i].isValid();
if (log.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 (log.isDebugEnabled())
{
log.debug("setProperties(" + cls.getName() + ")");
}
Field>[] flds = mapToObjectFields.get(cls.getName());
if (flds != null)
{
for (int i = flds.length - 1; i >= 0; i--)
{
flds[i].setProperty(obj);
}
}
// Also check any interfaces
Class>[] interfaces = cls.getInterfaces();
for (int idx = 0; idx < interfaces.length; idx++)
{
Field>[] interfaceFields =
mapToObjectFields.get(interfaces[idx].getName());
if (interfaceFields != null)
{
for (int i = 0; i < interfaceFields.length; i++)
{
interfaceFields[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 = mapToObjectFields.get(cls.getName());
if (flds != null)
{
for (int i = flds.length - 1; i >= 0; i--)
{
try
{
flds[i].setProperty(obj);
}
catch (IntakeException e)
{
// just move on to next field
}
}
}
// Also check any interfaces
Class>[] interfaces = cls.getInterfaces();
for (int idx = 0; idx < interfaces.length; idx++)
{
Field>[] interfaceFields =
mapToObjectFields.get(interfaces[idx].getName());
if (interfaceFields != null)
{
for (int i = 0; i < interfaceFields.length; i++)
{
try
{
interfaceFields[i].setProperty(obj);
}
catch(IntakeException 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 = mapToObjectFields.get(cls.getName());
if (flds != null)
{
for (int i = flds.length - 1; i >= 0; i--)
{
flds[i].getProperty(obj);
}
}
// Also check any interfaces
Class>[] interfaces = cls.getInterfaces();
for (int idx = 0; idx < interfaces.length; idx++)
{
Field>[] interfaceFields =
mapToObjectFields.get(interfaces[idx].getName());
if (interfaceFields != null)
{
for (int i = 0; i < interfaceFields.length; i++)
{
interfaceFields[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 String
value
*/
public String getHtmlFormInput()
{
StringBuilder sb = new StringBuilder(64);
appendHtmlFormInput(sb);
return sb.toString();
}
/**
* A xhtml valid hidden input field that notifies intake of the
* group's presence.
*/
public void appendHtmlFormInput(StringBuilder sb)
{
if (!isDeclared)
{
isDeclared = true;
sb.append("\n");
}
}
/**
* Creates a string representation of this input group. This
* is an xml representation.
*/
@Override
public String toString()
{
StringBuilder result = new StringBuilder();
result.append("\n");
if (fieldsArray != null)
{
for (Field> field : fieldsArray)
{
result.append(field);
}
}
result.append(" \n");
return result.toString();
}
/**
* Get the parent AppData for this group
*
* @return the parent
*/
public AppData getAppData()
{
return parent;
}
/**
* JAXB callback to set the parent object
*
* @param um the Unmarshaller
* @param parent the parent object (an AppData object)
*/
public void afterUnmarshal(Unmarshaller um, Object parent)
{
this.parent = (AppData)parent;
Map>> mapToObjectFieldLists =
new HashMap>>((int) (1.25 * fieldsArray.length + 1));
// Fix fields
for (Field> field : fieldsArray)
{
if (StringUtils.isNotEmpty(field.mapToObject))
{
field.mapToObject = this.parent.getBasePackage() + field.mapToObject;
}
// map fields by their mapToObject
List> tmpFields = mapToObjectFieldLists.get(field.getMapToObject());
if (tmpFields == null)
{
tmpFields = new ArrayList>(fieldsArray.length);
mapToObjectFieldLists.put(field.getMapToObject(), tmpFields);
}
tmpFields.add(field);
}
// Change the mapToObjectFields values to Field[]
mapToObjectFields = new HashMap[]>((int) (1.25 * fieldsArray.length + 1));
for (Map.Entry>> entry : mapToObjectFieldLists.entrySet())
{
mapToObjectFields.put(entry.getKey(),
entry.getValue().toArray(new Field[entry.getValue().size()]));
}
}
// ********** PoolableObjectFactory implementation ******************
public static class GroupFactory
extends BaseKeyedPooledObjectFactory
{
private final AppData appData;
public GroupFactory(AppData appData)
{
this.appData = appData;
}
/**
* Creates an instance that can be returned by the pool.
* @param key the name of the group
* @return an instance that can be returned by the pool.
* @throws IntakeException indicates that the group could not be retrieved
*/
@Override
public Group create(String key) throws IntakeException
{
return appData.getGroup(key);
}
/**
* @see org.apache.commons.pool2.BaseKeyedPooledObjectFactory#wrap(java.lang.Object)
*/
@Override
public PooledObject wrap(Group group)
{
return new DefaultPooledObject(group);
}
/**
* Uninitialize an instance to be returned to the pool.
* @param key the name of the group
* @param pooledGroup the instance to be passivated
*/
@Override
public void passivateObject(String key, PooledObject pooledGroup)
{
Group group = pooledGroup.getObject();
group.oid = null;
group.pp = null;
for (int i = group.fieldsArray.length - 1; i >= 0; i--)
{
group.fieldsArray[i].dispose();
}
group.isDeclared = false;
}
}
}