Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/port80/eclipse/jdt/annotation/AnnotationManager.java


1   package com.port80.eclipse.jdt.annotation;
2   
3   import java.io.File;
4   import java.sql.Timestamp;
5   import java.util.ArrayList;
6   import java.util.HashMap;
7   import java.util.List;
8   import java.util.Map;
9   
10  import org.eclipse.core.resources.ResourcesPlugin;
11  import org.eclipse.core.runtime.IPath;
12  import org.eclipse.jdt.core.IClassFile;
13  import org.eclipse.jdt.core.ICompilationUnit;
14  import org.eclipse.jdt.core.IJavaElement;
15  import org.eclipse.jdt.internal.ui.viewsupport.JavaUILabelProvider;
16  import org.eclipse.jface.preference.IPreferenceStore;
17  import org.eclipse.jface.viewers.IStructuredSelection;
18  import org.eclipse.jface.viewers.ITreeContentProvider;
19  import org.eclipse.jface.viewers.LabelProvider;
20  import org.eclipse.jface.viewers.StructuredSelection;
21  import org.eclipse.jface.viewers.Viewer;
22  import org.eclipse.swt.graphics.Image;
23  import org.eclipse.ui.IMemento;
24  import org.eclipse.ui.IPartListener;
25  import org.eclipse.ui.IWorkbenchPart;
26  import org.eclipse.ui.XMLMemento;
27  
28  import com.port80.eclipse.jdt.IConstants;
29  import com.port80.eclipse.jdt.JdtPlugin;
30  import com.port80.eclipse.jdt.Util;
31  import com.port80.eclipse.jdt.util.JavaUtil;
32  import com.port80.eclipse.jdt.util.PersistentFolder;
33  import com.port80.eclipse.jdt.util.PersistentItem;
34  
35  /**
36   * Data model for AnnotationView.
37   * 
38   * Annotation keep the set of visited IResources together with access information.  
39   * It kept all visited objects automatically upon visiting instead of manually by user.  
40   * It can be used to backtrace visit history.  It can be further bookmark or annotated
41   * by user.  This is an enchancement of the BookmarkView which, unfortunately as of
42   * Eclipse 2.0, cannot bookmark binary classes and it is not easy for annotation.
43   *
44   * AnnotationManager also works without the AnnotationView active and manage
45   * only the visited object list and its related actions.
46   * 
47   * @author chrisl
48   */
49  public class AnnotationManager extends LabelProvider implements ITreeContentProvider {
50  
51    // Static fields ///////////////////////////////////////////////////////
52    //
53    private static final String NAME = "AnnotationManager";
54    private static final String ID = "com.port80.eclipse.jdt.annotation.AnnotationManager";
55    private static final String TAG_ROOT = "Annotation";
56    private static final String TAG_VERSION = "Version";
57    private static final String[] VERSIONS = new String[] { "1.0" };
58  
59    public static final String INBOX = "INBOX";
60    public static final int RET_FAIL = -1;
61    public static final int RET_OK = 0;
62    public static final int RET_CANCEL = 1;
63    public static final int RET_RESET = 2;
64  
65    private static final boolean DEBUG = true;
66    private static final boolean VERBOSE = true;
67  
68    // Instance fields /////////////////////////////////////////////////////
69    //
70    private PersistentFolder fRoot = new PersistentFolder("");
71    private PersistentFolder fInbox = new PersistentFolder(INBOX);
72    private JavaUILabelProvider labelProvider = new JavaUILabelProvider();
73    // A Map is used instead of a Set, so that we can retreive the original item already exists 
74    // and equals to the requested one.
75    private Map fAnnotation = new HashMap();
76    private List fModelListeners = new ArrayList();
77    private Map fFolders = new HashMap();
78  
79    private int fInboxLimit;
80    private IPartListener fPartListener;
81  
82    // Constructors ////////////////////////////////////////////////////////
83    //
84    public AnnotationManager() {
85      fFolders.put(INBOX, fInbox);
86      fRoot.addChild(fInbox);
87      restoreXMLState();
88      initPrefs();
89    }
90  
91    public void initPrefs() {
92      IPreferenceStore prefs = JdtPlugin.getDefault().getPreferenceStore();
93      fInboxLimit = prefs.getInt(IConstants.PREF_ANNOTATIONVIEW_INBOX_LIMIT);
94    }
95  
96    ////////////////////////////////////////////////////////////////////////
97    //
98  
99    public void addModelListener(IAnnotationModelListener l) {
100     fModelListeners.add(l);
101   }
102   public void removeModelListener(IAnnotationModelListener l) {
103     fModelListeners.remove(l);
104   }
105 
106   private void fireModelChange() {
107     for (int i = 0; i < fModelListeners.size(); ++i) {
108       ((IAnnotationModelListener) fModelListeners.get(i)).refresh();
109     }
110   }
111 
112   private void fireModelChange(PersistentItem item) {
113     for (int i = 0; i < fModelListeners.size(); ++i) {
114       ((IAnnotationModelListener) fModelListeners.get(i)).refresh(item);
115     }
116   }
117 
118   private void fireModelChange(IStructuredSelection selection) {
119     for (int i = 0; i < fModelListeners.size(); ++i)
120        ((IAnnotationModelListener) fModelListeners.get(i)).setSelection(selection, true);
121   }
122 
123   ////////////////////////////////////////////////////////////////////////
124   //
125 
126   public Object getRoot() {
127     return fRoot;
128   }
129 
130   public Object findItem(Object a) {
131     return fAnnotation.get(a);
132   }
133 
134   ////////////////////////////////////////////////////////////////////////
135 
136   /**
137    * Find folder with given name, create one if not found.
138    * @return fInbox if name==null, otherwise the folder with the given name.
139    */
140   public PersistentFolder findFolder(String name) {
141     if (name == null || name.length() == 0)
142       return fInbox;
143     PersistentFolder folder = (PersistentFolder) fFolders.get(name);
144     if (folder == null) {
145       folder = new PersistentFolder(name);
146       fFolders.put(name, folder);
147       fRoot.addChild(folder);
148     }
149     return folder;
150   }
151 
152   public void saveState(IMemento memento) {
153     if (DEBUG)
154       System.err.println(NAME + ".saveState(IMemento)");
155     memento.putString(TAG_VERSION, VERSIONS[0]);
156     Object[] items = fAnnotation.values().toArray();
157     IMemento child;
158     for (int i = 0; i < items.length; ++i) {
159       child = memento.createChild(PersistentItem.NAME);
160       PersistentItem item = (PersistentItem) items[i];
161       item.saveState(child);
162     }
163   }
164 
165   public boolean restoreState(IMemento memento) {
166     if (DEBUG)
167       System.err.println(NAME + ".restoreState(IMemento)");
168     if (memento == null)
169       return true;
170     IMemento[] ms = memento.getChildren(PersistentItem.NAME);
171     if (ms == null)
172       return false;
173     fAnnotation.clear();
174     PersistentItem item;
175     if (DEBUG)
176       System.err.println(NAME + ".restoreState(IMemento): " + ms.length + " items.");
177     for (int i = 0; i < ms.length; ++i) {
178       item = PersistentItem.restore(ms[i]);
179       if (item != null) {
180         fAnnotation.put(item, item);
181         PersistentFolder folder = findFolder(item.getParent());
182         folder.addChild(item);
183       }
184     }
185     return true;
186   }
187 
188   public boolean saveXMLState() {
189     if (saveXMLState(IConstants.ANNOTATION_STATE_FILENAME)) {
190       // Remove recover files if save succeeded.
191       IPath path = JdtPlugin.getDefault().getStateLocation();
192       File file =
193         ((IPath) path.clone())
194           .append(IConstants.ANNOTATION_STATE_FILENAME + ".recover")
195           .toFile();
196       if (file.exists())
197         file.delete();
198       file =
199         ((IPath) path.clone())
200           .append(IConstants.ANNOTATION_STATE_FILENAME + ".recover.backup")
201           .toFile();
202       if (file.exists())
203         file.delete();
204       return true;
205     }
206     return false;
207   }
208 
209   /** Save Annotation to state file in metadata area.*/
210   public boolean saveXMLState(String filename) {
211     XMLMemento memento = XMLMemento.createWriteRoot(ID);
212     saveState(memento);
213     return Util.saveXMLState(filename, memento);
214   }
215 
216   /** Restore Annotation from state file in metadata area.*/
217   public boolean restoreXMLState() {
218     IMemento memento = Util.restoreXMLState(IConstants.ANNOTATION_STATE_FILENAME);
219     return restoreState(memento);
220   }
221 
222   ////////////////////////////////////////////////////////////////////////
223   //
224 
225   public boolean contains(PersistentItem a) {
226     return contains(a, a.getParent());
227   }
228 
229   public boolean contains(Object a, String parent) {
230     if (a == null)
231       return false;
232     PersistentItem item;
233     if (a instanceof PersistentItem) {
234       item = (PersistentItem) a;
235     } else {
236       item = PersistentItem.create(a, parent);
237       if (item == null)
238         return false;
239     }
240     return fAnnotation.get(item) != null;
241   }
242 
243   /**
244    * Get item in Annotation that represents the given object.
245    * @return The PersistentItem if it exists, else null.
246    */
247   public PersistentItem get(PersistentItem a) {
248     return get(a, a.getParent());
249   }
250 
251   public PersistentItem get(Object a, String parent) {
252     if (a == null)
253       return null;
254     PersistentItem item;
255     if (a instanceof PersistentItem) {
256       item = (PersistentItem) a;
257     } else {
258       item = PersistentItem.create(a, parent);
259       if (item == null)
260         return null;
261     }
262     item = (PersistentItem) fAnnotation.get(item);
263     return item;
264   }
265 
266   /** 
267    * Added automatically Type or File visited on editor activation.
268    * @return Item added or the original item that is updated.
269    */
270   public PersistentItem addAutomatic(IJavaElement element) {
271     element = findAutomaticElement(element);
272     if (false)
273       System.err.println(JdtPlugin.msg(NAME + ".addAutomaticFromEditor(): suitable", element));
274     if (element == null)
275       return null;
276     PersistentItem item = PersistentItem.create(element);
277     item = add(item, INBOX);
278     fireModelChange(new StructuredSelection(item));
279     return item;
280   }
281 
282   /** 
283    * Add an object to the specified folder in Annotation.  If folder is null, add to INBOX.
284    * If object is a PersistentItem, it's parent is set to the specified folder regardless of its existing value.
285    * @return Item added or the original item that is updated.
286    * @param a The object to add.
287    * @param parent The folder to add to.
288    */
289   public PersistentItem add(PersistentItem item, String parent) {
290     if (item == null) {
291       JdtPlugin.log(NAME + ".add(PresistentItem,String): item should not be null, parent=" + parent);
292       return null;
293     }
294     if (parent == null)
295       parent = INBOX;
296     item.setParent(parent);
297     PersistentItem original = (PersistentItem) fAnnotation.get(item);
298     if (original == null) {
299       long time = System.currentTimeMillis();
300       item.setATime(new Timestamp(time));
301       fAnnotation.put(item, item);
302       PersistentFolder folder = findFolder(parent);
303       PersistentItem child;
304       if (parent == INBOX)
305         for (int i = folder.getChildrenCount(); i >= fInboxLimit; --i) {
306           child=folder.removeOldestChild();
307           fAnnotation.remove(child);
308           child.setParent(null);
309         }
310       folder.addChild(item);
311       saveXMLState(IConstants.ANNOTATION_RECOVER_FILENAME);
312       fireModelChange();
313     } else {
314       item = original;
315       original.setATime(new Timestamp(System.currentTimeMillis()));
316       fireModelChange(original);
317     }
318     return item;
319   }
320 
321   /**
322    * Remove object from Annotation.
323    * @return True if successfully removed, else false (object invalid or not exist in Annotation).
324    */
325   public boolean remove(PersistentItem a) {
326     if (a == null)
327       return false;
328     return remove(a, a.getParent());
329   }
330 
331   public boolean remove(Object a, String parent) {
332     if (a == null)
333       return false;
334     PersistentItem item;
335     if (a instanceof PersistentItem) {
336       item = (PersistentItem) a;
337     } else {
338       item = PersistentItem.create(a, parent);
339       if (item == null)
340         return false;
341     }
342     if (item instanceof PersistentFolder) {
343       PersistentFolder folder = (PersistentFolder) item;
344       removeAll(folder.getChildren());
345       fRoot.removeChild(item);
346       item.setParent(null);
347       return true;
348     }
349     item = (PersistentItem) fAnnotation.remove(item);
350     if (item != null) {
351       PersistentFolder folder = findFolder(parent);
352       folder.removeChild(item);
353       item.setParent(null);
354       saveXMLState(IConstants.ANNOTATION_RECOVER_FILENAME);
355       fireModelChange();
356       return true;
357     }
358     return false;
359   }
360 
361   /** 
362    * Remove an array of items. Ignoring objects that are not an item.
363    * @return true if any item is removed.
364    * @param items An array of AnnotationItem.
365    */
366   public boolean removeAll(Object[] items) {
367     if (items == null || items.length == 0)
368       return false;
369     PersistentItem item;
370     boolean removed = false;
371     for (int i = 0; i < items.length; ++i) {
372       if (items[i] instanceof PersistentFolder) {
373         PersistentFolder folder = (PersistentFolder) items[i];
374         removeAll(folder.getChildren());
375         fRoot.removeChild(folder);
376         folder.setParent(null);
377         removed = true;
378         continue;
379       }
380       if (!(items[i] instanceof PersistentItem))
381         continue;
382       item = (PersistentItem) fAnnotation.remove(items[i]);
383       if (item != null) {
384         PersistentFolder folder = findFolder(item.getParent());
385         folder.removeChild(item);
386         item.setParent(null);
387         removed = true;
388       }
389     }
390     if (removed) {
391       saveXMLState(IConstants.ANNOTATION_RECOVER_FILENAME);
392       fireModelChange();
393     }
394     return removed;
395   }
396 
397   /**
398    * @return A Suitable Java element that can be added automatically to a Annotation (IType) 
399    * that enclose the given Java element.
400    */
401   protected IJavaElement findAutomaticElement(IJavaElement element) {
402     if (element == null)
403       return null;
404     String message = NAME + ".findAutomaticElement()";
405     int type = element.getElementType();
406     if (type < IJavaElement.COMPILATION_UNIT)
407       return null;
408     if (type > IJavaElement.TYPE)
409       return findAutomaticElement(element.getParent());
410     try {
411       switch (type) {
412         case IJavaElement.COMPILATION_UNIT :
413           message = message + ": COMPILATION_UNIT";
414           return ((ICompilationUnit) element).getTypes()[0];
415         case IJavaElement.CLASS_FILE :
416           message = message + ": CLASS_FILE";
417           return ((IClassFile) element).getType();
418         case IJavaElement.TYPE :
419           return element;
420         default :
421           System.err.println(JdtPlugin.msg(message, element));
422       }
423     } catch (Exception e) {
424       JdtPlugin.log(message, element, e);
425     }
426     return null;
427   }
428 
429   /** 
430    * Added currently selected IJavaElement in editor to the Annotation INBOX. 
431    * @return PresistentItem added, null if cannot create item.
432      */
433   public PersistentItem addFromEditor(IWorkbenchPart part) {
434     IJavaElement element = JavaUtil.getSelectedElementFromEditor(part);
435     element = findSuitableElement(element);
436     if (false)
437       System.err.println(JdtPlugin.msg(NAME + ".addFromEditor(): suitable", element));
438     if (element != null) {
439       return add(PersistentItem.create(element), INBOX);
440     }
441     return null;
442   }
443 
444   /**
445    * @return A Suitable Java element that can be added to Annotation (IType,IMETHOD) 
446    * that enclose the given Java element.
447    */
448   protected IJavaElement findSuitableElement(IJavaElement element) {
449     if (element == null)
450       return null;
451     String message = NAME + ".findSuitableElement()";
452     int type = element.getElementType();
453     if (type < IJavaElement.COMPILATION_UNIT)
454       return null;
455     if (type > IJavaElement.METHOD || type == IJavaElement.FIELD)
456       return findSuitableElement(element.getParent());
457     try {
458       switch (type) {
459         case IJavaElement.COMPILATION_UNIT :
460           message = message + ": COMPILATION_UNIT";
461           return ((ICompilationUnit) element).getTypes()[0];
462         case IJavaElement.CLASS_FILE :
463           message = message + ": CLASS_FILE";
464           return ((IClassFile) element).getType();
465         case IJavaElement.TYPE :
466           return element;
467         case IJavaElement.METHOD :
468           return element;
469         default :
470           System.err.println(JdtPlugin.msg(message, element));
471       }
472     } catch (Exception e) {
473       JdtPlugin.log(message, element, e);
474     }
475     return null;
476   }
477 
478   public boolean copyItem(PersistentItem item, String newfolder) {
479     if (internalCopyItem(item, newfolder)) {
480       saveXMLState(IConstants.ANNOTATION_RECOVER_FILENAME);
481       fireModelChange();
482       return true;
483     }
484     return false;
485   }
486 
487   public boolean copyItems(Object[] items, String newfolder) {
488     boolean moved = false;
489     for (int i = 0; i < items.length; ++i) {
490       if (!(items[i] instanceof PersistentItem))
491         continue;
492       if (internalCopyItem((PersistentItem) items[i], newfolder))
493         moved = true;
494     }
495     if (moved) {
496       saveXMLState(IConstants.ANNOTATION_RECOVER_FILENAME);
497       fireModelChange();
498       return true;
499     }
500     return false;
501   }
502 
503   private boolean internalCopyItem(PersistentItem item, String newfolder) {
504     if (item == null)
505       return false;
506     if (item instanceof PersistentFolder)
507       return false;
508     //
509     // Duplicate item and item to new folder.
510     //
511     IMemento m = XMLMemento.createWriteRoot(ID);
512     item.saveState(m);
513     PersistentItem dup = (PersistentItem) item.clone();
514     if (dup == null) {
515       JdtPlugin.log(
516         JdtPlugin.getString(NAME, "internalCopyItem", "DuplicateFailed") + ": item=" + item);
517       return false;
518     }
519     dup.setParent(newfolder);
520     if (fAnnotation.containsKey(dup)) {
521       JdtPlugin.log(
522         JdtPlugin.getString(NAME, "internalCopyItem", "DestinationExist") + ": item=" + item);
523       return false;
524     } else {
525       fAnnotation.put(dup, dup);
526       PersistentFolder folder = findFolder(newfolder);
527       folder.addChild(dup);
528       return true;
529     }
530   }
531 
532   /** 
533    * Move item to another folder, create new folder if not yet exists.  
534    * The item to be moved must exists, otherwise error and do nothing.
535    * @return true if move success, else false.
536    * @param item The item to move.
537    * @param newfolder The new folder to move the item to.
538    */
539   public boolean moveItem(PersistentItem item, String newfolder) {
540     if (internalMoveItem(item, newfolder)) {
541       saveXMLState(IConstants.ANNOTATION_RECOVER_FILENAME);
542       fireModelChange();
543       return true;
544     }
545     return false;
546   }
547 
548   /**
549    * Move all items to the new folder.  Ignoring objects that is not a valid item.
550    * @return true if any item is moved, else false.
551    * @param items An array of PersistentItem to be moved.
552    * @param newfolder The new folder to move to.
553    */
554   public boolean moveItems(Object[] items, String newfolder) {
555     boolean moved = false;
556     for (int i = 0; i < items.length; ++i) {
557       if (!(items[i] instanceof PersistentItem))
558         continue;
559       if (internalMoveItem((PersistentItem) items[i], newfolder))
560         moved = true;
561     }
562     if (moved) {
563       saveXMLState(IConstants.ANNOTATION_RECOVER_FILENAME);
564       fireModelChange();
565       return true;
566     }
567     return false;
568   }
569 
570   private boolean internalMoveItem(PersistentItem src, String newfolder) {
571     if (src == null)
572       return false;
573     if (src instanceof PersistentFolder)
574       return false;
575     if (!fAnnotation.containsKey(src)) {
576       JdtPlugin.log(
577         JdtPlugin.getString(NAME, "internalMoveItem", "SourceItemNotExist") + ": item=" + src);
578       return false;
579     }
580     PersistentItem dest = (PersistentItem) src.clone();
581     dest.setParent(newfolder);
582     if (fAnnotation.containsKey(dest)) {
583       //TODO: May be we should merge to dest. if dest. already exists.
584       JdtPlugin.log(
585         JdtPlugin.getString(NAME, "internalMoveItem", "DestinationItemExist")
586           + ": item="
587           + src);
588       return false;
589     }
590     PersistentItem original = (PersistentItem) fAnnotation.remove(src);
591     PersistentFolder folder = findFolder(original.getParent());
592     folder.removeChild(original);
593     original.setParent(newfolder);
594     fAnnotation.put(original, original);
595     folder = findFolder(newfolder);
596     folder.addChild(original);
597     return true;
598   }
599 
600   // ITreeContentProvider /////////////////////////////////////////////
601   //
602 
603   public Object getParent(Object child) {
604     if (child instanceof PersistentItem) {
605       return ((PersistentItem) child).getParent();
606     }
607     return null;
608   }
609 
610   public Object[] getChildren(Object parent) {
611     if (parent instanceof PersistentFolder) {
612       return ((PersistentFolder) parent).getChildren();
613     }
614     return new Object[0];
615   }
616 
617   public boolean hasChildren(Object parent) {
618     if (parent instanceof PersistentFolder)
619       return ((PersistentFolder) parent).hasChildren();
620     return false;
621   }
622 
623   // IStructuredContentProvider interface ///////////////////////////
624   //
625 
626   public Object[] getElements(Object parent) {
627     if (parent == null || parent.equals(ResourcesPlugin.getWorkspace())) {
628       return getChildren(fRoot);
629     }
630     return getChildren(parent);
631   }
632 
633   // IContentProvider interface /////////////////////////////////
634   //
635 
636   public void inputChanged(Viewer v, Object oldInput, Object newInput) {
637   }
638 
639   public void dispose() {
640   }
641 
642   // LabelProvider interface /////////////////////////////////////
643   //
644 
645   public String getText(Object obj) {
646     if (!(obj instanceof PersistentItem))
647       return null;
648     PersistentItem item = (PersistentItem) obj;
649     String ret = item.getName();
650     ret += "      #" + item.getCount() + " ";
651     String desc = item.getAnnotation();
652     if (desc != null)
653       ret += desc;
654     return ret;
655   }
656 
657   public Image getImage(Object obj) {
658     return ((PersistentItem) obj).getImage();
659   }
660 
661   ////////////////////////////////////////////////////////////////////////
662 
663   /** Validate Memento is in valid version format. */
664   boolean validVersion(IMemento memento) {
665     String version = memento.getString(TAG_VERSION);
666     for (int i = 0; i < VERSIONS.length; i++) {
667       if (VERSIONS[i].equals(version)) {
668         return true;
669       }
670     }
671     return false;
672   }
673 
674   ////////////////////////////////////////////////////////////////////////
675 
676 }