View Javadoc
1   package org.apache.turbine.services.intake;
2   
3   
4   /*
5    * Licensed to the Apache Software Foundation (ASF) under one
6    * or more contributor license agreements.  See the NOTICE file
7    * distributed with this work for additional information
8    * regarding copyright ownership.  The ASF licenses this file
9    * to you under the Apache License, Version 2.0 (the
10   * "License"); you may not use this file except in compliance
11   * with the License.  You may obtain a copy of the License at
12   *
13   *   http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing,
16   * software distributed under the License is distributed on an
17   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18   * KIND, either express or implied.  See the License for the
19   * specific language governing permissions and limitations
20   * under the License.
21   */
22  
23  
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.fulcrum.intake.IntakeException;
31  import org.apache.fulcrum.intake.IntakeService;
32  import org.apache.fulcrum.intake.Retrievable;
33  import org.apache.fulcrum.intake.model.Group;
34  import org.apache.fulcrum.parser.ValueParser;
35  import org.apache.fulcrum.pool.Recyclable;
36  import org.apache.turbine.annotation.TurbineService;
37  import org.apache.turbine.services.pull.ApplicationTool;
38  import org.apache.turbine.util.RunData;
39  
40  
41  /**
42   * The main class through which Intake is accessed.  Provides easy access
43   * to the Fulcrum Intake component.
44   *
45   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
46   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
47   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
48   * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
49   * @version $Id: IntakeTool.java 1773378 2016-12-09 13:19:59Z tv $
50   */
51  public class IntakeTool
52          implements ApplicationTool, Recyclable
53  {
54      /** Used for logging */
55      protected static final Log log = LogFactory.getLog(IntakeTool.class);
56  
57      /** Constant for default key */
58      public static final String DEFAULT_KEY = "_0";
59  
60      /** Constant for the hidden fieldname */
61      public static final String INTAKE_GRP = "intake-grp";
62  
63      /** Groups from intake.xml */
64      protected HashMap<String, Group> groups = null;
65  
66      /** ValueParser instance */
67      protected ValueParser pp;
68  
69      private final HashMap<String, Group> declaredGroups = new HashMap<String, Group>();
70      private final StringBuilder allGroupsSB = new StringBuilder(256);
71      private final StringBuilder groupSB = new StringBuilder(128);
72  
73      /** The cache of PullHelpers. **/
74      private Map<String, IntakeTool.PullHelper> pullMap = null;
75  
76      /**
77       * The Intake service.
78       */
79      @TurbineService
80      protected IntakeService intakeService;
81  
82      /**
83       * Constructor
84       */
85      public IntakeTool()
86      {
87      }
88  
89      /**
90       * Prepares intake for a single request
91       */
92      @Override
93      public void init(Object runData)
94      {
95          if (groups == null) // Initialize only once
96          {
97              String[] groupNames = intakeService.getGroupNames();
98              int groupCount = 0;
99              if (groupNames != null)
100             {
101                 groupCount = groupNames.length;
102             }
103             groups = new HashMap<String, Group>((int) (1.25 * groupCount + 1));
104             pullMap = new HashMap<String, IntakeTool.PullHelper>((int) (1.25 * groupCount + 1));
105 
106             for (int i = groupCount - 1; i >= 0; i--)
107             {
108                 pullMap.put(groupNames[i], new PullHelper(groupNames[i]));
109             }
110         }
111 
112         this.pp = ((RunData) runData).getParameters();
113 
114         String[] groupKeys = pp.getStrings(INTAKE_GRP);
115         String[] groupNames = null;
116         if (groupKeys == null || groupKeys.length == 0)
117         {
118             groupNames = intakeService.getGroupNames();
119         }
120         else
121         {
122             groupNames = new String[groupKeys.length];
123             for (int i = groupKeys.length - 1; i >= 0; i--)
124             {
125                 groupNames[i] = intakeService.getGroupName(groupKeys[i]);
126             }
127 
128         }
129 
130         for (int i = groupNames.length - 1; i >= 0; i--)
131         {
132             try
133             {
134                 List<Group> foundGroups = intakeService.getGroup(groupNames[i])
135                     .getObjects(pp);
136 
137                 if (foundGroups != null)
138                 {
139                     for (Group group : foundGroups)
140                     {
141                         groups.put(group.getObjectKey(), group);
142                     }
143                 }
144             }
145             catch (IntakeException e)
146             {
147                 log.error(e);
148             }
149         }
150     }
151 
152     /**
153      * Add all registered group ids to the value parser
154      *
155      * @param vp the value parser
156      */
157     public void addGroupsToParameters(ValueParser vp)
158     {
159         for (Group group : groups.values())
160         {
161             if (!declaredGroups.containsKey(group.getIntakeGroupName()))
162             {
163                 declaredGroups.put(group.getIntakeGroupName(), null);
164                 vp.add("intake-grp", group.getGID());
165             }
166             vp.add(group.getGID(), group.getOID());
167         }
168         declaredGroups.clear();
169     }
170 
171     /**
172      * A convenience method to write out the hidden form fields
173      * that notify intake of the relevant groups.  It should be used
174      * only in templates with 1 form.  In multiform templates, the groups
175      * that are relevant for each form need to be declared using
176      * $intake.newForm() and $intake.declareGroup($group) for the relevant
177      * groups in the form.
178      *
179      * @return the HTML that declares all groups to Intake in hidden input fields
180      *
181      */
182     public String declareGroups()
183     {
184         allGroupsSB.setLength(0);
185         for (Group group : groups.values())
186         {
187             declareGroup(group, allGroupsSB);
188         }
189         return allGroupsSB.toString();
190     }
191 
192     /**
193      * A convenience method to write out the hidden form fields
194      * that notify intake of the group.
195      *
196      * @param group the group to declare
197      * @return the HTML that declares the group to Intake in a hidden input field
198      */
199     public String declareGroup(Group group)
200     {
201         groupSB.setLength(0);
202         declareGroup(group, groupSB);
203         return groupSB.toString();
204     }
205 
206     /**
207      * xhtml valid hidden input field(s) that notifies intake of the
208      * group's presence.
209      * @param group the group to declare
210      * @param sb a String Builder where the hidden field HTML will be appended
211      */
212     public void declareGroup(Group group, StringBuilder sb)
213     {
214         if (!declaredGroups.containsKey(group.getIntakeGroupName()))
215         {
216             declaredGroups.put(group.getIntakeGroupName(), null);
217             sb.append("<input type=\"hidden\" name=\"")
218                     .append(INTAKE_GRP)
219                     .append("\" value=\"")
220                     .append(group.getGID())
221                     .append("\"/>\n");
222         }
223         group.appendHtmlFormInput(sb);
224     }
225 
226     /**
227      * Declare that a new form starts
228      */
229     public void newForm()
230     {
231         declaredGroups.clear();
232         for (Group group : groups.values())
233         {
234             group.resetDeclared();
235         }
236     }
237 
238     /**
239      * Implementation of ApplicationTool interface is not needed for this
240      * tool as it is request scoped
241      */
242     @Override
243     public void refresh()
244     {
245         // empty
246     }
247 
248     /**
249      * Inner class to present a nice interface to the template designer
250      */
251     public class PullHelper
252     {
253         /** Name of the group used by the pull helper */
254         String groupName;
255 
256         /**
257          * Protected constructor to force use of factory method.
258          *
259          * @param groupName the group name
260          */
261         protected PullHelper(String groupName)
262         {
263             this.groupName = groupName;
264         }
265 
266         /**
267          * Populates the object with the default values from the XML File
268          *
269          * @return a Group object with the default values
270          * @throws IntakeException if getting the group fails
271          */
272         public Group getDefault()
273                 throws IntakeException
274         {
275             return setKey(DEFAULT_KEY);
276         }
277 
278         /**
279          * Calls setKey(key,true)
280          *
281          * @param key the group key
282          * @return an Intake Group
283          * @throws IntakeException if getting the group fails
284          */
285         public Group setKey(String key)
286                 throws IntakeException
287         {
288             return setKey(key, true);
289         }
290 
291         /**
292          * Return the group identified by its key
293          *
294          * @param key the group key
295          * @param create true if a non-existing group should be created
296          * @return an Intake Group
297          * @throws IntakeException if getting the group fails
298          */
299         public Group setKey(String key, boolean create)
300                 throws IntakeException
301         {
302             Group g = null;
303 
304             String inputKey = intakeService.getGroupKey(groupName) + key;
305             if (groups.containsKey(inputKey))
306             {
307                 g = groups.get(inputKey);
308             }
309             else if (create)
310             {
311                 g = intakeService.getGroup(groupName);
312                 groups.put(inputKey, g);
313                 g.init(key, pp);
314             }
315 
316             return g;
317         }
318 
319         /**
320          * maps an Intake Group to the values from a Retrievable object.
321          *
322          * @param obj A retrievable object
323          * @return an Intake Group
324          */
325         public Group mapTo(Retrievable obj)
326         {
327             Group g = null;
328 
329             try
330             {
331                 String inputKey = intakeService.getGroupKey(groupName)
332                         + obj.getQueryKey();
333                 if (groups.containsKey(inputKey))
334                 {
335                     g = groups.get(inputKey);
336                 }
337                 else
338                 {
339                     g = intakeService.getGroup(groupName);
340                     groups.put(inputKey, g);
341                 }
342 
343                 return g.init(obj);
344             }
345             catch (IntakeException e)
346             {
347                 log.error(e);
348             }
349 
350             return null;
351         }
352     }
353 
354     /**
355      * get a specific group
356      * @param groupName the name of the group
357      * @return a {@link PullHelper} wrapper around the group
358      */
359     public PullHelper get(String groupName)
360     {
361         return pullMap.get(groupName);
362     }
363 
364     /**
365      * Get a specific group
366      *
367      * @param groupName the name of the group
368      * @param throwExceptions if false, exceptions will be suppressed.
369      * @return a {@link PullHelper} wrapper around the group
370      * @throws IntakeException could not retrieve group
371      */
372     public PullHelper get(String groupName, boolean throwExceptions)
373             throws IntakeException
374     {
375         return pullMap.get(groupName);
376     }
377 
378     /**
379      * Loops through all of the Groups and checks to see if
380      * the data within the Group is valid.
381      * @return true if all groups are valid
382      */
383     public boolean isAllValid()
384     {
385         boolean allValid = true;
386         for (Group group : groups.values())
387         {
388             allValid &= group.isAllValid();
389         }
390         return allValid;
391     }
392 
393     /**
394      * Get a specific group by name and key.
395      * @param groupName the name of the group
396      * @param key the key for the group
397      * @return the {@link Group}
398      * @throws IntakeException if the group could not be retrieved
399      */
400     public Group get(String groupName, String key)
401             throws IntakeException
402     {
403         return get(groupName, key, true);
404     }
405 
406     /**
407      * Get a specific group by name and key. Also specify
408      * whether or not you want to create a new group.
409      * @param groupName the name of the group
410      * @param key the key for the group
411      * @param create true if a new group should be created
412      * @return the {@link Group}
413      * @throws IntakeException if the group could not be retrieved
414      */
415     public Group get(String groupName, String key, boolean create)
416             throws IntakeException
417     {
418         if (groupName == null)
419         {
420             throw new IntakeException("intakeService.get: groupName == null");
421         }
422         if (key == null)
423         {
424             throw new IntakeException("intakeService.get: key == null");
425         }
426 
427         PullHelper ph = get(groupName);
428         return (ph == null) ? null : ph.setKey(key, create);
429     }
430 
431     /**
432      * Removes group.  Primary use is to remove a group that has
433      * been processed by an action and is no longer appropriate
434      * in the view (screen).
435      * @param group the group instance to remove
436      */
437     public void remove(Group group)
438     {
439         if (group != null)
440         {
441             groups.remove(group.getObjectKey());
442             group.removeFromRequest();
443 
444             String[] groupKeys = pp.getStrings(INTAKE_GRP);
445 
446             pp.remove(INTAKE_GRP);
447 
448 			if (groupKeys != null)
449 			{
450 		        for (int i = 0; i < groupKeys.length; i++)
451 		        {
452 		            if (!groupKeys[i].equals(group.getGID()))
453 		            {
454 		                 pp.add(INTAKE_GRP, groupKeys[i]);
455 		            }
456                 }
457 		    }
458 
459             try
460             {
461                 intakeService.releaseGroup(group);
462             }
463             catch (IntakeException ie)
464             {
465                 log.error("Tried to release unknown group "
466                         + group.getIntakeGroupName());
467             }
468         }
469     }
470 
471     /**
472      * Removes all groups.  Primary use is to remove groups that have
473      * been processed by an action and are no longer appropriate
474      * in the view (screen).
475      */
476     public void removeAll()
477     {
478         Object[] allGroups = groups.values().toArray();
479         for (int i = allGroups.length - 1; i >= 0; i--)
480         {
481             Group group = (Group) allGroups[i];
482             remove(group);
483         }
484     }
485 
486     /**
487      * Get a Map containing all the groups.
488      *
489      * @return the Group Map
490      */
491     public Map<String, Group> getGroups()
492     {
493         return groups;
494     }
495 
496     // ****************** Recyclable implementation ************************
497 
498     private boolean disposed;
499 
500     /**
501      * Recycles the object for a new client. Recycle methods with
502      * parameters must be added to implementing object and they will be
503      * automatically called by pool implementations when the object is
504      * taken from the pool for a new client. The parameters must
505      * correspond to the parameters of the constructors of the object.
506      * For new objects, constructors can call their corresponding recycle
507      * methods whenever applicable.
508      * The recycle methods must call their super.
509      */
510     @Override
511     public void recycle()
512     {
513         disposed = false;
514     }
515 
516     /**
517      * Disposes the object after use. The method is called
518      * when the object is returned to its pool.
519      * The dispose method must call its super.
520      */
521     @Override
522     public void dispose()
523     {
524         for (Group group : groups.values())
525         {
526             try
527             {
528                 intakeService.releaseGroup(group);
529             }
530             catch (IntakeException ie)
531             {
532                 log.error("Tried to release unknown group "
533                         + group.getIntakeGroupName());
534             }
535         }
536 
537         groups.clear();
538         declaredGroups.clear();
539         pp = null;
540 
541         disposed = true;
542     }
543 
544     /**
545      * Checks whether the recyclable has been disposed.
546      *
547      * @return true, if the recyclable is disposed.
548      */
549     @Override
550     public boolean isDisposed()
551     {
552         return disposed;
553     }
554 }