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 }