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.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
58
59
60 /*** Default file name to log java calls. */
61 private static final String DEFAULT_JAVA_LOGFILE = "";
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
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 }