You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by hu...@apache.org on 2002/07/08 02:16:00 UTC

cvs commit: xml-cocoon2/src/scratchpad/src/org/apache/cocoon/selection DateSelector.java

huber       2002/07/07 17:16:00

  Added:       src/scratchpad/src/org/apache/cocoon/selection
                        DateSelector.java
  Log:
  A DateSelector selection depends on date/time value
  
  Revision  Changes    Path
  1.1                  xml-cocoon2/src/scratchpad/src/org/apache/cocoon/selection/DateSelector.java
  
  Index: DateSelector.java
  ===================================================================
  /*
   
   ============================================================================
                     The Apache Software License, Version 1.1
   ============================================================================
   
   Copyright (C) 1999-2002 The Apache Software Foundation. All rights reserved.
   
   Redistribution and use in source and binary forms, with or without modifica-
   tion, are permitted provided that the following conditions are met:
   
   1. Redistributions of  source code must  retain the above copyright  notice,
      this list of conditions and the following disclaimer.
   
   2. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
   
   3. The end-user documentation included with the redistribution, if any, must
      include  the following  acknowledgment:  "This product includes  software
      developed  by the  Apache Software Foundation  (http://www.apache.org/)."
      Alternately, this  acknowledgment may  appear in the software itself,  if
      and wherever such third-party acknowledgments normally appear.
   
   4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
      used to  endorse or promote  products derived from  this software without
      prior written permission. For written permission, please contact
      apache@apache.org.
   
   5. Products  derived from this software may not  be called "Apache", nor may
      "Apache" appear  in their name,  without prior written permission  of the
      Apache Software Foundation.
   
   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
   This software  consists of voluntary contributions made  by many individuals
   on  behalf of the Apache Software  Foundation and was  originally created by
   Stefano Mazzocchi  <st...@apache.org>. For more  information on the Apache
   Software Foundation, please see <http://www.apache.org/>.
   
   */
  package org.apache.cocoon.selection;
  
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.thread.ThreadSafe;
  
  import org.apache.cocoon.environment.ObjectModelHelper;
  import org.apache.cocoon.environment.Request;
  
  import java.text.DateFormat;
  import java.text.SimpleDateFormat;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.Iterator;
  import java.util.HashSet;
  import java.util.HashMap;
  import java.util.Locale;
  import java.util.Map;
  
  /**
   * A <code>Selector</code> depending on current date, and time.
   * <p>
   *  This selector matches when a configured date is before, respectivly
   *  after the current date, and time.
   * </p>
   * <p>
   *  You may want to use this selector to make the pipeline behaviour time
   *  dependent.
   * </p>
   * <p>
   *  The configuration of DataSelector configures for a symbolic name
   *  a compare-mode, and a date-time value.
   *  <br/>
   *  The compare mode is specified by the element name before, or after.
   *  The date-time value is specified by the attribute date. The attribute
   *  dateformat specifies the dateformat of the date-time value. Time only values
   *  are relative to the current date. Optionally you specify a country, and
   *  a language attribute for specifying the locale used in the date-value parsing.
   *  Per default the default locale is used.
   * </p>
   * <p>
   *   The following configuration example, sets partition a day into four time
   *   areas, each spanning six hours, giving them symbolic names night, morning,
   *   afternoon, and evening.
   * </p>
   * <pre><code>
   *  &lt;map:components&gt;
   *  ...
   *    &lt;map:selectors default="browser"&gt;
   *    ...
   *      &lt;map:selector type="date" src="org.apache.cocoon.selection.DateSelector"&gt;
   *        &lt;before name="night" date="06:00:00" dateformat="HH:mm:ss"/&gt;
   *        &lt;before name="morning" date="12:00:00" dateformat="HH:mm:ss"/&gt;
   *        &lt;before name="afternoon" date="18:00:00" dateformat="HH:mm:ss"/&gt;
   *        &lt;before name="evening" date="23:59:59" dateformat="HH:mm:ss"/&gt;
   *      &lt;/map:selector&gt;
   *    ...
   *    &lt;/map:selectors&gt;
   *  ...
   *  &lt;/map:components&gt;
   * </code></pre>
   * <p>
   *  The above date selector definition is used to control the behaviour of a
   *  pipeline processing depending on the current time of day.
   * </p>
   * <pre><code>
   *  &lt;map:pipelines&gt;
   *    &lt;map:pipeline&gt;
   *    ...
   *      &lt;map:match pattern="&asterik;&asterik;/resources/*.css"&gt;
   *      &lt;map:select type="date"&gt;
   *        &lt;map:when test="night"&gt;
   *          &lt;!-- do something for night publishing --&gt;
   *          &lt;map:read src="resources/{2}-night.css" mime-type="text/css&gt;
   *        &lt;/map:when&gt;
   *        &lt;map:when test="morning"&gt;
   *          &lt;!-- do something for night publishing --&gt;
   *          &lt;map:read src="resources/{2}-morning.css" mime-type="text/css&gt;
   *        &lt;/map:when&gt;
   *        ...
   *        &lt;map:otherwise&gt;
   *          &lt;!-- define for completness, and if selecting fails due to errors --&gt;
   *        &lt;/map:otherwise&gt;
   *      &lt;/map:select&gt;
   *    &lt;/map:pipeline&gt;
   *  &lt;/map:pipelines&gt;
   * </code></pre>
   *
   * @author <a href="mailto:huber@apache.org">Bernhard Huber</a>
   * @version CVS $Id: DateSelector.java,v 1.1 2002/07/08 00:15:59 huber Exp $
   */
  public class DateSelector extends AbstractSwitchSelector
  implements Configurable, ThreadSafe {
      /** the configuration
       */
      private Configuration config;
      
      final public static String AFTER_ELEMENT = "after";
      final public static String BEFORE_ELEMENT = "before";
      
      final public static String NAME_ATTR = "name";
      final public static String DATE_ATTR = "date";
      final public static String DATEFORMAT_ATTR = "dateformat";
      final public static String LANGUAGE_ATTR = "language";
      final public static String COUNTRY_ATTR = "country";
      
      public void configure(Configuration config) throws ConfigurationException {
          this.config = config;
      }
      
      protected void configure( final Configuration conf, final String confName, final Map configMap ) {
          final Configuration[] confs = conf.getChildren( confName );
          String name = null;
          String date = null;
          String dateformat = null;
          String language = null;
          String country = null;
          
          final Calendar now = Calendar.getInstance();
          for (int i = 0; i < confs.length; i++ ) {
              try {
                  name = confs[i].getAttribute( NAME_ATTR );
                  date = confs[i].getAttribute( DATE_ATTR );
                  dateformat = confs[i].getAttribute( DATEFORMAT_ATTR, null );
                  language = confs[i].getAttribute( LANGUAGE_ATTR, null );
                  country = confs[i].getAttribute( COUNTRY_ATTR, null);
                  Date parsed_date = null;
                  
                  SimpleDateFormat sdf = null;
                  if (dateformat != null && language != null && country != null) {
                      Locale locale = new Locale( language, country );
                      sdf = new SimpleDateFormat( dateformat, locale );
                  } else if (dateformat != null) {
                      sdf = new SimpleDateFormat( dateformat );
                  } else {
                      sdf = new SimpleDateFormat();
                  }
                  sdf.parse( date );
                  Calendar parsed_calendar = sdf.getCalendar();
                  setUnsetFields( parsed_calendar, now );
                  parsed_date = parsed_calendar.getTime();
                  
                  if (this.getLogger().isDebugEnabled()) {
                      this.getLogger().debug( "Parsed " + DATE_ATTR + " '" + String.valueOf(date) + "' to " +
                      "date object " + parsed_date.toString() );
                  }
                  if (parsed_date != null) {
                      // associate the name with associate date, and the compare-mode
                      configMap.put( name,
                      new DateComparator( parsed_date, confName ) );
                  }
              } catch (Exception e) {
                  if (this.getLogger().isErrorEnabled()) {
                      this.getLogger().error(
                      "Cannot parse date " + String.valueOf(date) + ", using " +
                      NAME_ATTR + " " + String.valueOf( name ) + ", " +
                      DATE_ATTR + " "  + String.valueOf( date ) + ", " +
                      DATEFORMAT_ATTR + " " + String.valueOf( dateformat ) + ", " +
                      LANGUAGE_ATTR + " " + String.valueOf( language ) + ", " +
                      COUNTRY_ATTR + " " + String.valueOf( country )
                      , e );
                  }
              }
          }
      }
      
      /**
       * set fields which are not set by parsing the date attribute value
       *
       * @param cal Calendar parsed in
       * @param defautl_cal Calendar object providing default value for unset
       *   fields of cal
       */
      protected void setUnsetFields( Calendar cal, Calendar default_cal ) {
          // set fields which are not set by parsing date attribute
          if (!cal.isSet( Calendar.YEAR )) {
              cal.set( Calendar.YEAR, default_cal.get( Calendar.YEAR ) );
          }
          if (!cal.isSet( Calendar.MONTH )) {
              cal.set( Calendar.MONTH, default_cal.get( Calendar.MONTH ) );
          }
          if (!cal.isSet( Calendar.DAY_OF_MONTH )) {
              cal.set( Calendar.DAY_OF_MONTH, default_cal.get( Calendar.DAY_OF_MONTH ) );
          }
          if (!cal.isSet( Calendar.HOUR_OF_DAY )) {
              cal.set( Calendar.HOUR_OF_DAY, default_cal.get( Calendar.HOUR_OF_DAY ) );
          }
          if (!cal.isSet( Calendar.MINUTE )) {
              cal.set( Calendar.MINUTE, default_cal.get( Calendar.MINUTE ) );
          }
          if (!cal.isSet( Calendar.SECOND )) {
              cal.set( Calendar.SECOND, default_cal.get( Calendar.SECOND ) );
          }
          //
      }
      
      /**
       * create an object representing a context for multiple select/when
       * invocations
       *
       * @param objectModel the objectModel of the pipeline processing
       * @param paramters unused here
       * @return Object which is an object of class DataSelectorContext
       */
      public Object getSelectorContext(Map objectModel, Parameters parameters) {
          // Inform proxies that response varies over time of request, which
          // ASFAIK not a specific header fields, thus Vary is set to *
          // Seems that caching is not possible for this resource
          ObjectModelHelper.getResponse(objectModel).addHeader("Vary", "*");
          
          // 1 create map : name -> { date, [after|before] } from configuration
          Map map = new HashMap();
          configure( config, BEFORE_ELEMENT, map );
          configure( config, AFTER_ELEMENT, map );
          
          // 2 create SelectorContext
          DateSelectorContext csc = new DateSelectorContext();
          // 3 precalculate result of comparing current date, and configuration map        
          csc.setup( map );
          
          return csc;
      }
      
      /**
       * Evaluate select for a given expression
       *
       * @param the expression to test against, as now expression
       *   should be a name defined as <code>NAME_ATTR</code> in the
       *   configuration section of this selector
       * @param selectorContext is the SelectorContext set up by
       *   the getSelectorContext() method
       * @return true if expression defining a name which yields
       *   true comparing the current date set in getSelectorContext,
       *   and the configured comparison, referenced by the
       *   expression name value.
       */
      public boolean select(String expression, Object selectorContext) {
          
          if (selectorContext == null) {
              getLogger().debug("selectorContext is null!" );
              return false;
          }
          // let SelectorContext do the work
          DateSelectorContext csc = (DateSelectorContext)selectorContext;
          return csc.select(expression);
      }
      
      /**
       * A helper class to store <code>Date, and compare-mode [after|before]</code>.
       * <p>For each configuration entry the compare-mode
       *   <code>[before|after]</code> is stored, and the date value associated with it.
       * </p>
       * <p>
       *  The 'main' method of this class is providing a compareTo method for easily comparing
       *  a given date to the configured compare-mode, and date.
       * </p>
       */
      private static class DateComparator {
          /** the configured date value
           */
          private Date date;
          /** indicator if after comparison should be performed
           */
          private boolean isCompareAfter;
          /** indicator if before comparison shoulde be performed
           */
          private boolean isCompareBefore;
          
          final String AFTER_COMPARATOR_MODE = "after";
          final String BEFORE_COMPARATOR_MODE = "before";
          
          public DateComparator( Date d, String comparator ) {
              this.date = d;
              this.isCompareAfter = AFTER_COMPARATOR_MODE.equalsIgnoreCase( comparator );
              this.isCompareBefore = BEFORE_COMPARATOR_MODE.equalsIgnoreCase( comparator );
          }
          public Date getDate() {
              return this.date;
          }
          public boolean isCompareAfter() {
              return this.isCompareAfter;
          }
          public boolean isCompareBefore() {
              return this.isCompareBefore();
          }
          
          /**
           * Compare when to date value of this object.
           * <p>
           *   Depending on the comparison mode the result of
           *   <code>now.after( date )</code>, or <code>now.before( date )</code>
           *   is returned.
           * </p>
           * @param now the current date and time value
           * @return true iff depending on compare-mode
           *   <code>now.after( date)</code>, or <code>now.before( date )</code>
           *   yield true
           */
          public boolean compareTo( Date now ) {
              if (isCompareAfter) {
                  return now.after( date );
              } else if (isCompareBefore) {
                  return now.before( date );
              } else {
                  return now.compareTo( date) == 0;
              }
          }
      }
      
      /**
       * A SelectorContext for this Selector
       * <p>
       *  This SelectorContext compares the configured date values in this context,
       *  and stores only configure name attributes of comparison yielding true,
       *  reducing the comparsion effort.
       * </p>
       */
      private class DateSelectorContext {
          Date now;
          HashSet set;
          
          public DateSelectorContext() {
              now = new Date();
              set = new HashSet();
          }
          
          public void setup( final Map map ) {
              Iterator i = map.entrySet().iterator();
              while (i.hasNext()) {
                  Map.Entry entry = (Map.Entry)i.next();
                  final String name = (String)entry.getKey();
                  final DateComparator dc = (DateComparator)entry.getValue();
                  
                  if (getLogger().isDebugEnabled()) {
                      getLogger().debug(
                      "Compare name " + name + " having date " + String.valueOf( dc.getDate() ) + " to " +
                      String.valueOf( now ) );
                  }
                  // only store name in set iff comparison is true
                  if (dc.compareTo( now )) {
                      if (getLogger().isDebugEnabled()) {
                          getLogger().debug(
                          "Storing name " + String.valueOf( name ) + " as it yielded true " );
                      }
                      this.set.add( name );
                  }
              }
          }
          
          /**
           * Select yields true iff expression is member of the precalculated set.
           * As the set may contain various elements the order of checking elements
           * is important, and should be considered in the &lt;map:select&gt; sequence.
           *
           * @param expression a symbolic name which may match member of set
           * @return true iff expression is member of set
           */
          public boolean select( String expression ) {
              return this.set.contains( expression );
          }
      }
  }
  
  
  

----------------------------------------------------------------------
In case of troubles, e-mail:     webmaster@xml.apache.org
To unsubscribe, e-mail:          cocoon-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: cocoon-cvs-help@xml.apache.org