001 /*
002 * Copyleft notice: This is public-domain software and documentation
003 * with no restrictions of any kind.
004 * Please feel free to use any of it in any way you want.
005 * This work is distributed in the hope that it will be useful,
006 * but WITHOUT ANY WARRANTY; without even the implied warranty of
007 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
008 */
009
010
011 package time;
012
013 import java.util.*;
014
015
016 /**
017 * Implements a field used in time representation.
018 *
019 * <p>
020 * A TimeField can be used by one or several different Chronologies.
021 * For example, Julian and Gregorian chronologies will likely share the same
022 * TimeFields. But Chinese chronology will have its own TimeField, although it
023 * may use the same TimeField for hour, minutes, second and millisecond.
024 *
025 * <p>
026 * <tt>TimeField</tt> objects are provided by Chronologies, and the end user
027 * should not create new ones.
028 *
029 * <p>
030 * A <tt>TimeField</tt> can have several values. Thoses value are defined with
031 * the <tt>TimeField</tt> itself, at TimeField creation. For example,
032 * <ul>
033 * <li> for Month : JANUARY, FEBRUARY, MARCH [...], DECEMBER
034 * <li> for Hour : 0, 1, [...], 23
035 * </ul>
036 *
037 * <p>
038 * The <tt>TimeField</tt> object defines also an order for its possible values,
039 * and that's why it implements <tt>Comparable<V></tt>.
040 * There are two options:
041 *
042 * <ul>
043 * <li> Either a <tt>java.util.Comparator</tt> is provided at <tt>TimeField</tt>
044 * creation, and this Comparator is used to achieve comparaisons between values.
045 * <li> Or no specific <tt>java.util.Comparator</tt> is used, and the
046 * <i>natural order</i> of values are used to achieve comparaisons. (so values
047 * must implement <tt>Comparable</tt> in this case).
048 * </ul>
049 *
050 *
051 * <p>
052 * TimeField have a <i>typical duration</i> which is exprimed in milliseconds. The <i>
053 * typical duration</i> of a TimeField is the mean, or the most common duration of
054 * this TimeField. More formaly, this <i>typical duration</i> is the variation of
055 * time implied by the minimal change of this TimeField values.
056 *
057 * <p>Here some examples of <i>typical duration</i>:
058 * <ul>
059 * <li> for Month: 30 days [2 592 000 000 milliseconds] (some month last 31 days, some 30 days,
060 * some 28 or even 29), so 30 days
061 * is a good <i>typical duration</i>.
062 * <li> for Hour : 3 600 000 milliseconds, as an hour lasts 3600 seconds.
063 * <li> for Days : 24 hours [86 400 000 milliseconds], as almost everyday lasts 24 hours,
064 * except with Day Light Saving, where some days last 23 hours, and some 25 hours.
065 * </ul>
066 *
067 * <p>The <i>typical duration</i> is <u>only</u> used for sorting the TimeFields together.
068 * TimeField have indeed a natural order.
069 *
070 * <p>
071 * TimeField are first compared by their <i>typical duration</i>. The greater the <i>typical
072 * duration</i>, the smaller the TimeField. So a TimeField like Month is before a TimeField
073 * like Days, which is before Hour. This allows to sort TimeFields with most significant
074 * TimeFields in first position.
075 *
076 * Two <tt>TimeFields</tt> objects with the same <i>typical duration</i> are sorted lexicographically
077 * using their name.
078 * It is suggested that a Chronology does <i>not</i> provide
079 * such TimeFields with the same <i>typical duration</i>.
080 *
081 * <p>
082 * As <tt>equals()</tt> method is not redefined, the natural order is <i>not</i> consistent
083 * with <tt>equals()</tt>: two <tt>TimeField</tt> defined by two different chronologies with
084 * the same name and duration will be equals according to <tt>compareTo()</tt> but not
085 * according to <tt>equals()</tt>.
086 * This should not be a problem as such <tt>TimeField</tt> should not
087 * be used together in the same <tt>TimeMask</tt>.
088 *
089 *
090 * @see TimeMask
091 * @see Chronology
092 *
093 *
094 *
095 * @author Arnaud Roques
096 *
097 *
098 * @stereotype "Comparator"
099 * @stereotype "Comparable"
100 *
101 */
102 public final class TimeField<V> implements Comparable<TimeField<V>>, Comparator<V>
103 {
104 private final String name;
105 private final long typicalDuration;
106 private final Collection<V> possibleValues;
107 private final SortedSet<V> possibleValuesSorted;
108 private final Comparator<V> comparator;
109
110 /**
111 * Create a new <tt>TimeField</tt>.
112 *
113 * <br>End user should not create new <tt>TimeField</tt>. This constructor is
114 * provided for new <tt>Chronology</tt> implementations.
115 *
116 * <p>
117 * If values of this <tt>TimeField</tt> object are not <tt>Comparable</tt>, the
118 * order of the <tt>possibleValues</tt> collection is used to achieve comparaisons between
119 * values.
120 * (this collections is assumed ordered by ascending order).
121 *
122 * <p>
123 * The provided <tt>possibleValues</tt> set should not be changed after <tt>TimeField</tt>
124 * creation (with <tt>add</tt> or <tt>remove</tt> methods for example),
125 * as this implementation keeps a reference to this set for testing
126 * future acceptable values.
127 *
128 * @param name the name of the new <tt>TimeField</tt>
129 * @param typicalDuration the typical duration in millisecond of the new <tt>TimeField</tt>
130 * @param possibleValues all possible values of the new <tt>TimeField</tt>
131 * @param comparator <tt>null</tt> if the <i>natural order</i> of <tt>possibleValues</tt>
132 * is used, otherwise a comparator that defines the order for <tt>possibleValues</tt>.
133 *
134 * @throws NullPointerException if <tt>name</tt> is null
135 * @throws IllegalArgumentException if <tt>possibleValues</tt> is empty.
136 *
137 * @see #isValueOk
138 */
139 public TimeField(String name, long typicalDuration, Collection<V> possibleValues, Comparator<V> comparator)
140 {
141 if (name==null) throw new NullPointerException();
142 if (possibleValues.isEmpty()) throw new IllegalArgumentException();
143 this.name = name;
144 this.typicalDuration = typicalDuration;
145 this.possibleValues = Collections.unmodifiableCollection(possibleValues);
146 this.comparator = comparator;
147 TreeSet ts = new TreeSet(this);
148 ts.addAll(possibleValues);
149 this.possibleValuesSorted = Collections.unmodifiableSortedSet(ts);
150 }
151
152 /**
153 * Compare this TimeField with another one.
154 *
155 * The most significant TimeField is sorted first.
156 *
157 * @return 0 if this TimeField has the same <i>typical duration</i> and name than the
158 * <tt>other</tt> TimeField;
159 * a value less than 0 if this TimeField is more significant than the <tt>other</tt>
160 * TimeField (ie has a higher <i>typical duration</i>), or if it has the same
161 * <i>typical duration</i>, but a name lexicographically smaller;
162 * a value greater than 0 if this TimeField is less significant than the <tt>other</tt>
163 * TimeField (ie has a lower <i>typical duration</i>), or if it has the same
164 * <i>typical duration</i>, but a name lexicographically greater;
165 *
166 * @throws NullPointerException if <tt>other</tt> is null.
167 */
168 public int compareTo(TimeField other)
169 {
170 if (other.typicalDuration > typicalDuration) return 1;
171 if (other.typicalDuration < typicalDuration) return -1;
172 return name.compareTo(other.name);
173 }
174
175 /**
176 * Return the name of this <tt>TimeField</tt>.
177 * The name was specified at <tt>TimeField</tt> creation.
178 *
179 * @return the name of this <tt>TimeField</tt>
180 */
181 public String getName()
182 {
183 return name;
184 }
185
186
187 /**
188 * Return all possible values of this TimeField.
189 *
190 * @return all possible values of this TimeField.
191 */
192 public SortedSet<V> values()
193 {
194 return possibleValuesSorted;
195 }
196
197 /**
198 * Test if a value is acceptable for this TimeField.
199 *
200 * <p>The <tt>value</tt> is acceptable if is contained in the possible value
201 * set provided at <tt>TimeField</tt> creation.
202 *
203 * @return <tt>true</tt> if <tt>value</tt> is acceptable for this TimeField;
204 * <tt>false</tt> otherwise.
205 *
206 * @throws NullPointerException if <tt>value</tt> is null.
207 */
208 public boolean isValueOk(V value)
209 {
210 if (value==null) throw new NullPointerException();
211 return possibleValues.contains(value);
212 }
213
214 /*
215 V next(V elt)
216 {
217 for (Iterator<V> it = possibleValues.iterator(); it.hasNext();)
218 {
219 if (it.next().equals(elt)) return it.next();
220 }
221 throw new NoSuchElementException();
222 }
223
224 V previous(V elt)
225 {
226 V last = null;
227 for (Iterator<V> it = possibleValues.iterator(); it.hasNext();)
228 {
229 if (it.next().equals(elt))
230 {
231 if (last==null) throw new NoSuchElementException();
232 return last;
233 };
234 }
235 throw new NoSuchElementException();
236 }*/
237
238 /**
239 * Convert this <tt>TimeField</tt> into a <tt>String</tt>.
240 *
241 * @return a <tt>String</tt> describing the <tt>TimeField</tt>
242 */
243 @Override
244 public String toString()
245 {
246 return getName();
247 }
248
249 /**
250 * Compare two possibles values of a TimeField according to this TimeField rule.
251 *
252 * <p>
253 * If the values are <tt>Comparable</tt> if no <tt>Comparator</tt> have been
254 * provided at <tt>TimeField</tt> creation, the natural order of the values is used.
255 *
256 * <p>
257 * Otherwise, the <tt>Comparator</tt> provided at <tt>TimeField</tt> creation is
258 * used.
259 *
260 *
261 * @return the result
262 *
263 * @throws IllegalArgumentException if <tt>o1</tt> or <tt>o2</tt> is not a legal
264 * value for this <tt>TimeField</tt> object.
265 *
266 * @throws ClassCastException if values are not <tt>Comparable</tt> and
267 * no <tt>Comparator</tt> have been provided
268 */
269 public int compare(V o1, V o2)
270 {
271 if (possibleValues.contains(o1)==false) throw new IllegalArgumentException();
272 if (possibleValues.contains(o2)==false) throw new IllegalArgumentException();
273
274 if (comparator!=null)
275 {
276 return comparator.compare(o1, o2);
277 }
278 Comparable c1 = (Comparable) o1;
279 Comparable c2 = (Comparable) o2;
280 return c1.compareTo(c2);
281 }
282 }
283