/* 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. * * */