Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   *  Copyright 2001-2006 Stephen Colebourne
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
  *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
 package org.joda.time.format;
 
 
DateTimeParserBucket is an advanced class, intended mainly for parser implementations. It can also be used during normal parsing operations to capture more information about the parse.

This class allows fields to be saved in any order, but be physically set in a consistent order. This is useful for parsing against formats that allow field values to contradict each other.

Field values are applied in an order where the "larger" fields are set first, making their value less likely to stick. A field is larger than another when it's range duration is longer. If both ranges are the same, then the larger field has the longer duration. If it cannot be determined which field is larger, then the fields are set in the order they were saved.

For example, these fields were saved in this order: dayOfWeek, monthOfYear, dayOfMonth, dayOfYear. When computeMillis is called, the fields are set in this order: monthOfYear, dayOfYear, dayOfMonth, dayOfWeek.

DateTimeParserBucket is mutable and not thread-safe.

Author(s):
Brian S O'Neill
Fredrik Borgh
Since:
1.0
 
 public class DateTimeParserBucket {

    
The chronology to use for parsing.
 
     private final Chronology iChrono;
     private final long iMillis;
     
     // TimeZone to switch to in computeMillis. If null, use offset.
     private DateTimeZone iZone;
     private int iOffset;
    
The locale to use for parsing.
 
     private Locale iLocale;
    
Used for parsing two-digit years.
 
     private Integer iPivotYear;
 
     private SavedField[] iSavedFields = new SavedField[8];
     private int iSavedFieldsCount;
     private boolean iSavedFieldsShared;
     
     private Object iSavedState;

    
Constucts a bucket.

Parameters:
instantLocal the initial millis from 1970-01-01T00:00:00, local time
chrono the chronology to use
locale the locale to use
 
     public DateTimeParserBucket(long instantLocalChronology chronoLocale locale) {
         this(instantLocalchronolocalenull);
     }

    
Constucts a bucket, with the option of specifying the pivot year for two-digit year parsing.

Parameters:
instantLocal the initial millis from 1970-01-01T00:00:00, local time
chrono the chronology to use
locale the locale to use
pivotYear the pivot year to use when parsing two-digit years
Since:
1.1
 
     public DateTimeParserBucket(long instantLocalChronology chronoLocale localeInteger pivotYear) {
         super();
         chrono = DateTimeUtils.getChronology(chrono);
          = instantLocal;
          = chrono.withUTC();
         = (locale == null ? Locale.getDefault() : locale);
        setZone(chrono.getZone());
         = pivotYear;
    }
    //-----------------------------------------------------------------------
    
Gets the chronology of the bucket, which will be a local (UTC) chronology.
    public Chronology getChronology() {
        return ;
    }
    //-----------------------------------------------------------------------
    
Returns the locale to be used during parsing.

Returns:
the locale to use
    public Locale getLocale() {
        return ;
    }
    //-----------------------------------------------------------------------
    
Returns the time zone used by computeMillis, or null if an offset is used instead.
    public DateTimeZone getZone() {
        return ;
    }
    
    
Set a time zone to be used when computeMillis is called, which overrides any set time zone offset.

Parameters:
zone the date time zone to operate in, or null if UTC
    public void setZone(DateTimeZone zone) {
         = null;
         = zone == . ? null : zone;
         = 0;
    }
    
    //-----------------------------------------------------------------------
    
Returns the time zone offset in milliseconds used by computeMillis, unless getZone doesn't return null.
    public int getOffset() {
        return ;
    }
    
    
Set a time zone offset to be used when computeMillis is called, which overrides the time zone.
    public void setOffset(int offset) {
         = null;
         = offset;
         = null;
    }
    //-----------------------------------------------------------------------
    
Returns the pivot year used for parsing two-digit years.

If null is returned, this indicates default behaviour

Returns:
Integer value of the pivot year, null if not set
Since:
1.1
    public Integer getPivotYear() {
        return ;
    }

    
Sets the pivot year to use when parsing two digit years.

If the value is set to null, this will indicate that default behaviour should be used.

Parameters:
pivotYear the pivot year to use
Since:
1.1
    public void setPivotYear(Integer pivotYear) {
         = pivotYear;
    }
    //-----------------------------------------------------------------------
    
Saves a datetime field value.

Parameters:
field the field, whose chronology must match that of this bucket
value the value
    public void saveField(DateTimeField fieldint value) {
        saveField(new SavedField(fieldvalue));
    }
    
    
Saves a datetime field value.

Parameters:
fieldType the field type
value the value
    public void saveField(DateTimeFieldType fieldTypeint value) {
        saveField(new SavedField(fieldType.getField(), value));
    }
    
    
Saves a datetime field text value.

Parameters:
fieldType the field type
text the text value
locale the locale to use
    public void saveField(DateTimeFieldType fieldTypeString textLocale locale) {
        saveField(new SavedField(fieldType.getField(), textlocale));
    }
    
    private void saveField(SavedField field) {
        SavedField[] savedFields = ;
        int savedFieldsCount = ;
        
        if (savedFieldsCount == savedFields.length || ) {
            // Expand capacity or merely copy if saved fields are shared.
            SavedField[] newArray = new SavedField
                [savedFieldsCount == savedFields.length ? savedFieldsCount * 2 : savedFields.length];
            System.arraycopy(savedFields, 0, newArray, 0, savedFieldsCount);
             = savedFields = newArray;
             = false;
        }
        
         = null;
        savedFields[savedFieldsCount] = field;
         = savedFieldsCount + 1;
    }
    
    
Saves the state of this bucket, returning it in an opaque object. Call restoreState to undo any changes that were made since the state was saved. Calls to saveState may be nested.

Returns:
opaque saved state, which may be passed to restoreState
    public Object saveState() {
        if ( == null) {
             = new SavedState();
        }
        return ;
    }
    
    
Restores the state of this bucket from a previously saved state. The state object passed into this method is not consumed, and it can be used later to restore to that state again.

Parameters:
savedState opaque saved state, returned from saveState
Returns:
true state object is valid and state restored
    public boolean restoreState(Object savedState) {
        if (savedState instanceof SavedState) {
            if (((SavedStatesavedState).restoreState(this)) {
                 = savedState;
                return true;
            }
        }
        return false;
    }
    
    
Computes the parsed datetime by setting the saved fields. This method is idempotent, but it is not thread-safe.

Returns:
milliseconds since 1970-01-01T00:00:00Z
Throws:
java.lang.IllegalArgumentException if any field is out of range
    public long computeMillis() {
        return computeMillis(falsenull);
    }
    
    
Computes the parsed datetime by setting the saved fields. This method is idempotent, but it is not thread-safe.

Parameters:
resetFields false by default, but when true, unsaved field values are cleared
Returns:
milliseconds since 1970-01-01T00:00:00Z
Throws:
java.lang.IllegalArgumentException if any field is out of range
    public long computeMillis(boolean resetFields) {
        return computeMillis(resetFieldsnull);
    }

    
Computes the parsed datetime by setting the saved fields. This method is idempotent, but it is not thread-safe.

Parameters:
resetFields false by default, but when true, unsaved field values are cleared
text optional text being parsed, to be included in any error message
Returns:
milliseconds since 1970-01-01T00:00:00Z
Throws:
java.lang.IllegalArgumentException if any field is out of range
Since:
1.3
    public long computeMillis(boolean resetFieldsString text) {
        SavedField[] savedFields = ;
        int count = ;
        if () {
             = savedFields = (SavedField[]).clone();
             = false;
        }
        sort(savedFieldscount);
        long millis = ;
        try {
            for (int i=0; i<counti++) {
                millis = savedFields[i].set(millisresetFields);
            }
        } catch (IllegalFieldValueException e) {
            if (text != null) {
                e.prependMessage("Cannot parse \"" + text + '"');
            }
            throw e;
        }
        
        if ( == null) {
            millis -= ;
        } else {
            int offset = .getOffsetFromLocal(millis);
            millis -= offset;
            if (offset != .getOffset(millis)) {
                String message =
                    "Illegal instant due to time zone offset transition (" +  + ')';
                if (text != null) {
                    message = "Cannot parse \"" + text + "\": " + message;
                }
                throw new IllegalArgumentException(message);
            }
        }
        
        return millis;
    }
    
    
Sorts elements [0,high). Calling java.util.Arrays isn't always the right choice since it always creates an internal copy of the array, even if it doesn't need to. If the array slice is small enough, an insertion sort is chosen instead, but it doesn't need a copy!

This method has a modified version of that insertion sort, except it doesn't create an unnecessary array copy. If high is over 10, then java.util.Arrays is called, which will perform a merge sort, which is faster than insertion sort on large lists.

The end result is much greater performace when computeMillis is called. Since the amount of saved fields is small, the insertion sort is a better choice. Additional performance is gained since there is no extra array allocation and copying. Also, the insertion sort here does not perform any casting operations. The version in java.util.Arrays performs casts within the insertion sort loop.

    private static void sort(Comparable[] arrayint high) {
        if (high > 10) {
            Arrays.sort(array, 0, high);
        } else {
            for (int i=0; i<highi++) {
                for (int j=ij>0 && (array[j-1]).compareTo(array[j])>0; j--) {
                    Comparable t = array[j];
                    array[j] = array[j-1];
                    array[j-1] = t;
                }
            }
        }
    }
    class SavedState {
        final DateTimeZone iZone;
        final int iOffset;
        final SavedField[] iSavedFields;
        final int iSavedFieldsCount;
        
        SavedState() {
            this. = DateTimeParserBucket.this.;
            this. = DateTimeParserBucket.this.;
            this. = DateTimeParserBucket.this.;
            this. = DateTimeParserBucket.this.;
        }
        
        boolean restoreState(DateTimeParserBucket enclosing) {
            if (enclosing != DateTimeParserBucket.this) {
                return false;
            }
            enclosing.iZone = this.;
            enclosing.iOffset = this.;
            enclosing.iSavedFields = this.;
            if (this. < enclosing.iSavedFieldsCount) {
                // Since count is being restored to a lower count, the
                // potential exists for new saved fields to destroy data being
                // shared by another state. Set this flag such that the array
                // of saved fields is cloned prior to modification.
                enclosing.iSavedFieldsShared = true;
            }
            enclosing.iSavedFieldsCount = this.;
            return true;
        }
    }
    
    static class SavedField implements Comparable {
        final DateTimeField iField;
        final int iValue;
        final String iText;
        final Locale iLocale;
        
        SavedField(DateTimeField fieldint value) {
             = field;
             = value;
             = null;
             = null;
        }
        
        SavedField(DateTimeField fieldString textLocale locale) {
             = field;
             = 0;
             = text;
             = locale;
        }
        
        long set(long millisboolean reset) {
            if ( == null) {
                millis = .set(millis);
            } else {
                millis = .set(millis);
            }
            if (reset) {
                millis = .roundFloor(millis);
            }
            return millis;
        }
        
        
The field with the longer range duration is ordered first, where null is considered infinite. If the ranges match, then the field with the longer duration is ordered first.
        public int compareTo(Object obj) {
            DateTimeField other = ((SavedField)obj).;
            int result = compareReverse
                (.getRangeDurationField(), other.getRangeDurationField());
            if (result != 0) {
                return result;
            }
            return compareReverse
                (.getDurationField(), other.getDurationField());
        }
        
        private int compareReverse(DurationField aDurationField b) {
            if (a == null || !a.isSupported()) {
                if (b == null || !b.isSupported()) {
                    return 0;
                }
                return -1;
            }
            if (b == null || !b.isSupported()) {
                return 1;
            }
            return -a.compareTo(b);
        }
    }
New to GrepCode? Check out our FAQ X