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