View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Blacksmith, Inc.
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.web;
20  
21  import java.io.IOException;
22  import java.util.Iterator;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  
27  /***
28  * A pure java implementation of WOContext.
29  *
30  * @author michael@mpowers.net
31  * @author $Author: cgruber $
32  * @version $Revision: 905 $
33  */
34  public class WOContext
35  {
36  	private WOSession session;
37  	private WORequest request;
38  	private WOResponse response;
39      private List elementStack;
40  	private boolean isInForm;
41  	private boolean isDistributionEnabled;
42      private static final String EMPTY = "";
43      private static final String SEP = ".";
44      private static final String ZERO = "0";
45      private static final String HTTP = "http://";
46      private static final String HTTPS = "https://";
47  
48  	// package access
49  	WOComponent page;
50  	WOComponent component;
51  	StringBuffer elementID;
52      
53      private static volatile int contextCounter = 0;
54      private String contextID;
55      
56  	/***
57  	* Default constructor performs necessary initialization.
58  	* Subclassers should override the static factory method below.
59  	*/
60      public WOContext ()
61      {
62          contextID = null;
63      	elementID = new StringBuffer();
64          elementStack = new LinkedList();
65      }
66      
67  	/***
68  	* Preferred constructor performs necessary initialization.
69  	* Subclassers should override this method.
70  	*/
71      public WOContext (WORequest aRequest)
72      {
73      	this();
74  	    request = aRequest;
75          response = new WOResponse();
76      }
77      
78  	/*** 
79  	* Simply calls the preferred constructor.
80  	* Included for compatibility.
81  	*/
82      public static WOContext contextWithRequest (WORequest aRequest)
83      {
84          String id = Integer.toString( contextCounter++ );
85      	WOContext result = new WOContext( aRequest );
86          result.contextID = id;
87          return result;
88      }
89      
90  	/*** 
91  	* Returns the context id.
92  	*/
93      public String contextID ()
94      {
95      	return contextID;
96      }
97      
98  	/*** 
99  	* Returns the sender id, or null if it doesn't exist.
100 	*/
101     public String senderID ()
102     {
103     	return request.senderID();
104     }
105     
106 	/*** 
107 	* Returns the element id, or null if it doesn't exist.
108 	*/
109     public String elementID ()
110     {
111     	return elementID.toString();
112     }
113 
114 	/*** 
115 	* Returns this context's application, or null if it doesn't exist.
116 	*/
117     public WOApplication application ()
118     {
119     	return request.application();
120     }
121      
122 	/*** 
123     * Returns whether a session has been created for the associated request.
124 	*/
125     public boolean hasSession ()
126     {
127     	return ( session != null );
128     }
129     
130 	/*** 
131 	* Returns this context's session, creating one if it doesn't exist.
132 	*/
133     public WOSession session ()
134     {
135     	if ( session == null )
136 	    {
137             // so far we can't figure out how the direct action handler can avoid creating a session
138             throw new RuntimeException( "WOContext.session: Lazy instantiation not yet implemented." );
139 /*            
140             session = application().restoreSessionWithID( 
141                 request.sessionID(), this );
142             if ( session == null )
143             {
144                 session = application().createSessionForRequest( request );
145                 session.setContext( this );
146             }
147 */            
148 	    }
149 	    return session;
150     }
151     
152     /***
153     * Package access only.
154     */
155     void setSession( WOSession aSession )
156     {
157         session = aSession;
158     }
159     
160 	/*** 
161 	* Returns this context's request.
162 	*/
163     public WORequest request ()
164     {
165     	return request;
166     }
167     
168 	/*** 
169 	* Returns this context's response.
170 	*/
171     public WOResponse response ()
172     {
173     	return response;
174     }
175     
176 	/*** 
177 	* Returns the current page.
178 	*/
179     public WOComponent page ()
180     {
181         return (WOComponent) elementStack.get( elementStack.size()-1 );
182     }
183     
184 	/*** 
185 	* Returns the current component.
186 	*/
187     public WOComponent component ()
188     {
189         Object o;
190     	Iterator i = elementStack.iterator();
191         while ( i.hasNext() )
192         {
193             o = i.next();
194             if ( o instanceof WOComponent ) return (WOComponent) o;
195         }
196         return null;
197     }	
198 
199     /***
200     * Returns the current component's parent.
201     */    
202     WOComponent parent ()
203     {
204         Object o;
205     	Iterator i = elementStack.iterator();
206         if ( i.hasNext() )
207         {
208             // skip current component
209             o = i.next();
210         }
211         while ( i.hasNext() )
212         {
213             o = i.next();
214             if ( o instanceof WOComponent ) 
215             {
216                 return (WOComponent) o;
217             }
218         }
219         return null;
220     }
221     
222     /***
223     * Pushes an element onto the stack.
224     * Package access only.
225     */
226     void pushElement( WOElement aComponent )
227     {
228         elementStack.add( 0, aComponent );
229     }
230     
231     /***
232     * Pops an element off the stack.
233     * Package access only.
234     */
235     WOElement popElement()
236     {
237         return (WOElement) elementStack.remove(0);
238     }
239     
240 	/*** 
241 	* Returns whether the current context is in a form.
242 	*/
243     public boolean isInForm ()
244     {
245 	    return isInForm;
246 	}
247     
248 	/*** 
249 	* Sets whether the current context is in a WOForm. 
250 	*/
251     public void setInForm (boolean inForm)
252     {
253     	isInForm = inForm;
254     }
255     
256 	/*** 
257 	* Appends the specified string to the end of the element id.
258 	* For example, if the element id is "0.1", sending "Test" 
259 	* changes the element id to "0.1.Test".
260 	*/
261     public void appendElementIDComponent (String aString)
262     {
263         if ( elementID.length() > 0 )
264         {
265     	    elementID.append( SEP );
266         }
267         elementID.append( aString );
268     }
269     
270 	/*** 
271 	* Appends a zero to the element id to represent the first
272 	* new child component.  For example, if the element id is "0.1",
273 	* calling this changes the element id to "0.1.0".
274 	*/
275     public void appendZeroElementIDComponent ()
276     {
277         if ( elementID.length() > 0 )
278         {
279             elementID.append( SEP );
280         }
281         elementID.append( ZERO );
282     }
283     
284 	/*** 
285 	* Increments the last component of the element id.
286 	* For example, if the element id is "0.1", calling this
287 	* changes the element id to "0.2".
288 	*/
289     public void incrementLastElementIDComponent ()
290     {        
291 	    String last;
292         String id = elementID.toString();
293         int index = id.lastIndexOf( SEP );
294 	    if ( index == -1 )
295         {
296             last = id;
297         }
298         else
299         {
300             last = id.substring( index + 1 );
301         }
302         
303         deleteLastElementIDComponent();
304         
305         try
306         {
307             appendElementIDComponent( 
308                 Integer.toString( Integer.parseInt( last ) + 1 ) );
309         }
310         catch ( Exception exc )
311         {
312             System.err.println( "Error parsing id: " + last );
313             appendZeroElementIDComponent();
314         }
315         //System.out.println( "WOContext: " + elementID );        
316     }
317     
318 	/*** 
319 	* Deletes the last component of the element id.
320 	* For example, if the element id is "0.1", callling this
321 	* changes the element id to "0".
322 	*/
323     public void deleteLastElementIDComponent ()
324     {
325 	    int index = elementID.toString().lastIndexOf( SEP );
326 	    if ( index == -1 )
327         {
328             elementID.setLength( 0 );   
329         }
330         else
331         {
332             elementID.setLength( index );
333         }
334     }
335     
336 	/*** 
337 	* Deletes all components of the element id.
338 	* This makes the element id an empty string.
339 	*/
340     public void deleteAllElementIDComponents ()
341     {
342     	elementID.setLength( 0 );
343     }
344     
345 	/*** 
346 	* Returns a URL for the named action with query parameters
347 	* as specified by the dictionary.
348 	*/
349     public String directActionURLForActionNamed (
350     	String anActionName, Map aQueryDict)
351     {
352     	StringBuffer query = new StringBuffer();
353         
354         try
355         {
356             String key;
357             Iterator i = aQueryDict.keySet().iterator();
358             while ( i.hasNext() )
359             {
360                 key = i.next().toString();
361                 query.append( URI.encode( key, 
362                     URI.allowed_within_query ) );
363                 query.append( '=' );
364                 query.append( URI.encode( aQueryDict.get( key ).toString(), 
365                     URI.allowed_within_query ) );
366                 if ( i.hasNext() )
367                 {
368                     query.append( '&' );
369                 }
370             }
371         } 
372         catch ( IOException exc )
373         {
374             // report error
375             System.err.println( 
376                 "directActionURLForActionNamed: " + anActionName + " : " + aQueryDict );
377             System.err.println( exc );
378             
379             // delete query string
380             query = new StringBuffer();
381         }
382         
383         return urlWithRequestHandlerKey(
384             WOApplication.directActionRequestHandlerKey(), 
385             anActionName, query.toString() );
386     }
387     
388 	/*** 
389 	* Returns the complete URL for the current component action.
390 	*/
391     public String componentActionURL ()
392     {
393         StringBuffer buffer = new StringBuffer();
394         buffer.append( request().applicationName() );
395         buffer.append( '/' );
396         buffer.append( WOApplication.application().componentRequestHandlerKey() );
397         buffer.append( '/' );
398         buffer.append( page().name() );
399         buffer.append( '/' );
400         buffer.append( contextID );
401         buffer.append( '/' );
402         buffer.append( elementID );
403     	return buffer.toString();
404     }
405     
406 	/*** 
407 	* Returns a URL relative to the servlet for the specified 
408 	* request handler, action, and query string.
409 	*/
410     public String urlWithRequestHandlerKey (
411     	String aRequestHandlerKey, String aPath, String aQueryString)
412     {
413         StringBuffer buffer = new StringBuffer();
414         buffer.append( request().applicationName() );
415         buffer.append( '/' );
416         buffer.append( aRequestHandlerKey );
417         buffer.append( '/' );
418         buffer.append( aPath );
419         if ( aQueryString != null && aQueryString.trim().length() > 0 )
420         {
421             buffer.append( '?' );
422             buffer.append( aQueryString );
423         }
424     	return buffer.toString();
425     }
426     
427 	/*** 
428 	* Returns the complete URL for the specified request handler,
429 	* path, and query string.  isSecure determines the protocol:
430 	* http or https.  port is appended to the protocol, if zero,
431 	* the port is omitted from the URL.
432 	*/
433     public String completeURLWithRequestHandlerKey (     
434     	String aRequestHandlerKey, String aPath, String aQueryString,
435 		boolean isSecure, int port)
436     {
437     	StringBuffer buffer = new StringBuffer();
438         
439         if ( isSecure )
440         {
441             buffer.append( HTTPS );
442         }
443         else
444         {
445             buffer.append( HTTP );
446         }
447         
448         buffer.append( request.applicationHost() );
449         
450         if ( port != 0 && port != 80 )
451         {
452             buffer.append( ':' );
453             buffer.append( Integer.toString( port ) );
454         }
455         
456         buffer.append( urlWithRequestHandlerKey( 
457             aRequestHandlerKey, aPath, aQueryString ) );
458         
459         return buffer.toString();
460     }
461     
462 	/*** 
463 	* Sets whether distribution is enabled.
464 	*/
465     public void setDistributionEnabled (boolean distributionEnabled)
466     {
467     	isDistributionEnabled = distributionEnabled;
468     }
469     
470 	/*** 
471 	* Returns whether distribution is enabled.
472 	*/
473     public boolean isDistributionEnabled ()
474     {
475     	return isDistributionEnabled;
476     }
477     
478 	/*** 
479 	* This method is not included in the WOContext specification.
480     * This implementation returns "" since only cookie ids are 
481     * currently supported.
482 	*/
483     String urlSessionPrefix ()
484     {
485     	return EMPTY; // "/" + sessionid;
486     }
487     
488 	/*** 
489 	* Returns the relative URL for the current page.
490 	*/
491     String url ()
492     {
493     	throw new RuntimeException( "Not implemented yet." );
494     }
495     
496     public String toString()
497     {
498         return "[WOContext@"+Integer.toHexString(System.identityHashCode(this))
499             +":id=" + contextID +":page=" + page + ":component=" + component + ":element=" + elementID + "]";
500     }
501 }
502 
503 /*
504  * $Log$
505  * Revision 1.2  2006/02/19 01:44:02  cgruber
506  * Add xmlrpc files
507  * Remove jclark and replace with dom4j and javax.xml.sax stuff
508  * Re-work dependencies and imports so it all compiles.
509  *
510  * Revision 1.1  2006/02/16 13:22:22  cgruber
511  * Check in all sources in eclipse-friendly maven-enabled packages.
512  *
513  * Revision 1.19  2003/08/07 00:15:15  chochos
514  * general cleanup (mostly removing unused imports)
515  *
516  * Revision 1.18  2003/02/21 16:40:23  mpowers
517  * Now reading port and smtp host from system properties.
518  * Implemented WOApplication.main.
519  *
520  * Revision 1.17  2003/01/24 20:12:54  mpowers
521  * Better parent determination.
522  *
523  * Revision 1.16  2003/01/21 22:27:02  mpowers
524  * Corrected context id usage.
525  * Implemented backtracking.
526  *
527  * Revision 1.15  2003/01/17 20:58:19  mpowers
528  * Fixed up WOHyperlink.
529  *
530  * Revision 1.14  2003/01/17 20:34:57  mpowers
531  * Better handling for components and parents in the context's element stack.
532  *
533  * Revision 1.13  2003/01/14 19:48:36  mpowers
534  *  - fixes to property synchronization
535  *    - forms now pass takeValuesFromRequest to children
536  *  - fixes to action invocation
537  *    - now supporting multipleSubmit in forms
538  *
539  * Revision 1.12  2003/01/13 22:24:44  mpowers
540  * Request-response cycle is working with session and page persistence.
541  *
542  * Revision 1.11  2003/01/10 20:17:41  mpowers
543  * Component action urls are now working.
544  *
545  * Revision 1.8  2003/01/09 21:16:48  mpowers
546  * Bringing request-response cycle more into conformance.
547  *
548  * Revision 1.7  2003/01/09 16:13:59  mpowers
549  * Implemented WOComponentRequestHandler:
550  * Bringing the request-response cycle more into conformance.
551  *
552  * Revision 1.4  2002/12/20 22:56:33  mpowers
553  * Reimplemented the template parsing again.
554  * Nested components are now correctly parsed.
555  * ElementID numbering is now working.
556  *
557  * Revision 1.3  2002/12/18 14:12:38  mpowers
558  * Support for differentiated request handlers.
559  * Support url generation for WOContext and WORequest.
560  *
561  * Revision 1.2  2002/12/17 14:57:42  mpowers
562  * Minor corrections to WORequests's parsing, and updated javadocs.
563  *
564  * Revision 1.1.1.1  2000/12/21 15:53:04  mpowers
565  * Contributing wotonomy.
566  *
567  * Revision 1.3  2000/12/20 16:25:49  michael
568  * Added log to all files.
569  *
570  *
571  */
572