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.service;
24  
25  import java.io.File;
26  import java.io.FileNotFoundException;
27  import java.io.FileOutputStream;
28  import java.io.PrintStream;
29  import java.lang.reflect.Method;
30  import java.lang.reflect.Proxy;
31  import java.util.Calendar;
32  import java.util.HashMap;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.Map;
36  
37  import lotus.domino.Session;
38  import de.bea.domingo.DDatabase;
39  import de.bea.domingo.DDocument;
40  import de.bea.domingo.DItem;
41  import de.bea.domingo.DNotesFactory;
42  import de.bea.domingo.DSession;
43  import de.bea.domingo.DView;
44  import de.bea.domingo.DViewEntry;
45  import de.bea.domingo.exception.ExceptionUtil;
46  import de.bea.domingo.proxy.BaseProxy;
47  import de.bea.domingo.proxy.ViewProxy.ViewEntriesIterator;
48  
49  
50  /***
51   * Creates Java source code of all Notes Java-API calls.
52   *
53   * @author <a href="mailto:kriede@users.sourceforge.net">Kurt Riede</a>
54   */
55  public final class NotesJavaWriter {
56  
57      // todo getFirstEntry() is not called after creating an Iterator of a ViewEntryCollection
58      // todo code for list arguments is not correct Java code
59  
60      /*** Default file name to log java calls. */
61      private static final String DEFAULT_JAVA_LOGFILE = ""; // "notes-java-calls.log";
62  
63      /*** Name of property of java log file. */
64      public static final String JAVA_LOGFILE = "de.bea.domingo.java.logfile";
65  
66      /*** Map from domingo types to corresponding Lotus types. */
67      private static final Map TYPES = new HashMap();
68  
69      /*** Singleton instance. */
70      private static NotesJavaWriter instance = null;
71  
72      /*** Reference to the output stream. */
73      private PrintStream stream = null;
74  
75      static {
76          // Initializing the TYPES map
77          TYPES.put(DDatabase.class, "Database");
78          TYPES.put(DView.class, "View");
79          TYPES.put(DViewEntry.class, "ViewEntry");
80          TYPES.put(DDocument.class, "Document");
81          TYPES.put(DItem.class, "Item");
82          TYPES.put(ViewEntriesIterator.class, "ViewEntriesIterator");
83          TYPES.put(Iterator.class, "Iterator");
84          TYPES.put(String.class, "String");
85          TYPES.put(Integer.class, "Integer");
86          TYPES.put(Double.class, "Double");
87          TYPES.put(List.class, "List");
88      }
89  
90      /***
91       * Constructor.
92       *
93       * <p>The <code>de.bea.domingo.java.logfile</code> property from the
94       * resource file <code>de/bea/domingo/domingo.properties</code>
95       * is used to determine the output file for java logging. If this
96       * property is empty or not set, no output is generated.</p>
97       */
98      private NotesJavaWriter() {
99          final String javaLogfile = DNotesFactory.getProperty(JAVA_LOGFILE, DEFAULT_JAVA_LOGFILE);
100         if (javaLogfile != null && !"".equals(javaLogfile)) {
101             final File file = new File(javaLogfile);
102             try {
103                 stream = new PrintStream(new FileOutputStream(file));
104                 stream.println("NotesThread.sinitThread();");
105                 stream.println("Session session = NotesFactory.createSession();");
106                 stream.println("Map map = new HashMap();");
107             } catch (FileNotFoundException e) {
108                 System.out.println("Cannot open java-logfile: " + javaLogfile);
109             }
110         }
111     }
112 
113     /***
114      * Returns the singleton instance of this class.
115      * @return singleton instance of this class
116      */
117     public static NotesJavaWriter getInstance() {
118         if (instance == null) {
119             instance = new NotesJavaWriter();
120         }
121         return instance;
122     }
123 
124     /***
125      * Logs an invocation to Java-like line.
126      *
127      * @param result the return value of the method call
128      * @param object the proxy instance that the method was invoked on
129      * @param method the <code>Method</code> instance corresponding to
130      * the interface method invoked on the proxy instance.  The declaring
131      * class of the <code>Method</code> object will be the interface that
132      * the method was declared in, which may be a super-interface of the
133      * proxy interface that the proxy class inherits the method through.
134      *
135      * @param args an array of objects containing the values of the
136      * @param throwable an optional throwable that occurred during method call
137      * <p>arguments passed in the method invocation on the proxy instance,
138      * or <code>null</code> if interface method takes no arguments.
139      * Arguments of primitive types are wrapped in instances of the
140      * appropriate primitive wrapper class, such as
141      * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.</p>
142      */
143     public void logInvocation(final Object result, final Object object, final Method method, final Object[] args,
144             final Throwable throwable) {
145         logInvocation(result, object, method.getName(), args, throwable);
146     }
147 
148     /***
149      * Logs a method invocation as Java code.
150      *
151      * @param result the return value of the method call
152      * @param object the proxy instance that the method was invoked on
153      * @param method the name of the method.
154      * @param args an array of objects containing the values of the
155      * arguments passed in the method invocation on the proxy instance,
156      * or <code>null</code>
157      * @param throwable an optional throwable that occurred during method call
158      * <p>if interface method takes no arguments.
159      * Arguments of primitive types are wrapped in instances of the
160      * appropriate primitive wrapper class, such as
161      * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.</p>
162      */
163     public void logInvocation(final Object result, final Object object, final String method, final Object[] args,
164             final Throwable throwable) {
165         if (stream ==  null) {
166             return;
167         }
168         final StringBuffer arguments = new StringBuffer();
169         if (args != null) {
170             for (int i = 0; i < args.length; i++) {
171                 final Object arg = args[i];
172                 if (i > 0) {
173                     arguments.append(", ");
174                 }
175                 arguments.append(objectToString(arg));
176             }
177         }
178         final String methodName;
179         if (method.equals("hasNext")) {
180             methodName = null;
181         } else if (method.equals("next")) {
182             methodName = "getNextEntry";
183         } else {
184             methodName = method;
185         }
186         if (methodName != null) {
187             stream.println(setObjectString(result, objectToString(object) + "." + methodName + "("
188                     + arguments.toString() + ")") + ";" + commentObject(result, throwable));
189         }
190         if (object instanceof Session && method.equals("recycle")) {
191             stream.println("NotesThread.stermThread();");
192         }
193     }
194 
195     /***
196      * Creates a Java comment about an object.
197      * @param obj the object
198      * @param throwable an optional throwable that occurred during method call
199      * @return Java comment for the object
200      */
201     private String commentObject(final Object obj, final Throwable throwable) {
202         if (throwable != null) {
203             return " //* throws " + throwable.getMessage() + "\n" + ExceptionUtil.getStackTrace(throwable) + "\n */";
204         } else if (obj == null) {
205             return " // returns null";
206         } else if (obj instanceof String && "".equals(obj)) {
207             return " // returns empty string";
208         } else if (obj instanceof String) {
209             return " // returns \"" + obj + "\"";
210         } else if (obj instanceof Number) {
211             return " // returns " + obj;
212         } else if (obj instanceof Calendar) {
213             return " // returns " + obj;
214         } else if (obj instanceof BaseProxy) {
215             return " // return " + ((BaseProxy) obj).refereceHashCode();
216         } else {
217             return " // returns " + obj.hashCode();
218         }
219     }
220 
221     /***
222      * Returns a Java statement that sets the value of an expression
223      * into a map where the key is the hashcode of the object.
224      *
225      * @param obj the object
226      * @param expression the expression
227      * @return statement to set expression in a map
228      */
229     private String setObjectString(final Object obj, final String expression) {
230         if (obj == null) {
231             return expression;
232         } else if (obj instanceof String) {
233             return expression;
234         } else if (obj instanceof Double) {
235             return expression;
236         } else if (obj instanceof Long) {
237             return expression;
238         } else if (obj instanceof Integer) {
239             return expression;
240         } else if (obj instanceof Calendar) {
241             return expression;
242         } else if (obj instanceof DSession) {
243             return "session";
244         } else if (obj instanceof BaseProxy) {
245             return "map.put(\"" + ((BaseProxy) obj).refereceHashCode() + "\", " + expression + ")";
246         } else {
247             return "map.put(\"" + obj.hashCode() + "\", " + expression + ")";
248         }
249     }
250 
251     /***
252      * Converts an object to a string that can be used in Java code
253      * as an expression for that object.
254      * @param obj the object
255      * @return a Java expression for that object
256      */
257     private String objectToString(final Object obj) {
258         if (obj == null) {
259             return "null";
260         }
261         if (obj instanceof String) {
262             return "\"" + obj.toString().replace('//', '/') + "\"";
263         } else if (obj instanceof Number) {
264             return obj.toString();
265         } else if (obj instanceof Boolean) {
266             return obj.toString();
267         } else if (obj instanceof Session) {
268             return "session";
269         } else if (Proxy.isProxyClass(obj.getClass())) {
270             return "error(" + obj.getClass().getName() + ")";
271         } else if (obj instanceof List) {
272             return obj.toString();
273         } else if (obj instanceof BaseProxy) {
274             final BaseProxy proxy = (BaseProxy) obj;
275             return "((" + getType(obj) + ") map.get(\"" + proxy.refereceHashCode() + "\"))";
276         } else {
277             return "((" + getType(obj) + ") map.get(\"" + obj.hashCode() + "\"))";
278         }
279     }
280 
281     /***
282      * Returns the type of an object as to be used e.g. in a cast operator.
283      * @param obj the object
284      * @return type of the object
285      */
286     static String getType(final Object obj) {
287         Iterator iterator = TYPES.keySet().iterator();
288         while (iterator.hasNext()) {
289             Class clazz = (Class) iterator.next();
290             if (clazz.isAssignableFrom(obj.getClass())) {
291                 return (String) TYPES.get(clazz);
292             }
293         }
294         if (obj.getClass().getName().startsWith("lotus.")) {
295             final String className = obj.getClass().getName();
296             return className.substring(className.lastIndexOf('.') + 1);
297         } else {
298             return "unknown." + obj.getClass().getName();
299         }
300     }
301 }