| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
| WOContext |
|
| 1.6363636363636365;1.636 |
| 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 | 0 | 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 | 0 | 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 | 0 | public WOContext () |
| 61 | 0 | { |
| 62 | 0 | contextID = null; |
| 63 | 0 | elementID = new StringBuffer(); |
| 64 | 0 | elementStack = new LinkedList(); |
| 65 | 0 | } |
| 66 | ||
| 67 | /** |
|
| 68 | * Preferred constructor performs necessary initialization. |
|
| 69 | * Subclassers should override this method. |
|
| 70 | */ |
|
| 71 | public WOContext (WORequest aRequest) |
|
| 72 | { |
|
| 73 | 0 | this(); |
| 74 | 0 | request = aRequest; |
| 75 | 0 | response = new WOResponse(); |
| 76 | 0 | } |
| 77 | ||
| 78 | /** |
|
| 79 | * Simply calls the preferred constructor. |
|
| 80 | * Included for compatibility. |
|
| 81 | */ |
|
| 82 | public static WOContext contextWithRequest (WORequest aRequest) |
|
| 83 | { |
|
| 84 | 0 | String id = Integer.toString( contextCounter++ ); |
| 85 | 0 | WOContext result = new WOContext( aRequest ); |
| 86 | 0 | result.contextID = id; |
| 87 | 0 | return result; |
| 88 | } |
|
| 89 | ||
| 90 | /** |
|
| 91 | * Returns the context id. |
|
| 92 | */ |
|
| 93 | public String contextID () |
|
| 94 | { |
|
| 95 | 0 | return contextID; |
| 96 | } |
|
| 97 | ||
| 98 | /** |
|
| 99 | * Returns the sender id, or null if it doesn't exist. |
|
| 100 | */ |
|
| 101 | public String senderID () |
|
| 102 | { |
|
| 103 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | if ( session == null ) |
| 136 | { |
|
| 137 | // so far we can't figure out how the direct action handler can avoid creating a session |
|
| 138 | 0 | 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 | 0 | return session; |
| 150 | } |
|
| 151 | ||
| 152 | /** |
|
| 153 | * Package access only. |
|
| 154 | */ |
|
| 155 | void setSession( WOSession aSession ) |
|
| 156 | { |
|
| 157 | 0 | session = aSession; |
| 158 | 0 | } |
| 159 | ||
| 160 | /** |
|
| 161 | * Returns this context's request. |
|
| 162 | */ |
|
| 163 | public WORequest request () |
|
| 164 | { |
|
| 165 | 0 | return request; |
| 166 | } |
|
| 167 | ||
| 168 | /** |
|
| 169 | * Returns this context's response. |
|
| 170 | */ |
|
| 171 | public WOResponse response () |
|
| 172 | { |
|
| 173 | 0 | return response; |
| 174 | } |
|
| 175 | ||
| 176 | /** |
|
| 177 | * Returns the current page. |
|
| 178 | */ |
|
| 179 | public WOComponent page () |
|
| 180 | { |
|
| 181 | 0 | 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 | 0 | Iterator i = elementStack.iterator(); |
| 191 | 0 | while ( i.hasNext() ) |
| 192 | { |
|
| 193 | 0 | o = i.next(); |
| 194 | 0 | if ( o instanceof WOComponent ) return (WOComponent) o; |
| 195 | } |
|
| 196 | 0 | return null; |
| 197 | } |
|
| 198 | ||
| 199 | /** |
|
| 200 | * Returns the current component's parent. |
|
| 201 | */ |
|
| 202 | WOComponent parent () |
|
| 203 | { |
|
| 204 | Object o; |
|
| 205 | 0 | Iterator i = elementStack.iterator(); |
| 206 | 0 | if ( i.hasNext() ) |
| 207 | { |
|
| 208 | // skip current component |
|
| 209 | 0 | o = i.next(); |
| 210 | } |
|
| 211 | 0 | while ( i.hasNext() ) |
| 212 | { |
|
| 213 | 0 | o = i.next(); |
| 214 | 0 | if ( o instanceof WOComponent ) |
| 215 | { |
|
| 216 | 0 | return (WOComponent) o; |
| 217 | } |
|
| 218 | } |
|
| 219 | 0 | return null; |
| 220 | } |
|
| 221 | ||
| 222 | /** |
|
| 223 | * Pushes an element onto the stack. |
|
| 224 | * Package access only. |
|
| 225 | */ |
|
| 226 | void pushElement( WOElement aComponent ) |
|
| 227 | { |
|
| 228 | 0 | elementStack.add( 0, aComponent ); |
| 229 | 0 | } |
| 230 | ||
| 231 | /** |
|
| 232 | * Pops an element off the stack. |
|
| 233 | * Package access only. |
|
| 234 | */ |
|
| 235 | WOElement popElement() |
|
| 236 | { |
|
| 237 | 0 | 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 | 0 | return isInForm; |
| 246 | } |
|
| 247 | ||
| 248 | /** |
|
| 249 | * Sets whether the current context is in a WOForm. |
|
| 250 | */ |
|
| 251 | public void setInForm (boolean inForm) |
|
| 252 | { |
|
| 253 | 0 | isInForm = inForm; |
| 254 | 0 | } |
| 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 | 0 | if ( elementID.length() > 0 ) |
| 264 | { |
|
| 265 | 0 | elementID.append( SEP ); |
| 266 | } |
|
| 267 | 0 | elementID.append( aString ); |
| 268 | 0 | } |
| 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 | 0 | if ( elementID.length() > 0 ) |
| 278 | { |
|
| 279 | 0 | elementID.append( SEP ); |
| 280 | } |
|
| 281 | 0 | elementID.append( ZERO ); |
| 282 | 0 | } |
| 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 | 0 | String id = elementID.toString(); |
| 293 | 0 | int index = id.lastIndexOf( SEP ); |
| 294 | 0 | if ( index == -1 ) |
| 295 | { |
|
| 296 | 0 | last = id; |
| 297 | 0 | } |
| 298 | else |
|
| 299 | { |
|
| 300 | 0 | last = id.substring( index + 1 ); |
| 301 | } |
|
| 302 | ||
| 303 | 0 | deleteLastElementIDComponent(); |
| 304 | ||
| 305 | try |
|
| 306 | { |
|
| 307 | 0 | appendElementIDComponent( |
| 308 | 0 | Integer.toString( Integer.parseInt( last ) + 1 ) ); |
| 309 | } |
|
| 310 | 0 | catch ( Exception exc ) |
| 311 | { |
|
| 312 | 0 | System.err.println( "Error parsing id: " + last ); |
| 313 | 0 | appendZeroElementIDComponent(); |
| 314 | 0 | } |
| 315 | //System.out.println( "WOContext: " + elementID ); |
|
| 316 | 0 | } |
| 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 | 0 | int index = elementID.toString().lastIndexOf( SEP ); |
| 326 | 0 | if ( index == -1 ) |
| 327 | { |
|
| 328 | 0 | elementID.setLength( 0 ); |
| 329 | 0 | } |
| 330 | else |
|
| 331 | { |
|
| 332 | 0 | elementID.setLength( index ); |
| 333 | } |
|
| 334 | 0 | } |
| 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 | 0 | elementID.setLength( 0 ); |
| 343 | 0 | } |
| 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 | 0 | StringBuffer query = new StringBuffer(); |
| 353 | ||
| 354 | try |
|
| 355 | { |
|
| 356 | String key; |
|
| 357 | 0 | Iterator i = aQueryDict.keySet().iterator(); |
| 358 | 0 | while ( i.hasNext() ) |
| 359 | { |
|
| 360 | 0 | key = i.next().toString(); |
| 361 | 0 | query.append( URI.encode( key, |
| 362 | 0 | URI.allowed_within_query ) ); |
| 363 | 0 | query.append( '=' ); |
| 364 | 0 | query.append( URI.encode( aQueryDict.get( key ).toString(), |
| 365 | 0 | URI.allowed_within_query ) ); |
| 366 | 0 | if ( i.hasNext() ) |
| 367 | { |
|
| 368 | 0 | query.append( '&' ); |
| 369 | 0 | } |
| 370 | } |
|
| 371 | } |
|
| 372 | 0 | catch ( IOException exc ) |
| 373 | { |
|
| 374 | // report error |
|
| 375 | 0 | System.err.println( |
| 376 | 0 | "directActionURLForActionNamed: " + anActionName + " : " + aQueryDict ); |
| 377 | 0 | System.err.println( exc ); |
| 378 | ||
| 379 | // delete query string |
|
| 380 | 0 | query = new StringBuffer(); |
| 381 | 0 | } |
| 382 | ||
| 383 | 0 | return urlWithRequestHandlerKey( |
| 384 | 0 | WOApplication.directActionRequestHandlerKey(), |
| 385 | 0 | anActionName, query.toString() ); |
| 386 | } |
|
| 387 | ||
| 388 | /** |
|
| 389 | * Returns the complete URL for the current component action. |
|
| 390 | */ |
|
| 391 | public String componentActionURL () |
|
| 392 | { |
|
| 393 | 0 | StringBuffer buffer = new StringBuffer(); |
| 394 | 0 | buffer.append( request().applicationName() ); |
| 395 | 0 | buffer.append( '/' ); |
| 396 | 0 | buffer.append( WOApplication.application().componentRequestHandlerKey() ); |
| 397 | 0 | buffer.append( '/' ); |
| 398 | 0 | buffer.append( page().name() ); |
| 399 | 0 | buffer.append( '/' ); |
| 400 | 0 | buffer.append( contextID ); |
| 401 | 0 | buffer.append( '/' ); |
| 402 | 0 | buffer.append( elementID ); |
| 403 | 0 | 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 | 0 | StringBuffer buffer = new StringBuffer(); |
| 414 | 0 | buffer.append( request().applicationName() ); |
| 415 | 0 | buffer.append( '/' ); |
| 416 | 0 | buffer.append( aRequestHandlerKey ); |
| 417 | 0 | buffer.append( '/' ); |
| 418 | 0 | buffer.append( aPath ); |
| 419 | 0 | if ( aQueryString != null && aQueryString.trim().length() > 0 ) |
| 420 | { |
|
| 421 | 0 | buffer.append( '?' ); |
| 422 | 0 | buffer.append( aQueryString ); |
| 423 | } |
|
| 424 | 0 | 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 | 0 | StringBuffer buffer = new StringBuffer(); |
| 438 | ||
| 439 | 0 | if ( isSecure ) |
| 440 | { |
|
| 441 | 0 | buffer.append( HTTPS ); |
| 442 | 0 | } |
| 443 | else |
|
| 444 | { |
|
| 445 | 0 | buffer.append( HTTP ); |
| 446 | } |
|
| 447 | ||
| 448 | 0 | buffer.append( request.applicationHost() ); |
| 449 | ||
| 450 | 0 | if ( port != 0 && port != 80 ) |
| 451 | { |
|
| 452 | 0 | buffer.append( ':' ); |
| 453 | 0 | buffer.append( Integer.toString( port ) ); |
| 454 | } |
|
| 455 | ||
| 456 | 0 | buffer.append( urlWithRequestHandlerKey( |
| 457 | 0 | aRequestHandlerKey, aPath, aQueryString ) ); |
| 458 | ||
| 459 | 0 | return buffer.toString(); |
| 460 | } |
|
| 461 | ||
| 462 | /** |
|
| 463 | * Sets whether distribution is enabled. |
|
| 464 | */ |
|
| 465 | public void setDistributionEnabled (boolean distributionEnabled) |
|
| 466 | { |
|
| 467 | 0 | isDistributionEnabled = distributionEnabled; |
| 468 | 0 | } |
| 469 | ||
| 470 | /** |
|
| 471 | * Returns whether distribution is enabled. |
|
| 472 | */ |
|
| 473 | public boolean isDistributionEnabled () |
|
| 474 | { |
|
| 475 | 0 | 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 | 0 | return EMPTY; // "/" + sessionid; |
| 486 | } |
|
| 487 | ||
| 488 | /** |
|
| 489 | * Returns the relative URL for the current page. |
|
| 490 | */ |
|
| 491 | String url () |
|
| 492 | { |
|
| 493 | 0 | throw new RuntimeException( "Not implemented yet." ); |
| 494 | } |
|
| 495 | ||
| 496 | public String toString() |
|
| 497 | { |
|
| 498 | 0 | return "[WOContext@"+Integer.toHexString(System.identityHashCode(this)) |
| 499 | 0 | +":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 |