View Javadoc

1   /*
2    * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/event/EventEngine.java,v 1.40 2006/02/17 11:37:32 hkollmann Exp $
3    * $Revision: 1.40 $
4    * $Date: 2006/02/17 11:37:32 $
5    *
6    * DbForms - a Rapid Application Development Framework
7    * Copyright (C) 2001 Joachim Peer <joepeer@excite.com>
8    *
9    * This library is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU Lesser General Public
11   * License as published by the Free Software Foundation; either
12   * version 2.1 of the License, or (at your option) any later version.
13   *
14   * This library is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this library; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22   */
23  
24  package org.dbforms.event;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  import org.dbforms.config.Constants;
30  import org.dbforms.config.DbFormsConfig;
31  import org.dbforms.config.Table;
32  
33  import org.dbforms.event.eventtype.EventType;
34  import org.dbforms.event.eventtype.EventTypeUtil;
35  
36  import org.dbforms.util.ParseUtil;
37  import org.dbforms.util.StringUtil;
38  import org.dbforms.util.Util;
39  
40  import java.util.Enumeration;
41  import java.util.Vector;
42  
43  import javax.servlet.http.HttpServletRequest;
44  
45  
46  
47  /***
48   * This class is invoked by the Controller-Servlet. It parses a request to find
49   * out which Event(s) need to be instanciated. The fine-grained parsing
50   * (parsing of additional data, etc) is done by the WebEvent-Object itself (in
51   * order to hide complexity from this class and to keep the framework open for
52   * implementations of new Event-classes)
53   *
54   * @author Joe Peer
55   *
56   */
57  public class EventEngine {
58     /*** logging category for this class */
59     private static Log logCat = LogFactory.getLog(EventEngine.class.getName());
60  
61     /*** instance of DatabaseEventFactory */
62     private AbstractDatabaseEventFactory dbEventFactory = DatabaseEventFactoryImpl
63                                                   .instance();
64     private DbFormsConfig        config;
65     private HttpServletRequest   request;
66  
67     /*** instance of NavigationEventFactory */
68     private AbstractNavEventFactory navEventFactory = NavEventFactoryImpl.instance();
69  
70     /***
71      * this vector will contain which tables where on the jsp page: one jsp file
72      * may contain multiple dbforms, and each forms could contain many subforms
73      * nested inside
74      */
75     private Vector involvedTables;
76  
77     /***
78      * Constructor.
79      *
80      * @param request the request object
81      * @param config the configuration object
82      */
83     public EventEngine(HttpServletRequest request,
84                        DbFormsConfig      config) {
85        this.request = request;
86        this.config  = config;
87  
88        // find out which tables where on the jsp
89        involvedTables = parseInvolvedTables();
90     }
91  
92     /***
93      * Get the involvedTables attribute of the EventEngine class.
94      *
95      * @return the involvedTables attribute of the EventEngine class
96      */
97     public Vector getInvolvedTables() {
98        return involvedTables;
99     }
100 
101 
102    /***
103     * Generate the primary event object, depending on the data contained into
104     * the incoming http request object.
105     *
106     * @return a new WebEvent object
107     */
108    public AbstractWebEvent generatePrimaryEvent() {
109       AbstractWebEvent e      = null;
110       String   action = ParseUtil.getFirstParameterStartingWith(request, "ac_");
111       String   customEvent = ParseUtil.getParameter(request, "customEvent");
112 
113       if (Util.isNull(action) && !Util.isNull(customEvent)) {
114          // 2003-07-21-HKK: fixing NS 4.79 bug! (From bug list)
115          action = customEvent.trim();
116       }
117 
118       // NOOP EVENT
119       //
120       // family: web event
121       if (Util.isNull(action)) {
122          logCat.info("##### N O O P   ELEMENT ######");
123          e = new NoopEvent(-1, request, config);
124          initializeWebEvent(e);
125 
126          return e;
127       }
128 
129       // RELOAD EVENT
130       //
131       // family: web event
132       //
133       // ReloadEvent use to refresh field values from request object
134       // and to allow server side manipulation for these fields
135       //
136       // This event is created if customEvent is set to re_x_x!!
137       //
138       if (action.startsWith("re_")) {
139          logCat.info("##### RELOAD  EVENT ######");
140          e = new PageReloadEvent(StringUtil.getEmbeddedStringAsInteger(action,
141                                                                       2, '_'),
142                                  request, config);
143          e.setType(EventType.EVENT_NAVIGATION_RELOAD);
144          initializeWebEvent(e);
145 
146          return e;
147       }
148 
149       // make the image button data (if any) look like a submit button;
150       action = getImageButtonAction(action);
151 
152       // get the EventType class and identify the event type
153       // and use the related factory class to create the event;
154       EventType eventType = EventTypeUtil.getEventType(action);
155 
156       switch (eventType.getEventGroup()) {
157          case EventType.EVENT_GROUP_DATABASE: {
158             logCat.info("::generatePrimaryEvent - generating a database event");
159             e = dbEventFactory.createEvent(action, request, config);
160 
161             break;
162          }
163 
164          case EventType.EVENT_GROUP_NAVIGATION: {
165             logCat.info("::generatePrimaryEvent - generating a navigation event");
166             e = navEventFactory.createEvent(action, request, config);
167 
168             break;
169          }
170 
171          default: {
172             logCat.error("::generatePrimaryEvent - WARNING: generating NO event. Why ?");
173 
174             break;
175          }
176       }
177 
178       // setting the followUp attributes for the generated event
179       setEventFollowUp(e, action);
180 
181       return e;
182    }
183 
184 
185    /***
186     * Generate secundary events (update events)
187     *
188     * @param actTable DOCUMENT ME!
189     * @param exclude the parent web event (related to the main form)
190     *
191     * @return DOCUMENT ME!
192     */
193    public Enumeration generateSecundaryEvents(Table    actTable,
194                                               AbstractWebEvent exclude) {
195       Vector  result           = new Vector();
196       int     excludeTableId   = -1;
197       String  excludeKeyId     = null;
198       boolean collissionDanger = false;
199 
200       // first of all, we check if there is some real potential for collisions
201       // in the "to exclude"-event
202       if (exclude instanceof AbstractDatabaseEvent) {
203          collissionDanger = true;
204          excludeTableId   = exclude.getTable().getId();
205          excludeKeyId     = ((AbstractDatabaseEvent) exclude).getKeyId();
206       }
207 
208       String param = "autoupdate_" + String.valueOf(actTable.getId());
209       String res = ParseUtil.getParameter(request, param);
210 
211       // auto-updating may be disabled, so we have to check:
212       if (res.equalsIgnoreCase("true") || (res.equalsIgnoreCase("OnUpdateOnly") && exclude.getType().equals("update"))) {
213          // we can only update existing rowsets. so we just look for
214          // key-values
215          String      paramStub          = "k_" + actTable.getId() + "_";
216          Enumeration keysOfCurrentTable = ParseUtil.getParametersStartingWith(request,
217                                                                               paramStub)
218                                                    .elements();
219 
220          while (keysOfCurrentTable.hasMoreElements()) {
221             String aKeyParam = (String) keysOfCurrentTable.nextElement();
222             String keyId = aKeyParam.substring(paramStub.length());
223 
224             logCat.info("autoaupdate debug info: keyId=" + keyId
225                         + " excludeKeyId=" + excludeKeyId);
226 
227             if ( !collissionDanger || (excludeTableId != actTable.getId()) || !keyId.equals(excludeKeyId) ) {
228                AbstractDatabaseEvent e = dbEventFactory.createUpdateEvent(actTable.getId(),
229                                                                   keyId,
230                                                                   request,
231                                                                   config);
232                result.addElement(e);
233             }
234          }
235 
236          // now try the same with insert records
237          if ( !collissionDanger || (excludeTableId != actTable.getId()) ) {
238             paramStub = "f_" + actTable.getId() + "_"
239                         + Constants.FIELDNAME_INSERTPREFIX;
240             Vector v = ParseUtil.getParametersStartingWith(request, paramStub);
241             if (v.size() > 0) {
242                String aKeyParam = (String) v.firstElement();
243                String keyId = aKeyParam.substring(paramStub.length());
244                keyId = StringUtil.getEmbeddedString(keyId, 0, '_');
245                AbstractDatabaseEvent e = dbEventFactory.createInsertEvent(actTable
246                                                                   .getId(),
247                                                                   keyId,
248                                                                   request,
249                                                                   config);
250                result.addElement(e);
251             }
252          }
253       }
254 
255       return result.elements();
256    }
257 
258 
259    /***
260     * Sets the eventFollowUp and followUpOnError attributes of the input Event
261     * object
262     *
263     * @param e the event object
264     * @param action the action string
265     */
266    private void setEventFollowUp(AbstractWebEvent e,
267                                  String   action) {
268       // now we have to find the followup-site the app-developer wants us to
269       // display.
270       String followUp = ParseUtil.getParameter(request, "data" + action + "_fu");
271 
272       // if not...
273       // ...then check if §2-followup exists (should always exist!)
274       if (followUp == null) {
275          followUp = ParseUtil.getParameter(request,
276                                            "fu_"
277                                            + (((e == null)
278                                               || (e.getTable() == null)) ? (-1)
279                                                                          : e.getTable().getId()));
280       }
281 
282       logCat.info("setting follow up to:" + followUp);
283 
284       if (e != null) {
285          e.setFollowUp(followUp);
286       }
287 
288       String followUpOnError = ParseUtil.getParameter(request,
289                                                       "data" + action + "_fue");
290 
291       // if not...
292       // ...then check if §2-followup exists
293       if (followUpOnError == null) {
294          followUpOnError = ParseUtil.getParameter(request,
295                                                   "fue_"
296                                                   + (((e == null)
297                                                      || (e.getTable() == null))
298                                                      ? (-1)
299                                                      : e.getTable().getId()));
300       }
301 
302       // Still no followup on error - use general followup
303       if (followUpOnError == null) {
304          followUpOnError = followUp;
305       }
306 
307       logCat.info("setting follow up on Error to:" + followUpOnError);
308 
309       if (e != null) {
310          e.setFollowUpOnError(followUpOnError);
311       }
312    }
313 
314 
315    /***
316     * Make the image button data look like a submit button. <br>
317     * Image buttons submit different parameters than submit buttons for submit
318     * the browser sends one value parameter:ac_insert_0_root=Submit this bug!
319     * for image buttons, the browser returns two values, showing the x and y
320     * position of the mouse parameter ac_insert_0_root.y=24 parameter
321     * ac_insert_0_root.x=34
322     *
323     * @param action Description of the Parameter
324     *
325     * @return The imageButtonAction value
326     */
327    private String getImageButtonAction(String action) {
328       if (action.endsWith(".y") || action.endsWith(".x")) {
329          action = action.substring(0, action.length() - 2);
330       }
331 
332       logCat.info("::getImageButtonAction - action = [" + action + "]");
333 
334       return action;
335    }
336 
337 
338    /***
339     * PRIVATE METHODS here
340     *
341     * @param contextPath DOCUMENT ME!
342     * @param sourcePath DOCUMENT ME!
343     *
344     * @return DOCUMENT ME!
345     */
346    /***
347     * Sets the sourcePath attribute of the EventEngine object
348     *
349     * @param contextPath The new sourcePath value
350     * @param sourcePath The new sourcePath value
351     *
352     * @return Description of the Return Value
353     */
354    private String setSourcePath(String contextPath,
355                                 String sourcePath) {
356       if (!Util.isNull(contextPath)
357                 && !Util.isNull(sourcePath)
358                 && sourcePath.startsWith(contextPath)) {
359          // shouldn't! just make sure!
360          if (contextPath.endsWith("/")) {
361             sourcePath = sourcePath.substring(contextPath.length() - 1);
362          } else {
363             sourcePath = sourcePath.substring(contextPath.length());
364          }
365       }
366 
367       return sourcePath;
368    }
369 
370 
371    /***
372     * Initialize the input web event.
373     *
374     * @param e the web event to initialize
375     */
376    private void initializeWebEvent(AbstractWebEvent e) {
377       String contextPath = request.getContextPath();
378       String sourcePath = ParseUtil.getParameter(request, "source");
379 
380       logCat.info("sourcePath = " + sourcePath);
381       sourcePath = setSourcePath(contextPath, sourcePath);
382 
383       e.setFollowUp(sourcePath);
384       logCat.info("followup=" + e.getFollowUp());
385    }
386 
387 
388    /***
389     * Find out which tables where on the jsp (one jsp file may contain multiple
390     * dbforms, and each forms could contain many subforms nested inside!)
391     *
392     * @return the vector object containing the Table objects involved with the
393     *         main table
394     */
395    private Vector parseInvolvedTables() {
396       String[] invTables = ParseUtil.getParameterValues(request, "invtable");
397 
398       // in empty forms, for example, we don't have any involved tables..!
399       if (invTables == null) {
400          return null;
401       }
402 
403       Vector result = new Vector();
404 
405       for (int i = 0; i < invTables.length; i++) {
406          int   tableIndex = Integer.parseInt(invTables[i]);
407          Table t = config.getTable(tableIndex);
408 
409          result.addElement(t);
410       }
411 
412       return result;
413    }
414 }