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 }