1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package de.bea.domingo.proxy;
24
25 import java.lang.ref.WeakReference;
26 import java.util.ArrayList;
27 import java.util.Calendar;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Vector;
31
32 import lotus.domino.Base;
33 import lotus.domino.DateRange;
34 import lotus.domino.DateTime;
35 import lotus.domino.Document;
36 import lotus.domino.DocumentCollection;
37 import lotus.domino.NotesException;
38 import de.bea.domingo.DBase;
39 import de.bea.domingo.DNotesException;
40 import de.bea.domingo.DNotesIterator;
41 import de.bea.domingo.DNotesMonitor;
42 import de.bea.domingo.DNotesRuntimeException;
43 import de.bea.domingo.exception.DominoException;
44 import de.bea.domingo.i18n.ResourceManager;
45 import de.bea.domingo.i18n.Resources;
46 import de.bea.domingo.monitor.AbstractMonitorEnabled;
47
48 /***
49 * Abstract base class for all implementations of interfaces derived from
50 * <code>DBase</code>.
51 *
52 * @author <a href=mailto:kriede@users.sourceforge.net>Kurt Riede</a>
53 */
54 public abstract class BaseProxy extends AbstractMonitorEnabled implements DBase {
55
56 /*** Internationalized resources. */
57 protected static final Resources RESOURCES = ResourceManager.getPackageResources(NotesProxyFactory.class);
58
59
60
61
62
63 /*** Number of characters needed to represent a date/time value. */
64 protected static final int DATETIME_STRING_LENGTH = 20;
65
66 /*** Maximum number of items supported in method getItemValues(). */
67 protected static final int NUM_DATETIME_VALUES = 1000;
68
69 /*** maximum number of characters to parse in method getItemValues(). */
70 protected static final int MAX_DATETIME_LENGTH = NUM_DATETIME_VALUES * DATETIME_STRING_LENGTH;
71
72 /*** The empty string. */
73 protected static final String EMPTY_STRING = "";
74
75
76
77
78
79 /*** Reference to the parent object. */
80 private final DBase parent;
81
82 /***
83 * Reference to the notes object.
84 *
85 * <p>This reference can be either a notes object directly or a
86 * <code>WeakReference</code> to a notes object, if the notes object
87 * can be recycled.
88 *
89 * @see de.bea.domingo.proxy.BaseProxy#getNotesObject()
90 */
91 private Object ref;
92
93 /*** Reference to the factory which controls this instance. */
94 private final NotesProxyFactory factory;
95
96
97
98
99
100 /***
101 * Creates a new DBaseImpl object.
102 *
103 * @param theFactory the controlling factory
104 * @param theParent the parent object
105 * @param object the notes object
106 * @param monitor the monitor
107 */
108 protected BaseProxy(final NotesProxyFactory theFactory, final DBase theParent,
109 final Base object, final DNotesMonitor monitor) {
110 super(monitor);
111 this.factory = theFactory;
112 this.parent = theParent;
113 this.ref = object;
114
115 }
116
117
118
119
120
121 /***
122 * Returns the Notes object associated with an instance.
123 *
124 * @return associated Notes object
125 */
126 protected final Base getNotesObject() {
127 if (ref instanceof WeakReference) {
128 return (Base) ((WeakReference) ref).get();
129 }
130 return (Base) ref;
131 }
132
133 /***
134 * Clears the associated notes object, used during dispose/recycle operations.
135 */
136 protected final void clearNotesObject() {
137 ref = null;
138 }
139
140 /***
141 * Returns the parent object.
142 *
143 * @return the parent object or <code>null</code> if no parent available
144 */
145 protected final DBase getParent() {
146 return parent;
147 }
148
149 /***
150 * Returns the factory corresponding to this instance.
151 *
152 * @return the corresponding factory
153 */
154 protected final NotesProxyFactory getFactory() {
155 return factory;
156 }
157
158 /***
159 * Utility method for the toString method of derived classes.
160 * Returns a string representation of the object.
161 *
162 * @return string representation of the object
163 *
164 * @see java.lang.Object#toString()
165 */
166 public final String toStringGeneric() {
167 final Base base = getNotesObject();
168 if (base != null) {
169 return base.toString();
170 }
171 return super.toString();
172 }
173
174 /***
175 * Convert List to Vector.
176 *
177 * <p>If the implementation of the <code>List</code> interface is a Vector,
178 * this vector is used unchanged, else the references of elements of the
179 * <code>List</code> are copied to a new result <code>Vector</code>.
180 *
181 * @param list a list
182 * @return a Vector containing the elements of the list argument
183 */
184 protected final Vector convertListToVector(final List list) {
185 if (list == null) {
186 return null;
187 }
188 if (list instanceof Vector) {
189 return (Vector) list;
190 }
191 final Vector v = new Vector();
192 for (int i = 0; i < list.size(); i++) {
193 v.addElement(list.get(i));
194 }
195 return v;
196 }
197
198 /***
199 * Convert Vector to List.
200 *
201 * A Vector does also implement the <code>List</code> interface. But it is
202 * synchronized. Therefore this conversion method provides a real List
203 * that is not synchronized.
204 *
205 * @param vector a vector
206 * @return a List
207 * @deprecated the usage of the method convertVectorToList() is not needed and impacts performance
208 */
209 protected final List convertVectorToList(final Vector vector) {
210 if (vector == null) {
211 return null;
212 }
213 final List list = new ArrayList();
214 list.addAll(vector);
215 return Collections.unmodifiableList(list);
216 }
217
218 /***
219 * Converts every occurrence of a <code>DateTime</code> object to a
220 * <code>java.util.Calendar</code> instance.
221 *
222 * <p>The created DateTime objects must get recycled somewhen later, when
223 * they are not needed anymore. To ensure this, the calling method must call
224 * the method recycleDateTimeList() after usage.</p>
225 * <p>Lists are processed recursivly; this is e.g. needed for column values
226 * that might contain value lists in the list of column values.</p>
227 *
228 * @param list the list to convert its content
229 * @return List a new List with the same elements in same order,
230 * but converted dates
231 */
232 protected final List convertNotesDateTimesToCalendar(final List list) {
233 if (list == null) {
234 return null;
235 }
236 final List result = new ArrayList();
237 for (int i = 0; i < list.size(); i++) {
238 final Object object = list.get(i);
239 if (object instanceof DateTime) {
240 final DateTime dateTime = (DateTime) object;
241 checkSession(dateTime);
242 result.add(createCalendar(dateTime));
243 } else if (object instanceof List) {
244 result.add(convertNotesDateTimesToCalendar((List) object));
245 } else {
246 result.add(object);
247 }
248 }
249 return Collections.unmodifiableList(result);
250 }
251
252 /***
253 * Converts every occurrence of a <code>java.util.Calendar</code> or
254 * <code>java.util.Date</code> in a List to a <code>DateTime</code> instance.
255 *
256 * <p><b>ATTENTION:</b><br/>
257 * The created DateTime objects must get recycled somewhen later, when
258 * they are not needed anymore. To ensure this, the calling method must call
259 * the method recycleDateTimeList() after usage.</p>
260 *
261 * <p><b>Example</b><br/>
262 * <pre>
263 * List keys;
264 * // initialize and fill list with Date objects and others
265 * List convertedList = convertDatesToNotesDateTime(keys);
266 * // now the list contains Notes DateTime objects.
267 * // do something with the list
268 * recycleDateTimeList(convertedKeys);
269 * </pre>
270 * </p>
271 *
272 * @param list the list to convert its content
273 * @return List a new List with the same elements in same order,
274 * but converted dates
275 */
276 protected final List convertCalendarsToNotesDateTime(final List list) {
277 if (list == null) {
278 return null;
279 }
280 final List result = new ArrayList();
281 for (int i = 0; i < list.size(); i++) {
282 final Object object = list.get(i);
283 if (object instanceof Calendar) {
284 final DateTime date = createDateTime((Calendar) object);
285 result.add(date);
286 } else {
287 result.add(object);
288 }
289 }
290 return Collections.unmodifiableList(result);
291 }
292
293 /***
294 * Recycles all Notes DateTime objects in a list.
295 *
296 * <p>The list can have mixed content and must not contain only DateTime
297 * objects. But only DateTime objects are recycled.</p>
298 *
299 * @param list any list implementation
300 */
301 protected final void recycleDateTimeList(final List list) {
302 if (list == null) {
303 return;
304 }
305 for (int i = 0; i < list.size(); i++) {
306 final Object object = list.get(i);
307 if (object instanceof DateTime) {
308 getFactory().recycle(object);
309 }
310 }
311 }
312
313 /***
314 * Returns the Domingo session that created the current object.
315 *
316 * @return Domingo session of current object
317 */
318 protected final SessionProxy getDSession() {
319 BaseProxy proxy = this;
320 while (!(proxy instanceof SessionProxy)) {
321 proxy = (BaseProxy) proxy.getParent();
322 }
323 return (SessionProxy) proxy;
324 }
325
326 /***
327 * Creates a Notes <code>DateTime</code> instance from a
328 * <code>java.util.Calendar</code>.
329 *
330 * Adapter to InternationalProxy.
331 * This is a convenience method to allow every object to convert dates.
332 *
333 * @param calendar the calendar to convert
334 * @return DateTime object representing the same point in time
335 */
336 protected final DateTime createDateTime(final Calendar calendar) {
337 return getDSession().getInternational().createDateTime(calendar);
338 }
339
340 /***
341 * Creates a Notes <code>DateRange</code> instance from two
342 * <code>java.util.Calendar</code>s.
343 *
344 * Adapter to InternationalProxy.
345 * This is a convenience method to allow every object to convert dates.
346 *
347 * @param calendar1 the start calendar to convert
348 * @param calendar2 the end calendar to convert
349 * @return DateTime object representing the same point in time
350 * @see de.bea.domingo.proxy.InternationalProxy#createDateRange(Calendar, Calendar)
351 */
352 protected final DateRange createDateRange(final Calendar calendar1, final Calendar calendar2) {
353 return getDSession().getInternational().createDateRange(calendar1, calendar2);
354 }
355
356 /***
357 * Converts a Notes DateTime object into a Calendar.
358 * <p>Milli seconds are cleared in all cases.</p>
359 *
360 * Adapter to InternationalProxy.
361 * This is a convenience method to allow every object to convert dates.
362 *
363 * @param dateTime a Notes DateTime object
364 * @return a Calendar
365 * @see de.bea.domingo.proxy.InternationalProxy#createCalendar(DateTime)
366 */
367 protected final Calendar createCalendar(final DateTime dateTime) {
368 return getDSession().getInternational().createCalendar(dateTime);
369 }
370
371 /***
372 * Checks and monitors an error if a <code>DateTime</code> object doesn't
373 * have a parent session.
374 *
375 * @param dateTime the DateTime object to check
376 */
377 protected final void checkSession(final DateTime dateTime) {
378 try {
379 if (dateTime.getParent() == null) {
380 getMonitor().error("created a DateTime object without parent session");
381 }
382 } catch (NotesException e) {
383 getMonitor().error("created a DateTime object with parent session not available", e);
384 }
385 }
386
387
388
389
390
391
392 /***
393 * @see java.lang.Object#finalize()
394 * @throws Throwable the <code>Exception</code> raised by this method
395 */
396 protected final void finalize() throws Throwable {
397 factory.getBaseCache().remove(getNotesObject());
398 getFactory().recycleLater(this);
399 super.finalize();
400 }
401
402 /***
403 * Returns the hashCode of the referenced Notes object.
404 * @return hashCode of referenced Notes object
405 *
406 * @see java.lang.Object#hashCode()
407 */
408 public final int refereceHashCode() {
409 if (ref != null) {
410 return ref.hashCode();
411 } else {
412 return 0;
413 }
414 }
415
416
417
418
419
420 /***
421 * Returns a string representation of the object. This method
422 * returns a string that "textually represents" this object.
423 * The result is a concise but informative representation that
424 * is easy for a person to read.
425 *
426 * <p>The <code>toStringIntern</code> method
427 * returns a string consisting of the name of the class of which the
428 * object is an instance, the at-sign character `<code>@</code>', and
429 * the unsigned hexadecimal representation of the hash code of the
430 * object. In other words, this method returns a string equal to the
431 * value of:
432 * <blockquote>
433 * <pre>
434 * object.getClass().getName() + '@' + Integer.toHexString(object.hashCode())
435 * </pre></blockquote>
436 *
437 * @param object the reference object to use.
438 * @return a string representation of the object.
439 * @see java.lang.Object#toString()
440 */
441 public static String toStringIntern(final Object object) {
442 return object.getClass().getName() + "@" + Integer.toHexString(object.hashCode());
443 }
444
445 /*** Counter for number of currently existing DateTime objects. */
446 private static int countDateTime = 0;
447
448 /***
449 * Returns the global counter for DateTime objects.
450 *
451 * @return global counter for DateTime objects.
452 */
453 public static int getCountDateTime() {
454 return countDateTime;
455 }
456
457 /***
458 * Increments the global counter for DateTime objects.
459 *
460 * @return new value of the counter after incrementation
461 */
462 public static int incrementDateTimeCounter() {
463 return ++countDateTime;
464 }
465
466 /***
467 * Decrements the global counter for DateTime objects.
468 *
469 * @return new value of the counter after decrementation
470 */
471 public static int decrementDateTimeCounter() {
472 return --countDateTime;
473 }
474
475
476
477
478
479 /***
480 * Iterator for collections of documents.
481 *
482 * <p>A Mapper instance is used to map Notes document into a business
483 * object. If no Mapper is provided, a simple default mapper is used that
484 * maps the Notes document into a simple data structure that implements
485 * the DDocument interface.</p>
486 *
487 * @see de.bea.domingo.DDocument
488 *
489 * @author <a href=mailto:kriede@users.sourceforge.net>Kurt Riede</a>
490 */
491 public static final class DocumentCollectionIterator extends BaseProxy implements DNotesIterator {
492
493 /*** serial version ID for serialization. */
494 private static final long serialVersionUID = 3258412845845524528L;
495
496 /*** Reference to current document within the view. */
497 private BaseDocumentProxy document = null;
498
499 /***
500 * Constructor.
501 *
502 * @param theFactory the controlling factory
503 * @param theParent the parent database
504 * @param monitor the monitor
505 * @param theCollection the Notes document collection
506 */
507 protected DocumentCollectionIterator(final NotesProxyFactory theFactory,
508 final DBase theParent,
509 final DocumentCollection theCollection,
510 final DNotesMonitor monitor) {
511 super(theFactory, theParent, theCollection, monitor);
512 initialize(theFactory);
513 }
514
515 /***
516 * initialization.
517 * @param theFactory the controlling factory
518 */
519 private void initialize(final NotesProxyFactory theFactory) {
520 getFactory().preprocessMethod();
521 try {
522 if (getCollection() != null && getCollection().getCount() > 0) {
523 final Document notesDocument = getCollection().getFirstDocument();
524 document = BaseDocumentProxy.getInstance(theFactory, getParent(), notesDocument, getMonitor());
525 }
526 } catch (NotesException e) {
527 getMonitor().warn("Cannot create Iterator: ", e);
528 return;
529 }
530 }
531
532 /***
533 * Returns the associated document collection.
534 *
535 * @return the associated document collection
536 */
537 private DocumentCollection getCollection() {
538 return (DocumentCollection) getNotesObject();
539 }
540
541 /***
542 * {@inheritDoc}
543 * @see java.util.Iterator#hasNext()
544 */
545 public boolean hasNext() {
546 return document != null;
547 }
548
549 /***
550 * {@inheritDoc}
551 * @see java.util.Iterator#next()
552 */
553 public Object next() {
554 getFactory().preprocessMethod();
555 final Object result = document;
556 try {
557 final Document nextNotesDoc = getCollection().getNextDocument((Document) document.getNotesObject());
558 document = null;
559 document = BaseDocumentProxy.getInstance(getFactory(), getParent(), nextNotesDoc, getMonitor());
560 } catch (NotesException e) {
561 document = null;
562 }
563 return result;
564 }
565
566 /***
567 * The <tt>remove</tt> operation is not supported by this Iterator.
568 * An <code>UnsupportedOperationException</code> is thrown if this
569 * method is called.
570 *
571 * @see java.util.Iterator#remove()
572 */
573 public void remove() {
574 throw new UnsupportedOperationException();
575 }
576
577 /***
578 * {@inheritDoc}
579 * @see de.bea.domingo.proxy.BaseProxy#toString()
580 */
581 public String toString() {
582 return BaseProxy.toStringIntern(this);
583 }
584
585 /***
586 * {@inheritDoc}
587 * @see de.bea.domingo.DNotesIterator#getSize()
588 */
589 public int getSize() {
590 try {
591 return getCollection().getCount();
592 } catch (NotesException e) {
593 throw newRuntimeException("Cannot get size of iterator", e);
594 }
595 }
596 }
597
598 /***
599 * Creates and returns a new exception wrapping a given
600 * NotesException.
601 * @param e the NotesException, can be null
602 * @return new DNotesException
603 */
604 protected final DNotesException newException(final NotesException e) {
605 return newException(null, e);
606 }
607
608 /***
609 * Creates and returns a new exception with the given message.
610 * @param message the message
611 * @return new DNotesException
612 */
613 protected final DNotesException newException(final String message) {
614 return newException(message, null);
615 }
616
617 /***
618 * Creates and returns a new exception wrapping a given NotesException.
619 * @param message the message
620 * @param e the NotesException, can be null
621 * @return new DNotesException
622 */
623 protected final DNotesException newException(final String message, final NotesException e) {
624 final DominoException d = e != null ? new DominoException(e) : null;
625 return new NotesProxyException(getFullMessage(message, d), d);
626 }
627
628 /***
629 * Creates and returns a new runtime exception wrapping a given
630 * NotesException.
631 * @param e the NotesException, can be null
632 * @return new DNotesRuntimeException
633 */
634 protected final DNotesRuntimeException newRuntimeException(final NotesException e) {
635 return newRuntimeException(null, e);
636 }
637
638 /***
639 * Creates and returns a new runtime exception with the given message.
640 * @param message the message
641 * @return new DNotesRuntimeException
642 */
643 protected final DNotesRuntimeException newRuntimeException(final String message) {
644 return newRuntimeException(message, null);
645 }
646
647 /***
648 * Creates and returns a new runtime exception with the given message.
649 * @param message the message
650 * @param e the NotesException, can be null
651 * @return new DNotesRuntimeException
652 */
653 protected final DNotesRuntimeException newRuntimeException(final String message, final Exception e) {
654 final Exception cause = e instanceof NotesException ? new DominoException((NotesException) e) : e;
655 return new NotesProxyRuntimeException(getFullMessage(message, cause), cause);
656 }
657
658 /***
659 * Returns a message for a domino exception.
660 * @param message a message string
661 * @param a @link{de.bea.domingo.proxy.DominoException DominoException}
662 * @return combined full message
663 */
664 private String getFullMessage(final String message, final Exception d) {
665 final String msg = message != null ? message : (d != null ? d.getMessage() : "Unknown problem");
666 final String fullMessage = this.getClass().getName() + ": " + msg;
667 return fullMessage;
668 }
669 }