Source code: com/port80/eclipse/jdt/util/PersistentItem.java
1 package com.port80.eclipse.jdt.util;
2
3 import java.sql.Timestamp;
4
5 import org.eclipse.core.resources.IFile;
6 import org.eclipse.core.resources.IProject;
7 import org.eclipse.core.resources.IResource;
8 import org.eclipse.core.resources.IWorkspaceRoot;
9 import org.eclipse.core.resources.ResourcesPlugin;
10 import org.eclipse.core.runtime.IAdaptable;
11 import org.eclipse.core.runtime.IPath;
12 import org.eclipse.core.runtime.Path;
13 import org.eclipse.jdt.core.IClassFile;
14 import org.eclipse.jdt.core.ICompilationUnit;
15 import org.eclipse.jdt.core.IJavaElement;
16 import org.eclipse.jdt.core.IJavaProject;
17 import org.eclipse.jdt.core.IMember;
18 import org.eclipse.jdt.core.IMethod;
19 import org.eclipse.jdt.core.IType;
20 import org.eclipse.jdt.core.JavaModelException;
21 import org.eclipse.jdt.core.Signature;
22 import org.eclipse.jdt.internal.ui.JavaPluginImages;
23 import org.eclipse.swt.graphics.Image;
24 import org.eclipse.ui.IElementFactory;
25 import org.eclipse.ui.IMemento;
26 import org.eclipse.ui.IPersistableElement;
27 import org.eclipse.ui.ISharedImages;
28 import org.eclipse.ui.PlatformUI;
29
30 import com.port80.eclipse.jdt.JdtPlugin;
31 import com.port80.eclipse.jdt.Util;
32 import com.port80.eclipse.util.UtilPluginImages;
33
34 /**
35 * Wrapper around object to make it a tree item and persistable.
36 * <p>
37 * Currently, valid objects that can be persisted are IType, IMethod,
38 * other object type are not persisted.
39 * <p>
40 * Each item is uniquely identified by its Kind, FullName, FullPath, Project.
41 * Items with the fields equal is the same item.
42 * <p>
43 * When the presistable object is resolved to an actual object in the workspace,
44 * it is cached and subsequently return by getCached(). Use resolve() to
45 * force resolving again.
46 * <p>
47 * Used by MethodView and WorkingSetView to save history objects.
48 *
49 * @author chrisl
50 */
51 public class PersistentItem implements IPersistableElement, IElementFactory, IAdaptable {
52
53 public static final String NAME = "PersistentItem";
54 public static final String ID = "com.port80.eclipse.jdt.util.PersistentItem";
55
56 public static final String[] KIND_NAMES =
57 new String[] { "None", "Class", "Interface", "Method", "Folder", "File" };
58 public static final int NONE = 0;
59 public static final int CLASS = 1;
60 public static final int INTERFACE = 2;
61 public static final int METHOD = 3;
62 public static final int FOLDER = 4;
63 public static final int FILE = 5;
64
65 public static final int IS_ERR = 0x0001;
66 public static final int IS_NOTEXIST = 0x0001;
67 public static final int IS_ACTIVE = 0x0100;
68
69 private static final boolean DEBUG = false;
70
71 /** Icons for the item (without any java decoration).*/
72 private static final Image[] ICONS;
73 private static final Image ICON_ERROR;
74 private static final Image ICON_OPEN_FOLDER;
75 static {
76 ICONS =
77 new Image[] {
78 PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT),
79 JavaPluginImages.get(JavaPluginImages.IMG_OBJS_CLASS),
80 JavaPluginImages.get(JavaPluginImages.IMG_OBJS_INTERFACE),
81 JavaPluginImages.get(JavaPluginImages.IMG_MISC_PUBLIC),
82 UtilPluginImages.get(UtilPluginImages.IMGKEY_CLOSED_FOLDER),
83 PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE)};
84 ICON_ERROR = UtilPluginImages.get(UtilPluginImages.IMGKEY_DELETE);
85 ICON_OPEN_FOLDER = UtilPluginImages.get(UtilPluginImages.IMGKEY_OPEN_FOLDER);
86 }
87
88 // Persistent fields.
89 private int fKind;
90 /** Number of visits. */
91 private int fCount;
92 /** Short name of the item, eg. simple type name or method signature. */
93 private String fName;
94 /** Full name eg. FullyQualifiedName of types or type qualified method signature. */
95 private String fFullName;
96 /**
97 * Full path to the object's underlying resource, relative to workspace root
98 * or absolute if resource is external to workspace.
99 */
100 private String fFullPath;
101 /** Project name in workspace. */
102 private String fProject;
103 /** A flag (the name is obsoleted). For JavaElement, 1 for binary, 0 for source. */
104 private int fExternal;
105 private String fAnnotation;
106 private Timestamp fCreateTime;
107 private Timestamp fAccessTime;
108 /** The parent folder name. */
109 private String fParent;
110
111 // Transient fields.
112 private Object fCached;
113 private int fStatus; /** Error resolving to actual object.*/
114
115 ////////////////////////////////////////////////////////////////////////
116
117 /**
118 * Static factory method to create a PersistentItem.
119 * @return Item created or null if fail to create item.
120 */
121 public static PersistentItem create(Object a) {
122 if (!(a instanceof IJavaElement) && !(a instanceof IFile))
123 return null;
124 PersistentItem ret = new PersistentItem();
125 return ret.init(a);
126 }
127
128 public static PersistentItem create(Object a, String parent) {
129 if (!(a instanceof IJavaElement) && !(a instanceof IFile))
130 return null;
131 PersistentItem ret = new PersistentItem();
132 ret.fParent = parent;
133 return ret.init(a);
134 }
135
136 /**
137 * Static factory method to create a PersistentItem from an IMemento.
138 * @return Item created or null if fail to create item.
139 */
140 public static PersistentItem restore(IMemento m) {
141 PersistentItem ret = new PersistentItem();
142 ret = (PersistentItem) ret.restoreState(m);
143 return ret;
144 }
145
146 ////////////////////////////////////////////////////////////////////////
147
148 private PersistentItem() {
149 }
150
151 public PersistentItem(int kind, String name) {
152 fKind = kind;
153 fName = name;
154 fFullName = name;
155 }
156
157 public Object clone() {
158 PersistentItem ret = new PersistentItem();
159 ret.fParent = fParent;
160 ret.fKind = fKind;
161 ret.fName = fName;
162 ret.fFullName = fFullName;
163 ret.fFullPath = fFullPath;
164 ret.fProject = fProject;
165 ret.fExternal = fExternal;
166 ret.fAnnotation = fAnnotation;
167 ret.fCreateTime = fCreateTime;
168 ret.fAccessTime = fAccessTime;
169 ret.fCount = fCount;
170 return ret;
171 }
172
173 ////////////////////////////////////////////////////////////////////////
174
175 private PersistentItem init(Object a) {
176 if (a == null)
177 return null;
178 fKind = NONE;
179 fCount = 1;
180 if (a instanceof IJavaElement)
181 return initJavaElement((IJavaElement) a);
182 if (a instanceof IFile)
183 return initFile((IFile) a);
184 return null;
185 }
186
187 private PersistentItem initJavaElement(IJavaElement element) {
188 try {
189 IType type;
190 if (element instanceof IType) {
191 type = (IType) element;
192 fKind = (type.isClass()) ? CLASS : INTERFACE;
193 fFullName = ((IType) element).getFullyQualifiedName();
194 fName = Signature.getSimpleName(fFullName);
195 } else if (element instanceof IMethod) {
196 fKind = METHOD;
197 fFullName = Util.getFullMethodSignature((IMethod)element);
198 fName = Util.getMethodSignature((IMethod) element);
199 }
200 if (fKind != NONE) {
201 fCached = element;
202 ICompilationUnit unit = ((IMember) element).getCompilationUnit();
203 if (unit != null) {
204 fProject = unit.getJavaProject().getProject().getName();
205 fFullPath = unit.getPath().toString();
206 fExternal = 0;
207 if (fKind != METHOD)
208 fName += ".java";
209 } else {
210 IClassFile file = ((IMember) element).getClassFile();
211 //NOTE: IJavaElement.getResource() return null for external jar.
212 // So it useless.
213 // if (file != null && file.getResource() != null) {
214 // resource = file.getResource();
215 // fProject = resource.getProject().getName();
216 // fFullPath = resource.getFullPath().toString();
217 // }
218 // This would be an absolute path to the class/jar file in the file system.
219 fProject = file.getJavaProject().getProject().getName();
220 fFullPath = file.getPath().toString();
221 fExternal = 1;
222 if (fKind != METHOD)
223 fName += ".class ";
224 }
225 }
226 if (false)
227 System.err.println(
228 NAME
229 + ".init(): fProject="
230 + fProject
231 + ", fFullPath="
232 + fFullPath
233 + ", fFullName="
234 + fFullName);
235 fCreateTime = new Timestamp(System.currentTimeMillis());
236 } catch (JavaModelException e) {
237 JdtPlugin.log(NAME + ".init()", element, e);
238 return null;
239 }
240 if (fKind == NONE)
241 return null;
242 return this;
243 }
244
245 private PersistentItem initFile(IFile file) {
246 fKind = FILE;
247 fProject = file.getProject().getName();
248 fFullPath = file.getFullPath().toString();
249 fName = file.getName();
250 fFullName = fName;
251 fCreateTime = new Timestamp(System.currentTimeMillis());
252 fExternal = 0;
253 return this;
254 }
255
256 ////////////////////////////////////////////////////////////////////////
257
258 public void setParent(String parent) {
259 fParent = parent;
260 }
261 public void setActive(boolean isactive) {
262 fStatus |= IS_ACTIVE;
263 if (!isactive)
264 fStatus ^= IS_ACTIVE;
265 }
266 public void setATime(Timestamp t) {
267 ++fCount;
268 if (t != null)
269 fAccessTime = t;
270 else
271 fAccessTime = fCreateTime;
272 }
273 public void setAnnotation(String text) {
274 fAnnotation = text;
275 }
276
277 public String getParent() {
278 return fParent;
279 }
280 public boolean isExternal() {
281 return fExternal != 0;
282 }
283 public boolean isActive() {
284 return (fStatus & IS_ACTIVE) != 0;
285 }
286 public boolean isNotExist() {
287 return (fStatus & IS_NOTEXIST) != 0;
288 }
289 public int getStatus() {
290 return fStatus;
291 }
292 public int getKind() {
293 return fKind;
294 }
295 public String getKindName() {
296 return KIND_NAMES[fKind];
297 }
298 public int getCount() {
299 return fCount;
300 }
301 public String getName() {
302 return fName;
303 }
304 public String getFullName() {
305 return fFullName;
306 }
307 public String getFullPath() {
308 return fFullPath;
309 }
310 public String getProject() {
311 return fProject;
312 }
313 public String getAnnotation() {
314 return fAnnotation;
315 }
316 public Timestamp getATime() {
317 return fAccessTime;
318 }
319 public Timestamp getCTime() {
320 return fCreateTime;
321 }
322
323 ////////////////////////////////////////////////////////////////////////
324
325 public Image getImage() {
326 if ((fStatus & IS_ERR) != 0)
327 return ICON_ERROR;
328 if (fKind == FOLDER && isActive())
329 return ICON_OPEN_FOLDER;
330 return ICONS[fKind];
331 }
332
333 // IPersistableElement interface //////////////////////////////////////
334 //
335 public String getFactoryId() {
336 return ID;
337 }
338
339 public void saveState(IMemento m) {
340 m.putInteger("Kind", fKind);
341 m.putString("Parent", fParent);
342 m.putString("KindName", KIND_NAMES[fKind]);
343 m.putInteger("Count", fCount);
344 m.putInteger("External", fExternal);
345 m.putString("Name", fName);
346 m.putString("FullName", fFullName);
347 m.putString("FullPath", fFullPath);
348 m.putString("Project", fProject);
349 m.putString("Annotation", fAnnotation);
350 if (fCreateTime != null)
351 m.putString("CreateTime", fCreateTime.toString());
352 if (fAccessTime != null)
353 m.putString("AccessTime", fAccessTime.toString());
354 }
355
356 // IElementFactory interface ///////////////////////////////////////////
357 //
358 public IAdaptable createElement(IMemento m) {
359 return restoreState(m);
360 }
361
362 public IAdaptable restoreState(IMemento m) {
363 String ret;
364 fKind = getIntDef(m, "Kind", NONE);
365 fCount = getIntDef(m, "Count", 1);
366 fExternal = getIntDef(m, "External", 0);
367 fParent = m.getString("Parent");
368 fName = m.getString("Name");
369 if (fName == null)
370 fName = "";
371 fFullName = m.getString("FullName");
372 if (fFullName == null)
373 fFullName = fName;
374 fFullPath = m.getString("FullPath");
375 fProject = m.getString("Project");
376 fAnnotation = m.getString("Annotation");
377 ret = m.getString("CreateTime");
378 if (ret == null)
379 fCreateTime = new Timestamp(System.currentTimeMillis());
380 else
381 fCreateTime = Timestamp.valueOf(ret);
382 ret = m.getString("AccessTime");
383 if (ret == null)
384 fAccessTime = fCreateTime;
385 else
386 fAccessTime = Timestamp.valueOf(ret);
387 return this;
388 }
389
390 // IAdaptable interface ///////////////////////////////////////////////
391 //
392 public Object getAdapter(Class c) {
393 if (c.isInstance(this))
394 return this;
395 return null;
396 }
397
398 /**
399 * Get the cached object if it have been resolved,
400 * else resolve to an actual object, cache it and return it.
401 */
402 public Object getCached() {
403 if (fCached == null)
404 fCached = resolve();
405 return fCached;
406 }
407
408 public Object resolve() {
409 if (fKind == CLASS || fKind == INTERFACE || fKind == METHOD)
410 return resolveJavaElement();
411 if (fKind == FILE)
412 return resolveFile();
413 return null;
414 }
415
416 private IFile resolveFile() {
417 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
418 IFile file = root.getFile(new Path(fFullPath));
419 fStatus |= IS_NOTEXIST;
420 if (file != null)
421 fStatus ^= IS_NOTEXIST;
422 if (DEBUG)
423 System.err.println(NAME + ".resolveFile(): file=" + file.getFullPath().toString());
424 return file;
425 }
426
427 /**
428 * Resolve the persistable reference to an actual object.
429 * If cahed object does not equal to the resolved object, the cache it cleared.
430 * If cached object equals to the resolved object, it stay in the cache.
431 */
432 private IJavaElement resolveJavaElement() {
433 final String METHODNAME = NAME + ".resolveJavaElement";
434 fStatus |= IS_NOTEXIST;
435 IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
436 IResource resource = null;
437 if (fFullPath == null)
438 return null;
439 if (fProject == null)
440 return null;
441 IProject project = root.getProject(fProject);
442 if (project == null) {
443 if (DEBUG)
444 System.err.println(METHODNAME + "(): project not exist.");
445 return null;
446 }
447 IJavaElement ret = null;
448 if (isExternal()) {
449 IJavaProject javaproject = JavaUtil.getJavaProject(project);
450 if (javaproject == null)
451 if (DEBUG)
452 System.err.println(METHODNAME + "(): javaproject==null");
453 try {
454 ret = javaproject.findPackageFragmentRoot(new Path(fFullPath));
455 if (ret == null)
456 return null;
457 } catch (Exception e) {
458 JdtPlugin.log(METHODNAME + "(): javaproject==null", e);
459 return null;
460 }
461 switch (fKind) {
462 case CLASS :
463 case INTERFACE :
464 ret = Util.findJavaType(ret, fFullName);
465 break;
466 case METHOD :
467 ret = Util.findJavaMethod(ret, fFullName);
468 break;
469 }
470 } else {
471 // Resource local to workspace.
472 IJavaProject javaproject = JavaUtil.getJavaProject(project);
473 if (javaproject == null) {
474 if (DEBUG)
475 System.err.println(
476 METHODNAME + "(): Java project not found: project=" + fProject);
477 return null;
478 }
479 String typename = fFullName;
480 if(fKind==METHOD) typename=Util.getTypeNameOfMethodSignature(typename);
481 fFullName.replace('.', '/');
482 typename += ".java";
483 IPath path = new Path(typename);
484 if (DEBUG)
485 System.err.println(METHODNAME + "(): path=" + path.toString());
486 try {
487 ret = javaproject.findElement(path);
488 } catch (JavaModelException e) {
489 JdtPlugin.log(
490 METHODNAME + "(): finding element: " + resource.getFullPath().toString(),
491 e);
492 return null;
493 }
494 if (fKind == METHOD)
495 ret = Util.findJavaMethod(ret, fFullName);
496 // if (resource != null)
497 // ret = JavaCore.create(resource);
498 }
499 if (false)
500 System.err.println(JdtPlugin.msg(METHODNAME + "(): 1: ", ret));
501 if (fCached != null && !fCached.equals(ret))
502 fCached = null;
503 if (ret != null)
504 fStatus ^= IS_NOTEXIST;
505 if (DEBUG)
506 System.err.println(JdtPlugin.msg(METHODNAME + "(): ret=", ret));
507 return ret;
508 }
509
510 ////////////////////////////////////////////////////////////////////////
511
512 public int hashCode() {
513 StringBuffer buf = new StringBuffer();
514 buf.append(fParent);
515 buf.append('#');
516 buf.append(fFullPath);
517 buf.append('#');
518 buf.append(fFullName);
519 buf.append('#');
520 buf.append(fName);
521 buf.append('#');
522 buf.append(fKind);
523 return (buf.toString().hashCode());
524 }
525
526 public boolean equals(Object a) {
527 if (!(a instanceof PersistentItem))
528 return false;
529 PersistentItem item = (PersistentItem) a;
530 if (fKind != item.fKind)
531 return false;
532 if (!(fFullName == null ? item.fFullName == null : fFullName.equals(item.fFullName)))
533 return false;
534 //NOTE:
535 // FullPath already have the project name for source item.
536 // For binary item, the project is ignored.
537 // As long as the FullPath points to the same file, it is same object.
538 if (!(fFullPath == null ? item.fFullPath == null : fFullPath.equals(item.fFullPath)))
539 return false;
540 if (!(fParent == null ? item.fParent == null : fParent.equals(item.fParent)))
541 return false;
542 return true;
543 }
544
545 /** String representation in format: Parent*Project@FullPath#FullName:KindName. */
546 public String toString() {
547 StringBuffer buf = new StringBuffer("# PersistentItem\n# ");
548 if (fParent != null)
549 buf.append(fParent);
550 buf.append("\n# ");
551 if (fProject != null)
552 buf.append(fProject);
553 buf.append("\n# ");
554 if (fFullPath != null)
555 buf.append(fFullPath);
556 buf.append("\n# ");
557 buf.append(fFullName);
558 buf.append("\n# ");
559 buf.append(getKindName());
560 buf.append("\n");
561 if (fAnnotation != null)
562 buf.append(fAnnotation);
563 buf.append("\n");
564 return buf.toString();
565 }
566
567 ////////////////////////////////////////////////////////////////////////
568
569 private int getIntDef(IMemento m, String key, int def) {
570 Integer n = m.getInteger(key);
571 if (n != null)
572 return n.intValue();
573 else
574 return def;
575 }
576
577 ////////////////////////////////////////////////////////////////////////
578 }