View Javadoc

1   /*
2    * This file is part of Domingo
3    * an Open Source Java-API to Lotus Notes/Domino
4    * hosted at http://domingo.sourceforge.net
5    *
6    * Copyright (c) 2003-2007 Beck et al. projects GmbH Munich, Germany (http://www.bea.de)
7    *
8    * This library is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU Lesser General Public
10   * License as published by the Free Software Foundation; either
11   * version 2.1 of the License, or (at your option) any later version.
12   *
13   * This library is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   * Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public
19   * License along with this library; if not, write to the Free Software
20   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21   */
22  
23  package de.bea.domingo.proxy;
24  
25  import java.io.Serializable;
26  import java.util.Calendar;
27  import java.util.Date;
28  import java.util.TimeZone;
29  
30  import lotus.domino.DateRange;
31  import lotus.domino.DateTime;
32  import lotus.domino.NotesException;
33  import lotus.domino.Session;
34  import de.bea.domingo.DNotesMonitor;
35  import de.bea.domingo.exception.DominoException;
36  import de.bea.domingo.i18n.ResourceManager;
37  import de.bea.domingo.i18n.Resources;
38  import de.bea.domingo.util.GregorianDate;
39  import de.bea.domingo.util.GregorianDateTime;
40  import de.bea.domingo.util.GregorianTime;
41  
42  /***
43   * Proxy to the Notes international settings.
44   *
45   * @author <a href=mailto:kriede@users.sourceforge.net>Kurt Riede</a>
46   */
47  public final class InternationalProxy implements Serializable {
48  
49      private static final int TZ_MINUTES_OFFSET = 100;
50  
51      /*** serial version ID for serialization. */
52      private static final long serialVersionUID = -2957715893148795275L;
53  
54      /*** Minutes per hour. */
55      private static final int MINUTES_PER_HOUR = 60;
56  
57      /*** Seconds per minute. */
58      private static final int SECONDS_PER_MINUTE = 60;
59  
60      /*** Milli seconds per hour. */
61      private static final int MILLIS_PER_SECOND = 1000;
62  
63      /*** Milli seconds per hour. */
64      private static final int MILLIS_PER_MINUTE = MILLIS_PER_SECOND * SECONDS_PER_MINUTE;
65  
66      /*** Milli seconds per hour. */
67      private static final int MILLIS_PER_HOUR = MILLIS_PER_MINUTE * MINUTES_PER_HOUR;
68  
69      /*** Internationalized resources. */
70      private static final Resources RESOURCES = ResourceManager.getPackageResources(NotesProxyFactory.class);
71  
72      /*** Default separator for date components. */
73      public static final char DATE_SEPARATOR_DEFAULT = '/';
74  
75      /*** Default separator for time components. */
76      public static final char TIME_SEPARATOR_DEFAULT = ':';
77  
78      /***
79       * Date format ordered as day/month/year. The slashes will be replaced with
80       * the concrete date separator.
81       */
82      public static final String DATE_FORMAT_DMY = "dd/MM/yy";
83  
84      /***
85       * Date format ordered as month/day/year. The slashes will be replaced with
86       * the concrete date separator.
87       */
88      public static final String DATE_FORMAT_MDY = "MM/dd/yy";
89  
90      /***
91       * Date format ordered as year/month/day. The slashes will be replaced with
92       * the concrete date separator.
93       */
94      public static final String DATE_FORMAT_YMD = "yy/MM/dd";
95  
96      /***
97       * Default date format. The slashes will be replaced with the concrete date
98       * separator.
99       */
100     public static final String DATE_FORMAT_DEFAULT = DATE_FORMAT_DMY;
101 
102     /***
103      * Time format in 24 hour display. The colons will be replaced with the
104      * concrete date separator.
105      */
106     public static final String TIME_FORMAT_24 = "HH:mm:ss";
107 
108     /***
109      * Default time format. The colons will be replaced with the concrete date
110      * separator.
111      */
112     public static final String TIME_FORMAT_DEFAULT = "hh:mm:ss a";
113 
114     /*** Format string for time zones in format of RFC 822 , e.g. <tt>"-0800"</tt>. */
115     public static final String TIMEZONE_FORMAT = "Z";
116 
117     /*** Base Monitor instance. */
118     private DNotesMonitor monitor = null;
119 
120     /*** Associated session (local call or IIOP). */
121     private Session session;
122 
123     /***
124      * Constructor.
125      *
126      * @param theSession the Notes Session
127      * @param theMonitor the monitor
128      */
129     public InternationalProxy(final Session theSession, final DNotesMonitor theMonitor) {
130         super();
131         this.session = theSession;
132         this.monitor = theMonitor;
133     }
134 
135     /***
136      * Get the current monitor.
137      *
138      * @return current monitor
139      * @see de.bea.domingo.DNotesFactory#getMonitor()
140      */
141     protected DNotesMonitor getMonitor() {
142         return monitor;
143     }
144 
145     /***
146      * Creates a Notes <code>DateTime</code> instance from a
147      * <code>java.util.Calendar</code>.
148      *
149      * @param calendar the calendar to convert
150      * @return DateTime a Notes DateTime object
151      */
152     protected DateTime createDateTime(final Calendar calendar) {
153         final Calendar correct = (Calendar) calendar.clone();
154         try {
155             TimeZone gmtZone = TimeZone.getTimeZone("GMT");
156             correct.setTimeZone(gmtZone);
157             TimeZone timeZone = calendar.getTimeZone();
158             correct.add(Calendar.SECOND, -timeZone.getOffset(correct.getTimeInMillis()) / MILLIS_PER_SECOND);
159             final DateTime dateTime = session.createDateTime(correct);
160             int zoneValue = getNotesTimeZoneValue(timeZone, calendar.getTimeInMillis());
161             dateTime.convertToZone(zoneValue, timeZone.inDaylightTime(calendar.getTime()));
162             if (!isDateSet(correct)) {
163                 dateTime.setAnyDate();
164             } else if (!isTimeSet(correct)) {
165                 dateTime.setAnyTime();
166             }
167             return dateTime;
168         } catch (NotesException e) {
169             error("Cannot convert calendar to DateTime", e);
170         }
171         return null;
172     }
173 
174     /***
175      * Converts a Notes DateTime object into a Calendar. <p>Milli seconds are
176      * cleared in all cases.</p>
177      *
178      * @param dateTime a Notes DateTime object
179      * @return a Calendar
180      */
181     protected Calendar createCalendar(final DateTime dateTime) {
182         final Date date = createDate(dateTime);
183         if (date == null) {
184             return null;
185         }
186         TimeZone zone = getTimeZone(dateTime);
187         Calendar calendar = Calendar.getInstance(zone);
188         calendar.setTime(date);
189         if (!isTimeSet(dateTime)) {
190             calendar.add(Calendar.MILLISECOND, zone.getOffset(calendar.getTimeInMillis()));
191             return new GregorianDate(calendar.getTime());
192         } else if (!isDateSet(dateTime)) {
193             calendar.add(Calendar.MILLISECOND, zone.getOffset(calendar.getTimeInMillis()));
194             return new GregorianTime(calendar);
195         } else {
196             calendar.add(Calendar.MILLISECOND, -getOffset(dateTime));
197             calendar.add(Calendar.MILLISECOND, -zone.getOffset(calendar.getTimeInMillis()));
198             return new GregorianDateTime(calendar);
199         }
200     }
201 
202     /***
203      * Returns the Java time zone as expected when converting a given Notes
204      * date/time instance to a Java date..
205      *
206      * <p>In case of local calls, the converted Java date is in the local default time zone.
207      * In case of remote calls (DIIOP), the converted Java date is always in GMT.
208      *
209      * @param dateTime a Notes date/tim einstance
210      * @return The time zone of the date/time instance when converted to Java
211      */
212     private TimeZone getTimeZone(final DateTime dateTime) {
213         TimeZone zone = TimeZone.getDefault();
214         if (dateTime.getClass().getName().equals("lotus.domino.cso.DateTime")) {
215             zone = TimeZone.getTimeZone("GMT");
216         }
217         return zone;
218     }
219 
220     /***
221      * Creates a Notes <code>DateRange</code> instance from two
222      * <code>java.util.Calendar</code>s.
223      *
224      * @param calendar1 the start calendar to convert
225      * @param calendar2 the end calendar to convert
226      * @return DateRange a Notes DateTime object
227      */
228     protected DateRange createDateRange(final Calendar calendar1, final Calendar calendar2) {
229         final DateTime time1 = createDateTime(calendar1);
230         final DateTime time2 = createDateTime(calendar2);
231         try {
232             return session.createDateRange(time1, time2);
233         } catch (NotesException e) {
234             error("Cannot convert DDateRange to DateRange", e);
235         }
236         return null;
237     }
238 
239     /***
240      * Computes the Notes time zone value for a given time zone.
241      *
242      * <p>For time zones that are not a full hour increment from GMT, the
243      * return value is an integer in the format <code>mmhh</code> where
244      * <code>mm</code> is the minutes component of the time relative to GMT and
245      * <code>hh</code> is the hours component of the time relative to GMT.
246      *
247      * <p>The offset might have changed in history. For this case, a reference
248      * date is given to find the correct offset at the given date.</p>
249      *
250      * @param zone a Java time zone
251      * @param date a reference date for computing the zone
252      * @return the Notes time zone value
253      * @see #getOffset(DateTime)
254      */
255     static int getNotesTimeZoneValue(final TimeZone zone, final long date) {
256         int offsetMinutes = zone.getRawOffset() / MILLIS_PER_MINUTE;
257         int offsetHours = offsetMinutes / MINUTES_PER_HOUR;
258         if (MINUTES_PER_HOUR * offsetHours == offsetMinutes) {
259             return -offsetHours;
260         } else {
261             return -(offsetMinutes % MINUTES_PER_HOUR) * TZ_MINUTES_OFFSET - offsetHours;
262         }
263     }
264 
265     /***
266      * Returns the raw offset of the time zone in milli seconds for a given Notes time zone value.
267      *
268      * @param dateTime a Notes date/time value
269      * @return war offset in milli seconds as used in Java time zones
270      * @see #getNotesTimeZoneValue(TimeZone, long)
271      */
272     int getOffset(final DateTime dateTime) {
273         int zoneValue;
274         try {
275             zoneValue = dateTime.getTimeZone();
276             int offset = getRawOffset(zoneValue);
277             if (dateTime.isDST()) {
278                 offset -= MILLIS_PER_HOUR;
279             }
280             return offset;
281         } catch (NotesException e) {
282             getMonitor().warn("Cannot get time zone from Notes date/time", e);
283             return 0;
284         }
285     }
286 
287     /***
288      * Returns the offset in milli seconds for a given Notes time zone value.
289      *
290      * @param zoneValue Notes time zone value
291      * @return offset in milli seconds as used in Java time zones
292      * @see #getOffset(DateTime)
293      */
294     protected static int getRawOffset(final int zoneValue) {
295         if (zoneValue < TZ_MINUTES_OFFSET && zoneValue > -TZ_MINUTES_OFFSET) {
296             return zoneValue * MILLIS_PER_HOUR;
297         } else {
298             return (zoneValue % TZ_MINUTES_OFFSET) * MILLIS_PER_HOUR + (zoneValue / TZ_MINUTES_OFFSET) * MILLIS_PER_MINUTE;
299         }
300     }
301 
302     /***
303      * Checks if a DateTime object defines a date.
304      *
305      * @param dateTime a Notes DateTime object
306      * @return <code>true</code> if a date is defined, else <code>false</code>
307      */
308     protected boolean isDateSet(final DateTime dateTime) {
309         try {
310             return !"".equals(dateTime.getDateOnly());
311         } catch (NotesException e) {
312             error("Cannot check if date is set.", e);
313             return false;
314         }
315     }
316 
317     /***
318      * Checks if a DateTime object defines a time.
319      *
320      * @param dateTime a Notes DateTime object
321      * @return <code>true</code> if a time is defined, else <code>false</code>
322      */
323     protected boolean isTimeSet(final DateTime dateTime) {
324         try {
325             return !"".equals(dateTime.getTimeOnly());
326         } catch (NotesException e) {
327             error("Cannot check if time is set.", e);
328             return false;
329         }
330     }
331 
332     /***
333      * Checks if a Calendar object defines a time.
334      *
335      * @param calendar a Calendar object
336      * @return <code>true</code> if a time is defined, else <code>false</code>
337      */
338     protected boolean isTimeSet(final Calendar calendar) {
339         return calendar.isSet(Calendar.HOUR_OF_DAY) || calendar.isSet(Calendar.MINUTE) || calendar.isSet(Calendar.SECOND)
340                 || calendar.isSet(Calendar.MILLISECOND);
341     }
342 
343     /***
344      * Checks if a Calendar object defines a date.
345      *
346      * @param calendar a Calendar object
347      * @return <code>true</code> if a date is defined, else <code>false</code>
348      */
349     protected boolean isDateSet(final Calendar calendar) {
350         return calendar.isSet(Calendar.YEAR) || calendar.isSet(Calendar.MONTH) || calendar.isSet(Calendar.DATE);
351     }
352 
353     /***
354      * Converts a Notes DateTime into a <code>java.util.Calendar</code>. For
355      * invalid dates (e.g. no date- and no time-component available) a null
356      * value is returned.
357      *
358      * @param dateTime a Notes DateTime object
359      * @return a <code>java.util.Calendar</code> object or <code>null</code>
360      *         if dateTime is invalid
361      */
362     private Date createDate(final DateTime dateTime) {
363         if (dateTime == null) {
364             return null;
365         }
366         try {
367             return dateTime.toJavaDate();
368         } catch (NotesException e) {
369             return null;
370         }
371     }
372 
373     /***
374      * Sends an error to the current monitor.
375      *
376      * @param message the message
377      * @param e the NotesException, can be null
378      */
379     protected void error(final String message, final NotesException e) {
380         getMonitor().error(this.getClass().getName() + ":" + message, new DominoException(e));
381     }
382 
383     /***
384      * Sends a warning to the current monitor.
385      *
386      * @param message the message
387      * @param e the NotesException, can be null
388      */
389     protected void info(final String message, final NotesException e) {
390         getMonitor().info(this.getClass().getName() + ":" + message, new DominoException(e));
391     }
392 
393     /***
394      * Sends an info to the current monitor.
395      *
396      * @param message the message
397      * @param e the NotesException, can be null
398      */
399     protected void warn(final String message, final NotesException e) {
400         getMonitor().warn(this.getClass().getName() + ":" + message, new DominoException(e));
401     }
402 }