Coverage Report - org.apache.turbine.services.intake.IntakeTool
 
Classes in this File Line Coverage Branch Coverage Complexity
IntakeTool
31%
35/110
22%
11/48
2,417
IntakeTool$PullHelper
45%
11/24
33%
2/6
2,417
 
 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  1
     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  2
     protected HashMap<String, Group> groups = null;
 65  
 
 66  
     /** ValueParser instance */
 67  
     protected ValueParser pp;
 68  
 
 69  2
     private final HashMap<String, Group> declaredGroups = new HashMap<String, Group>();
 70  2
     private final StringBuilder allGroupsSB = new StringBuilder(256);
 71  2
     private final StringBuilder groupSB = new StringBuilder(128);
 72  
 
 73  
     /** The cache of PullHelpers. **/
 74  2
     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  2
     {
 87  2
     }
 88  
 
 89  
     /**
 90  
      * Prepares intake for a single request
 91  
      */
 92  
     @Override
 93  
     public void init(Object runData)
 94  
     {
 95  2
         if (groups == null) // Initialize only once
 96  
         {
 97  2
             String[] groupNames = intakeService.getGroupNames();
 98  2
             int groupCount = 0;
 99  2
             if (groupNames != null)
 100  
             {
 101  2
                 groupCount = groupNames.length;
 102  
             }
 103  2
             groups = new HashMap<String, Group>((int) (1.25 * groupCount + 1));
 104  2
             pullMap = new HashMap<String, IntakeTool.PullHelper>((int) (1.25 * groupCount + 1));
 105  
 
 106  4
             for (int i = groupCount - 1; i >= 0; i--)
 107  
             {
 108  2
                 pullMap.put(groupNames[i], new PullHelper(groupNames[i]));
 109  
             }
 110  
         }
 111  
 
 112  2
         this.pp = ((RunData) runData).getParameters();
 113  
 
 114  2
         String[] groupKeys = pp.getStrings(INTAKE_GRP);
 115  2
         String[] groupNames = null;
 116  2
         if (groupKeys == null || groupKeys.length == 0)
 117  
         {
 118  2
             groupNames = intakeService.getGroupNames();
 119  
         }
 120  
         else
 121  
         {
 122  0
             groupNames = new String[groupKeys.length];
 123  0
             for (int i = groupKeys.length - 1; i >= 0; i--)
 124  
             {
 125  0
                 groupNames[i] = intakeService.getGroupName(groupKeys[i]);
 126  
             }
 127  
 
 128  
         }
 129  
 
 130  4
         for (int i = groupNames.length - 1; i >= 0; i--)
 131  
         {
 132  
             try
 133  
             {
 134  2
                 List<Group> foundGroups = intakeService.getGroup(groupNames[i])
 135  
                     .getObjects(pp);
 136  
 
 137  2
                 if (foundGroups != null)
 138  
                 {
 139  0
                     for (Group group : foundGroups)
 140  
                     {
 141  0
                         groups.put(group.getObjectKey(), group);
 142  0
                     }
 143  
                 }
 144  
             }
 145  0
             catch (IntakeException e)
 146  
             {
 147  0
                 log.error(e);
 148  2
             }
 149  
         }
 150  2
     }
 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  0
         for (Group group : groups.values())
 160  
         {
 161  0
             if (!declaredGroups.containsKey(group.getIntakeGroupName()))
 162  
             {
 163  0
                 declaredGroups.put(group.getIntakeGroupName(), null);
 164  0
                 vp.add("intake-grp", group.getGID());
 165  
             }
 166  0
             vp.add(group.getGID(), group.getOID());
 167  0
         }
 168  0
         declaredGroups.clear();
 169  0
     }
 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  0
         allGroupsSB.setLength(0);
 185  0
         for (Group group : groups.values())
 186  
         {
 187  0
             declareGroup(group, allGroupsSB);
 188  0
         }
 189  0
         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  0
         groupSB.setLength(0);
 202  0
         declareGroup(group, groupSB);
 203  0
         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  0
         if (!declaredGroups.containsKey(group.getIntakeGroupName()))
 215  
         {
 216  0
             declaredGroups.put(group.getIntakeGroupName(), null);
 217  0
             sb.append("<input type=\"hidden\" name=\"")
 218  
                     .append(INTAKE_GRP)
 219  
                     .append("\" value=\"")
 220  
                     .append(group.getGID())
 221  
                     .append("\"/>\n");
 222  
         }
 223  0
         group.appendHtmlFormInput(sb);
 224  0
     }
 225  
 
 226  
     /**
 227  
      * Declare that a new form starts
 228  
      */
 229  
     public void newForm()
 230  
     {
 231  0
         declaredGroups.clear();
 232  0
         for (Group group : groups.values())
 233  
         {
 234  0
             group.resetDeclared();
 235  0
         }
 236  0
     }
 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  1
     }
 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  2
         {
 263  2
             this.groupName = groupName;
 264  2
         }
 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  0
             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  0
             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  1
             Group g = null;
 303  
 
 304  1
             String inputKey = intakeService.getGroupKey(groupName) + key;
 305  1
             if (groups.containsKey(inputKey))
 306  
             {
 307  0
                 g = groups.get(inputKey);
 308  
             }
 309  1
             else if (create)
 310  
             {
 311  1
                 g = intakeService.getGroup(groupName);
 312  1
                 groups.put(inputKey, g);
 313  1
                 g.init(key, pp);
 314  
             }
 315  
 
 316  1
             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  0
             Group g = null;
 328  
 
 329  
             try
 330  
             {
 331  0
                 String inputKey = intakeService.getGroupKey(groupName)
 332  
                         + obj.getQueryKey();
 333  0
                 if (groups.containsKey(inputKey))
 334  
                 {
 335  0
                     g = groups.get(inputKey);
 336  
                 }
 337  
                 else
 338  
                 {
 339  0
                     g = intakeService.getGroup(groupName);
 340  0
                     groups.put(inputKey, g);
 341  
                 }
 342  
 
 343  0
                 return g.init(obj);
 344  
             }
 345  0
             catch (IntakeException e)
 346  
             {
 347  0
                 log.error(e);
 348  
             }
 349  
 
 350  0
             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  1
         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  0
         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  0
         boolean allValid = true;
 386  0
         for (Group group : groups.values())
 387  
         {
 388  0
             allValid &= group.isAllValid();
 389  0
         }
 390  0
         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  1
         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  1
         if (groupName == null)
 419  
         {
 420  0
             throw new IntakeException("intakeService.get: groupName == null");
 421  
         }
 422  1
         if (key == null)
 423  
         {
 424  0
             throw new IntakeException("intakeService.get: key == null");
 425  
         }
 426  
 
 427  1
         PullHelper ph = get(groupName);
 428  1
         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  0
         if (group != null)
 440  
         {
 441  0
             groups.remove(group.getObjectKey());
 442  0
             group.removeFromRequest();
 443  
 
 444  0
             String[] groupKeys = pp.getStrings(INTAKE_GRP);
 445  
 
 446  0
             pp.remove(INTAKE_GRP);
 447  
 
 448  0
                         if (groupKeys != null)
 449  
                         {
 450  0
                         for (int i = 0; i < groupKeys.length; i++)
 451  
                         {
 452  0
                             if (!groupKeys[i].equals(group.getGID()))
 453  
                             {
 454  0
                                  pp.add(INTAKE_GRP, groupKeys[i]);
 455  
                             }
 456  
                 }
 457  
                     }
 458  
 
 459  
             try
 460  
             {
 461  0
                 intakeService.releaseGroup(group);
 462  
             }
 463  0
             catch (IntakeException ie)
 464  
             {
 465  0
                 log.error("Tried to release unknown group "
 466  
                         + group.getIntakeGroupName());
 467  0
             }
 468  
         }
 469  0
     }
 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  0
         Object[] allGroups = groups.values().toArray();
 479  0
         for (int i = allGroups.length - 1; i >= 0; i--)
 480  
         {
 481  0
             Group group = (Group) allGroups[i];
 482  0
             remove(group);
 483  
         }
 484  0
     }
 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  2
         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  0
         disposed = false;
 514  0
     }
 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  0
         for (Group group : groups.values())
 525  
         {
 526  
             try
 527  
             {
 528  0
                 intakeService.releaseGroup(group);
 529  
             }
 530  0
             catch (IntakeException ie)
 531  
             {
 532  0
                 log.error("Tried to release unknown group "
 533  
                         + group.getIntakeGroupName());
 534  0
             }
 535  0
         }
 536  
 
 537  0
         groups.clear();
 538  0
         declaredGroups.clear();
 539  0
         pp = null;
 540  
 
 541  0
         disposed = true;
 542  0
     }
 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  0
         return disposed;
 553  
     }
 554  
 }