/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2000 Blacksmith, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.web; import java.io.IOException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * A pure java implementation of WOContext. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 905 $ */ public class WOContext { private WOSession session; private WORequest request; private WOResponse response; private List elementStack; private boolean isInForm; private boolean isDistributionEnabled; private static final String EMPTY = ""; private static final String SEP = "."; private static final String ZERO = "0"; private static final String HTTP = "http://"; private static final String HTTPS = "https://"; // package access WOComponent page; WOComponent component; StringBuffer elementID; private static volatile int contextCounter = 0; private String contextID; /** * Default constructor performs necessary initialization. * Subclassers should override the static factory method below. */ public WOContext () { contextID = null; elementID = new StringBuffer(); elementStack = new LinkedList(); } /** * Preferred constructor performs necessary initialization. * Subclassers should override this method. */ public WOContext (WORequest aRequest) { this(); request = aRequest; response = new WOResponse(); } /** * Simply calls the preferred constructor. * Included for compatibility. */ public static WOContext contextWithRequest (WORequest aRequest) { String id = Integer.toString( contextCounter++ ); WOContext result = new WOContext( aRequest ); result.contextID = id; return result; } /** * Returns the context id. */ public String contextID () { return contextID; } /** * Returns the sender id, or null if it doesn't exist. */ public String senderID () { return request.senderID(); } /** * Returns the element id, or null if it doesn't exist. */ public String elementID () { return elementID.toString(); } /** * Returns this context's application, or null if it doesn't exist. */ public WOApplication application () { return request.application(); } /** * Returns whether a session has been created for the associated request. */ public boolean hasSession () { return ( session != null ); } /** * Returns this context's session, creating one if it doesn't exist. */ public WOSession session () { if ( session == null ) { // so far we can't figure out how the direct action handler can avoid creating a session throw new RuntimeException( "WOContext.session: Lazy instantiation not yet implemented." ); /* session = application().restoreSessionWithID( request.sessionID(), this ); if ( session == null ) { session = application().createSessionForRequest( request ); session.setContext( this ); } */ } return session; } /** * Package access only. */ void setSession( WOSession aSession ) { session = aSession; } /** * Returns this context's request. */ public WORequest request () { return request; } /** * Returns this context's response. */ public WOResponse response () { return response; } /** * Returns the current page. */ public WOComponent page () { return (WOComponent) elementStack.get( elementStack.size()-1 ); } /** * Returns the current component. */ public WOComponent component () { Object o; Iterator i = elementStack.iterator(); while ( i.hasNext() ) { o = i.next(); if ( o instanceof WOComponent ) return (WOComponent) o; } return null; } /** * Returns the current component's parent. */ WOComponent parent () { Object o; Iterator i = elementStack.iterator(); if ( i.hasNext() ) { // skip current component o = i.next(); } while ( i.hasNext() ) { o = i.next(); if ( o instanceof WOComponent ) { return (WOComponent) o; } } return null; } /** * Pushes an element onto the stack. * Package access only. */ void pushElement( WOElement aComponent ) { elementStack.add( 0, aComponent ); } /** * Pops an element off the stack. * Package access only. */ WOElement popElement() { return (WOElement) elementStack.remove(0); } /** * Returns whether the current context is in a form. */ public boolean isInForm () { return isInForm; } /** * Sets whether the current context is in a WOForm. */ public void setInForm (boolean inForm) { isInForm = inForm; } /** * Appends the specified string to the end of the element id. * For example, if the element id is "0.1", sending "Test" * changes the element id to "0.1.Test". */ public void appendElementIDComponent (String aString) { if ( elementID.length() > 0 ) { elementID.append( SEP ); } elementID.append( aString ); } /** * Appends a zero to the element id to represent the first * new child component. For example, if the element id is "0.1", * calling this changes the element id to "0.1.0". */ public void appendZeroElementIDComponent () { if ( elementID.length() > 0 ) { elementID.append( SEP ); } elementID.append( ZERO ); } /** * Increments the last component of the element id. * For example, if the element id is "0.1", calling this * changes the element id to "0.2". */ public void incrementLastElementIDComponent () { String last; String id = elementID.toString(); int index = id.lastIndexOf( SEP ); if ( index == -1 ) { last = id; } else { last = id.substring( index + 1 ); } deleteLastElementIDComponent(); try { appendElementIDComponent( Integer.toString( Integer.parseInt( last ) + 1 ) ); } catch ( Exception exc ) { System.err.println( "Error parsing id: " + last ); appendZeroElementIDComponent(); } //System.out.println( "WOContext: " + elementID ); } /** * Deletes the last component of the element id. * For example, if the element id is "0.1", callling this * changes the element id to "0". */ public void deleteLastElementIDComponent () { int index = elementID.toString().lastIndexOf( SEP ); if ( index == -1 ) { elementID.setLength( 0 ); } else { elementID.setLength( index ); } } /** * Deletes all components of the element id. * This makes the element id an empty string. */ public void deleteAllElementIDComponents () { elementID.setLength( 0 ); } /** * Returns a URL for the named action with query parameters * as specified by the dictionary. */ public String directActionURLForActionNamed ( String anActionName, Map aQueryDict) { StringBuffer query = new StringBuffer(); try { String key; Iterator i = aQueryDict.keySet().iterator(); while ( i.hasNext() ) { key = i.next().toString(); query.append( URI.encode( key, URI.allowed_within_query ) ); query.append( '=' ); query.append( URI.encode( aQueryDict.get( key ).toString(), URI.allowed_within_query ) ); if ( i.hasNext() ) { query.append( '&' ); } } } catch ( IOException exc ) { // report error System.err.println( "directActionURLForActionNamed: " + anActionName + " : " + aQueryDict ); System.err.println( exc ); // delete query string query = new StringBuffer(); } return urlWithRequestHandlerKey( WOApplication.directActionRequestHandlerKey(), anActionName, query.toString() ); } /** * Returns the complete URL for the current component action. */ public String componentActionURL () { StringBuffer buffer = new StringBuffer(); buffer.append( request().applicationName() ); buffer.append( '/' ); buffer.append( WOApplication.application().componentRequestHandlerKey() ); buffer.append( '/' ); buffer.append( page().name() ); buffer.append( '/' ); buffer.append( contextID ); buffer.append( '/' ); buffer.append( elementID ); return buffer.toString(); } /** * Returns a URL relative to the servlet for the specified * request handler, action, and query string. */ public String urlWithRequestHandlerKey ( String aRequestHandlerKey, String aPath, String aQueryString) { StringBuffer buffer = new StringBuffer(); buffer.append( request().applicationName() ); buffer.append( '/' ); buffer.append( aRequestHandlerKey ); buffer.append( '/' ); buffer.append( aPath ); if ( aQueryString != null && aQueryString.trim().length() > 0 ) { buffer.append( '?' ); buffer.append( aQueryString ); } return buffer.toString(); } /** * Returns the complete URL for the specified request handler, * path, and query string. isSecure determines the protocol: * http or https. port is appended to the protocol, if zero, * the port is omitted from the URL. */ public String completeURLWithRequestHandlerKey ( String aRequestHandlerKey, String aPath, String aQueryString, boolean isSecure, int port) { StringBuffer buffer = new StringBuffer(); if ( isSecure ) { buffer.append( HTTPS ); } else { buffer.append( HTTP ); } buffer.append( request.applicationHost() ); if ( port != 0 && port != 80 ) { buffer.append( ':' ); buffer.append( Integer.toString( port ) ); } buffer.append( urlWithRequestHandlerKey( aRequestHandlerKey, aPath, aQueryString ) ); return buffer.toString(); } /** * Sets whether distribution is enabled. */ public void setDistributionEnabled (boolean distributionEnabled) { isDistributionEnabled = distributionEnabled; } /** * Returns whether distribution is enabled. */ public boolean isDistributionEnabled () { return isDistributionEnabled; } /** * This method is not included in the WOContext specification. * This implementation returns "" since only cookie ids are * currently supported. */ String urlSessionPrefix () { return EMPTY; // "/" + sessionid; } /** * Returns the relative URL for the current page. */ String url () { throw new RuntimeException( "Not implemented yet." ); } public String toString() { return "[WOContext@"+Integer.toHexString(System.identityHashCode(this)) +":id=" + contextID +":page=" + page + ":component=" + component + ":element=" + elementID + "]"; } } /* * $Log$ * Revision 1.2 2006/02/19 01:44:02 cgruber * Add xmlrpc files * Remove jclark and replace with dom4j and javax.xml.sax stuff * Re-work dependencies and imports so it all compiles. * * Revision 1.1 2006/02/16 13:22:22 cgruber * Check in all sources in eclipse-friendly maven-enabled packages. * * Revision 1.19 2003/08/07 00:15:15 chochos * general cleanup (mostly removing unused imports) * * Revision 1.18 2003/02/21 16:40:23 mpowers * Now reading port and smtp host from system properties. * Implemented WOApplication.main. * * Revision 1.17 2003/01/24 20:12:54 mpowers * Better parent determination. * * Revision 1.16 2003/01/21 22:27:02 mpowers * Corrected context id usage. * Implemented backtracking. * * Revision 1.15 2003/01/17 20:58:19 mpowers * Fixed up WOHyperlink. * * Revision 1.14 2003/01/17 20:34:57 mpowers * Better handling for components and parents in the context's element stack. * * Revision 1.13 2003/01/14 19:48:36 mpowers * - fixes to property synchronization * - forms now pass takeValuesFromRequest to children * - fixes to action invocation * - now supporting multipleSubmit in forms * * Revision 1.12 2003/01/13 22:24:44 mpowers * Request-response cycle is working with session and page persistence. * * Revision 1.11 2003/01/10 20:17:41 mpowers * Component action urls are now working. * * Revision 1.8 2003/01/09 21:16:48 mpowers * Bringing request-response cycle more into conformance. * * Revision 1.7 2003/01/09 16:13:59 mpowers * Implemented WOComponentRequestHandler: * Bringing the request-response cycle more into conformance. * * Revision 1.4 2002/12/20 22:56:33 mpowers * Reimplemented the template parsing again. * Nested components are now correctly parsed. * ElementID numbering is now working. * * Revision 1.3 2002/12/18 14:12:38 mpowers * Support for differentiated request handlers. * Support url generation for WOContext and WORequest. * * Revision 1.2 2002/12/17 14:57:42 mpowers * Minor corrections to WORequests's parsing, and updated javadocs. * * Revision 1.1.1.1 2000/12/21 15:53:04 mpowers * Contributing wotonomy. * * Revision 1.3 2000/12/20 16:25:49 michael * Added log to all files. * * */