    001    package org.apache.turbine.services.intake;
    002
    003
    004    /*
    005     * Licensed to the Apache Software Foundation (ASF) under one
    006     * or more contributor license agreements.  See the NOTICE file
    007     * distributed with this work for additional information
    008     * regarding copyright ownership.  The ASF licenses this file
    009     * to you under the Apache License, Version 2.0 (the
    010     * "License"); you may not use this file except in compliance
    011     * with the License.  You may obtain a copy of the License at
    012     *
    013     *   http://www.apache.org/licenses/LICENSE-2.0
    014     *
    015     * Unless required by applicable law or agreed to in writing,
    016     * software distributed under the License is distributed on an
    017     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    018     * KIND, either express or implied.  See the License for the
    019     * specific language governing permissions and limitations
    020     * under the License.
    021     */
    022
    023
    024    import java.util.HashMap;
    025    import java.util.Iterator;
    026    import java.util.List;
    027    import java.util.Map;
    028
    029    import org.apache.commons.logging.Log;
    030    import org.apache.commons.logging.LogFactory;
    031    import org.apache.fulcrum.intake.IntakeException;
    032    import org.apache.fulcrum.intake.IntakeServiceFacade;
    033    import org.apache.fulcrum.intake.Retrievable;
    034    import org.apache.fulcrum.intake.model.Group;
    035    import org.apache.fulcrum.parser.ValueParser;
    036    import org.apache.fulcrum.pool.Recyclable;
    037    import org.apache.turbine.services.pull.ApplicationTool;
    038    import org.apache.turbine.util.RunData;
    039
    040
    041    /**
    042     * The main class through which Intake is accessed.  Provides easy access
    043     * to the Fulcrum Intake component.
    044     *
    045     * @author <a href="mailto:jmcnally@collab.net">John D. 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="mailto:epugh@upstate.com">Eric Pugh</a>
    049     * @version $Id: IntakeTool.java 1078552 2011-03-06 19:58:46Z tv $
    050     */
    051    public class IntakeTool
    052            implements ApplicationTool, Recyclable
    053    {
    054        /** Used for logging */
    055        protected static Log log = LogFactory.getLog(IntakeTool.class);
    056
    057        /** Constant for default key */
    058        public static final String DEFAULT_KEY = "_0";
    059
    060        /** Constant for the hidden fieldname */
    061        public static final String INTAKE_GRP = "intake-grp";
    062
    063        /** Groups from intake.xml */
    064        protected HashMap<String, Group> groups;
    065
    066        /** ValueParser instance */
    067        protected ValueParser pp;
    068
    069        private final HashMap<String, Group> declaredGroups = new HashMap<String, Group>();
    070        private final StringBuffer allGroupsSB = new StringBuffer(256);
    071        private final StringBuffer groupSB = new StringBuffer(128);
    072
    073        /** The cache of PullHelpers. **/
    074        private final Map<String, IntakeTool.PullHelper> pullMap;
    075
    076        /**
    077         * Constructor
    078         */
    079        @SuppressWarnings("null")
    080        public IntakeTool()
    081        {
    082            String[] groupNames = IntakeServiceFacade.getGroupNames();
    083            int groupCount = 0;
    084            if (groupNames != null)
    085            {
    086                groupCount = groupNames.length;
    087            }
    088            groups = new HashMap<String, Group>((int) (1.25 * groupCount + 1));
    089            pullMap = new HashMap<String, IntakeTool.PullHelper>((int) (1.25 * groupCount + 1));
    090
    091            for (int i = groupCount - 1; i >= 0; i--)
    092            {
    093                pullMap.put(groupNames[i], new PullHelper(groupNames[i]));
    094            }
    095        }
    096
    097        /**
    098         * Prepares intake for a single request
    099         */
    100        public void init(Object runData)
    101        {
    102            this.pp = ((RunData) runData).getParameters();
    103
    104            String[] groupKeys = pp.getStrings(INTAKE_GRP);
    105            String[] groupNames = null;
    106            if (groupKeys == null || groupKeys.length == 0)
    107            {
    108                groupNames = IntakeServiceFacade.getGroupNames();
    109            }
    110            else
    111            {
    112                groupNames = new String[groupKeys.length];
    113                for (int i = groupKeys.length - 1; i >= 0; i--)
    114                {
    115                    groupNames[i] = IntakeServiceFacade.getGroupName(groupKeys[i]);
    116                }
    117
    118            }
    119
    120            for (int i = groupNames.length - 1; i >= 0; i--)
    121            {
    122                try
    123                {
    124                    List foundGroups = IntakeServiceFacade.getGroup(groupNames[i])
    125                        .getObjects(pp);
    126
    127                    if (foundGroups != null)
    128                    {
    129                        for (Iterator iter = foundGroups.iterator();
    130                             iter.hasNext();)
    131                        {
    132                            Group group = (Group) iter.next();
    133                            groups.put(group.getObjectKey(), group);
    134                        }
    135                    }
    136                }
    137                catch (IntakeException e)
    138                {
    139                    log.error(e);
    140                }
    141            }
    142        }
    143
    144        public void addGroupsToParameters(ValueParser vp)
    145        {
    146            for (Iterator i = groups.values().iterator(); i.hasNext();)
    147            {
    148                Group group = (Group) i.next();
    149                if (!declaredGroups.containsKey(group.getIntakeGroupName()))
    150                {
    151                    declaredGroups.put(group.getIntakeGroupName(), null);
    152                    vp.add("intake-grp", group.getGID());
    153                }
    154                vp.add(group.getGID(), group.getOID());
    155            }
    156            declaredGroups.clear();
    157        }
    158
    159        /**
    160         * A convenience method to write out the hidden form fields
    161         * that notify intake of the relevant groups.  It should be used
    162         * only in templates with 1 form.  In multiform templates, the groups
    163         * that are relevant for each form need to be declared using
    164         * $intake.newForm() and $intake.declareGroup($group) for the relevant
    165         * groups in the form.
    166         *
    167         */
    168        public String declareGroups()
    169        {
    170            allGroupsSB.setLength(0);
    171            for (Iterator i = groups.values().iterator(); i.hasNext();)
    172            {
    173                declareGroup((Group) i.next(), allGroupsSB);
    174            }
    175            return allGroupsSB.toString();
    176        }
    177
    178        /**
    179         * A convenience method to write out the hidden form fields
    180         * that notify intake of the group.
    181         */
    182        public String declareGroup(Group group)
    183        {
    184            groupSB.setLength(0);
    185            declareGroup(group, groupSB);
    186            return groupSB.toString();
    187        }
    188
    189        /**
    190         * xhtml valid hidden input field(s) that notifies intake of the
    191         * group's presence.
    192         */
    193        public void declareGroup(Group group, StringBuffer sb)
    194        {
    195            if (!declaredGroups.containsKey(group.getIntakeGroupName()))
    196            {
    197                declaredGroups.put(group.getIntakeGroupName(), null);
    198                sb.append("<input type=\"hidden\" name=\"")
    199                        .append(INTAKE_GRP)
    200                        .append("\" value=\"")
    201                        .append(group.getGID())
    202                        .append("\"/>\n");
    203            }
    204            group.appendHtmlFormInput(sb);
    205        }
    206
    207        public void newForm()
    208        {
    209            declaredGroups.clear();
    210            for (Iterator i = groups.values().iterator(); i.hasNext();)
    211            {
    212                ((Group) i.next()).resetDeclared();
    213            }
    214        }
    215
    216        /**
    217         * Implementation of ApplicationTool interface is not needed for this
    218         * tool as it is request scoped
    219         */
    220        public void refresh()
    221        {
    222            // empty
    223        }
    224
    225        /**
    226         * Inner class to present a nice interface to the template designer
    227         */
    228        public class PullHelper
    229        {
    230            /** Name of the group used by the pull helper */
    231            String groupName;
    232
    233            /**
    234             * Protected constructor to force use of factory method.
    235             *
    236             * @param groupName
    237             */
    238            protected PullHelper(String groupName)
    239            {
    240                this.groupName = groupName;
    241            }
    242
    243            /**
    244             * Populates the object with the default values from the XML File
    245             *
    246             * @return a Group object with the default values
    247             * @throws IntakeException
    248             */
    249            public Group getDefault()
    250                    throws IntakeException
    251            {
    252                return setKey(DEFAULT_KEY);
    253            }
    254
    255            /**
    256             * Calls setKey(key,true)
    257             *
    258             * @param key
    259             * @return an Intake Group
    260             * @throws IntakeException
    261             */
    262            public Group setKey(String key)
    263                    throws IntakeException
    264            {
    265                return setKey(key, true);
    266            }
    267
    268            /**
    269             *
    270             * @param key
    271             * @param create
    272             * @return an Intake Group
    273             * @throws IntakeException
    274             */
    275            public Group setKey(String key, boolean create)
    276                    throws IntakeException
    277            {
    278                Group g = null;
    279
    280                String inputKey = IntakeServiceFacade.getGroupKey(groupName) + key;
    281                if (groups.containsKey(inputKey))
    282                {
    283                    g = groups.get(inputKey);
    284                }
    285                else if (create)
    286                {
    287                    g = IntakeServiceFacade.getGroup(groupName);
    288                    groups.put(inputKey, g);
    289                    g.init(key, pp);
    290                }
    291
    292                return g;
    293            }
    294
    295            /**
    296             * maps an Intake Group to the values from a Retrievable object.
    297             *
    298             * @param obj A retrievable object
    299             * @return an Intake Group
    300             */
    301            public Group mapTo(Retrievable obj)
    302            {
    303                Group g = null;
    304
    305                try
    306                {
    307                    String inputKey = IntakeServiceFacade.getGroupKey(groupName)
    308                            + obj.getQueryKey();
    309                    if (groups.containsKey(inputKey))
    310                    {
    311                        g = groups.get(inputKey);
    312                    }
    313                    else
    314                    {
    315                        g = IntakeServiceFacade.getGroup(groupName);
    316                        groups.put(inputKey, g);
    317                    }
    318
    319                    return g.init(obj);
    320                }
    321                catch (IntakeException e)
    322                {
    323                    log.error(e);
    324                }
    325
    326                return null;
    327            }
    328        }
    329
    330        /**
    331         * get a specific group
    332         */
    333        public PullHelper get(String groupName)
    334        {
    335            return pullMap.get(groupName);
    336        }
    337
    338        /**
    339         * Get a specific group
    340         *
    341         * @param throwExceptions if false, exceptions will be supressed.
    342         * @throws IntakeException could not retrieve group
    343         */
    344        public PullHelper get(String groupName, boolean throwExceptions)
    345                throws IntakeException
    346        {
    347            return pullMap.get(groupName);
    348        }
    349
    350        /**
    351         * Loops through all of the Groups and checks to see if
    352         * the data within the Group is valid.
    353         */
    354        public boolean isAllValid()
    355        {
    356            boolean allValid = true;
    357            for (Iterator iter = groups.values().iterator(); iter.hasNext();)
    358            {
    359                Group group = (Group) iter.next();
    360                allValid &= group.isAllValid();
    361            }
    362            return allValid;
    363        }
    364
    365        /**
    366         * Get a specific group by name and key.
    367         */
    368        public Group get(String groupName, String key)
    369                throws IntakeException
    370        {
    371            if (groupName == null)
    372            {
    373                throw new IntakeException("IntakeServiceFacade.get: groupName == null");
    374            }
    375            if (key == null)
    376            {
    377                throw new IntakeException("IntakeServiceFacade.get: key == null");
    378            }
    379
    380            PullHelper ph = get(groupName);
    381            return (ph == null) ? null : ph.setKey(key);
    382        }
    383
    384        /**
    385         * Get a specific group by name and key. Also specify
    386         * whether or not you want to create a new group.
    387         */
    388        public Group get(String groupName, String key, boolean create)
    389                throws IntakeException
    390        {
    391            if (groupName == null)
    392            {
    393                throw new IntakeException("IntakeServiceFacade.get: groupName == null");
    394            }
    395            if (key == null)
    396            {
    397                throw new IntakeException("IntakeServiceFacade.get: key == null");
    398            }
    399
    400            PullHelper ph = get(groupName);
    401            return (ph == null) ? null : ph.setKey(key, create);
    402        }
    403
    404        /**
    405         * Removes group.  Primary use is to remove a group that has
    406         * been processed by an action and is no longer appropriate
    407         * in the view (screen).
    408         */
    409        public void remove(Group group)
    410        {
    411            if (group != null)
    412            {
    413                groups.remove(group.getObjectKey());
    414                group.removeFromRequest();
    415
    416                String[] groupKeys = pp.getStrings(INTAKE_GRP);
    417
    418                pp.remove(INTAKE_GRP);
    419
    420                            if (groupKeys != null)
    421                            {
    422                            for (int i = 0; i < groupKeys.length; i++)
    423                            {
    424                                if (!groupKeys[i].equals(group.getGID()))
    425                                {
    426                                     pp.add(INTAKE_GRP, groupKeys[i]);
    427                                }
    428                    }
    429                        }
    430
    431
    432                try
    433                {
    434                    IntakeServiceFacade.releaseGroup(group);
    435                }
    436                catch (IntakeException ie)
    437                {
    438                    log.error("Tried to release unknown group "
    439                            + group.getIntakeGroupName());
    440                }
    441            }
    442        }
    443
    444        /**
    445         * Removes all groups.  Primary use is to remove groups that have
    446         * been processed by an action and are no longer appropriate
    447         * in the view (screen).
    448         */
    449        public void removeAll()
    450        {
    451            Object[] allGroups = groups.values().toArray();
    452            for (int i = allGroups.length - 1; i >= 0; i--)
    453            {
    454                Group group = (Group) allGroups[i];
    455                remove(group);
    456            }
    457        }
    458
    459        /**
    460         * Get a Map containing all the groups.
    461         *
    462         * @return the Group Map
    463         */
    464        public Map getGroups()
    465        {
    466            return groups;
    467        }
    468
    469        // ****************** Recyclable implementation ************************
    470
    471        private boolean disposed;
    472
    473        /**
    474         * Recycles the object for a new client. Recycle methods with
    475         * parameters must be added to implementing object and they will be
    476         * automatically called by pool implementations when the object is
    477         * taken from the pool for a new client. The parameters must
    478         * correspond to the parameters of the constructors of the object.
    479         * For new objects, constructors can call their corresponding recycle
    480         * methods whenever applicable.
    481         * The recycle methods must call their super.
    482         */
    483        public void recycle()
    484        {
    485            disposed = false;
    486        }
    487
    488        /**
    489         * Disposes the object after use. The method is called
    490         * when the object is returned to its pool.
    491         * The dispose method must call its super.
    492         */
    493        public void dispose()
    494        {
    495            for (Iterator iter = groups.values().iterator(); iter.hasNext();)
    496            {
    497                Group g = (Group) iter.next();
    498
    499                try
    500                {
    501                    IntakeServiceFacade.releaseGroup(g);
    502                }
    503                catch (IntakeException ie)
    504                {
    505                    log.error("Tried to release unknown group "
    506                            + g.getIntakeGroupName());
    507                }
    508            }
    509
    510            groups.clear();
    511            declaredGroups.clear();
    512            pp = null;
    513
    514            disposed = true;
    515        }
    516
    517        /**
    518         * Checks whether the recyclable has been disposed.
    519         *
    520         * @return true, if the recyclable is disposed.
    521         */
    522        public boolean isDisposed()
    523        {
    524            return disposed;
    525        }
    526    }