| 1 | /******************************************************************************* | 
| 2 |  * Copyright (c) 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.search; | 
| 12 |   | 
| 13 | import java.io.BufferedWriter; | 
| 14 | import java.io.File; | 
| 15 | import java.io.FileInputStream; | 
| 16 | import java.io.FileNotFoundException; | 
| 17 | import java.io.FileWriter; | 
| 18 | import java.io.IOException; | 
| 19 | import java.util.HashMap; | 
| 20 | import java.util.HashSet; | 
| 21 | import java.util.Iterator; | 
| 22 | import java.util.Map; | 
| 23 |   | 
| 24 | import javax.xml.parsers.DocumentBuilder; | 
| 25 | import javax.xml.parsers.DocumentBuilderFactory; | 
| 26 | import javax.xml.parsers.FactoryConfigurationError; | 
| 27 | import javax.xml.parsers.ParserConfigurationException; | 
| 28 |   | 
| 29 | import org.eclipse.core.runtime.CoreException; | 
| 30 | import org.eclipse.pde.api.tools.internal.IApiXmlConstants; | 
| 31 | import org.eclipse.pde.api.tools.internal.builder.Reference; | 
| 32 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; | 
| 33 | import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers; | 
| 34 | import org.eclipse.pde.api.tools.internal.provisional.builder.IReference; | 
| 35 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IComponentDescriptor; | 
| 36 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; | 
| 37 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IFieldDescriptor; | 
| 38 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMemberDescriptor; | 
| 39 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor; | 
| 40 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor; | 
| 41 | import org.eclipse.pde.api.tools.internal.util.Signatures; | 
| 42 | import org.eclipse.pde.api.tools.internal.util.Util; | 
| 43 | import org.w3c.dom.Document; | 
| 44 | import org.w3c.dom.Element; | 
| 45 | import org.w3c.dom.NodeList; | 
| 46 | import org.xml.sax.SAXException; | 
| 47 | import org.xml.sax.helpers.DefaultHandler; | 
| 48 |   | 
| 49 | /** | 
| 50 |  * Writes reference descriptions to XML files. | 
| 51 |  *  | 
| 52 |  * @since 1.0.1 | 
| 53 |  */ | 
| 54 | public class XmlReferenceDescriptorWriter { | 
| 55 |   | 
| 56 |         /** | 
| 57 |          * file names for the output reference files | 
| 58 |          */ | 
| 59 |         public static final String TYPE_REFERENCES = "type_references"; //$NON-NLS-1$ | 
| 60 |         public static final String METHOD_REFERENCES = "method_references"; //$NON-NLS-1$ | 
| 61 |         public static final String FIELD_REFERENCES = "field_references"; //$NON-NLS-1$ | 
| 62 |          | 
| 63 |         private String fLocation = null; | 
| 64 |         private HashMap fReferenceMap = null; | 
| 65 |         private DocumentBuilder parser = null; | 
| 66 |          | 
| 67 |         /** | 
| 68 |          * Alternate API component where references were unresolved, or <code>null</code> | 
| 69 |          * if not to be reported. | 
| 70 |          */ | 
| 71 |         private IComponentDescriptor alternate; | 
| 72 |          | 
| 73 |         /** | 
| 74 |          * Constructor | 
| 75 |          *  | 
| 76 |          * @param location the absolute path in the local file system to the folder to write the reports to  | 
| 77 |          * @param debug if debugging infos should be written out to the console | 
| 78 |          */ | 
| 79 |         public XmlReferenceDescriptorWriter(String location) { | 
| 80 |                 fLocation = location; | 
| 81 |                 try { | 
| 82 |                         parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); | 
| 83 |                         parser.setErrorHandler(new DefaultHandler()); | 
| 84 |                 } | 
| 85 |                 catch(FactoryConfigurationError fce) { | 
| 86 |                         ApiPlugin.log(fce); | 
| 87 |                 }  | 
| 88 |                 catch (ParserConfigurationException pce) { | 
| 89 |                         ApiPlugin.log(pce); | 
| 90 |                 } | 
| 91 |         } | 
| 92 |   | 
| 93 |         /** | 
| 94 |          * Writes the given references to XML files. | 
| 95 |          *  | 
| 96 |          * @param references | 
| 97 |          */ | 
| 98 |         public void writeReferences(IReferenceDescriptor[] references) { | 
| 99 |                 if(fLocation != null) { | 
| 100 |                         try { | 
| 101 |                                 File parent = new File(fLocation); | 
| 102 |                                 if(!parent.exists()) { | 
| 103 |                                         parent.mkdirs(); | 
| 104 |                                 } | 
| 105 |                                 collateResults(references); | 
| 106 |                                 writeXML(parent); | 
| 107 |                         }  | 
| 108 |                         catch (Exception e) { | 
| 109 |                                 ApiPlugin.log(e); | 
| 110 |                         } | 
| 111 |                         finally { | 
| 112 |                                 if(fReferenceMap != null) { | 
| 113 |                                         fReferenceMap.clear(); | 
| 114 |                                         fReferenceMap = null; | 
| 115 |                                 } | 
| 116 |                         } | 
| 117 |                 } | 
| 118 |         } | 
| 119 |          | 
| 120 |         /** | 
| 121 |          * Collates the results into like reference kinds | 
| 122 |          * @param references | 
| 123 |          */ | 
| 124 |         private void collateResults(IReferenceDescriptor[] references) throws CoreException { | 
| 125 |                 if(fReferenceMap == null) { | 
| 126 |                         fReferenceMap = new HashMap(); | 
| 127 |                 } | 
| 128 |                 Integer type = null; | 
| 129 |                 Integer visibility = null; | 
| 130 |                 String id = null; | 
| 131 |                 String tname = null; | 
| 132 |                 HashMap rmap = null; | 
| 133 |                 HashMap mmap = null; | 
| 134 |                 HashMap vmap = null; | 
| 135 |                 HashMap tmap = null; | 
| 136 |                 HashSet reflist = null; | 
| 137 |                 IComponentDescriptor rcomponent = null; | 
| 138 |                 IComponentDescriptor mcomponent = null; | 
| 139 |                 for (int i = 0; i < references.length; i++) { | 
| 140 |                         rcomponent = references[i].getReferencedComponent();  | 
| 141 |                         id = getId(rcomponent); | 
| 142 |                         rmap = (HashMap) fReferenceMap.get(id); | 
| 143 |                         if(rmap == null) { | 
| 144 |                                 rmap = new HashMap(); | 
| 145 |                                 fReferenceMap.put(id, rmap); | 
| 146 |                         } | 
| 147 |                         mcomponent = references[i].getComponent();  | 
| 148 |                         id = getId(mcomponent); | 
| 149 |                         mmap = (HashMap) rmap.get(id); | 
| 150 |                         if(mmap == null) { | 
| 151 |                                 mmap = new HashMap(); | 
| 152 |                                 rmap.put(id, mmap); | 
| 153 |                         } | 
| 154 |                         visibility = new Integer(references[i].getVisibility()); | 
| 155 | //                        fDescription = rcomponent.getApiDescription(); | 
| 156 | //                        annot = fDescription.resolveAnnotations(references[i].getResolvedReference().getHandle()); | 
| 157 | //                        if(annot != null) { | 
| 158 | //                                visibility = new Integer(annot.getVisibility()); | 
| 159 | //                                if(annot.getVisibility() == VisibilityModifiers.PRIVATE) { | 
| 160 | //                                        IApiComponent host = mcomponent.getHost(); | 
| 161 | //                                        if(host != null && host.getId().equals(rcomponent.getId())) { | 
| 162 | //                                                visibility = new Integer(UseReportConverter.FRAGMENT_PERMISSIBLE); | 
| 163 | //                                        } | 
| 164 | //                                        else { | 
| 165 | //                                                IApiAccess access = fDescription.resolveAccessLevel( | 
| 166 | //                                                                mcomponent.getHandle(),  | 
| 167 | //                                                                getPackageDescriptor(references[i].getResolvedReference())); | 
| 168 | //                                                if(access != null && access.getAccessLevel() == IApiAccess.FRIEND) { | 
| 169 | //                                                        visibility = new Integer(VisibilityModifiers.PRIVATE_PERMISSIBLE); | 
| 170 | //                                                } | 
| 171 | //                                        } | 
| 172 | //                                } | 
| 173 | //                        } | 
| 174 | //                        else { | 
| 175 | //                                //overflow for those references that cannot be resolved | 
| 176 | //                                visibility = new Integer(VisibilityModifiers.ALL_VISIBILITIES); | 
| 177 | //                        } | 
| 178 |                         vmap = (HashMap) mmap.get(visibility); | 
| 179 |                         if(vmap == null) { | 
| 180 |                                 vmap = new HashMap(); | 
| 181 |                                 mmap.put(visibility, vmap); | 
| 182 |                         } | 
| 183 |                         type = new Integer(references[i].getReferenceType()); | 
| 184 |                         tmap = (HashMap) vmap.get(type); | 
| 185 |                         if(tmap == null) { | 
| 186 |                                 tmap = new HashMap(); | 
| 187 |                                 vmap.put(type, tmap); | 
| 188 |                         } | 
| 189 |                         tname = getText(references[i].getReferencedMember()); | 
| 190 |                         reflist = (HashSet) tmap.get(tname); | 
| 191 |                         if(reflist == null) { | 
| 192 |                                 reflist = new HashSet(); | 
| 193 |                                 tmap.put(tname, reflist); | 
| 194 |                         } | 
| 195 |                         reflist.add(references[i]); | 
| 196 |                 } | 
| 197 |         } | 
| 198 |          | 
| 199 |         /** | 
| 200 |          * Resolves the id to use for the component in the mapping | 
| 201 |          * @param component | 
| 202 |          * @return the id to use for the component in the mapping, includes the version information as well | 
| 203 |          * @throws CoreException | 
| 204 |          */ | 
| 205 |         String getId(IComponentDescriptor component) throws CoreException { | 
| 206 |                 StringBuffer buffer = new StringBuffer(); | 
| 207 |                 buffer.append(component.getId()).append(" ").append('(').append(component.getVersion()).append(')'); //$NON-NLS-1$ | 
| 208 |                 return buffer.toString(); | 
| 209 |         } | 
| 210 |          | 
| 211 |         /** | 
| 212 |          * Returns a formatted version of the references xml file name for use during conversion via the default | 
| 213 |          * XSLT file | 
| 214 |          * @param groupname | 
| 215 |          * @return a formatted version of the references file name | 
| 216 |          */ | 
| 217 |         private String getFormattedTypeName(String groupname) { | 
| 218 |                 if(TYPE_REFERENCES.equals(groupname)) { | 
| 219 |                         return "Types"; //$NON-NLS-1$ | 
| 220 |                 } | 
| 221 |                 if(METHOD_REFERENCES.equals(groupname)) { | 
| 222 |                         return "Methods"; //$NON-NLS-1$ | 
| 223 |                 } | 
| 224 |                 if(FIELD_REFERENCES.equals(groupname)) { | 
| 225 |                         return "Fields"; //$NON-NLS-1$ | 
| 226 |                 } | 
| 227 |                 return "unknown references"; //$NON-NLS-1$ | 
| 228 |         } | 
| 229 |          | 
| 230 |         /** | 
| 231 |          * Returns the name for the file of references base on the given type | 
| 232 |          * @param type | 
| 233 |          * @return | 
| 234 |          */ | 
| 235 |         private String getRefTypeName(int type) { | 
| 236 |                 switch(type) { | 
| 237 |                         case IReference.T_TYPE_REFERENCE: return TYPE_REFERENCES; | 
| 238 |                         case IReference.T_METHOD_REFERENCE: return METHOD_REFERENCES; | 
| 239 |                         case IReference.T_FIELD_REFERENCE: return FIELD_REFERENCES; | 
| 240 |                 } | 
| 241 |                 return "unknown_reference_kinds"; //$NON-NLS-1$ | 
| 242 |         } | 
| 243 |          | 
| 244 |         /** | 
| 245 |          * Writes out the XML for the given api element using the collated {@link IReference}s | 
| 246 |          * @param parent | 
| 247 |          * @throws CoreException | 
| 248 |          * @throws FileNotFoundException | 
| 249 |          * @throws IOException | 
| 250 |          */ | 
| 251 |         private void writeXML(File parent) throws CoreException, FileNotFoundException, IOException { | 
| 252 |                 HashMap vismap = null; | 
| 253 |                 HashMap typemap = null; | 
| 254 |                 HashMap rmap = null; | 
| 255 |                 HashMap mmap = null; | 
| 256 |                 Integer type = null; | 
| 257 |                 Integer vis = null; | 
| 258 |                 String id = null; | 
| 259 |                 String referee = null; | 
| 260 |                 File root = null; | 
| 261 |                 File location = null; | 
| 262 |                 File base = null; | 
| 263 |                 for(Iterator iter = fReferenceMap.entrySet().iterator(); iter.hasNext();) { | 
| 264 |                         Map.Entry entry = (Map.Entry) iter.next(); | 
| 265 |                         id = (String) entry.getKey(); | 
| 266 |                         referee = id; | 
| 267 |                         base = new File(parent, id); | 
| 268 |                         if(!base.exists()) { | 
| 269 |                                 base.mkdir(); | 
| 270 |                         } | 
| 271 |                         rmap = (HashMap) entry.getValue(); | 
| 272 |                         for(Iterator iter2 = rmap.entrySet().iterator(); iter2.hasNext();) { | 
| 273 |                                 Map.Entry entry2 = (Map.Entry) iter2.next(); | 
| 274 |                                 id = (String) entry2.getKey(); | 
| 275 |                                 root = new File(base, id); | 
| 276 |                                 if(!root.exists()) { | 
| 277 |                                         root.mkdir(); | 
| 278 |                                 } | 
| 279 |                                 mmap = (HashMap) entry2.getValue(); | 
| 280 |                                 for(Iterator iter4 = mmap.entrySet().iterator(); iter4.hasNext();) { | 
| 281 |                                         Map.Entry entry3 = (Map.Entry) iter4.next(); | 
| 282 |                                         vis = (Integer) entry3.getKey(); | 
| 283 |                                         location = new File(root, VisibilityModifiers.getVisibilityName(vis.intValue())); | 
| 284 |                                         if(!location.exists()) { | 
| 285 |                                                 location.mkdir(); | 
| 286 |                                         } | 
| 287 |                                         vismap = (HashMap) entry3.getValue(); | 
| 288 |                                         for(Iterator iter3 = vismap.entrySet().iterator(); iter3.hasNext();) { | 
| 289 |                                                 Map.Entry entry4 = (Map.Entry) iter3.next(); | 
| 290 |                                                 type = (Integer) entry4.getKey(); | 
| 291 |                                                 typemap = (HashMap) entry4.getValue(); | 
| 292 |                                                 writeGroup(id, referee, location, getRefTypeName(type.intValue()), typemap, vis.intValue()); | 
| 293 |                                         } | 
| 294 |                                 } | 
| 295 |                         } | 
| 296 |                 } | 
| 297 |         } | 
| 298 |          | 
| 299 |         /** | 
| 300 |          * Writes out a group of references under the newly created element with the given name | 
| 301 |          * @param origin the name of the bundle that has the references in it | 
| 302 |          * @param referee the name of the bundle that is referenced | 
| 303 |          * @param parent | 
| 304 |          * @param name | 
| 305 |          * @param map | 
| 306 |          * @param visibility | 
| 307 |          */ | 
| 308 |         private void writeGroup(String origin, String referee, File parent, String name, HashMap map, int visibility) throws CoreException, FileNotFoundException, IOException { | 
| 309 |                 if(parent.exists()) { | 
| 310 |                         BufferedWriter writer = null; | 
| 311 |                         try { | 
| 312 |                                 Document doc = null; | 
| 313 |                                 Element root = null; | 
| 314 |                                 int count = 0; | 
| 315 |                                 File out = new File(parent, name+".xml"); //$NON-NLS-1$ | 
| 316 |                                 if(out.exists()) { | 
| 317 |                                         try { | 
| 318 |                                                 FileInputStream inputStream = null; | 
| 319 |                                                 try { | 
| 320 |                                                         inputStream = new FileInputStream(out); | 
| 321 |                                                         doc = this.parser.parse(inputStream); | 
| 322 |                                                 } catch (IOException e) { | 
| 323 |                                                         e.printStackTrace(); | 
| 324 |                                                 } finally { | 
| 325 |                                                         if (inputStream != null) { | 
| 326 |                                                                 inputStream.close(); | 
| 327 |                                                         } | 
| 328 |                                                 } | 
| 329 |                                                 if (doc == null) { | 
| 330 |                                                         return; | 
| 331 |                                                 } | 
| 332 |                                                 root = doc.getDocumentElement(); | 
| 333 |                                                 String value = root.getAttribute(IApiXmlConstants.ATTR_REFERENCE_COUNT); | 
| 334 |                                                 count = Integer.parseInt(value); | 
| 335 |                                         } | 
| 336 |                                         catch(SAXException se) { | 
| 337 |                                                 se.printStackTrace(); | 
| 338 |                                         } | 
| 339 |                                 } | 
| 340 |                                 else { | 
| 341 |                                         doc = Util.newDocument(); | 
| 342 |                                         root = doc.createElement(IApiXmlConstants.REFERENCES); | 
| 343 |                                         doc.appendChild(root); | 
| 344 |                                         root.setAttribute(IApiXmlConstants.ATTR_REFERENCE_VISIBILITY, Integer.toString(visibility)); | 
| 345 |                                         root.setAttribute(IApiXmlConstants.ATTR_ORIGIN, origin); | 
| 346 |                                         root.setAttribute(IApiXmlConstants.ATTR_REFEREE, referee); | 
| 347 |                                         root.setAttribute(IApiXmlConstants.ATTR_NAME, getFormattedTypeName(name)); | 
| 348 |                                         if (alternate != null) { | 
| 349 |                                                 root.setAttribute(IApiXmlConstants.ATTR_ALTERNATE, getId(alternate)); | 
| 350 |                                         } | 
| 351 |                                 } | 
| 352 |                                 if(doc == null) { | 
| 353 |                                         return; | 
| 354 |                                 } | 
| 355 |                                 String tname = null; | 
| 356 |                                 HashSet refs = null; | 
| 357 |                                 Element telement = null; | 
| 358 |                                 for(Iterator iter = map.entrySet().iterator(); iter.hasNext();) { | 
| 359 |                                         Map.Entry entry = (Map.Entry) iter.next(); | 
| 360 |                                         tname = (String) entry.getKey(); | 
| 361 |                                         telement = findTypeElement(root, tname); | 
| 362 |                                         if(telement == null) { | 
| 363 |                                                 telement = doc.createElement(IApiXmlConstants.ELEMENT_TARGET); | 
| 364 |                                                 telement.setAttribute(IApiXmlConstants.ATTR_NAME, tname); | 
| 365 |                                                 root.appendChild(telement); | 
| 366 |                                         } | 
| 367 |                                         refs = (HashSet) entry.getValue(); | 
| 368 |                                         if(refs != null) { | 
| 369 |                                                 for(Iterator iter2 = refs.iterator(); iter2.hasNext();) { | 
| 370 |                                                         count++; | 
| 371 |                                                         IReferenceDescriptor ref = (IReferenceDescriptor) iter2.next(); | 
| 372 |                                                         writeReference(doc, telement, ref); | 
| 373 |                                                         if (!iter2.hasNext()) { | 
| 374 |                                                                 // set qualified referenced attributes | 
| 375 |                                                                 IMemberDescriptor resolved  = ref.getReferencedMember(); | 
| 376 |                                                                 if (resolved != null) { | 
| 377 |                                                                         addMemberDetails(telement, resolved); | 
| 378 |                                                                 } | 
| 379 |                                                         } | 
| 380 |                                                 } | 
| 381 |                                         } | 
| 382 |                                 } | 
| 383 |                                 root.setAttribute(IApiXmlConstants.ATTR_REFERENCE_COUNT, Integer.toString(count)); | 
| 384 |                                 writer = new BufferedWriter(new FileWriter(out)); | 
| 385 |                                 writer.write(Util.serializeDocument(doc)); | 
| 386 |                                 writer.flush(); | 
| 387 |                         } | 
| 388 |                         finally { | 
| 389 |                                 if (writer != null) { | 
| 390 |                                         writer.close(); | 
| 391 |                                 } | 
| 392 |                         } | 
| 393 |                 } | 
| 394 |         } | 
| 395 |          | 
| 396 |         /** | 
| 397 |          * Add member descriptor details to the given element. | 
| 398 |          *  | 
| 399 |          * @param element XML element | 
| 400 |          * @param member member to add details for | 
| 401 |          */ | 
| 402 |         private void addMemberDetails(Element element, IMemberDescriptor member) { | 
| 403 |                 switch (member.getElementType()) { | 
| 404 |                 case IElementDescriptor.TYPE: | 
| 405 |                         element.setAttribute(IApiXmlConstants.ATTR_TYPE, ((IReferenceTypeDescriptor)member).getQualifiedName()); | 
| 406 |                         break; | 
| 407 |                 case IElementDescriptor.FIELD: | 
| 408 |                         IReferenceTypeDescriptor encl = member.getEnclosingType(); | 
| 409 |                         element.setAttribute(IApiXmlConstants.ATTR_TYPE, encl.getQualifiedName()); | 
| 410 |                         element.setAttribute(IApiXmlConstants.ATTR_MEMBER_NAME, member.getName()); | 
| 411 |                         break; | 
| 412 |                 case IElementDescriptor.METHOD: | 
| 413 |                         encl = member.getEnclosingType(); | 
| 414 |                         element.setAttribute(IApiXmlConstants.ATTR_TYPE, encl.getQualifiedName()); | 
| 415 |                         element.setAttribute(IApiXmlConstants.ATTR_MEMBER_NAME, member.getName()); | 
| 416 |                         element.setAttribute(IApiXmlConstants.ATTR_SIGNATURE, ((IMethodDescriptor)member).getSignature()); | 
| 417 |                         break; | 
| 418 |                 } | 
| 419 |         } | 
| 420 |          | 
| 421 |         /** | 
| 422 |          * gets the root kind element | 
| 423 |          * @param root | 
| 424 |          * @param kind | 
| 425 |          * @return | 
| 426 |          */ | 
| 427 |         private Element findTypeElement(Element root, String tname) { | 
| 428 |                 if(tname == null) { | 
| 429 |                         return null; | 
| 430 |                 } | 
| 431 |                 Element kelement = null; | 
| 432 |                 NodeList nodes = root.getElementsByTagName(IApiXmlConstants.ELEMENT_TARGET); | 
| 433 |                 for (int i = 0; i < nodes.getLength(); i++) { | 
| 434 |                         kelement = (Element) nodes.item(i); | 
| 435 |                         if(tname.equals(kelement.getAttribute(IApiXmlConstants.ATTR_NAME))) { | 
| 436 |                                 return kelement; | 
| 437 |                         } | 
| 438 |                 } | 
| 439 |                 return null; | 
| 440 |         } | 
| 441 |          | 
| 442 |         /** | 
| 443 |          * gets the root kind element | 
| 444 |          * @param root | 
| 445 |          * @param kind | 
| 446 |          * @return | 
| 447 |          */ | 
| 448 |         private Element findKindElement(Element root, Integer kind) { | 
| 449 |                 Element kelement = null; | 
| 450 |                 NodeList nodes = root.getElementsByTagName(IApiXmlConstants.REFERENCE_KIND); | 
| 451 |                 for (int i = 0; i < nodes.getLength(); i++) { | 
| 452 |                         kelement = (Element) nodes.item(i); | 
| 453 |                         if(kind.toString().equals(kelement.getAttribute(IApiXmlConstants.ATTR_KIND))) { | 
| 454 |                                 return kelement; | 
| 455 |                         } | 
| 456 |                 } | 
| 457 |                 return null; | 
| 458 |         } | 
| 459 |          | 
| 460 |         /** | 
| 461 |          * Writes the attributes from the given {@link IReference} into a new {@link Element} that is added to  | 
| 462 |          * the given parent. | 
| 463 |          *  | 
| 464 |          * @param document | 
| 465 |          * @param parent | 
| 466 |          * @param reference | 
| 467 |          */ | 
| 468 |         private void writeReference(Document document, Element parent, IReferenceDescriptor reference) throws CoreException { | 
| 469 |                 Element kelement = null; | 
| 470 |                 Integer kind = new Integer(reference.getReferenceKind()); | 
| 471 |                 kelement = findKindElement(parent, kind); | 
| 472 |                 if(kelement == null) { | 
| 473 |                         kelement = document.createElement(IApiXmlConstants.REFERENCE_KIND); | 
| 474 |                         kelement.setAttribute(IApiXmlConstants.ATTR_REFERENCE_KIND_NAME, Reference.getReferenceText(kind.intValue())); | 
| 475 |                         kelement.setAttribute(IApiXmlConstants.ATTR_KIND, kind.toString()); | 
| 476 |                         parent.appendChild(kelement); | 
| 477 |                 } | 
| 478 |                 Element relement = document.createElement(IApiXmlConstants.ATTR_REFERENCE); | 
| 479 |                 IMemberDescriptor member = reference.getMember(); | 
| 480 |                 relement.setAttribute(IApiXmlConstants.ATTR_ORIGIN, getText(member)); | 
| 481 |                 // add detailed information about origin | 
| 482 |                 addMemberDetails(relement, member); | 
| 483 |                 member = reference.getReferencedMember(); | 
| 484 |                 if(member != null) { | 
| 485 |                         relement.setAttribute(IApiXmlConstants.ATTR_LINE_NUMBER, Integer.toString(reference.getLineNumber())); | 
| 486 |                         kelement.appendChild(relement); | 
| 487 |                 } | 
| 488 |         } | 
| 489 |          | 
| 490 |         /** | 
| 491 |          * Returns the text to set in the attribute for the given {@link IApiMember} | 
| 492 |          * @param member | 
| 493 |          * @return | 
| 494 |          * @throws CoreException | 
| 495 |          */ | 
| 496 |         private String getText(IMemberDescriptor member) throws CoreException { | 
| 497 |                 switch(member.getElementType()) { | 
| 498 |                         case IElementDescriptor.TYPE: return Signatures.getQualifiedTypeSignature((IReferenceTypeDescriptor) member); | 
| 499 |                         case IElementDescriptor.METHOD: return Signatures.getQualifiedMethodSignature((IMethodDescriptor) member); | 
| 500 |                         case IElementDescriptor.FIELD: return Signatures.getQualifiedFieldSignature((IFieldDescriptor) member); | 
| 501 |                 } | 
| 502 |                 return null; | 
| 503 |         } | 
| 504 |   | 
| 505 |         /** | 
| 506 |          * Sets the alternate component where references were unresolved, or <code>null</code> | 
| 507 |          * if none. | 
| 508 |          *  | 
| 509 |          * @param other component descriptor or <code>null</code> | 
| 510 |          */ | 
| 511 |         public void setAlternate(IComponentDescriptor other) { | 
| 512 |                 alternate = other; | 
| 513 |         } | 
| 514 | } |