001    /*
002     *  Copyright 2001-2005 Stephen Colebourne
003     *
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package org.joda.time.field;
017    
018    import org.joda.time.DateTimeField;
019    import org.joda.time.DateTimeFieldType;
020    import org.joda.time.DurationField;
021    
022    /**
023     * Counterpart remainder datetime field to {@link DividedDateTimeField}. The
024     * field's unit duration is unchanged, but the range duration is scaled
025     * accordingly.
026     * <p>
027     * RemainderDateTimeField is thread-safe and immutable.
028     *
029     * @see DividedDateTimeField
030     *
031     * @author Brian S O'Neill
032     * @since 1.0
033     */
034    public class RemainderDateTimeField extends DecoratedDateTimeField {
035    
036        private static final long serialVersionUID = 5708241235177666790L;
037    
038        // Shared with DividedDateTimeField.
039        final int iDivisor;
040        final DurationField iRangeField;
041    
042        /**
043         * Constructor.
044         * 
045         * @param field  the field to wrap, like "year()".
046         * @param type  the field type this field actually uses
047         * @param divisor  divisor, such as 100 years in a century
048         * @throws IllegalArgumentException if divisor is less than two
049         */
050        public RemainderDateTimeField(DateTimeField field,
051                                      DateTimeFieldType type, int divisor) {
052            super(field, type);
053    
054            if (divisor < 2) {
055                throw new IllegalArgumentException("The divisor must be at least 2");
056            }
057    
058            DurationField rangeField = field.getDurationField();
059            if (rangeField == null) {
060                iRangeField = null;
061            } else {
062                iRangeField = new ScaledDurationField(
063                    rangeField, type.getRangeDurationType(), divisor);
064            }
065    
066            iDivisor = divisor;
067        }
068    
069        /**
070         * Construct a RemainderDateTimeField that compliments the given
071         * DividedDateTimeField.
072         *
073         * @param dividedField  complimentary divided field, like "century()".
074         */
075        public RemainderDateTimeField(DividedDateTimeField dividedField) {
076            this(dividedField, dividedField.getType());
077        }
078    
079        /**
080         * Construct a RemainderDateTimeField that compliments the given
081         * DividedDateTimeField.
082         *
083         * @param dividedField  complimentary divided field, like "century()".
084         * @param type  the field type this field actually uses
085         */
086        public RemainderDateTimeField(DividedDateTimeField dividedField, DateTimeFieldType type) {
087            super(dividedField.getWrappedField(), type);
088            iDivisor = dividedField.iDivisor;
089            iRangeField = dividedField.iDurationField;
090        }
091    
092        //-----------------------------------------------------------------------
093        /**
094         * Get the remainder from the specified time instant.
095         * 
096         * @param instant  the time instant in millis to query.
097         * @return the remainder extracted from the input.
098         */
099        public int get(long instant) {
100            int value = getWrappedField().get(instant);
101            if (value >= 0) {
102                return value % iDivisor;
103            } else {
104                return (iDivisor - 1) + ((value + 1) % iDivisor);
105            }
106        }
107    
108        /**
109         * Add the specified amount to the specified time instant, wrapping around
110         * within the remainder range if necessary. The amount added may be
111         * negative.
112         * 
113         * @param instant  the time instant in millis to update.
114         * @param amount  the amount to add (can be negative).
115         * @return the updated time instant.
116         */
117        public long addWrapField(long instant, int amount) {
118            return set(instant, FieldUtils.getWrappedValue(get(instant), amount, 0, iDivisor - 1));
119        }
120    
121        /**
122         * Set the specified amount of remainder units to the specified time instant.
123         * 
124         * @param instant  the time instant in millis to update.
125         * @param value  value of remainder units to set.
126         * @return the updated time instant.
127         * @throws IllegalArgumentException if value is too large or too small.
128         */
129        public long set(long instant, int value) {
130            FieldUtils.verifyValueBounds(this, value, 0, iDivisor - 1);
131            int divided = getDivided(getWrappedField().get(instant));
132            return getWrappedField().set(instant, divided * iDivisor + value);
133        }
134    
135        /**
136         * Returns a scaled version of the wrapped field's unit duration field.
137         */
138        public DurationField getRangeDurationField() {
139            return iRangeField;
140        }
141    
142        /**
143         * Get the minimum value for the field, which is always zero.
144         * 
145         * @return the minimum value of zero.
146         */
147        public int getMinimumValue() {
148            return 0;
149        }
150    
151        /**
152         * Get the maximum value for the field, which is always one less than the
153         * divisor.
154         * 
155         * @return the maximum value
156         */
157        public int getMaximumValue() {
158            return iDivisor - 1;
159        }
160    
161        public long roundFloor(long instant) {
162            return getWrappedField().roundFloor(instant);
163        }
164    
165        public long roundCeiling(long instant) {
166            return getWrappedField().roundCeiling(instant);
167        }
168    
169        public long roundHalfFloor(long instant) {
170            return getWrappedField().roundHalfFloor(instant);
171        }
172    
173        public long roundHalfCeiling(long instant) {
174            return getWrappedField().roundHalfCeiling(instant);
175        }
176    
177        public long roundHalfEven(long instant) {
178            return getWrappedField().roundHalfEven(instant);
179        }
180    
181        public long remainder(long instant) {
182            return getWrappedField().remainder(instant);
183        }
184    
185        /**
186         * Returns the divisor applied, in the field's units.
187         * 
188         * @return the divisor
189         */
190        public int getDivisor() {
191            return iDivisor;
192        }
193    
194        private int getDivided(int value) {
195            if (value >= 0) {
196                return value / iDivisor;
197            } else {
198                return ((value + 1) / iDivisor) - 1;
199            }
200        }
201    
202    }