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    }