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: * Divides a DateTimeField such that the retrieved values are reduced by a
024: * fixed divisor. The field's unit duration is scaled accordingly, but the
025: * range duration is unchanged.
026: * <p>
027: * DividedDateTimeField is thread-safe and immutable.
028: *
029: * @see RemainderDateTimeField
030: *
031: * @author Stephen Colebourne
032: * @author Brian S O'Neill
033: * @since 1.0
034: */
035: public class DividedDateTimeField extends DecoratedDateTimeField {
036:
037: private static final long serialVersionUID = 8318475124230605365L;
038:
039: // Shared with RemainderDateTimeField.
040: final int iDivisor;
041: final DurationField iDurationField;
042:
043: private final int iMin;
044: private final int iMax;
045:
046: /**
047: * Constructor.
048: *
049: * @param field the field to wrap, like "year()".
050: * @param type the field type this field will actually use
051: * @param divisor divisor, such as 100 years in a century
052: * @throws IllegalArgumentException if divisor is less than two
053: */
054: public DividedDateTimeField(DateTimeField field,
055: DateTimeFieldType type, int divisor) {
056: super (field, type);
057:
058: if (divisor < 2) {
059: throw new IllegalArgumentException(
060: "The divisor must be at least 2");
061: }
062:
063: DurationField unitField = field.getDurationField();
064: if (unitField == null) {
065: iDurationField = null;
066: } else {
067: iDurationField = new ScaledDurationField(unitField, type
068: .getDurationType(), divisor);
069: }
070:
071: iDivisor = divisor;
072:
073: int i = field.getMinimumValue();
074: int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1);
075:
076: int j = field.getMaximumValue();
077: int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1);
078:
079: iMin = min;
080: iMax = max;
081: }
082:
083: /**
084: * Construct a DividedDateTimeField that compliments the given
085: * RemainderDateTimeField.
086: *
087: * @param remainderField complimentary remainder field, like "yearOfCentury()".
088: * @param type the field type this field will actually use
089: */
090: public DividedDateTimeField(RemainderDateTimeField remainderField,
091: DateTimeFieldType type) {
092: super (remainderField.getWrappedField(), type);
093: int divisor = iDivisor = remainderField.iDivisor;
094: iDurationField = remainderField.iRangeField;
095:
096: DateTimeField field = getWrappedField();
097: int i = field.getMinimumValue();
098: int min = (i >= 0) ? i / divisor : ((i + 1) / divisor - 1);
099:
100: int j = field.getMaximumValue();
101: int max = (j >= 0) ? j / divisor : ((j + 1) / divisor - 1);
102:
103: iMin = min;
104: iMax = max;
105: }
106:
107: /**
108: * Get the amount of scaled units from the specified time instant.
109: *
110: * @param instant the time instant in millis to query.
111: * @return the amount of scaled units extracted from the input.
112: */
113: public int get(long instant) {
114: int value = getWrappedField().get(instant);
115: if (value >= 0) {
116: return value / iDivisor;
117: } else {
118: return ((value + 1) / iDivisor) - 1;
119: }
120: }
121:
122: /**
123: * Add the specified amount of scaled units to the specified time
124: * instant. The amount added may be negative.
125: *
126: * @param instant the time instant in millis to update.
127: * @param amount the amount of scaled units to add (can be negative).
128: * @return the updated time instant.
129: */
130: public long add(long instant, int amount) {
131: return getWrappedField().add(instant, amount * iDivisor);
132: }
133:
134: /**
135: * Add the specified amount of scaled units to the specified time
136: * instant. The amount added may be negative.
137: *
138: * @param instant the time instant in millis to update.
139: * @param amount the amount of scaled units to add (can be negative).
140: * @return the updated time instant.
141: */
142: public long add(long instant, long amount) {
143: return getWrappedField().add(instant, amount * iDivisor);
144: }
145:
146: /**
147: * Add to the scaled component of the specified time instant,
148: * wrapping around within that component if necessary.
149: *
150: * @param instant the time instant in millis to update.
151: * @param amount the amount of scaled units to add (can be negative).
152: * @return the updated time instant.
153: */
154: public long addWrapField(long instant, int amount) {
155: return set(instant, FieldUtils.getWrappedValue(get(instant),
156: amount, iMin, iMax));
157: }
158:
159: public int getDifference(long minuendInstant, long subtrahendInstant) {
160: return getWrappedField().getDifference(minuendInstant,
161: subtrahendInstant)
162: / iDivisor;
163: }
164:
165: public long getDifferenceAsLong(long minuendInstant,
166: long subtrahendInstant) {
167: return getWrappedField().getDifferenceAsLong(minuendInstant,
168: subtrahendInstant)
169: / iDivisor;
170: }
171:
172: /**
173: * Set the specified amount of scaled units to the specified time instant.
174: *
175: * @param instant the time instant in millis to update.
176: * @param value value of scaled units to set.
177: * @return the updated time instant.
178: * @throws IllegalArgumentException if value is too large or too small.
179: */
180: public long set(long instant, int value) {
181: FieldUtils.verifyValueBounds(this , value, iMin, iMax);
182: int remainder = getRemainder(getWrappedField().get(instant));
183: return getWrappedField().set(instant,
184: value * iDivisor + remainder);
185: }
186:
187: /**
188: * Returns a scaled version of the wrapped field's unit duration field.
189: */
190: public DurationField getDurationField() {
191: return iDurationField;
192: }
193:
194: /**
195: * Get the minimum value for the field.
196: *
197: * @return the minimum value
198: */
199: public int getMinimumValue() {
200: return iMin;
201: }
202:
203: /**
204: * Get the maximum value for the field.
205: *
206: * @return the maximum value
207: */
208: public int getMaximumValue() {
209: return iMax;
210: }
211:
212: public long roundFloor(long instant) {
213: DateTimeField field = getWrappedField();
214: return field.roundFloor(field.set(instant, get(instant)
215: * iDivisor));
216: }
217:
218: public long remainder(long instant) {
219: return set(instant, get(getWrappedField().remainder(instant)));
220: }
221:
222: /**
223: * Returns the divisor applied, in the field's units.
224: *
225: * @return the divisor
226: */
227: public int getDivisor() {
228: return iDivisor;
229: }
230:
231: private int getRemainder(int value) {
232: if (value >= 0) {
233: return value % iDivisor;
234: } else {
235: return (iDivisor - 1) + ((value + 1) % iDivisor);
236: }
237: }
238:
239: }
|