KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > util > log > TreeItemLogger


1 /*
2  * Copyright 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.dev.util.log;
17
18 import com.google.gwt.core.ext.TreeLogger;
19
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.graphics.Color;
22 import org.eclipse.swt.graphics.Image;
23 import org.eclipse.swt.widgets.Display;
24 import org.eclipse.swt.widgets.Tree;
25 import org.eclipse.swt.widgets.TreeItem;
26
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.LinkedList JavaDoc;
31 import java.util.List JavaDoc;
32
33 /**
34  * Tree logger built on an SWT tree item.
35  */

36 public final class TreeItemLogger extends AbstractTreeLogger {
37
38   /**
39    * Represents an individual log event.
40    */

41   public static class LogEvent {
42     public final Throwable JavaDoc caught;
43
44     public final int index;
45
46     public final boolean isBranchCommit;
47
48     public final TreeItemLogger logger;
49
50     public final String JavaDoc message;
51
52     public final TreeLogger.Type type;
53
54     public LogEvent(TreeItemLogger logger, boolean isBranchCommit, int index,
55         Type type, String JavaDoc message, Throwable JavaDoc caught) {
56       this.logger = logger;
57       this.isBranchCommit = isBranchCommit;
58       this.index = index;
59       this.type = type;
60       this.message = message;
61       this.caught = caught;
62     }
63
64     public String JavaDoc toString() {
65       String JavaDoc s = "";
66       s += "[logger " + logger.toString();
67       s += ", " + (isBranchCommit ? "BRANCH" : "LOG");
68       s += ", index " + index;
69       s += ", type " + type.toString();
70       s += ", msg '" + message + "'";
71       s += "]";
72       return s;
73     }
74
75     /**
76      * Can only be called by the UI thread.
77      */

78     public void uiFlush(Tree tree) {
79       // Get or create the tree item associated with this logger.
80
//
81
TreeItem treeItem = createTreeItem(tree);
82
83       if (treeItem == null) {
84         // The logger associated with this log event is dead, so it should
85
// show no ui at all.
86
//
87
return;
88       }
89
90       // Style all ancestors.
91
//
92
uiStyleChildAndAncestors(type, treeItem);
93     }
94
95     /**
96      * Creates a tree item in a way that is sensitive to the log event and its
97      * position in the tree.
98      */

99     private TreeItem createTreeItem(Tree tree) {
100       TreeItem treeItem = null;
101
102       if (isBranchCommit) {
103         // A child logger is committing.
104
// 'logger' is the logger that needs a tree item.
105
// We can be sure that the child logger's parent is non-null
106
// and that either (1) it is has a TreeItem meaning it is not a
107
// top-level entry or (2) it is a top-level entry and gets attached to
108
// the Tree.
109
//
110
TreeItemLogger parentLogger = (TreeItemLogger) logger.getParentLogger();
111         if (parentLogger.lazyTreeItem == null) {
112           // Is top level.
113
//
114
treeItem = new TreeItem(tree, SWT.NONE);
115           logger.lazyTreeItem = treeItem;
116         } else if (!parentLogger.lazyTreeItem.isDisposed()) {
117           // Is not top level, but still valid to write to.
118
//
119
treeItem = new TreeItem(parentLogger.lazyTreeItem, SWT.NONE);
120           logger.lazyTreeItem = treeItem;
121         } else {
122           // The tree item associated with this logger's parent has been
123
// disposed, so we simply ignore all pending updates related to it.
124
// We also mark that logger dead to avoid adding log events for it.
125
//
126
parentLogger.markLoggerDead();
127           return null;
128         }
129       } else {
130         // Create a regular log item on 'logger'.
131
// The logger may be the root logger, in which case we create TreeItems
132
// directly underneath Tree, or it may be a branched logger, in which
133
// case we create TreeItems underneath the branched logger's TreeItem
134
// (which cannot be null because of the careful ordering of log events).
135
//
136
if (logger.lazyTreeItem == null) {
137           // Is top level.
138
//
139
treeItem = new TreeItem(tree, SWT.NONE);
140         } else if (!logger.lazyTreeItem.isDisposed()) {
141           // Is not top level, but still valid to write to.
142
//
143
treeItem = new TreeItem(logger.lazyTreeItem, SWT.NONE);
144         } else {
145           // The tree item associated with this logger's parent has been
146
// disposed, so we simply ignore all pending updates related to it.
147
// We also mark that logger dead to avoid adding log events for it.
148
//
149
logger.markLoggerDead();
150           return null;
151         }
152       }
153
154       // Set the text of the new tree item.
155
//
156
String JavaDoc label = message;
157       if (label == null) {
158         if (caught != null) {
159           label = caught.getMessage();
160
161           if (label == null || label.trim().length() == 0) {
162             label = caught.toString();
163           }
164         }
165       }
166       treeItem.setText(label);
167
168       // This LogEvent object becomes the tree item's custom data.
169
//
170
treeItem.setData(this);
171
172       return treeItem;
173     }
174
175     /**
176      * Can only be called by the UI thread.
177      */

178     private void uiStyleChildAndAncestors(TreeLogger.Type type, TreeItem child) {
179       Display display = child.getDisplay();
180       Color color;
181
182       Image image = null;
183       if (type == TreeLogger.ERROR) {
184         color = display.getSystemColor(SWT.COLOR_RED);
185         image = imageError;
186       } else if (type == TreeLogger.WARN) {
187         color = display.getSystemColor(SWT.COLOR_DARK_YELLOW);
188         image = imageWarning;
189       } else if (type == TreeLogger.INFO) {
190         color = display.getSystemColor(SWT.COLOR_BLACK);
191         image = imageInfo;
192       } else if (type == TreeLogger.TRACE) {
193         color = display.getSystemColor(SWT.COLOR_DARK_GRAY);
194         image = imageTrace;
195       } else if (type == TreeLogger.DEBUG) {
196         color = display.getSystemColor(SWT.COLOR_DARK_CYAN);
197         image = imageDebug;
198       } else {
199         // if (type == TreeLogger.SPAM)
200
color = display.getSystemColor(SWT.COLOR_DARK_GREEN);
201         image = imageSpam;
202       }
203
204       if (image != null) {
205         child.setImage(image);
206       }
207
208       // Set this item's color.
209
//
210
child.setForeground(color);
211
212       // For types needing attention, set all parents to the warning color.
213
//
214
if (type.needsAttention()) {
215         boolean propagateColor = true;
216         TreeItem parent = child.getParentItem();
217         while (parent != null) {
218           LogEvent parentEvent = (LogEvent) parent.getData();
219           if (propagateColor) {
220             if (parentEvent.type.isLowerPriorityThan(type)) {
221               parent.setForeground(color);
222             } else {
223               propagateColor = false;
224             }
225           }
226           parent.setExpanded(true);
227           parent = parent.getParentItem();
228         }
229       }
230     }
231   }
232   /**
233    * One object that is shared across all logger instances in the same tree.
234    * This class is the synchronization choke point that prevents the ui thread
235    * from flushing events while other threads are adding them, and it also
236    * provides tree-wide shared objects such as log item images.
237    */

238   private static class PendingUpdates {
239     private List JavaDoc updates = new LinkedList JavaDoc();
240
241     private final Object JavaDoc updatesLock = new Object JavaDoc();
242
243     public void add(LogEvent update) {
244       synchronized (updatesLock) {
245         updates.add(update);
246       }
247     }
248
249     /**
250      * Flushes any pending log entries.
251      *
252      * @return <code>true</code> if any new entries were written
253      */

254     public synchronized boolean uiFlush(Tree tree) {
255       // Move the list to flush into a local copy then release the udpate
256
// lock so log events can keep coming in while we flush.
257
//
258
List JavaDoc toFlush = null;
259       synchronized (updatesLock) {
260         if (updates.isEmpty()) {
261           // Nothing to do.
262
//
263
return false;
264         }
265         toFlush = updates;
266         updates = new LinkedList JavaDoc();
267       }
268
269       for (Iterator JavaDoc iter = toFlush.iterator(); iter.hasNext();) {
270         LogEvent update = (LogEvent) iter.next();
271         // Loggers can be die while flushing, so we have to be sure never
272
// to try to flush an entry to a dead logger.
273
//
274
if (!update.logger.isLoggerDead()) {
275           update.uiFlush(tree);
276         }
277       }
278
279       return true;
280     }
281   }
282
283   // These don't get disposed, but they do last for the entire process, so
284
// not a big deal.
285
//
286
private static final Image imageDebug = tryLoadImage("log-item-debug.gif");
287   private static final Image imageError = tryLoadImage("log-item-error.gif");
288   private static final Image imageInfo = tryLoadImage("log-item-info.gif");
289   private static final Image imageSpam = tryLoadImage("log-item-spam.gif");
290   private static final Image imageTrace = tryLoadImage("log-item-trace.gif");
291   private static final Image imageWarning = tryLoadImage("log-item-warning.gif");
292
293   private static Image tryLoadImage(String JavaDoc simpleName) {
294     InputStream JavaDoc is = TreeItemLogger.class.getResourceAsStream(simpleName);
295     if (is != null) {
296       try {
297         Image image = new Image(null, is);
298         return image;
299       } finally {
300         try {
301           is.close();
302         } catch (IOException JavaDoc e) {
303         }
304       }
305     } else {
306       // Bad image.
307
//
308
return null;
309     }
310   }
311
312   private boolean dead;
313
314   private TreeItem lazyTreeItem;
315
316   private final PendingUpdates sharedPendingUpdates;
317
318   /**
319    * Constructs the top-level TreeItemLogger.
320    */

321   public TreeItemLogger() {
322     sharedPendingUpdates = new PendingUpdates();
323   }
324
325   /**
326    * Constructs an internal logger.
327    */

328   private TreeItemLogger(PendingUpdates sharedPendingUpdates) {
329     // Inherit the one and only update list from my parent.
330
this.sharedPendingUpdates = sharedPendingUpdates;
331   }
332
333   public void markLoggerDead() {
334     // Cannot kill the root logger, even if attempted.
335
//
336
if (getParentLogger() != null) {
337       dead = true;
338     }
339   }
340
341   /**
342    * Flushes log records to the UI; must only be called by the UI thread.
343    *
344    * @return <code>true</code> if any new entries were written
345    */

346   public boolean uiFlush(Tree tree) {
347     return sharedPendingUpdates.uiFlush(tree);
348   }
349
350   protected AbstractTreeLogger doBranch() {
351     return new TreeItemLogger(sharedPendingUpdates);
352   }
353
354   protected void doCommitBranch(AbstractTreeLogger childBeingCommitted,
355       Type type, String JavaDoc msg, Throwable JavaDoc caught) {
356     if (isLoggerDead()) {
357       return;
358     }
359
360     TreeItemLogger commitChild = (TreeItemLogger) childBeingCommitted;
361     sharedPendingUpdates.add(new LogEvent(commitChild, true,
362         commitChild.getBranchedIndex(), type, msg, caught));
363   }
364
365   protected void doLog(int index, TreeLogger.Type type, String JavaDoc msg,
366       Throwable JavaDoc caught) {
367     if (isLoggerDead()) {
368       return;
369     }
370
371     sharedPendingUpdates.add(new LogEvent(this, false, index, type, msg, caught));
372   }
373
374   /**
375    * Used for an extra check to avoid creating log events for dead loggers. A
376    * dead logger is one that can no longer interact with the UI.
377    */

378   private boolean isLoggerDead() {
379     // Deadness was cached.
380
//
381
if (dead) {
382       return true;
383     }
384
385     // Check upward in the parent chain for any dead parent.
386
//
387
TreeItemLogger parentLogger = (TreeItemLogger) getParentLogger();
388     if (parentLogger == null) {
389       // This is the root logger, which cannot die.
390
//
391
return false;
392     }
393
394     // Otherwise, this logger is dead if its parent is dead (recursively).
395
//
396
if (parentLogger.isLoggerDead()) {
397       // This logger is dead because my parent is dead.
398
//
399
markLoggerDead();
400       return true;
401     }
402
403     // I'm not quite dead yet.
404
//
405
return false;
406   }
407 }
408
Popular Tags