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