All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.fulcrum.intake.model.Group Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
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;
        }
    }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy