Clover coverage report -
Coverage timestamp: Sat Apr 30 2005 21:58:28 PDT
file stats: LOC: 797   Methods: 22
NCLOC: 412   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
CacheTag.java 0% 0% 0% 0%
coverage
 1   
 /*
 2   
  * Copyright (c) 2002-2003 by OpenSymphony
 3   
  * All rights reserved.
 4   
  */
 5   
 package com.opensymphony.oscache.web.tag;
 6   
 
 7   
 import com.opensymphony.oscache.base.Cache;
 8   
 import com.opensymphony.oscache.base.NeedsRefreshException;
 9   
 import com.opensymphony.oscache.util.StringUtil;
 10   
 import com.opensymphony.oscache.web.ServletCacheAdministrator;
 11   
 import com.opensymphony.oscache.web.WebEntryRefreshPolicy;
 12   
 
 13   
 import org.apache.commons.logging.Log;
 14   
 import org.apache.commons.logging.LogFactory;
 15   
 
 16   
 import java.io.IOException;
 17   
 
 18   
 import java.util.ArrayList;
 19   
 import java.util.List;
 20   
 
 21   
 import javax.servlet.http.HttpServletRequest;
 22   
 import javax.servlet.jsp.JspTagException;
 23   
 import javax.servlet.jsp.PageContext;
 24   
 import javax.servlet.jsp.tagext.BodyTagSupport;
 25   
 import javax.servlet.jsp.tagext.TryCatchFinally;
 26   
 
 27   
 /**
 28   
  * CacheTag is a tag that allows for server-side caching of post-processed JSP content.<p>
 29   
  *
 30   
  * It also gives great programatic control over refreshing, flushing and updating the cache.<p>
 31   
  *
 32   
  * Usage Example:
 33   
  * <pre><code>
 34   
  *     &lt;%@ taglib uri="oscache" prefix="cache" %&gt;
 35   
  *     &lt;cache:cache key="mycache"
 36   
  *                 scope="application"
 37   
  *                 refresh="false"
 38   
  *                 time="30">
 39   
  *              jsp content here... refreshed every 30 seconds
 40   
  *     &lt;/cache:cache&gt;
 41   
  * </code></pre>
 42   
  *
 43   
  * @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
 44   
  * @author <a href="mailto:tgochenour@peregrine.com">Todd Gochenour</a>
 45   
  * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
 46   
  * @author <a href="mailto:abergevin@pyxis-tech.com">Alain Bergevin</a>
 47   
  * @version        $Revision: 1.6 $
 48   
  */
 49   
 public class CacheTag extends BodyTagSupport implements TryCatchFinally {
 50   
     /**
 51   
     * Constants for time computation
 52   
     */
 53   
     private final static int SECOND = 1;
 54   
     private final static int MINUTE = 60 * SECOND;
 55   
     private final static int HOUR = 60 * MINUTE;
 56   
     private final static int DAY = 24 * HOUR;
 57   
     private final static int WEEK = 7 * DAY;
 58   
     private final static int MONTH = 30 * DAY;
 59   
     private final static int YEAR = 365 * DAY;
 60   
 
 61   
     /**
 62   
     * The key under which the tag counter will be stored in the request
 63   
     */
 64   
     private final static String CACHE_TAG_COUNTER_KEY = "__oscache_tag_counter";
 65   
 
 66   
     /**
 67   
     * Constants for refresh time
 68   
     */
 69   
     final static private int ONE_MINUTE = 60;
 70   
     final static private int ONE_HOUR = 60 * ONE_MINUTE;
 71   
     final static private int DEFAULT_TIMEOUT = ONE_HOUR;
 72   
     private static transient Log log = LogFactory.getLog(CacheTag.class);
 73   
 
 74   
     /**
 75   
     * Cache modes
 76   
     */
 77   
     final static private int SILENT_MODE = 1;
 78   
 
 79   
     /**
 80   
     * A flag to indicate whether a NeedsRefreshException was thrown and
 81   
     * the update needs to be cancelled
 82   
     */
 83   
     boolean cancelUpdateRequired = false;
 84   
     private Cache cache = null;
 85   
 
 86   
     /**
 87   
     * If no groups are specified, the cached content does not get put into any groups
 88   
     */
 89   
     private List groups = null;
 90   
     private ServletCacheAdministrator admin = null;
 91   
 
 92   
     /**
 93   
     * The actual key to use. This is generated based on the supplied key, scope etc.
 94   
     */
 95   
     private String actualKey = null;
 96   
 
 97   
     /**
 98   
     * The content that was retrieved from cache
 99   
     */
 100   
     private String content = null;
 101   
 
 102   
     /**
 103   
     * The cron expression that is used to expire cache entries at specific dates and/or times.
 104   
     */
 105   
     private String cron = null;
 106   
 
 107   
     /**
 108   
     * if cache key is null, the request URI is used
 109   
     */
 110   
     private String key = null;
 111   
 
 112   
     /**
 113   
     *  The ISO-639 language code to distinguish different pages in application scope
 114   
     */
 115   
     private String language = null;
 116   
 
 117   
     /**
 118   
     * Class used to handle the refresh policy logic
 119   
     */
 120   
     private String refreshPolicyClass = null;
 121   
 
 122   
     /**
 123   
     * Parameters that will be passed to the init method of the
 124   
     * refresh policy instance.
 125   
     */
 126   
     private String refreshPolicyParam = null;
 127   
 
 128   
     /**
 129   
     * Whether the cache should be refreshed instantly
 130   
     */
 131   
     private boolean refresh = false;
 132   
 
 133   
     /**
 134   
     * used for subtags to tell this tag that we should use the cached version
 135   
     */
 136   
     private boolean useBody = true;
 137   
 
 138   
     /**
 139   
     * The cache mode. Valid values are SILENT_MODE
 140   
     */
 141   
     private int mode = 0;
 142   
 
 143   
     /**
 144   
     * The cache scope to use
 145   
     */
 146   
     private int scope = PageContext.APPLICATION_SCOPE;
 147   
 
 148   
     /**
 149   
     * time (in seconds) before cache should be refreshed
 150   
     */
 151   
     private int time = DEFAULT_TIMEOUT;
 152   
 
 153   
     /**
 154   
     * Set the time this cache entry will be cached for. A date and/or time in
 155   
     * either ISO-8601 format or a simple format can be specified. The acceptable
 156   
     * syntax for the simple format can be any one of the following:
 157   
     *
 158   
     * <ul>
 159   
     * <li>0 (seconds)
 160   
     * <li>0s (seconds)
 161   
     * <li>0m (minutes)
 162   
     * <li>0h (hours)
 163   
     * <li>0d (days)
 164   
     * <li>0w (weeks)
 165   
     * </ul>
 166   
     *
 167   
     * @param duration The duration to cache this content (using either the simple
 168   
     * or the ISO-8601 format). Passing in a duration of zero will turn off the
 169   
     * caching, while a negative value will result in the cached content never
 170   
     * expiring (ie, the cached content will always be served as long as it is
 171   
     * present).
 172   
     */
 173  0
     public void setDuration(String duration) {
 174  0
         try {
 175   
             // Try Simple Date Format Duration first because it's faster
 176  0
             this.time = parseDuration(duration);
 177   
         } catch (Exception ex) {
 178  0
             if (log.isDebugEnabled()) {
 179  0
                 log.debug("Failed parsing simple duration format '" + duration + "' (" + ex.getMessage() + "). Trying ISO-8601 format...");
 180   
             }
 181   
 
 182  0
             try {
 183   
                 // Try ISO-8601 Duration
 184  0
                 this.time = parseISO_8601_Duration(duration);
 185   
             } catch (Exception ex1) {
 186   
                 // An invalid duration entered, not much impact.
 187   
                 // The default timeout will be used
 188  0
                 log.warn("The requested cache duration '" + duration + "' is invalid (" + ex1.getMessage() + "). Reverting to the default timeout");
 189  0
                 this.time = DEFAULT_TIMEOUT;
 190   
             }
 191   
         }
 192   
     }
 193   
 
 194   
     /**
 195   
     * Sets the cron expression that should be used to expire content at specific
 196   
     * dates and/or times.
 197   
     */
 198  0
     public void setCron(String cron) {
 199  0
         this.cron = cron;
 200   
     }
 201   
 
 202   
     /**
 203   
      * Sets the groups for this cache entry. Any existing groups will
 204   
      * be replaced.
 205   
      *
 206   
      * @param groups A comma-delimited list of groups that the cache entry belongs to.
 207   
      */
 208  0
     public void setGroups(String groups) {
 209  0
         this.groups = StringUtil.split(groups, ',');
 210   
     }
 211   
 
 212   
     /**
 213   
      * Adds to the groups for this cache entry.
 214   
      *
 215   
      * @param group A group to which the cache entry should belong.
 216   
      */
 217  0
     void addGroup(String group) {
 218  0
         if (groups == null) {
 219  0
             groups = new ArrayList();
 220   
         }
 221   
 
 222  0
         groups.add(group);
 223   
     }
 224   
 
 225   
     /**
 226   
     * Set the key for this cache entry.
 227   
     *
 228   
     * @param key The key for this cache entry.
 229   
     */
 230  0
     public void setKey(String key) {
 231  0
         this.key = key;
 232   
     }
 233   
 
 234   
     /**
 235   
     * Set the ISO-639 language code to distinguish different pages in application scope
 236   
     *
 237   
     * @param language The language code for this cache entry.
 238   
     */
 239  0
     public void setLanguage(String language) {
 240  0
         this.language = language;
 241   
     }
 242   
 
 243   
     /**
 244   
     * This method allows the user to programatically decide whether the cached
 245   
     * content should be refreshed immediately.
 246   
     *
 247   
     * @param refresh Whether or not to refresh this cache entry immediately.
 248   
     */
 249  0
     public void setRefresh(boolean refresh) {
 250  0
         this.refresh = refresh;
 251   
     }
 252   
 
 253   
     /**
 254   
     * Setting this to <code>true</code> prevents the cache from writing any output
 255   
     * to the response, however the JSP content is still cached as normal.
 256   
     * @param mode The cache mode to use.
 257   
     */
 258  0
     public void setMode(String mode) {
 259  0
         if ("silent".equalsIgnoreCase(mode)) {
 260  0
             this.mode = SILENT_MODE;
 261   
         } else {
 262  0
             this.mode = 0;
 263   
         }
 264   
     }
 265   
 
 266   
     /**
 267   
     * Class used to handle the refresh policy logic
 268   
     */
 269  0
     public void setRefreshpolicyclass(String refreshPolicyClass) {
 270  0
         this.refreshPolicyClass = refreshPolicyClass;
 271   
     }
 272   
 
 273   
     /**
 274   
     * Parameters that will be passed to the init method of the
 275   
     * refresh policy instance.
 276   
     */
 277  0
     public void setRefreshpolicyparam(String refreshPolicyParam) {
 278  0
         this.refreshPolicyParam = refreshPolicyParam;
 279   
     }
 280   
 
 281   
     // ----------- setMethods ------------------------------------------------------
 282   
 
 283   
     /**
 284   
     * Set the scope of this cache.
 285   
     * <p>
 286   
     * @param scope The scope of this cache. Either "application" (default) or "session".
 287   
     */
 288  0
     public void setScope(String scope) {
 289  0
         if (scope.equalsIgnoreCase(ServletCacheAdministrator.SESSION_SCOPE_NAME)) {
 290  0
             this.scope = PageContext.SESSION_SCOPE;
 291   
         } else {
 292  0
             this.scope = PageContext.APPLICATION_SCOPE;
 293   
         }
 294   
     }
 295   
 
 296   
     /**
 297   
     * Set the time this cache entry will be cached for (in seconds)
 298   
     *
 299   
     * @param time The time to cache this content (in seconds). Passing in
 300   
     * a time of zero will turn off the caching. A negative value for the
 301   
     * time will result in the cached content never expiring (ie, the cached
 302   
     * content will always be served if it is present)
 303   
     */
 304  0
     public void setTime(int time) {
 305  0
         this.time = time;
 306   
     }
 307   
 
 308   
     /**
 309   
     * This controls whether or not the body of the tag is evaluated or used.<p>
 310   
     *
 311   
     * It is most often called by the &lt;UseCached /&gt; tag to tell this tag to
 312   
     * use the cached content.
 313   
     *
 314   
     * @see UseCachedTag
 315   
     * @param useBody Whether or not to use the cached content.
 316   
     */
 317  0
     public void setUseBody(boolean useBody) {
 318  0
         if (log.isDebugEnabled()) {
 319  0
             log.debug("<cache>: Set useBody to " + useBody);
 320   
         }
 321   
 
 322  0
         this.useBody = useBody;
 323   
     }
 324   
 
 325   
     /**
 326   
     * After the cache body, either update the cache, serve new cached content or
 327   
     *  indicate an error.
 328   
     *
 329   
     * @throws JspTagException The standard exception thrown.
 330   
     * @return The standard BodyTag return.
 331   
     */
 332  0
     public int doAfterBody() throws JspTagException {
 333  0
         String body = null;
 334   
 
 335  0
         try {
 336   
             // if we have a body, and we have not been told to use the cached version
 337  0
             if ((bodyContent != null) && (useBody || (time == 0)) && ((body = bodyContent.getString()) != null)) {
 338  0
                 if ((time != 0) || (refreshPolicyClass != null)) {
 339   
                     // Instantiate custom refresh policy if needed
 340  0
                     WebEntryRefreshPolicy policy = null;
 341   
 
 342  0
                     if (refreshPolicyClass != null) {
 343  0
                         try {
 344  0
                             policy = (WebEntryRefreshPolicy) Class.forName(refreshPolicyClass).newInstance();
 345  0
                             policy.init(actualKey, refreshPolicyParam);
 346   
                         } catch (Exception e) {
 347  0
                             if (log.isInfoEnabled()) {
 348  0
                                 log.info("<cache>: Problem instantiating or initializing refresh policy : " + refreshPolicyClass);
 349   
                             }
 350   
                         }
 351   
                     }
 352   
 
 353  0
                     if (log.isDebugEnabled()) {
 354  0
                         log.debug("<cache>: Updating cache entry with new content : " + actualKey);
 355   
                     }
 356   
 
 357  0
                     cancelUpdateRequired = false;
 358   
 
 359  0
                     if ((groups == null) || groups.isEmpty()) {
 360  0
                         cache.putInCache(actualKey, body, policy);
 361   
                     } else {
 362  0
                         String[] groupArray = new String[groups.size()];
 363  0
                         groups.toArray(groupArray);
 364  0
                         cache.putInCache(actualKey, body, groupArray, policy, null);
 365   
                     }
 366   
                 }
 367   
             }
 368   
             // otherwise if we have been told to use the cached content and we have cached content
 369   
             else {
 370  0
                 if (!useBody && (content != null)) {
 371  0
                     if (log.isInfoEnabled()) {
 372  0
                         log.info("<cache>: Using cached version as instructed, useBody = false : " + actualKey);
 373   
                     }
 374   
 
 375  0
                     body = content;
 376   
                 }
 377   
                 // either the cached entry is blank and a subtag has said don't useBody, or body is null
 378   
                 else {
 379  0
                     if (log.isInfoEnabled()) {
 380  0
                         log.info("<cache>: Missing cached content : " + actualKey);
 381   
                     }
 382   
 
 383  0
                     body = "Missing cached content";
 384   
                 }
 385   
             }
 386   
 
 387   
             // Only display anything if we're not running in silent mode
 388  0
             if (mode != SILENT_MODE) {
 389  0
                 bodyContent.clearBody();
 390  0
                 bodyContent.write(body);
 391  0
                 bodyContent.writeOut(bodyContent.getEnclosingWriter());
 392   
             }
 393   
         } catch (java.io.IOException e) {
 394  0
             throw new JspTagException("IO Error: " + e.getMessage());
 395   
         }
 396   
 
 397  0
         return SKIP_BODY;
 398   
     }
 399   
 
 400  0
     public void doCatch(Throwable throwable) throws Throwable {
 401  0
         throw throwable;
 402   
     }
 403   
 
 404   
     /**
 405   
     * The end tag - clean up variables used.
 406   
     *
 407   
     * @throws JspTagException The standard exception thrown.
 408   
     * @return The standard BodyTag return.
 409   
     */
 410  0
     public int doEndTag() throws JspTagException {
 411  0
         return EVAL_PAGE;
 412   
     }
 413   
 
 414  0
     public void doFinally() {
 415  0
         if (cancelUpdateRequired && (actualKey != null)) {
 416  0
             cache.cancelUpdate(actualKey);
 417   
         }
 418   
     }
 419   
 
 420   
     /**
 421   
     * The start of the tag.
 422   
     * <p>
 423   
     * Grabs the administrator, the cache, the specific cache entry, then decides
 424   
     * whether to refresh.
 425   
     * <p>
 426   
     * If no refresh is needed, this serves the cached content directly.
 427   
     *
 428   
     * @throws JspTagException The standard exception thrown.
 429   
     * @return The standard doStartTag() return.
 430   
     */
 431  0
     public int doStartTag() throws JspTagException {
 432  0
         cancelUpdateRequired = false;
 433  0
         useBody = true;
 434  0
         content = null;
 435   
 
 436   
         // We can only skip the body if the cache has the data
 437  0
         int returnCode = EVAL_BODY_BUFFERED;
 438   
 
 439  0
         if (admin == null) {
 440  0
             admin = ServletCacheAdministrator.getInstance(pageContext.getServletContext());
 441   
         }
 442   
 
 443   
         // Retrieve the cache
 444  0
         if (scope == PageContext.SESSION_SCOPE) {
 445  0
             cache = admin.getSessionScopeCache(((HttpServletRequest) pageContext.getRequest()).getSession(true));
 446   
         } else {
 447  0
             cache = admin.getAppScopeCache(pageContext.getServletContext());
 448   
         }
 449   
 
 450   
         // This allows to have multiple cache tags on a single page without
 451   
         // having to specify keys. However, nested cache tags are not supported.
 452   
         // In that case you would have to supply a key.
 453  0
         String suffix = null;
 454   
 
 455  0
         if (key == null) {
 456  0
             synchronized (pageContext.getRequest()) {
 457  0
                 Object o = pageContext.getRequest().getAttribute(CACHE_TAG_COUNTER_KEY);
 458   
 
 459  0
                 if (o == null) {
 460  0
                     suffix = "1";
 461   
                 } else {
 462  0
                     suffix = Integer.toString(Integer.parseInt((String) o) + 1);
 463   
                 }
 464   
             }
 465   
 
 466  0
             pageContext.getRequest().setAttribute(CACHE_TAG_COUNTER_KEY, suffix);
 467   
         }
 468   
 
 469   
         // Generate the actual cache key
 470  0
         actualKey = admin.generateEntryKey(key, (HttpServletRequest) pageContext.getRequest(), scope, language, suffix);
 471   
 
 472   
         /*
 473   
         if
 474   
         - refresh is not set,
 475   
         - the cacheEntry itself does not need to be refreshed before 'time' and
 476   
         - the administrator has not had the cache entry's scope flushed
 477   
 
 478   
         send out the cached version!
 479   
         */
 480  0
         try {
 481  0
             if (refresh) {
 482   
                 // Force a refresh
 483  0
                 content = (String) cache.getFromCache(actualKey, 0, cron);
 484   
             } else {
 485   
                 // Use the specified refresh period
 486  0
                 content = (String) cache.getFromCache(actualKey, time, cron);
 487   
             }
 488   
 
 489  0
             try {
 490  0
                 if (log.isDebugEnabled()) {
 491  0
                     log.debug("<cache>: Using cached entry : " + actualKey);
 492   
                 }
 493   
 
 494   
                 // Ensure that the cache returns the data correctly. Else re-evaluate the body
 495  0
                 if ((content != null)) {
 496  0
                     if (mode != SILENT_MODE) {
 497  0
                         pageContext.getOut().write(content);
 498   
                     }
 499   
 
 500  0
                     returnCode = SKIP_BODY;
 501   
                 }
 502   
             } catch (IOException e) {
 503  0
                 throw new JspTagException("IO Exception: " + e.getMessage());
 504   
             }
 505   
         } catch (NeedsRefreshException nre) {
 506  0
             cancelUpdateRequired = true;
 507  0
             content = (String) nre.getCacheContent();
 508   
         }
 509   
 
 510  0
         if (returnCode == EVAL_BODY_BUFFERED) {
 511  0
             if (log.isDebugEnabled()) {
 512  0
                 log.debug("<cache>: Cached content not used: New cache entry, cache stale or scope flushed : " + actualKey);
 513   
             }
 514   
         }
 515   
 
 516  0
         return returnCode;
 517   
     }
 518   
 
 519   
     /**
 520   
     * Convert a SimpleDateFormat string to seconds
 521   
     * Acceptable format are :
 522   
     * <ul>
 523   
     * <li>0s (seconds)
 524   
     * <li>0m (minute)
 525   
     * <li>0h (hour)
 526   
     * <li>0d (day)
 527   
     * <li>0w (week)
 528   
     * </ul>
 529   
     * @param   duration The simple date time to parse
 530   
     * @return  The value in seconds
 531   
     */
 532  0
     private int parseDuration(String duration) {
 533  0
         int time = 0;
 534   
 
 535   
         //Detect if the factor is specified
 536  0
         try {
 537  0
             time = Integer.parseInt(duration);
 538   
         } catch (Exception ex) {
 539   
             //Extract number and ajust this number with the time factor
 540  0
             for (int i = 0; i < duration.length(); i++) {
 541  0
                 if (!Character.isDigit(duration.charAt(i))) {
 542  0
                     time = Integer.parseInt(duration.substring(0, i));
 543   
 
 544  0
                     switch ((int) duration.charAt(i)) {
 545   
                         case (int) 's':
 546  0
                             time *= SECOND;
 547  0
                             break;
 548   
                         case (int) 'm':
 549  0
                             time *= MINUTE;
 550  0
                             break;
 551   
                         case (int) 'h':
 552  0
                             time *= HOUR;
 553  0
                             break;
 554   
                         case (int) 'd':
 555  0
                             time *= DAY;
 556  0
                             break;
 557   
                         case (int) 'w':
 558  0
                             time *= WEEK;
 559  0
                             break;
 560   
                         default:
 561   
                         //no defined use as is
 562   
                     }
 563   
 
 564  0
                     break;
 565   
                 }
 566   
 
 567   
                 // if
 568   
             }
 569   
 
 570   
             // for
 571   
         }
 572   
 
 573   
         // catch
 574  0
         return time;
 575   
     }
 576   
 
 577   
     /**
 578   
     * Parse an ISO-8601 format date and return it's value in seconds
 579   
     *
 580   
     * @param duration The ISO-8601 date
 581   
     * @return The equivalent number of seconds
 582   
     * @throws Exception
 583   
     */
 584  0
     private int parseISO_8601_Duration(String duration) throws Exception {
 585  0
         int years = 0;
 586  0
         int months = 0;
 587  0
         int days = 0;
 588  0
         int hours = 0;
 589  0
         int mins = 0;
 590  0
         int secs = 0;
 591   
 
 592   
         // If there is a negative sign, it must be first
 593   
         // If it is present, we will ignore it
 594  0
         int index = duration.indexOf("-");
 595   
 
 596  0
         if (index > 0) {
 597  0
             throw new Exception("Invalid duration (- must be at the beginning)");
 598   
         }
 599   
 
 600   
         // First caracter must be P
 601  0
         String workValue = duration.substring(index + 1);
 602   
 
 603  0
         if (workValue.charAt(0) != 'P') {
 604  0
             throw new Exception("Invalid duration (P must be at the beginning)");
 605   
         }
 606   
 
 607   
         // Must contain a value
 608  0
         workValue = workValue.substring(1);
 609   
 
 610  0
         if (workValue.length() == 0) {
 611  0
             throw new Exception("Invalid duration (nothing specified)");
 612   
         }
 613   
 
 614   
         // Check if there is a T
 615  0
         index = workValue.indexOf('T');
 616   
 
 617  0
         String timeString = "";
 618   
 
 619  0
         if (index > 0) {
 620  0
             timeString = workValue.substring(index + 1);
 621   
 
 622   
             // Time cannot be empty
 623  0
             if (timeString.equals("")) {
 624  0
                 throw new Exception("Invalid duration (T with no time)");
 625   
             }
 626   
 
 627  0
             workValue = workValue.substring(0, index);
 628  0
         } else if (index == 0) {
 629  0
             timeString = workValue.substring(1);
 630  0
             workValue = "";
 631   
         }
 632   
 
 633  0
         if (!workValue.equals("")) {
 634  0
             validateDateFormat(workValue);
 635   
 
 636  0
             int yearIndex = workValue.indexOf('Y');
 637  0
             int monthIndex = workValue.indexOf('M');
 638  0
             int dayIndex = workValue.indexOf('D');
 639   
 
 640  0
             if ((yearIndex != -1) && (monthIndex != -1) && (yearIndex > monthIndex)) {
 641  0
                 throw new Exception("Invalid duration (Date part not properly specified)");
 642   
             }
 643   
 
 644  0
             if ((yearIndex != -1) && (dayIndex != -1) && (yearIndex > dayIndex)) {
 645  0
                 throw new Exception("Invalid duration (Date part not properly specified)");
 646   
             }
 647   
 
 648  0
             if ((dayIndex != -1) && (monthIndex != -1) && (monthIndex > dayIndex)) {
 649  0
                 throw new Exception("Invalid duration (Date part not properly specified)");
 650   
             }
 651   
 
 652  0
             if (yearIndex >= 0) {
 653  0
                 years = (new Integer(workValue.substring(0, yearIndex))).intValue();
 654   
             }
 655   
 
 656  0
             if (monthIndex >= 0) {
 657  0
                 months = (new Integer(workValue.substring(yearIndex + 1, monthIndex))).intValue();
 658   
             }
 659   
 
 660  0
             if (dayIndex >= 0) {
 661  0
                 if (monthIndex >= 0) {
 662  0
                     days = (new Integer(workValue.substring(monthIndex + 1, dayIndex))).intValue();
 663   
                 } else {
 664  0
                     if (yearIndex >= 0) {
 665  0
                         days = (new Integer(workValue.substring(yearIndex + 1, dayIndex))).intValue();
 666   
                     } else {
 667  0
                         days = (new Integer(workValue.substring(0, dayIndex))).intValue();
 668   
                     }
 669   
                 }
 670   
             }
 671   
         }
 672   
 
 673  0
         if (!timeString.equals("")) {
 674  0
             validateHourFormat(timeString);
 675   
 
 676  0
             int hourIndex = timeString.indexOf('H');
 677  0
             int minuteIndex = timeString.indexOf('M');
 678  0
             int secondIndex = timeString.indexOf('S');
 679   
 
 680  0
             if ((hourIndex != -1) && (minuteIndex != -1) && (hourIndex > minuteIndex)) {
 681  0
                 throw new Exception("Invalid duration (Time part not properly specified)");
 682   
             }
 683   
 
 684  0
             if ((hourIndex != -1) && (secondIndex != -1) && (hourIndex > secondIndex)) {
 685  0
                 throw new Exception("Invalid duration (Time part not properly specified)");
 686   
             }
 687   
 
 688  0
             if ((secondIndex != -1) && (minuteIndex != -1) && (minuteIndex > secondIndex)) {
 689  0
                 throw new Exception("Invalid duration (Time part not properly specified)");
 690   
             }
 691   
 
 692  0
             if (hourIndex >= 0) {
 693  0
                 hours = (new Integer(timeString.substring(0, hourIndex))).intValue();
 694   
             }
 695   
 
 696  0
             if (minuteIndex >= 0) {
 697  0
                 mins = (new Integer(timeString.substring(hourIndex + 1, minuteIndex))).intValue();
 698   
             }
 699   
 
 700  0
             if (secondIndex >= 0) {
 701  0
                 if (timeString.length() != (secondIndex + 1)) {
 702  0
                     throw new Exception("Invalid duration (Time part not properly specified)");
 703   
                 }
 704   
 
 705  0
                 if (minuteIndex >= 0) {
 706  0
                     timeString = timeString.substring(minuteIndex + 1, timeString.length() - 1);
 707   
                 } else {
 708  0
                     if (hourIndex >= 0) {
 709  0
                         timeString = timeString.substring(hourIndex + 1, timeString.length() - 1);
 710   
                     } else {
 711  0
                         timeString = timeString.substring(0, timeString.length() - 1);
 712   
                     }
 713   
                 }
 714   
 
 715  0
                 if (timeString.indexOf('.') == (timeString.length() - 1)) {
 716  0
                     throw new Exception("Invalid duration (Time part not properly specified)");
 717   
                 }
 718   
 
 719  0
                 secs = (new Double(timeString)).intValue();
 720   
             }
 721   
         }
 722   
 
 723   
         // Compute Value
 724  0
         return secs + (mins * MINUTE) + (hours * HOUR) + (days * DAY) + (months * MONTH) + (years * YEAR);
 725   
     }
 726   
 
 727   
     /**
 728   
     * Validate the basic date format
 729   
     *
 730   
     * @param basicDate The string to validate
 731   
     * @throws Exception
 732   
     */
 733  0
     private void validateDateFormat(String basicDate) throws Exception {
 734  0
         int yearCounter = 0;
 735  0
         int monthCounter = 0;
 736  0
         int dayCounter = 0;
 737   
 
 738  0
         for (int counter = 0; counter < basicDate.length(); counter++) {
 739   
             // Check if there's any other caracters than Y, M, D and numbers
 740  0
             if (!Character.isDigit(basicDate.charAt(counter)) && (basicDate.charAt(counter) != 'Y') && (basicDate.charAt(counter) != 'M') && (basicDate.charAt(counter) != 'D')) {
 741  0
                 throw new Exception("Invalid duration (Date part not properly specified)");
 742   
             }
 743   
 
 744   
             // Check if the allowed caracters are present more than 1 time
 745  0
             if (basicDate.charAt(counter) == 'Y') {
 746  0
                 yearCounter++;
 747   
             }
 748   
 
 749  0
             if (basicDate.charAt(counter) == 'M') {
 750  0
                 monthCounter++;
 751   
             }
 752   
 
 753  0
             if (basicDate.charAt(counter) == 'D') {
 754  0
                 dayCounter++;
 755   
             }
 756   
         }
 757   
 
 758  0
         if ((yearCounter > 1) || (monthCounter > 1) || (dayCounter > 1)) {
 759  0
             throw new Exception("Invalid duration (Date part not properly specified)");
 760   
         }
 761   
     }
 762   
 
 763   
     /**
 764   
     * Validate the basic hour format
 765   
     *
 766   
     * @param basicHour The string to validate
 767   
     * @throws Exception
 768   
     */
 769  0
     private void validateHourFormat(String basicHour) throws Exception {
 770  0
         int minuteCounter = 0;
 771  0
         int secondCounter = 0;
 772  0
         int hourCounter = 0;
 773   
 
 774  0
         for (int counter = 0; counter < basicHour.length(); counter++) {
 775  0
             if (!Character.isDigit(basicHour.charAt(counter)) && (basicHour.charAt(counter) != 'H') && (basicHour.charAt(counter) != 'M') && (basicHour.charAt(counter) != 'S') && (basicHour.charAt(counter) != '.')) {
 776  0
                 throw new Exception("Invalid duration (Time part not properly specified)");
 777   
             }
 778   
 
 779  0
             if (basicHour.charAt(counter) == 'H') {
 780  0
                 hourCounter++;
 781   
             }
 782   
 
 783  0
             if (basicHour.charAt(counter) == 'M') {
 784  0
                 minuteCounter++;
 785   
             }
 786   
 
 787  0
             if (basicHour.charAt(counter) == 'S') {
 788  0
                 secondCounter++;
 789   
             }
 790   
         }
 791   
 
 792  0
         if ((hourCounter > 1) || (minuteCounter > 1) || (secondCounter > 1)) {
 793  0
             throw new Exception("Invalid duration (Time part not properly specified)");
 794   
         }
 795   
     }
 796   
 }
 797