1 /*
2  *  Copyright 2001-2013 Stephen Colebourne
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */

16 package org.joda.time.field;
17
18 import org.joda.time.DateTimeField;
19 import org.joda.time.DateTimeFieldType;
20 import org.joda.time.DurationField;
21
22 /**
23  * Counterpart remainder datetime field to {@link DividedDateTimeField}. The
24  * field's unit duration is unchanged, but the range duration is scaled
25  * accordingly.
26  * <p>
27  * RemainderDateTimeField is thread-safe and immutable.
28  *
29  * @see DividedDateTimeField
30  *
31  * @author Brian S O'Neill
32  * @since 1.0
33  */

34 public class RemainderDateTimeField extends DecoratedDateTimeField {
35
36     @SuppressWarnings("unused")
37     private static final long serialVersionUID = 5708241235177666790L;
38
39     // Shared with DividedDateTimeField.
40     final int iDivisor;
41     final DurationField iDurationField;
42     final DurationField iRangeField;
43
44     /**
45      * Constructor.
46      * 
47      * @param field  the field to wrap, like "year()".
48      * @param type  the field type this field actually uses
49      * @param divisor  divisor, such as 100 years in a century
50      * @throws IllegalArgumentException if divisor is less than two
51      */

52     public RemainderDateTimeField(DateTimeField field,
53                                   DateTimeFieldType type, int divisor) {
54         super(field, type);
55
56         if (divisor < 2) {
57             throw new IllegalArgumentException("The divisor must be at least 2");
58         }
59
60         DurationField rangeField = field.getDurationField();
61         if (rangeField == null) {
62             iRangeField = null;
63         } else {
64             iRangeField = new ScaledDurationField(
65                 rangeField, type.getRangeDurationType(), divisor);
66         }
67         iDurationField = field.getDurationField();
68         iDivisor = divisor;
69     }
70
71     /**
72      * Constructor.
73      * 
74      * @param field  the field to wrap, like "year()".
75      * @param rangeField  the range field
76      * @param type  the field type this field actually uses
77      * @param divisor  divisor, such as 100 years in a century
78      * @throws IllegalArgumentException if divisor is less than two
79      */

80     public RemainderDateTimeField(DateTimeField field, DurationField rangeField,
81                                   DateTimeFieldType type, int divisor) {
82         super(field, type);
83         if (divisor < 2) {
84             throw new IllegalArgumentException("The divisor must be at least 2");
85         }
86         iRangeField = rangeField;
87         iDurationField = field.getDurationField();
88         iDivisor = divisor;
89     }
90
91     /**
92      * Construct a RemainderDateTimeField that compliments the given
93      * DividedDateTimeField.
94      *
95      * @param dividedField  complimentary divided field, like "century()".
96      */

97     public RemainderDateTimeField(DividedDateTimeField dividedField) {
98         this(dividedField, dividedField.getType());
99     }
100
101     /**
102      * Construct a RemainderDateTimeField that compliments the given
103      * DividedDateTimeField.
104      *
105      * @param dividedField  complimentary divided field, like "century()".
106      * @param type  the field type this field actually uses
107      */

108     public RemainderDateTimeField(DividedDateTimeField dividedField, DateTimeFieldType type) {
109         this(dividedField, dividedField.getWrappedField().getDurationField(), type);
110     }
111
112     /**
113      * Construct a RemainderDateTimeField that compliments the given
114      * DividedDateTimeField.
115      * This constructor allows the duration field to be set.
116      *
117      * @param dividedField  complimentary divided field, like "century()".
118      * @param durationField  the duration field
119      * @param type  the field type this field actually uses
120      */

121     public RemainderDateTimeField(DividedDateTimeField dividedField, DurationField durationField, DateTimeFieldType type) {
122         super(dividedField.getWrappedField(), type);
123         iDivisor = dividedField.iDivisor;
124         iDurationField = durationField;
125         iRangeField = dividedField.iDurationField;
126     }
127
128     //-----------------------------------------------------------------------
129     /**
130      * Get the remainder from the specified time instant.
131      * 
132      * @param instant  the time instant in millis to query.
133      * @return the remainder extracted from the input.
134      */

135     public int get(long instant) {
136         int value = getWrappedField().get(instant);
137         if (value >= 0) {
138             return value % iDivisor;
139         } else {
140             return (iDivisor - 1) + ((value + 1) % iDivisor);
141         }
142     }
143
144     /**
145      * Add the specified amount to the specified time instant, wrapping around
146      * within the remainder range if necessary. The amount added may be
147      * negative.
148      * 
149      * @param instant  the time instant in millis to update.
150      * @param amount  the amount to add (can be negative).
151      * @return the updated time instant.
152      */

153     public long addWrapField(long instant, int amount) {
154         return set(instant, FieldUtils.getWrappedValue(get(instant), amount, 0, iDivisor - 1));
155     }
156
157     /**
158      * Set the specified amount of remainder units to the specified time instant.
159      * 
160      * @param instant  the time instant in millis to update.
161      * @param value  value of remainder units to set.
162      * @return the updated time instant.
163      * @throws IllegalArgumentException if value is too large or too small.
164      */

165     public long set(long instant, int value) {
166         FieldUtils.verifyValueBounds(this, value, 0, iDivisor - 1);
167         int divided = getDivided(getWrappedField().get(instant));
168         return getWrappedField().set(instant, divided * iDivisor + value);
169     }
170
171     @Override
172     public DurationField getDurationField() {
173         return iDurationField;
174     }
175
176     /**
177      * Returns a scaled version of the wrapped field's unit duration field.
178      */

179     public DurationField getRangeDurationField() {
180         return iRangeField;
181     }
182
183     /**
184      * Get the minimum value for the field, which is always zero.
185      * 
186      * @return the minimum value of zero.
187      */

188     public int getMinimumValue() {
189         return 0;
190     }
191
192     /**
193      * Get the maximum value for the field, which is always one less than the
194      * divisor.
195      * 
196      * @return the maximum value
197      */

198     public int getMaximumValue() {
199         return iDivisor - 1;
200     }
201
202     public long roundFloor(long instant) {
203         return getWrappedField().roundFloor(instant);
204     }
205
206     public long roundCeiling(long instant) {
207         return getWrappedField().roundCeiling(instant);
208     }
209
210     public long roundHalfFloor(long instant) {
211         return getWrappedField().roundHalfFloor(instant);
212     }
213
214     public long roundHalfCeiling(long instant) {
215         return getWrappedField().roundHalfCeiling(instant);
216     }
217
218     public long roundHalfEven(long instant) {
219         return getWrappedField().roundHalfEven(instant);
220     }
221
222     public long remainder(long instant) {
223         return getWrappedField().remainder(instant);
224     }
225
226     /**
227      * Returns the divisor applied, in the field's units.
228      * 
229      * @return the divisor
230      */

231     public int getDivisor() {
232         return iDivisor;
233     }
234
235     private int getDivided(int value) {
236         if (value >= 0) {
237             return value / iDivisor;
238         } else {
239             return ((value + 1) / iDivisor) - 1;
240         }
241     }
242
243 }
244