1 | /******************************************************************************* |
2 | * Copyright (c) 2008, 2009 IBM Corporation and others. |
3 | * All rights reserved. This program and the accompanying materials |
4 | * are made available under the terms of the Eclipse Public License v1.0 |
5 | * which accompanies this distribution, and is available at |
6 | * http://www.eclipse.org/legal/epl-v10.html |
7 | * |
8 | * Contributors: |
9 | * IBM Corporation - initial API and implementation |
10 | *******************************************************************************/ |
11 | package org.eclipse.pde.api.tools.internal; |
12 | |
13 | import java.util.ArrayList; |
14 | import java.util.HashSet; |
15 | import java.util.Iterator; |
16 | import java.util.List; |
17 | import java.util.Map; |
18 | import java.util.Set; |
19 | import java.util.jar.JarFile; |
20 | |
21 | import org.eclipse.core.resources.IFile; |
22 | import org.eclipse.core.resources.IResource; |
23 | import org.eclipse.core.runtime.CoreException; |
24 | import org.eclipse.core.runtime.IProgressMonitor; |
25 | import org.eclipse.core.runtime.IStatus; |
26 | import org.eclipse.core.runtime.Status; |
27 | import org.eclipse.jdt.core.IClassFile; |
28 | import org.eclipse.jdt.core.IClasspathEntry; |
29 | import org.eclipse.jdt.core.ICompilationUnit; |
30 | import org.eclipse.jdt.core.IJavaElement; |
31 | import org.eclipse.jdt.core.IJavaProject; |
32 | import org.eclipse.jdt.core.IPackageFragment; |
33 | import org.eclipse.jdt.core.IPackageFragmentRoot; |
34 | import org.eclipse.jdt.core.IType; |
35 | import org.eclipse.jdt.core.JavaModelException; |
36 | import org.eclipse.pde.api.tools.internal.model.BundleApiComponent; |
37 | import org.eclipse.pde.api.tools.internal.model.PluginProjectApiComponent; |
38 | import org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor; |
39 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
40 | import org.eclipse.pde.api.tools.internal.provisional.Factory; |
41 | import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations; |
42 | import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers; |
43 | import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers; |
44 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; |
45 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IPackageDescriptor; |
46 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor; |
47 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; |
48 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeContainer; |
49 | import org.eclipse.pde.api.tools.internal.provisional.scanner.TagScanner; |
50 | import org.eclipse.pde.api.tools.internal.util.Util; |
51 | import org.w3c.dom.Document; |
52 | import org.w3c.dom.Element; |
53 | |
54 | /** |
55 | * Implementation of an API description for a Java project. |
56 | * |
57 | * @since 1.0 |
58 | */ |
59 | public class ProjectApiDescription extends ApiDescription { |
60 | |
61 | /** |
62 | * Associated Java project |
63 | */ |
64 | private IJavaProject fProject; |
65 | |
66 | /** |
67 | * Time stamp at which package information was created |
68 | */ |
69 | public long fPackageTimeStamp = 0L; |
70 | |
71 | /** |
72 | * Whether a package refresh is in progress |
73 | */ |
74 | private boolean fRefreshingInProgress = false; |
75 | |
76 | /** |
77 | * Associated manifest file |
78 | */ |
79 | public IFile fManifestFile; |
80 | |
81 | /** |
82 | * Whether this API description is in synch with its project. Becomes |
83 | * false if anything in a project changes. When true, visiting can |
84 | * be performed by traversing the cached nodes, rather than traversing |
85 | * the java model elements (effectively building the cache). |
86 | */ |
87 | private boolean fInSynch = false; |
88 | |
89 | /** |
90 | * A node for a package. |
91 | */ |
92 | class PackageNode extends ManifestNode { |
93 | |
94 | IPackageFragment[] fFragments; |
95 | /** |
96 | * Constructs a new node. |
97 | * |
98 | * @param parent |
99 | * @param element |
100 | * @param visibility |
101 | * @param restrictions |
102 | */ |
103 | public PackageNode(IPackageFragment fragments[], ManifestNode parent, IElementDescriptor element, int visibility, int restrictions) { |
104 | super(parent, element, visibility, restrictions); |
105 | fFragments = fragments; |
106 | } |
107 | |
108 | /* (non-Javadoc) |
109 | * @see org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#refresh() |
110 | */ |
111 | protected ManifestNode refresh() { |
112 | refreshPackages(); |
113 | for (int i = 0; i < fFragments.length; i++) { |
114 | if (!fFragments[i].exists()) { |
115 | modified(); |
116 | return null; |
117 | } |
118 | } |
119 | return this; |
120 | } |
121 | |
122 | /* (non-Javadoc) |
123 | * @see org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#persistXML(org.w3c.dom.Document, org.w3c.dom.Element, java.lang.String) |
124 | */ |
125 | void persistXML(Document document, Element parent) { |
126 | if (hasApiVisibility(this)) { |
127 | Element pkg = document.createElement(IApiXmlConstants.ELEMENT_PACKAGE); |
128 | for (int i = 0; i < fFragments.length; i++) { |
129 | Element fragment = document.createElement(IApiXmlConstants.ELEMENT_PACKAGE_FRAGMENT); |
130 | fragment.setAttribute(IApiXmlConstants.ATTR_HANDLE, fFragments[i].getHandleIdentifier()); |
131 | pkg.appendChild(fragment); |
132 | } |
133 | pkg.setAttribute(IApiXmlConstants.ATTR_VISIBILITY, Integer.toString(this.visibility)); |
134 | persistChildren(document, pkg, children); |
135 | parent.appendChild(pkg); |
136 | } |
137 | } |
138 | |
139 | /* (non-Javadoc) |
140 | * @see org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#toString() |
141 | */ |
142 | public String toString() { |
143 | StringBuffer buffer = new StringBuffer(); |
144 | String name = ((IPackageDescriptor)element).getName(); |
145 | buffer.append("Package Node: ").append(name.equals("") ? "<default package>" : name); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
146 | buffer.append("\nVisibility: ").append(VisibilityModifiers.getVisibilityName(visibility)); //$NON-NLS-1$ |
147 | buffer.append("\nRestrictions: ").append(RestrictionModifiers.getRestrictionText(restrictions)); //$NON-NLS-1$ |
148 | if(fFragments != null) { |
149 | buffer.append("\nFragments:"); //$NON-NLS-1$ |
150 | IPackageFragment fragment = null; |
151 | for(int i = 0; i < fFragments.length; i++) { |
152 | fragment = fFragments[i]; |
153 | buffer.append("\n\t").append(fragment.getElementName()); //$NON-NLS-1$ |
154 | buffer.append(" ["); //$NON-NLS-1$ |
155 | buffer.append(fragment.getParent().getElementName()); |
156 | buffer.append("]"); //$NON-NLS-1$ |
157 | } |
158 | } |
159 | return buffer.toString(); |
160 | } |
161 | } |
162 | |
163 | /** |
164 | * Node for a reference type. |
165 | */ |
166 | class TypeNode extends ManifestNode { |
167 | |
168 | long fTimeStamp = -1L; |
169 | |
170 | private boolean fRefreshing = false; |
171 | |
172 | IType fType; |
173 | |
174 | /** |
175 | * Constructs a node for a reference type. |
176 | * |
177 | * @param type |
178 | * @param parent |
179 | * @param element |
180 | * @param visibility |
181 | * @param restrictions |
182 | */ |
183 | public TypeNode(IType type, ManifestNode parent, IElementDescriptor element, int visibility, int restrictions) { |
184 | super(parent, element, visibility, restrictions); |
185 | fType = type; |
186 | if (parent instanceof TypeNode) { |
187 | fTimeStamp = ((TypeNode)parent).fTimeStamp; |
188 | } |
189 | } |
190 | |
191 | /* (non-Javadoc) |
192 | * @see org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#refresh() |
193 | */ |
194 | protected synchronized ManifestNode refresh() { |
195 | if (fRefreshing) { |
196 | if(DEBUG) { |
197 | StringBuffer buffer = new StringBuffer(); |
198 | buffer.append("Refreshing manifest node: "); //$NON-NLS-1$ |
199 | buffer.append(this); |
200 | buffer.append(" aborted because a refresh is already in progress"); //$NON-NLS-1$ |
201 | System.out.println(buffer.toString()); |
202 | } |
203 | return this; |
204 | } |
205 | try { |
206 | fRefreshing = true; |
207 | ICompilationUnit unit = fType.getCompilationUnit(); |
208 | if (unit != null) { |
209 | IResource resource = null; |
210 | try { |
211 | resource = unit.getUnderlyingResource(); |
212 | } catch (JavaModelException e) { |
213 | if(DEBUG) { |
214 | StringBuffer buffer = new StringBuffer(); |
215 | buffer.append("Failed to get underlying resource for compilation unit: "); //$NON-NLS-1$ |
216 | buffer.append(unit); |
217 | System.out.println(buffer.toString()); |
218 | } |
219 | // exception if the resource does not exist |
220 | if (!e.getJavaModelStatus().isDoesNotExist()) { |
221 | ApiPlugin.log(e.getStatus()); |
222 | return this; |
223 | } |
224 | } |
225 | if (resource != null && resource.exists()) { |
226 | long stamp = resource.getModificationStamp(); |
227 | if (stamp != fTimeStamp) { |
228 | if(DEBUG) { |
229 | StringBuffer buffer = new StringBuffer(); |
230 | buffer.append("Resource has changed for type manifest node: "); //$NON-NLS-1$ |
231 | buffer.append(this); |
232 | buffer.append(" tag scanning the new type"); //$NON-NLS-1$ |
233 | System.out.println(buffer.toString()); |
234 | } |
235 | modified(); |
236 | children.clear(); |
237 | restrictions = RestrictionModifiers.NO_RESTRICTIONS; |
238 | fTimeStamp = resource.getModificationStamp(); |
239 | try { |
240 | TagScanner.newScanner().scan(unit, ProjectApiDescription.this, |
241 | getApiTypeContainer((IPackageFragmentRoot) fType.getPackageFragment().getParent()), null); |
242 | } catch (CoreException e) { |
243 | ApiPlugin.log(e.getStatus()); |
244 | } |
245 | } |
246 | } else { |
247 | if(DEBUG) { |
248 | StringBuffer buffer = new StringBuffer(); |
249 | buffer.append("Underlying resource for the type manifest node: "); //$NON-NLS-1$ |
250 | buffer.append(this); |
251 | buffer.append(" does not exist or is null"); //$NON-NLS-1$ |
252 | System.out.println(buffer.toString()); |
253 | } |
254 | // element has been removed |
255 | modified(); |
256 | parent.children.remove(element); |
257 | return null; |
258 | } |
259 | } else { |
260 | if(DEBUG) { |
261 | StringBuffer buffer = new StringBuffer(); |
262 | buffer.append("Failed to look up compilation unit for "); //$NON-NLS-1$ |
263 | buffer.append(fType); |
264 | buffer.append(" refreshing type manifest node: "); //$NON-NLS-1$ |
265 | buffer.append(this); |
266 | System.out.println(buffer.toString()); |
267 | } |
268 | // TODO: binary type |
269 | } |
270 | } finally { |
271 | fRefreshing = false; |
272 | } |
273 | return this; |
274 | } |
275 | |
276 | /* (non-Javadoc) |
277 | * @see org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#persistXML(org.w3c.dom.Document, org.w3c.dom.Element, java.lang.String) |
278 | */ |
279 | void persistXML(Document document, Element parent) { |
280 | if(hasApiVisibility(this)) { |
281 | Element type = document.createElement(IApiXmlConstants.ELEMENT_TYPE); |
282 | type.setAttribute(IApiXmlConstants.ATTR_HANDLE, fType.getHandleIdentifier()); |
283 | persistAnnotations(type); |
284 | type.setAttribute(IApiXmlConstants.ATTR_MODIFICATION_STAMP, Long.toString(fTimeStamp)); |
285 | persistChildren(document, type, children); |
286 | parent.appendChild(type); |
287 | } |
288 | } |
289 | |
290 | /* (non-Javadoc) |
291 | * @see org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode#toString() |
292 | */ |
293 | public String toString() { |
294 | StringBuffer buffer = new StringBuffer(); |
295 | buffer.append("Type Node: ").append(fType.getFullyQualifiedName()); //$NON-NLS-1$ |
296 | buffer.append("\nVisibility: ").append(VisibilityModifiers.getVisibilityName(visibility)); //$NON-NLS-1$ |
297 | buffer.append("\nRestrictions: ").append(RestrictionModifiers.getRestrictionText(restrictions)); //$NON-NLS-1$ |
298 | if(parent != null) { |
299 | String pname = parent.element.getElementType() == IElementDescriptor.PACKAGE ? |
300 | ((IPackageDescriptor)parent.element).getName() : ((IReferenceTypeDescriptor)parent.element).getQualifiedName(); |
301 | buffer.append("\nParent: ").append(pname); //$NON-NLS-1$ |
302 | } |
303 | return buffer.toString(); |
304 | } |
305 | } |
306 | |
307 | /** |
308 | * Constructs a new API description for the given project API component. |
309 | * |
310 | * @param component |
311 | */ |
312 | public ProjectApiDescription(IJavaProject project) { |
313 | super(project.getElementName()); |
314 | fProject = project; |
315 | } |
316 | |
317 | /* (non-Javadoc) |
318 | * @see org.eclipse.pde.api.tools.internal.provisional.IApiDescription#accept(org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor) |
319 | */ |
320 | public synchronized void accept(ApiDescriptionVisitor visitor, IProgressMonitor monitor) { |
321 | boolean completeVisit = true; |
322 | if (fInSynch) { |
323 | super.accept(visitor, monitor); |
324 | } else { |
325 | try { |
326 | IPackageFragment[] fragments = getLocalPackageFragments(); |
327 | IJavaElement[] children = null; |
328 | IJavaElement child = null; |
329 | ICompilationUnit unit = null; |
330 | for (int j = 0; j < fragments.length; j++) { |
331 | if (DEBUG) { |
332 | System.out.println("\t" + fragments[j].getElementName()); //$NON-NLS-1$ |
333 | } |
334 | IPackageDescriptor packageDescriptor = Factory.packageDescriptor(fragments[j].getElementName()); |
335 | // visit package |
336 | ManifestNode pkgNode = findNode(packageDescriptor, false); |
337 | if (pkgNode != null) { |
338 | IApiAnnotations annotations = resolveAnnotations(pkgNode, packageDescriptor); |
339 | if (visitor.visitElement(packageDescriptor, annotations)) { |
340 | children = fragments[j].getChildren(); |
341 | for (int k = 0; k < children.length; k++) { |
342 | child = children[k]; |
343 | if (child instanceof ICompilationUnit) { |
344 | unit = (ICompilationUnit) child; |
345 | String cuName = unit.getElementName(); |
346 | String tName = cuName.substring(0, cuName.length() - ".java".length()); //$NON-NLS-1$ |
347 | visit(visitor, unit.getType(tName)); |
348 | } else if (child instanceof IClassFile) { |
349 | visit(visitor, ((IClassFile)child).getType()); |
350 | } |
351 | } |
352 | } else { |
353 | completeVisit = false; |
354 | } |
355 | visitor.endVisitElement(packageDescriptor, annotations); |
356 | } |
357 | } |
358 | } catch (JavaModelException e) { |
359 | completeVisit = false; |
360 | ApiPlugin.log(e.getStatus()); |
361 | } finally { |
362 | if (completeVisit) { |
363 | fInSynch = true; |
364 | } |
365 | } |
366 | } |
367 | } |
368 | |
369 | /** |
370 | * Visits a type. |
371 | * |
372 | * @param visitor |
373 | * @param owningComponent |
374 | * @param type |
375 | */ |
376 | private void visit(ApiDescriptionVisitor visitor, IType type) { |
377 | IElementDescriptor element = getElementDescriptor(type); |
378 | ManifestNode typeNode = findNode(element, false); |
379 | if (typeNode != null) { |
380 | IApiAnnotations annotations = resolveAnnotations(typeNode, element); |
381 | if (visitor.visitElement(element, annotations)) { |
382 | // children |
383 | if (typeNode.children != null) { |
384 | visitChildren(visitor, typeNode.children, null); |
385 | } |
386 | } |
387 | visitor.endVisitElement(element, annotations); |
388 | } |
389 | } |
390 | |
391 | /* (non-Javadoc) |
392 | * @see org.eclipse.pde.api.tools.internal.ApiDescription#isInsertOnResolve(org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor) |
393 | */ |
394 | protected boolean isInsertOnResolve(IElementDescriptor elementDescriptor) { |
395 | switch (elementDescriptor.getElementType()) { |
396 | case IElementDescriptor.METHOD: |
397 | case IElementDescriptor.FIELD: |
398 | return false; |
399 | case IElementDescriptor.TYPE: |
400 | // no need to insert member types |
401 | return ((IReferenceTypeDescriptor) elementDescriptor).getEnclosingType() == null; |
402 | default: |
403 | return true; |
404 | } |
405 | } |
406 | |
407 | /* (non-Javadoc) |
408 | * @see org.eclipse.pde.api.tools.internal.ApiDescription#createNode(org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode, org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor) |
409 | */ |
410 | protected ManifestNode createNode(ManifestNode parentNode, IElementDescriptor element) { |
411 | switch (element.getElementType()) { |
412 | case IElementDescriptor.PACKAGE: |
413 | try { |
414 | IPackageDescriptor pkg = (IPackageDescriptor) element; |
415 | IPackageFragmentRoot[] roots = getJavaProject().getPackageFragmentRoots(); |
416 | List fragments = new ArrayList(1); |
417 | for (int i = 0; i < roots.length; i++) { |
418 | IPackageFragmentRoot root = roots[i]; |
419 | IClasspathEntry entry = root.getRawClasspathEntry(); |
420 | switch (entry.getEntryKind()) { |
421 | case IClasspathEntry.CPE_SOURCE: |
422 | case IClasspathEntry.CPE_LIBRARY: |
423 | IPackageFragment fragment = root.getPackageFragment(pkg.getName()); |
424 | if (fragment.exists()) { |
425 | fragments.add(fragment); |
426 | } |
427 | break; |
428 | default: |
429 | if (!root.isArchive() |
430 | && root.getKind() == IPackageFragmentRoot.K_BINARY) { |
431 | // class file folder |
432 | fragment = root.getPackageFragment(pkg.getName()); |
433 | if (fragment.exists()) { |
434 | fragments.add(fragment); |
435 | } |
436 | } |
437 | } |
438 | } |
439 | if (fragments.isEmpty()) { |
440 | return null; |
441 | } else { |
442 | return newPackageNode((IPackageFragment[])fragments.toArray(new IPackageFragment[fragments.size()]), parentNode, element, VisibilityModifiers.PRIVATE, RestrictionModifiers.NO_RESTRICTIONS); |
443 | } |
444 | |
445 | } catch (CoreException e) { |
446 | return null; |
447 | } |
448 | case IElementDescriptor.TYPE: |
449 | IReferenceTypeDescriptor descriptor = (IReferenceTypeDescriptor) element; |
450 | try { |
451 | IType type = null; |
452 | String name = descriptor.getName(); |
453 | if (parentNode instanceof PackageNode) { |
454 | IPackageFragment[] fragments = ((PackageNode) parentNode).fFragments; |
455 | for (int i = 0; i < fragments.length; i++) { |
456 | IPackageFragment fragment = fragments[i]; |
457 | if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) { |
458 | ICompilationUnit unit = fragment.getCompilationUnit(name + ".java"); //$NON-NLS-1$ |
459 | try { |
460 | IResource resource = unit.getUnderlyingResource(); |
461 | if (resource != null) { |
462 | type = unit.getType(name); |
463 | } |
464 | } catch (JavaModelException jme) { |
465 | // exception if the resource does not exist |
466 | if (!jme.getJavaModelStatus().isDoesNotExist()) { |
467 | throw jme; |
468 | } |
469 | } |
470 | } else { |
471 | IClassFile file = fragment.getClassFile(name + ".class"); //$NON-NLS-1$ |
472 | if (file.exists()) { |
473 | type = file.getType(); |
474 | } |
475 | } |
476 | } |
477 | } else if (parentNode instanceof TypeNode) { |
478 | type = ((TypeNode)parentNode).fType.getType(name); |
479 | } |
480 | if (type != null) { |
481 | return newTypeNode(type, parentNode, element, VISIBILITY_INHERITED, RestrictionModifiers.NO_RESTRICTIONS); |
482 | } |
483 | } catch (CoreException e ) { |
484 | return null; |
485 | } |
486 | return null; |
487 | } |
488 | return super.createNode(parentNode, element); |
489 | } |
490 | |
491 | /** |
492 | * Constructs and returns a new node for the given package fragment. |
493 | * |
494 | * @param fragment |
495 | * @param parent |
496 | * @param descriptor |
497 | * @param vis |
498 | * @param res |
499 | * @return |
500 | */ |
501 | public PackageNode newPackageNode(IPackageFragment[] fragments, ManifestNode parent, IElementDescriptor descriptor, int vis, int res) { |
502 | return new PackageNode(fragments, parent, descriptor, vis, res); |
503 | } |
504 | |
505 | /** |
506 | * Constructs and returns a new node for the given type. |
507 | * |
508 | * @param type |
509 | * @param parent |
510 | * @param descriptor |
511 | * @param vis |
512 | * @param res |
513 | * @return |
514 | */ |
515 | TypeNode newTypeNode(IType type, ManifestNode parent, IElementDescriptor descriptor, int vis, int res) { |
516 | return new TypeNode(type, parent, descriptor, vis, res); |
517 | } |
518 | |
519 | /** |
520 | * Constructs a new manifest node. |
521 | * |
522 | * @param parent |
523 | * @param element |
524 | * @param vis |
525 | * @param res |
526 | * @return |
527 | */ |
528 | ManifestNode newNode(ManifestNode parent, IElementDescriptor element, int vis, int res) { |
529 | return new ManifestNode(parent, element, vis, res); |
530 | } |
531 | |
532 | /** |
533 | * Refreshes package nodes if required. |
534 | */ |
535 | synchronized void refreshPackages() { |
536 | if (fRefreshingInProgress) { |
537 | if(DEBUG) { |
538 | StringBuffer buffer = new StringBuffer(); |
539 | buffer.append("Refreshing manifest node: "); //$NON-NLS-1$ |
540 | buffer.append(this); |
541 | buffer.append(" aborted because a refresh is already in progress"); //$NON-NLS-1$ |
542 | System.out.println(buffer.toString()); |
543 | } |
544 | return; |
545 | } |
546 | // check if in synch |
547 | if (fManifestFile == null || (fManifestFile.getModificationStamp() != fPackageTimeStamp)) { |
548 | try { |
549 | modified(); |
550 | fRefreshingInProgress = true; |
551 | // set all existing packages to PRIVATE (could clear |
552 | // the map, but it would be less efficient) |
553 | Iterator iterator = fPackageMap.values().iterator(); |
554 | while (iterator.hasNext()) { |
555 | PackageNode node = (PackageNode) iterator.next(); |
556 | node.visibility = VisibilityModifiers.PRIVATE; |
557 | } |
558 | fManifestFile = getJavaProject().getProject().getFile(JarFile.MANIFEST_NAME); |
559 | if (fManifestFile.exists()) { |
560 | try { |
561 | IPackageFragment[] fragments = getLocalPackageFragments(); |
562 | Set names = new HashSet(); |
563 | for (int i = 0; i < fragments.length; i++) { |
564 | names.add(fragments[i].getElementName()); |
565 | } |
566 | PluginProjectApiComponent component = getApiComponent(); |
567 | BundleApiComponent.initializeApiDescription(this, component.getBundleDescription(), names); |
568 | fPackageTimeStamp = fManifestFile.getModificationStamp(); |
569 | } catch (CoreException e) { |
570 | ApiPlugin.log(e.getStatus()); |
571 | } |
572 | } |
573 | } finally { |
574 | fRefreshingInProgress = false; |
575 | } |
576 | } |
577 | } |
578 | |
579 | private IElementDescriptor getElementDescriptor(IJavaElement element) { |
580 | switch (element.getElementType()) { |
581 | case IJavaElement.PACKAGE_FRAGMENT: |
582 | return Factory.packageDescriptor(element.getElementName()); |
583 | case IJavaElement.TYPE: |
584 | return Factory.typeDescriptor(((IType)element).getFullyQualifiedName('$')); |
585 | default: |
586 | return null; |
587 | } |
588 | } |
589 | |
590 | /** |
591 | * Returns the Java project associated with this component. |
592 | * |
593 | * @return associated Java project |
594 | */ |
595 | private IJavaProject getJavaProject() { |
596 | return fProject; |
597 | } |
598 | |
599 | /** |
600 | * Returns a class file container for the given package fragment root. |
601 | * |
602 | * @param root package fragment root |
603 | * @return class file container |
604 | * @exception CoreException if container cannot be located |
605 | */ |
606 | synchronized IApiTypeContainer getApiTypeContainer(IPackageFragmentRoot root) throws CoreException { |
607 | IApiTypeContainer container = getApiComponent().getTypeContainer(root); |
608 | if (container == null) { |
609 | throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, "Unable to resolve type conatiner for package fragment root")); //$NON-NLS-1$ |
610 | } |
611 | return container; |
612 | } |
613 | |
614 | /** |
615 | * Returns all package fragments that originate from this project. |
616 | * |
617 | * @return all package fragments that originate from this project |
618 | */ |
619 | private IPackageFragment[] getLocalPackageFragments() { |
620 | List local = new ArrayList(); |
621 | try { |
622 | IPackageFragmentRoot[] roots = getJavaProject().getPackageFragmentRoots(); |
623 | for (int i = 0; i < roots.length; i++) { |
624 | IPackageFragmentRoot root = roots[i]; |
625 | // only care about roots originating from this project (binary or source) |
626 | IResource resource = root.getCorrespondingResource(); |
627 | if (resource != null && resource.getProject().equals(getJavaProject().getProject())) { |
628 | IJavaElement[] children = root.getChildren(); |
629 | for (int j = 0; j < children.length; j++) { |
630 | local.add(children[j]); |
631 | } |
632 | } |
633 | } |
634 | } catch (JavaModelException e) { |
635 | // ignore |
636 | } |
637 | return (IPackageFragment[]) local.toArray(new IPackageFragment[local.size()]); |
638 | } |
639 | |
640 | /** |
641 | * Returns this API description as XML. |
642 | * |
643 | * @throws CoreException |
644 | */ |
645 | public synchronized String getXML() throws CoreException { |
646 | Document document = Util.newDocument(); |
647 | Element component = document.createElement(IApiXmlConstants.ELEMENT_COMPONENT); |
648 | component.setAttribute(IApiXmlConstants.ATTR_ID, getJavaProject().getElementName()); |
649 | component.setAttribute(IApiXmlConstants.ATTR_MODIFICATION_STAMP, Long.toString(fPackageTimeStamp)); |
650 | component.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_DESCRIPTION_CURRENT_VERSION); |
651 | document.appendChild(component); |
652 | persistChildren(document, component, fPackageMap); |
653 | return Util.serializeDocument(document); |
654 | } |
655 | |
656 | /** |
657 | * Persists the elements in the given map as XML elements, appended |
658 | * to the given xmlElement. |
659 | * |
660 | * @param document XML document |
661 | * @param xmlElement node to append children no |
662 | * @param elementMap elements to persist |
663 | */ |
664 | void persistChildren(Document document, Element xmlElement, Map elementMap) { |
665 | Iterator iterator = elementMap.values().iterator(); |
666 | while (iterator.hasNext()) { |
667 | ManifestNode node = (ManifestNode) iterator.next(); |
668 | node.persistXML(document, xmlElement); |
669 | } |
670 | } |
671 | |
672 | /** |
673 | * Cleans this API description so it will be re-populated with fresh data. |
674 | */ |
675 | public synchronized void clean() { |
676 | fPackageMap.clear(); |
677 | fPackageTimeStamp = -1L; |
678 | fInSynch = false; |
679 | modified(); |
680 | } |
681 | |
682 | /** |
683 | * Notes that the underlying project has changed in some way and that the |
684 | * description cache is no longer in synch with the project. |
685 | */ |
686 | public synchronized void projectChanged() { |
687 | fInSynch = false; |
688 | } |
689 | |
690 | /** |
691 | * Notes that the underlying project classpath has changed in some way and that the |
692 | * description cache is no longer in synch with the project. |
693 | */ |
694 | public synchronized void projectClasspathChanged() { |
695 | fInSynch = false; |
696 | // we want to flush the packages cache to "reload" all packages using the new package fragment roots |
697 | fPackageTimeStamp = -1L; |
698 | } |
699 | |
700 | /* (non-Javadoc) |
701 | * @see org.eclipse.pde.api.tools.internal.ApiDescription#toString() |
702 | */ |
703 | public String toString() { |
704 | StringBuffer buffer = new StringBuffer(); |
705 | buffer.append("Project API description for: ").append(getJavaProject().getElementName()); //$NON-NLS-1$ |
706 | return buffer.toString(); |
707 | } |
708 | |
709 | /** |
710 | * Returns the API component associated with this API description |
711 | * |
712 | * @return API component |
713 | * @exception CoreException if the API component cannot be located |
714 | */ |
715 | private PluginProjectApiComponent getApiComponent() throws CoreException { |
716 | IApiBaseline baseline = ApiBaselineManager.getManager().getWorkspaceBaseline(); |
717 | PluginProjectApiComponent component = (PluginProjectApiComponent) baseline.getApiComponent(getJavaProject().getProject()); |
718 | if (component == null) { |
719 | throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, "Unable to resolve project API component for API description")); //$NON-NLS-1$ |
720 | } |
721 | return component; |
722 | } |
723 | } |