| 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.io.BufferedInputStream; | 
| 14 | import java.io.File; | 
| 15 | import java.io.FileInputStream; | 
| 16 | import java.io.IOException; | 
| 17 | import java.util.ArrayList; | 
| 18 | import java.util.HashMap; | 
| 19 | import java.util.Iterator; | 
| 20 | import java.util.List; | 
| 21 | import java.util.Map; | 
| 22 | import java.util.Map.Entry; | 
| 23 | import java.util.jar.JarFile; | 
| 24 |   | 
| 25 | import org.eclipse.core.resources.ISaveContext; | 
| 26 | import org.eclipse.core.resources.ISaveParticipant; | 
| 27 | import org.eclipse.core.runtime.CoreException; | 
| 28 | import org.eclipse.core.runtime.IPath; | 
| 29 | import org.eclipse.core.runtime.IStatus; | 
| 30 | import org.eclipse.core.runtime.Status; | 
| 31 | import org.eclipse.jdt.core.ElementChangedEvent; | 
| 32 | import org.eclipse.jdt.core.ICompilationUnit; | 
| 33 | import org.eclipse.jdt.core.IElementChangedListener; | 
| 34 | import org.eclipse.jdt.core.IJavaElement; | 
| 35 | import org.eclipse.jdt.core.IJavaElementDelta; | 
| 36 | import org.eclipse.jdt.core.IJavaProject; | 
| 37 | import org.eclipse.jdt.core.IPackageFragment; | 
| 38 | import org.eclipse.jdt.core.IType; | 
| 39 | import org.eclipse.jdt.core.JavaCore; | 
| 40 | import org.eclipse.osgi.service.resolver.BundleDescription; | 
| 41 | import org.eclipse.pde.api.tools.internal.ApiDescription.ManifestNode; | 
| 42 | import org.eclipse.pde.api.tools.internal.ProjectApiDescription.TypeNode; | 
| 43 | import org.eclipse.pde.api.tools.internal.model.ApiModelCache; | 
| 44 | import org.eclipse.pde.api.tools.internal.model.PluginProjectApiComponent; | 
| 45 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; | 
| 46 | import org.eclipse.pde.api.tools.internal.provisional.Factory; | 
| 47 | import org.eclipse.pde.api.tools.internal.provisional.IApiDescription; | 
| 48 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; | 
| 49 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor; | 
| 50 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement; | 
| 51 | import org.eclipse.pde.api.tools.internal.provisional.scanner.ScannerMessages; | 
| 52 | import org.eclipse.pde.api.tools.internal.util.Util; | 
| 53 | import org.w3c.dom.Element; | 
| 54 | import org.w3c.dom.Node; | 
| 55 | import org.w3c.dom.NodeList; | 
| 56 |   | 
| 57 | import com.ibm.icu.text.MessageFormat; | 
| 58 |   | 
| 59 | /** | 
| 60 |  * Manages a cache of API descriptions for Java projects. Descriptions | 
| 61 |  * are re-used between API components for the same project. | 
| 62 |  *  | 
| 63 |  * @since 1.0 | 
| 64 |  */ | 
| 65 | public final class ApiDescriptionManager implements IElementChangedListener, ISaveParticipant { | 
| 66 |          | 
| 67 |         /** | 
| 68 |          * Singleton | 
| 69 |          */ | 
| 70 |         private static ApiDescriptionManager fgDefault; | 
| 71 |          | 
| 72 |         /** | 
| 73 |          * Maps Java projects to API descriptions | 
| 74 |          */ | 
| 75 |         private Map fDescriptions = new HashMap(); | 
| 76 |          | 
| 77 |         /** | 
| 78 |          * Path to the local directory where API descriptions are cached | 
| 79 |          * per project. | 
| 80 |          */ | 
| 81 |         public static final IPath API_DESCRIPTIONS_CONTAINER_PATH = | 
| 82 |                 ApiPlugin.getDefault().getStateLocation(); | 
| 83 |   | 
| 84 |         /** | 
| 85 |          * Constructs an API description manager. | 
| 86 |          */ | 
| 87 |         private ApiDescriptionManager() { | 
| 88 |                 JavaCore.addElementChangedListener(this, ElementChangedEvent.POST_CHANGE); | 
| 89 |                 ApiPlugin.getDefault().addSaveParticipant(this); | 
| 90 |         } | 
| 91 |   | 
| 92 |         /** | 
| 93 |          * Cleans up Java element listener | 
| 94 |          */ | 
| 95 |         public static void shutdown() { | 
| 96 |                 if (fgDefault != null) { | 
| 97 |                         JavaCore.removeElementChangedListener(fgDefault); | 
| 98 |                         ApiPlugin.getDefault().removeSaveParticipant(fgDefault); | 
| 99 |                 } | 
| 100 |         } | 
| 101 |          | 
| 102 |         /** | 
| 103 |          * Returns the singleton API description manager. | 
| 104 |          *  | 
| 105 |          * @return API description manager | 
| 106 |          */ | 
| 107 |         public synchronized static ApiDescriptionManager getDefault() { | 
| 108 |                 if (fgDefault == null) { | 
| 109 |                         fgDefault = new ApiDescriptionManager(); | 
| 110 |                 } | 
| 111 |                 return fgDefault; | 
| 112 |         } | 
| 113 |          | 
| 114 |         /** | 
| 115 |          * Returns an API description for the given project component and connect it to the | 
| 116 |          * given bundle description. | 
| 117 |          *  | 
| 118 |          * @param project Java project | 
| 119 |          * @return API description | 
| 120 |          */ | 
| 121 |         public synchronized IApiDescription getApiDescription(PluginProjectApiComponent component, BundleDescription bundle) { | 
| 122 |                 IJavaProject project = component.getJavaProject(); | 
| 123 |                 ProjectApiDescription description = (ProjectApiDescription) fDescriptions.get(project); | 
| 124 |                 if (description == null) { | 
| 125 |                         if (Util.isApiProject(project)) { | 
| 126 |                                 description = new ProjectApiDescription(project); | 
| 127 |                         } else { | 
| 128 |                                 description = new NonApiProjectDescription(project); | 
| 129 |                         } | 
| 130 |                         try { | 
| 131 |                                 restoreDescription(project, description); | 
| 132 |                         } catch (CoreException e) { | 
| 133 |                                 ApiPlugin.log(e.getStatus()); | 
| 134 |                                 description = new ProjectApiDescription(project); | 
| 135 |                         } | 
| 136 |                         fDescriptions.put(project, description); | 
| 137 |                 } | 
| 138 |                 return description; | 
| 139 |         } | 
| 140 |         /** | 
| 141 |          * Cleans the API description for the given project. | 
| 142 |          *  | 
| 143 |          * @param project | 
| 144 |          * @param delete whether to delete the file on disk | 
| 145 |          * @param remove whether to remove the cached API description | 
| 146 |          */ | 
| 147 |         public synchronized void clean(IJavaProject project, boolean delete, boolean remove) { | 
| 148 |                 ProjectApiDescription desc = null; | 
| 149 |                 if (remove) { | 
| 150 |                         desc = (ProjectApiDescription) fDescriptions.remove(project); | 
| 151 |                 } else { | 
| 152 |                         desc = (ProjectApiDescription) fDescriptions.get(project); | 
| 153 |                 } | 
| 154 |                 if (desc != null) { | 
| 155 |                         desc.clean(); | 
| 156 |                 } | 
| 157 |                 if (delete) { | 
| 158 |                         File file = API_DESCRIPTIONS_CONTAINER_PATH.append(project.getElementName()) | 
| 159 |                                 .append(IApiCoreConstants.API_DESCRIPTION_XML_NAME).toFile(); | 
| 160 |                         if (file.exists()) { | 
| 161 |                                 file.delete(); | 
| 162 |                         } | 
| 163 |                         file = API_DESCRIPTIONS_CONTAINER_PATH.append(project.getElementName()).toFile(); | 
| 164 |                         if(file.exists() && file.isDirectory()) { | 
| 165 |                                 file.delete(); | 
| 166 |                         } | 
| 167 |                 } | 
| 168 |         } | 
| 169 |          | 
| 170 |         /** | 
| 171 |          * Notifies the API description that the underlying project has changed. | 
| 172 |          *  | 
| 173 |          * @param project | 
| 174 |          */ | 
| 175 |         synchronized void projectChanged(IJavaProject project) { | 
| 176 |                 ProjectApiDescription desc = (ProjectApiDescription) fDescriptions.get(project); | 
| 177 |                 if (desc != null) { | 
| 178 |                         desc.projectChanged(); | 
| 179 |                 } | 
| 180 |         } | 
| 181 |   | 
| 182 |         /** | 
| 183 |          * Notifies the API description that the underlying project classpath has changed. | 
| 184 |          *  | 
| 185 |          * @param project | 
| 186 |          */ | 
| 187 |         synchronized void projectClasspathChanged(IJavaProject project) { | 
| 188 |                 ProjectApiDescription desc = (ProjectApiDescription) fDescriptions.get(project); | 
| 189 |                 if (desc != null) { | 
| 190 |                         desc.projectClasspathChanged(); | 
| 191 |                 } | 
| 192 |         } | 
| 193 |   | 
| 194 |         /* (non-Javadoc) | 
| 195 |          * @see org.eclipse.jdt.core.IElementChangedListener#elementChanged(org.eclipse.jdt.core.ElementChangedEvent) | 
| 196 |          */ | 
| 197 |         public void elementChanged(ElementChangedEvent event) { | 
| 198 |                 IJavaElementDelta delta = event.getDelta(); | 
| 199 |                 processJavaElementDeltas(delta.getAffectedChildren(), null); | 
| 200 |         } | 
| 201 |          | 
| 202 |         /** | 
| 203 |          * Remove projects that get closed or removed. | 
| 204 |          *  | 
| 205 |          * @param deltas | 
| 206 |          */ | 
| 207 |         private synchronized void processJavaElementDeltas(IJavaElementDelta[] deltas, IJavaProject proj) { | 
| 208 |                 IJavaElementDelta delta = null; | 
| 209 |                 for(int i = 0; i < deltas.length; i++) { | 
| 210 |                         delta = deltas[i]; | 
| 211 |                         switch(delta.getElement().getElementType()) { | 
| 212 |                                 case IJavaElement.JAVA_PROJECT: { | 
| 213 |                                         IJavaProject jproj = (IJavaProject) delta.getElement(); | 
| 214 |                                         switch (delta.getKind()) { | 
| 215 |                                                 case IJavaElementDelta.CHANGED: | 
| 216 |                                                         int flags = delta.getFlags(); | 
| 217 |                                                         if((flags & IJavaElementDelta.F_CLOSED) != 0) { | 
| 218 |                                                                 clean(jproj, false, true); | 
| 219 |                                                                 flushElementCache(delta); | 
| 220 |                                                         } else if((flags & (IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED | 
| 221 |                                                                         | IJavaElementDelta.F_CLASSPATH_CHANGED)) != 0) { | 
| 222 |                                                                 if (jproj != null) { | 
| 223 |                                                                         projectClasspathChanged(jproj); | 
| 224 |                                                                         flushElementCache(delta); | 
| 225 |                                                                 } | 
| 226 |                                                         } else if((flags & IJavaElementDelta.F_CONTENT) != 0) { | 
| 227 |                                                                 if (jproj != null) { | 
| 228 |                                                                         processJavaElementDeltas(delta.getAffectedChildren(), jproj); | 
| 229 |                                                                 } | 
| 230 |                                                         } else if ((flags & IJavaElementDelta.F_CHILDREN) != 0) { | 
| 231 |                                                                 processJavaElementDeltas(delta.getAffectedChildren(), jproj); | 
| 232 |                                                         } | 
| 233 |                                                         break; | 
| 234 |                                                 case IJavaElementDelta.REMOVED: | 
| 235 |                                                         clean(jproj, true, true); | 
| 236 |                                                         flushElementCache(delta); | 
| 237 |                                                         break; | 
| 238 |                                         } | 
| 239 |                                         break; | 
| 240 |                                 } | 
| 241 |                                 case IJavaElement.PACKAGE_FRAGMENT : { | 
| 242 |                                         int flags = delta.getFlags(); | 
| 243 |                                         if ((flags & IJavaElementDelta.F_CHILDREN) != 0) { | 
| 244 |                                                 processJavaElementDeltas(delta.getAffectedChildren(), proj); | 
| 245 |                                         } | 
| 246 |                                         break; | 
| 247 |                                 } | 
| 248 |                                 case IJavaElement.PACKAGE_FRAGMENT_ROOT : { | 
| 249 |                                         int flags = delta.getFlags(); | 
| 250 |                                         if ((flags & (IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED | 
| 251 |                                                         | IJavaElementDelta.F_ADDED_TO_CLASSPATH | 
| 252 |                                                         | IJavaElementDelta.F_REMOVED_FROM_CLASSPATH)) != 0) { | 
| 253 |                                                 projectClasspathChanged(proj); | 
| 254 |                                         } else if ((flags & IJavaElementDelta.F_CHILDREN) != 0) { | 
| 255 |                                                 processJavaElementDeltas(delta.getAffectedChildren(), proj); | 
| 256 |                                         } | 
| 257 |                                         break; | 
| 258 |                                 } | 
| 259 |                                 case IJavaElement.COMPILATION_UNIT : { | 
| 260 |                                         int flags = delta.getFlags(); | 
| 261 |                                         switch (delta.getKind()) { | 
| 262 |                                                 case IJavaElementDelta.CHANGED: { | 
| 263 |                                                         if ((flags & (IJavaElementDelta.F_CONTENT |  | 
| 264 |                                                                                         IJavaElementDelta.F_FINE_GRAINED |  | 
| 265 |                                                                                         IJavaElementDelta.F_PRIMARY_RESOURCE)) != 0){ | 
| 266 |                                                                 if (proj != null) { | 
| 267 |                                                                         projectChanged(proj); | 
| 268 |                                                                         flushElementCache(delta); | 
| 269 |                                                                         continue; | 
| 270 |                                                                 } | 
| 271 |                                                         } | 
| 272 |                                                         break; | 
| 273 |                                                 } | 
| 274 |                                                 case IJavaElementDelta.ADDED : | 
| 275 |                                                 case IJavaElementDelta.REMOVED : { | 
| 276 |                                                         if (proj != null) { | 
| 277 |                                                                 projectChanged(proj); | 
| 278 |                                                                 flushElementCache(delta); | 
| 279 |                                                                 continue; | 
| 280 |                                                         } | 
| 281 |                                                 } | 
| 282 |                                         } | 
| 283 |                                 } | 
| 284 |                         } | 
| 285 |                 } | 
| 286 |         } | 
| 287 |   | 
| 288 |         /** | 
| 289 |          * Flushes the changed element from the model cache | 
| 290 |          * @param delta | 
| 291 |          */ | 
| 292 |         void flushElementCache(IJavaElementDelta delta) { | 
| 293 |                 IJavaElement element = delta.getElement(); | 
| 294 |                 if((delta.getFlags() & IJavaElementDelta.F_MOVED_TO) > 0) { | 
| 295 |                         element = delta.getMovedToElement(); | 
| 296 |                 } | 
| 297 |                 if((delta.getFlags() & IJavaElementDelta.F_MOVED_FROM) > 0) { | 
| 298 |                         element = delta.getMovedFromElement(); | 
| 299 |                 } | 
| 300 |                 switch(element.getElementType()) { | 
| 301 |                         case IJavaElement.COMPILATION_UNIT: { | 
| 302 |                                 ICompilationUnit unit = (ICompilationUnit) element; | 
| 303 |                                 IType type = unit.findPrimaryType(); | 
| 304 |                                 if(type != null) { | 
| 305 |                                         ApiModelCache.getCache().removeElementInfo( | 
| 306 |                                                         ApiBaselineManager.WORKSPACE_API_BASELINE_ID, | 
| 307 |                                                         element.getJavaProject().getElementName(),  | 
| 308 |                                                         type.getFullyQualifiedName(),  | 
| 309 |                                                         IApiElement.TYPE); | 
| 310 |                                 } | 
| 311 |                                 break; | 
| 312 |                         } | 
| 313 |                         case IJavaElement.JAVA_PROJECT: { | 
| 314 |                                 ApiModelCache.getCache().removeElementInfo( | 
| 315 |                                                 ApiBaselineManager.WORKSPACE_API_BASELINE_ID, | 
| 316 |                                                 element.getElementName(),  | 
| 317 |                                                 null,  | 
| 318 |                                                 IApiElement.COMPONENT); | 
| 319 |                                 break; | 
| 320 |                         } | 
| 321 |                 } | 
| 322 |         } | 
| 323 |          | 
| 324 |         /* (non-Javadoc) | 
| 325 |          * @see org.eclipse.core.resources.ISaveParticipant#doneSaving(org.eclipse.core.resources.ISaveContext) | 
| 326 |          */ | 
| 327 |         public void doneSaving(ISaveContext context) { | 
| 328 |         } | 
| 329 |   | 
| 330 |         /* (non-Javadoc) | 
| 331 |          * @see org.eclipse.core.resources.ISaveParticipant#prepareToSave(org.eclipse.core.resources.ISaveContext) | 
| 332 |          */ | 
| 333 |         public void prepareToSave(ISaveContext context) throws CoreException { | 
| 334 |         } | 
| 335 |   | 
| 336 |         /* (non-Javadoc) | 
| 337 |          * @see org.eclipse.core.resources.ISaveParticipant#rollback(org.eclipse.core.resources.ISaveContext) | 
| 338 |          */ | 
| 339 |         public void rollback(ISaveContext context) { | 
| 340 |         } | 
| 341 |   | 
| 342 |         /* (non-Javadoc) | 
| 343 |          * @see org.eclipse.core.resources.ISaveParticipant#saving(org.eclipse.core.resources.ISaveContext) | 
| 344 |          */ | 
| 345 |         public synchronized void saving(ISaveContext context) throws CoreException { | 
| 346 |                 Iterator entries = fDescriptions.entrySet().iterator(); | 
| 347 |                 while (entries.hasNext()) { | 
| 348 |                         Entry entry = (Entry) entries.next(); | 
| 349 |                         IJavaProject project = (IJavaProject) entry.getKey(); | 
| 350 |                         ProjectApiDescription desc = (ProjectApiDescription) entry.getValue(); | 
| 351 |                         if (desc.isModified()) { | 
| 352 |                                 File dir = API_DESCRIPTIONS_CONTAINER_PATH.append(project.getElementName()).toFile(); | 
| 353 |                                 dir.mkdirs(); | 
| 354 |                                 String xml = desc.getXML(); | 
| 355 |                                 try { | 
| 356 |                                         Util.saveFile(new File(dir,  IApiCoreConstants.API_DESCRIPTION_XML_NAME), xml); | 
| 357 |                                 } catch (IOException e) { | 
| 358 |                                         abort(MessageFormat.format(ScannerMessages.ApiDescriptionManager_0, new String[]{project.getElementName()}), e); | 
| 359 |                                 } | 
| 360 |                         } | 
| 361 |                 } | 
| 362 |         }         | 
| 363 |          | 
| 364 |         /** | 
| 365 |          * Restores the API description from its saved file, if any and returns | 
| 366 |          * true if successful. | 
| 367 |          *  | 
| 368 |          * @param project | 
| 369 |          * @param description | 
| 370 |          * @return whether the restore succeeded | 
| 371 |          * @throws CoreException  | 
| 372 |          */ | 
| 373 |         private boolean restoreDescription(IJavaProject project, ProjectApiDescription description) throws CoreException { | 
| 374 |                 File file = API_DESCRIPTIONS_CONTAINER_PATH.append(project.getElementName()). | 
| 375 |                         append(IApiCoreConstants.API_DESCRIPTION_XML_NAME).toFile(); | 
| 376 |                 if (file.exists()) { | 
| 377 |                         BufferedInputStream stream = null; | 
| 378 |                         try { | 
| 379 |                                 stream = new BufferedInputStream(new FileInputStream(file)); | 
| 380 |                                 String xml = new String(Util.getInputStreamAsCharArray(stream, -1, IApiCoreConstants.UTF_8)); | 
| 381 |                                 Element root = Util.parseDocument(xml); | 
| 382 |                                 if (!root.getNodeName().equals(IApiXmlConstants.ELEMENT_COMPONENT)) { | 
| 383 |                                         abort(ScannerMessages.ComponentXMLScanner_0, null);  | 
| 384 |                                 } | 
| 385 |                                 long timestamp = getLong(root, IApiXmlConstants.ATTR_MODIFICATION_STAMP); | 
| 386 |                                 String version = root.getAttribute(IApiXmlConstants.ATTR_VERSION); | 
| 387 |                                 description.setEmbeddedVersion(version); | 
| 388 |                                 if (IApiXmlConstants.API_DESCRIPTION_CURRENT_VERSION.equals(version)) { | 
| 389 |                                         description.fPackageTimeStamp = timestamp; | 
| 390 |                                         description.fManifestFile = project.getProject().getFile(JarFile.MANIFEST_NAME); | 
| 391 |                                         restoreChildren(description, root, null, description.fPackageMap); | 
| 392 |                                         return true; | 
| 393 |                                 } | 
| 394 |                         } catch (IOException e) { | 
| 395 |                                 abort(MessageFormat.format(ScannerMessages.ApiDescriptionManager_1, | 
| 396 |                                                 new String[]{project.getElementName()}), e); | 
| 397 |                         } finally { | 
| 398 |                                 if (stream != null) { | 
| 399 |                                         try { | 
| 400 |                                                 stream.close(); | 
| 401 |                                         } catch (IOException e) { | 
| 402 |                                                 // ignore | 
| 403 |                                         } | 
| 404 |                                 } | 
| 405 |                         } | 
| 406 |                 } | 
| 407 |                 return false; | 
| 408 |         } | 
| 409 |          | 
| 410 |         private void restoreChildren(ProjectApiDescription apiDesc, Element element, ManifestNode parentNode, Map childrenMap) throws CoreException { | 
| 411 |                 NodeList children = element.getChildNodes(); | 
| 412 |                 for (int i = 0; i < children.getLength(); i++) { | 
| 413 |                         Node child = children.item(i); | 
| 414 |                         if (child.getNodeType() == Node.ELEMENT_NODE) { | 
| 415 |                                 restoreNode(apiDesc, (Element) child, parentNode, childrenMap); | 
| 416 |                         } | 
| 417 |                 } | 
| 418 |         } | 
| 419 |          | 
| 420 |         private void restoreNode(ProjectApiDescription apiDesc, Element element, ManifestNode parentNode, Map childrenMap) throws CoreException { | 
| 421 |                 ManifestNode node = null; | 
| 422 |                 IElementDescriptor elementDesc = null; | 
| 423 |                 if (element.getTagName().equals(IApiXmlConstants.ELEMENT_PACKAGE)) { | 
| 424 |                         int vis = getInt(element, IApiXmlConstants.ATTR_VISIBILITY); | 
| 425 |                         int res = getInt(element, IApiXmlConstants.ATTR_RESTRICTIONS); | 
| 426 |                         // collect fragments | 
| 427 |                         List fragments = new ArrayList(); | 
| 428 |                         NodeList childNodes = element.getChildNodes(); | 
| 429 |                         String pkgName = null; | 
| 430 |                         for (int i = 0; i < childNodes.getLength(); i++) { | 
| 431 |                                 Node child = childNodes.item(i); | 
| 432 |                                 if (child.getNodeType() == Node.ELEMENT_NODE) { | 
| 433 |                                         if (((Element)child).getTagName().equals(IApiXmlConstants.ELEMENT_PACKAGE_FRAGMENT)) { | 
| 434 |                                                 Element fragment = (Element) child; | 
| 435 |                                                 String handle = fragment.getAttribute(IApiXmlConstants.ATTR_HANDLE); | 
| 436 |                                                 IJavaElement je = JavaCore.create(handle); | 
| 437 |                                                 if (je.getElementType() != IJavaElement.PACKAGE_FRAGMENT) { | 
| 438 |                                                         abort(ScannerMessages.ApiDescriptionManager_2 + handle, null); | 
| 439 |                                                 } | 
| 440 |                                                 pkgName = je.getElementName(); | 
| 441 |                                                 fragments.add(je); | 
| 442 |                                         } | 
| 443 |                                 } | 
| 444 |                         } | 
| 445 |                         if (!fragments.isEmpty()) { | 
| 446 |                                 elementDesc = Factory.packageDescriptor(pkgName); | 
| 447 |                                 node = apiDesc.newPackageNode((IPackageFragment[])fragments.toArray(new IPackageFragment[fragments.size()]), parentNode, elementDesc, vis, res); | 
| 448 |                         } else { | 
| 449 |                                 abort(ScannerMessages.ApiDescriptionManager_2, null); | 
| 450 |                         } | 
| 451 |                 } else if (element.getTagName().equals(IApiXmlConstants.ELEMENT_PACKAGE_FRAGMENT)) { | 
| 452 |                         return; // nothing to do | 
| 453 |                 } else if (element.getTagName().equals(IApiXmlConstants.ELEMENT_TYPE)) { | 
| 454 |                         String handle = element.getAttribute(IApiXmlConstants.ATTR_HANDLE); | 
| 455 |                         int vis = getInt(element, IApiXmlConstants.ATTR_VISIBILITY); | 
| 456 |                         int res = getInt(element, IApiXmlConstants.ATTR_RESTRICTIONS); | 
| 457 |                         IJavaElement je = JavaCore.create(handle); | 
| 458 |                         if (je.getElementType() != IJavaElement.TYPE) { | 
| 459 |                                 abort(ScannerMessages.ApiDescriptionManager_3 + handle, null); | 
| 460 |                         } | 
| 461 |                         IType type = (IType) je; | 
| 462 |                         elementDesc = Factory.typeDescriptor(type.getFullyQualifiedName('$')); | 
| 463 |                         TypeNode tn = apiDesc.newTypeNode(type, parentNode, elementDesc, vis, res); | 
| 464 |                         node = tn; | 
| 465 |                         tn.fTimeStamp = getLong(element, IApiXmlConstants.ATTR_MODIFICATION_STAMP); | 
| 466 |                 } else if (element.getTagName().equals(IApiXmlConstants.ELEMENT_FIELD)) { | 
| 467 |                         if(parentNode.element instanceof IReferenceTypeDescriptor) { | 
| 468 |                                 IReferenceTypeDescriptor type = (IReferenceTypeDescriptor) parentNode.element; | 
| 469 |                                 int vis = getInt(element, IApiXmlConstants.ATTR_VISIBILITY); | 
| 470 |                                 int res = getInt(element, IApiXmlConstants.ATTR_RESTRICTIONS); | 
| 471 |                                 String name = element.getAttribute(IApiXmlConstants.ATTR_NAME); | 
| 472 |                                 elementDesc = type.getField(name); | 
| 473 |                                 node = apiDesc.newNode(parentNode, elementDesc, vis, res); | 
| 474 |                         } | 
| 475 |                 } else if (element.getTagName().equals(IApiXmlConstants.ELEMENT_METHOD)) { | 
| 476 |                         if(parentNode.element instanceof IReferenceTypeDescriptor) { | 
| 477 |                                 IReferenceTypeDescriptor type = (IReferenceTypeDescriptor) parentNode.element; | 
| 478 |                                 int vis = getInt(element, IApiXmlConstants.ATTR_VISIBILITY); | 
| 479 |                                 int res = getInt(element, IApiXmlConstants.ATTR_RESTRICTIONS); | 
| 480 |                                 String name = element.getAttribute(IApiXmlConstants.ATTR_NAME); | 
| 481 |                                 String sig = element.getAttribute(IApiXmlConstants.ATTR_SIGNATURE); | 
| 482 |                                 if (sig.indexOf('.') != -1) { | 
| 483 |                                         // old files might use '.' instead of '/' | 
| 484 |                                         sig = sig.replace('.', '/'); | 
| 485 |                                 } | 
| 486 |                                 elementDesc = type.getMethod(name,sig); | 
| 487 |                                 node = apiDesc.newNode(parentNode, elementDesc, vis, res); | 
| 488 |                         } | 
| 489 |                 } | 
| 490 |                 if (node == null) { | 
| 491 |                         abort(ScannerMessages.ApiDescriptionManager_4, null); | 
| 492 |                 } | 
| 493 |                 childrenMap.put(elementDesc, node); | 
| 494 |                 restoreChildren(apiDesc, element, node, node.children); | 
| 495 |         } | 
| 496 |          | 
| 497 |         /** | 
| 498 |          * Returns an integer attribute. | 
| 499 |          *  | 
| 500 |          * @param element element with the integer | 
| 501 |          * @param attr attribute name | 
| 502 |          * @return attribute value as an integer | 
| 503 |          */ | 
| 504 |         private int getInt(Element element, String attr) { | 
| 505 |                 String attribute = element.getAttribute(attr); | 
| 506 |                 try { | 
| 507 |                         return Integer.parseInt(attribute); | 
| 508 |                 }  | 
| 509 |                 catch (NumberFormatException e) {} | 
| 510 |                 return 0; | 
| 511 |         } | 
| 512 |          | 
| 513 |         /** | 
| 514 |          * Returns a long attribute. | 
| 515 |          *  | 
| 516 |          * @param element element with the long | 
| 517 |          * @param attr attribute name | 
| 518 |          * @return attribute value as an long | 
| 519 |          */ | 
| 520 |         private long getLong(Element element, String attr) { | 
| 521 |                 String attribute = element.getAttribute(attr); | 
| 522 |                 if (attribute != null) { | 
| 523 |                         try { | 
| 524 |                                 return Long.parseLong(attribute); | 
| 525 |                         }  | 
| 526 |                         catch (NumberFormatException e) {} | 
| 527 |                 } | 
| 528 |                 return 0L; | 
| 529 |         }         | 
| 530 |          | 
| 531 |         /** | 
| 532 |          * Throws an exception with the given message and underlying exception. | 
| 533 |          *  | 
| 534 |          * @param message error message | 
| 535 |          * @param exception underlying exception, or <code>null</code> | 
| 536 |          * @throws CoreException | 
| 537 |          */ | 
| 538 |         private static void abort(String message, Throwable exception) throws CoreException { | 
| 539 |                 IStatus status = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, message, exception); | 
| 540 |                 throw new CoreException(status); | 
| 541 |         } | 
| 542 | } |