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&lt;V&gt;</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