001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.javadoc;
011:
012: import java.io.ByteArrayInputStream;
013: import java.io.File;
014: import java.io.FileReader;
015: import java.io.IOException;
016: import java.io.InputStream;
017: import java.io.Reader;
018: import java.io.StringReader;
019: import java.io.UnsupportedEncodingException;
020: import java.lang.reflect.InvocationTargetException;
021: import java.net.MalformedURLException;
022: import java.net.URL;
023: import java.util.HashMap;
024: import java.util.Map;
025:
026: import javax.xml.parsers.DocumentBuilder;
027: import javax.xml.parsers.DocumentBuilderFactory;
028: import javax.xml.parsers.ParserConfigurationException;
029:
030: import org.eclipse.core.runtime.CoreException;
031: import org.eclipse.core.runtime.IPath;
032: import org.eclipse.core.runtime.IProgressMonitor;
033: import org.eclipse.core.runtime.IStatus;
034: import org.eclipse.core.runtime.Path;
035: import org.eclipse.core.runtime.QualifiedName;
036: import org.eclipse.core.runtime.Status;
037: import org.eclipse.core.runtime.SubProgressMonitor;
038: import org.eclipse.core.runtime.jobs.Job;
039:
040: import org.eclipse.core.resources.IWorkspaceRoot;
041: import org.eclipse.core.resources.IWorkspaceRunnable;
042: import org.eclipse.core.resources.ResourcesPlugin;
043:
044: import org.eclipse.jface.preference.IPreferenceStore;
045:
046: import org.eclipse.jdt.core.Flags;
047: import org.eclipse.jdt.core.IClassFile;
048: import org.eclipse.jdt.core.IClasspathAttribute;
049: import org.eclipse.jdt.core.IClasspathContainer;
050: import org.eclipse.jdt.core.IClasspathEntry;
051: import org.eclipse.jdt.core.ICompilationUnit;
052: import org.eclipse.jdt.core.IField;
053: import org.eclipse.jdt.core.IImportDeclaration;
054: import org.eclipse.jdt.core.IJavaElement;
055: import org.eclipse.jdt.core.IJavaProject;
056: import org.eclipse.jdt.core.IMember;
057: import org.eclipse.jdt.core.IMethod;
058: import org.eclipse.jdt.core.IPackageFragment;
059: import org.eclipse.jdt.core.IPackageFragmentRoot;
060: import org.eclipse.jdt.core.IType;
061: import org.eclipse.jdt.core.JavaCore;
062: import org.eclipse.jdt.core.JavaModelException;
063: import org.eclipse.jdt.core.Signature;
064:
065: import org.eclipse.jdt.internal.corext.CorextMessages;
066: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
067:
068: import org.eclipse.jdt.ui.JavaUI;
069: import org.eclipse.jdt.ui.PreferenceConstants;
070:
071: import org.eclipse.jdt.internal.ui.JavaPlugin;
072: import org.eclipse.jdt.internal.ui.JavaUIException;
073: import org.eclipse.jdt.internal.ui.JavaUIStatus;
074: import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter;
075: import org.eclipse.jdt.internal.ui.wizards.buildpaths.BuildPathSupport;
076: import org.eclipse.jdt.internal.ui.wizards.buildpaths.CPListElement;
077:
078: import org.w3c.dom.Element;
079: import org.w3c.dom.Node;
080: import org.w3c.dom.NodeList;
081: import org.xml.sax.InputSource;
082: import org.xml.sax.SAXException;
083:
084: public class JavaDocLocations {
085:
086: public static final String ARCHIVE_PREFIX = "jar:"; //$NON-NLS-1$
087: private static final String PREF_JAVADOCLOCATIONS = "org.eclipse.jdt.ui.javadoclocations"; //$NON-NLS-1$
088: public static final String PREF_JAVADOCLOCATIONS_MIGRATED = "org.eclipse.jdt.ui.javadoclocations.migrated"; //$NON-NLS-1$
089:
090: private static final String NODE_ROOT = "javadoclocation"; //$NON-NLS-1$
091: private static final String NODE_ENTRY = "location_01"; //$NON-NLS-1$
092: private static final String NODE_PATH = "path"; //$NON-NLS-1$
093: private static final String NODE_URL = "url"; //$NON-NLS-1$
094:
095: private static final QualifiedName PROJECT_JAVADOC = new QualifiedName(
096: JavaUI.ID_PLUGIN, "project_javadoc_location"); //$NON-NLS-1$
097:
098: public static void migrateToClasspathAttributes() {
099: final Map oldLocations = loadOldForCompatibility();
100: if (oldLocations.isEmpty()) {
101: IPreferenceStore preferenceStore = PreferenceConstants
102: .getPreferenceStore();
103: preferenceStore.setValue(PREF_JAVADOCLOCATIONS, ""); //$NON-NLS-1$
104: preferenceStore.setValue(PREF_JAVADOCLOCATIONS_MIGRATED,
105: true);
106: return;
107: }
108:
109: Job job = new Job(
110: CorextMessages.JavaDocLocations_migratejob_name) {
111: protected IStatus run(IProgressMonitor monitor) {
112: try {
113: IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
114: public void run(IProgressMonitor pm)
115: throws CoreException {
116: updateClasspathEntries(oldLocations, pm);
117: IPreferenceStore preferenceStore = PreferenceConstants
118: .getPreferenceStore();
119: preferenceStore.setValue(
120: PREF_JAVADOCLOCATIONS, ""); //$NON-NLS-1$
121: preferenceStore.setValue(
122: PREF_JAVADOCLOCATIONS_MIGRATED,
123: true);
124: }
125: };
126: new WorkbenchRunnableAdapter(runnable).run(monitor);
127: } catch (InvocationTargetException e) {
128: JavaPlugin.log(e);
129: } catch (InterruptedException e) {
130: // should not happen, cannot cancel
131: }
132: return Status.OK_STATUS;
133: }
134: };
135: job.schedule();
136: }
137:
138: final static void updateClasspathEntries(Map oldLocationMap,
139: IProgressMonitor monitor) throws JavaModelException {
140: IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
141: IJavaProject[] javaProjects = JavaCore.create(root)
142: .getJavaProjects();
143: try {
144: monitor.beginTask(
145: CorextMessages.JavaDocLocations_migrate_operation,
146: javaProjects.length);
147: for (int i = 0; i < javaProjects.length; i++) {
148: IJavaProject project = javaProjects[i];
149: String projectJavadoc = (String) oldLocationMap
150: .get(project.getPath());
151: if (projectJavadoc != null) {
152: try {
153: setProjectJavadocLocation(project,
154: projectJavadoc);
155: } catch (CoreException e) {
156: // ignore
157: }
158: }
159:
160: IClasspathEntry[] rawClasspath = project
161: .getRawClasspath();
162: boolean hasChange = false;
163: for (int k = 0; k < rawClasspath.length; k++) {
164: IClasspathEntry updated = getConvertedEntry(
165: rawClasspath[k], project, oldLocationMap);
166: if (updated != null) {
167: rawClasspath[k] = updated;
168: hasChange = true;
169: }
170: }
171: if (hasChange) {
172: project.setRawClasspath(rawClasspath,
173: new SubProgressMonitor(monitor, 1));
174: } else {
175: monitor.worked(1);
176: }
177: }
178: } finally {
179: monitor.done();
180: }
181: }
182:
183: private static IClasspathEntry getConvertedEntry(
184: IClasspathEntry entry, IJavaProject project,
185: Map oldLocationMap) {
186: IPath path = null;
187: switch (entry.getEntryKind()) {
188: case IClasspathEntry.CPE_SOURCE:
189: case IClasspathEntry.CPE_PROJECT:
190: return null;
191: case IClasspathEntry.CPE_CONTAINER:
192: convertContainer(entry, project, oldLocationMap);
193: return null;
194: case IClasspathEntry.CPE_LIBRARY:
195: path = entry.getPath();
196: break;
197: case IClasspathEntry.CPE_VARIABLE:
198: path = JavaCore.getResolvedVariablePath(entry.getPath());
199: break;
200: default:
201: return null;
202: }
203: if (path == null) {
204: return null;
205: }
206: IClasspathAttribute[] extraAttributes = entry
207: .getExtraAttributes();
208: for (int i = 0; i < extraAttributes.length; i++) {
209: if (IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME
210: .equals(extraAttributes[i].getName())) {
211: return null;
212: }
213: }
214: String libraryJavadocLocation = (String) oldLocationMap
215: .get(path);
216: if (libraryJavadocLocation != null) {
217: CPListElement element = CPListElement.createFromExisting(
218: entry, project);
219: element.setAttribute(CPListElement.JAVADOC,
220: libraryJavadocLocation);
221: return element.getClasspathEntry();
222: }
223: return null;
224: }
225:
226: private static void convertContainer(IClasspathEntry entry,
227: IJavaProject project, Map oldLocationMap) {
228: try {
229: IClasspathContainer container = JavaCore
230: .getClasspathContainer(entry.getPath(), project);
231: if (container == null) {
232: return;
233: }
234:
235: IClasspathEntry[] entries = container.getClasspathEntries();
236: boolean hasChange = false;
237: for (int i = 0; i < entries.length; i++) {
238: IClasspathEntry curr = entries[i];
239: IClasspathEntry updatedEntry = getConvertedEntry(curr,
240: project, oldLocationMap);
241: if (updatedEntry != null) {
242: entries[i] = updatedEntry;
243: hasChange = true;
244: }
245: }
246: if (hasChange) {
247: BuildPathSupport.requestContainerUpdate(project,
248: container, entries);
249: }
250: } catch (CoreException e) {
251: // ignore
252: }
253: }
254:
255: /**
256: * Sets the Javadoc location for an archive with the given path.
257: */
258: public static void setProjectJavadocLocation(IJavaProject project,
259: URL url) {
260: try {
261: String location = url != null ? url.toExternalForm() : null;
262: setProjectJavadocLocation(project, location);
263: } catch (CoreException e) {
264: JavaPlugin.log(e);
265: }
266: }
267:
268: private static void setProjectJavadocLocation(IJavaProject project,
269: String url) throws CoreException {
270: project.getProject()
271: .setPersistentProperty(PROJECT_JAVADOC, url);
272: }
273:
274: public static URL getProjectJavadocLocation(IJavaProject project) {
275: try {
276: String prop = project.getProject().getPersistentProperty(
277: PROJECT_JAVADOC);
278: if (prop == null) {
279: return null;
280: }
281: return new URL(prop);
282: } catch (CoreException e) {
283: JavaPlugin.log(e);
284: } catch (MalformedURLException e) {
285: JavaPlugin.log(e);
286: }
287: return null;
288: }
289:
290: public static URL getLibraryJavadocLocation(IClasspathEntry entry) {
291: if (entry == null) {
292: throw new IllegalArgumentException("Entry must not be null"); //$NON-NLS-1$
293: }
294:
295: int kind = entry.getEntryKind();
296: if (kind != IClasspathEntry.CPE_LIBRARY
297: && kind != IClasspathEntry.CPE_VARIABLE) {
298: throw new IllegalArgumentException(
299: "Entry must be of kind CPE_LIBRARY or CPE_VARIABLE"); //$NON-NLS-1$
300: }
301:
302: IClasspathAttribute[] extraAttributes = entry
303: .getExtraAttributes();
304: for (int i = 0; i < extraAttributes.length; i++) {
305: IClasspathAttribute attrib = extraAttributes[i];
306: if (IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME
307: .equals(attrib.getName())) {
308: try {
309: return new URL(attrib.getValue());
310: } catch (MalformedURLException e) {
311: return null;
312: }
313: }
314: }
315: return null;
316: }
317:
318: public static URL getJavadocBaseLocation(IJavaElement element)
319: throws JavaModelException {
320: if (element.getElementType() == IJavaElement.JAVA_PROJECT) {
321: return getProjectJavadocLocation((IJavaProject) element);
322: }
323:
324: IPackageFragmentRoot root = JavaModelUtil
325: .getPackageFragmentRoot(element);
326: if (root == null) {
327: return null;
328: }
329:
330: if (root.getKind() == IPackageFragmentRoot.K_BINARY) {
331: IClasspathEntry entry = root.getRawClasspathEntry();
332: if (entry == null) {
333: return null;
334: }
335: if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
336: entry = getRealClasspathEntry(root.getJavaProject(),
337: entry.getPath(), root.getPath());
338: if (entry == null) {
339: return null;
340: }
341: }
342: return getLibraryJavadocLocation(entry);
343: } else {
344: return getProjectJavadocLocation(root.getJavaProject());
345: }
346: }
347:
348: private static IClasspathEntry getRealClasspathEntry(
349: IJavaProject jproject, IPath containerPath, IPath libPath)
350: throws JavaModelException {
351: IClasspathContainer container = JavaCore.getClasspathContainer(
352: containerPath, jproject);
353: if (container != null) {
354: IClasspathEntry[] entries = container.getClasspathEntries();
355: for (int i = 0; i < entries.length; i++) {
356: IClasspathEntry curr = entries[i];
357: IClasspathEntry resolved = JavaCore
358: .getResolvedClasspathEntry(curr);
359: if (resolved != null
360: && libPath.equals(resolved.getPath())) {
361: return curr; // return the real entry
362: }
363: }
364: }
365: return null; // not found
366: }
367:
368: // loading for compatibility
369:
370: private static JavaUIException createException(Throwable t,
371: String message) {
372: return new JavaUIException(JavaUIStatus.createError(
373: IStatus.ERROR, message, t));
374: }
375:
376: private static Map/*<Path, String>*/loadOldForCompatibility() {
377: HashMap resultingOldLocations = new HashMap();
378:
379: // in 3.0, the javadoc locations were stored as one big string in the preferences
380: String string = PreferenceConstants.getPreferenceStore()
381: .getString(PREF_JAVADOCLOCATIONS);
382: if (string != null && string.length() > 0) {
383: byte[] bytes;
384: try {
385: bytes = string.getBytes("UTF-8"); //$NON-NLS-1$
386: } catch (UnsupportedEncodingException e) {
387: bytes = string.getBytes();
388: }
389: InputStream is = new ByteArrayInputStream(bytes);
390: try {
391: loadFromStream(new InputSource(is),
392: resultingOldLocations);
393: PreferenceConstants.getPreferenceStore().setValue(
394: PREF_JAVADOCLOCATIONS, ""); //$NON-NLS-1$
395: return resultingOldLocations;
396: } catch (CoreException e) {
397: JavaPlugin.log(e); // log but ignore
398: } finally {
399: try {
400: is.close();
401: } catch (IOException e) {
402: // ignore
403: }
404: }
405: }
406:
407: // in 2.1, the Javadoc locations were stored in a file in the meta data
408: // note that it is wrong to use a stream reader with XML declaring to be UTF-8
409: try {
410: final String STORE_FILE = "javadoclocations.xml"; //$NON-NLS-1$
411: File file = JavaPlugin.getDefault().getStateLocation()
412: .append(STORE_FILE).toFile();
413: if (file.exists()) {
414: Reader reader = null;
415: try {
416: reader = new FileReader(file);
417: loadFromStream(new InputSource(reader),
418: resultingOldLocations);
419: file.delete(); // remove file after successful store
420: return resultingOldLocations;
421: } catch (IOException e) {
422: JavaPlugin.log(e); // log but ignore
423: } finally {
424: try {
425: if (reader != null) {
426: reader.close();
427: }
428: } catch (IOException e) {
429: }
430: }
431: }
432: } catch (CoreException e) {
433: JavaPlugin.log(e); // log but ignore
434: }
435:
436: // in 2.0, the Javadoc locations were stored as one big string in the persistent properties
437: // note that it is wrong to use a stream reader with XML declaring to be UTF-8
438: try {
439: final QualifiedName QUALIFIED_NAME = new QualifiedName(
440: JavaUI.ID_PLUGIN, "jdoclocation"); //$NON-NLS-1$
441:
442: IWorkspaceRoot root = ResourcesPlugin.getWorkspace()
443: .getRoot();
444: String xmlString = root
445: .getPersistentProperty(QUALIFIED_NAME);
446: if (xmlString != null) { // only set when workspace is old
447: Reader reader = new StringReader(xmlString);
448: try {
449: loadFromStream(new InputSource(reader),
450: resultingOldLocations);
451: root.setPersistentProperty(QUALIFIED_NAME, null); // clear property
452: return resultingOldLocations;
453: } finally {
454:
455: try {
456: reader.close();
457: } catch (IOException e) {
458: // error closing reader: ignore
459: }
460: }
461: }
462: } catch (CoreException e) {
463: JavaPlugin.log(e); // log but ignore
464: }
465: return resultingOldLocations;
466: }
467:
468: private static void loadFromStream(InputSource inputSource,
469: Map/*<Path, String>*/oldLocations) throws CoreException {
470: Element cpElement;
471: try {
472: DocumentBuilder parser = DocumentBuilderFactory
473: .newInstance().newDocumentBuilder();
474: cpElement = parser.parse(inputSource).getDocumentElement();
475: } catch (SAXException e) {
476: throw createException(e,
477: CorextMessages.JavaDocLocations_error_readXML);
478: } catch (ParserConfigurationException e) {
479: throw createException(e,
480: CorextMessages.JavaDocLocations_error_readXML);
481: } catch (IOException e) {
482: throw createException(e,
483: CorextMessages.JavaDocLocations_error_readXML);
484: }
485:
486: if (cpElement == null)
487: return;
488: if (!cpElement.getNodeName().equalsIgnoreCase(NODE_ROOT)) {
489: return;
490: }
491: NodeList list = cpElement.getChildNodes();
492: int length = list.getLength();
493: for (int i = 0; i < length; ++i) {
494: Node node = list.item(i);
495: short type = node.getNodeType();
496: if (type == Node.ELEMENT_NODE) {
497: Element element = (Element) node;
498: if (element.getNodeName().equalsIgnoreCase(NODE_ENTRY)) {
499: String varPath = element.getAttribute(NODE_PATH);
500: String varURL = element.getAttribute(NODE_URL);
501:
502: oldLocations.put(Path.fromPortableString(varPath),
503: varURL);
504: }
505: }
506: }
507: }
508:
509: public static URL getJavadocLocation(IJavaElement element,
510: boolean includeMemberReference) throws JavaModelException {
511: URL baseLocation = getJavadocBaseLocation(element);
512: if (baseLocation == null) {
513: return null;
514: }
515:
516: String urlString = baseLocation.toExternalForm();
517:
518: StringBuffer pathBuffer = new StringBuffer(urlString);
519: if (!urlString.endsWith("/")) { //$NON-NLS-1$
520: pathBuffer.append('/');
521: }
522:
523: switch (element.getElementType()) {
524: case IJavaElement.PACKAGE_FRAGMENT:
525: appendPackageSummaryPath((IPackageFragment) element,
526: pathBuffer);
527: break;
528: case IJavaElement.JAVA_PROJECT:
529: case IJavaElement.PACKAGE_FRAGMENT_ROOT:
530: appendIndexPath(pathBuffer);
531: break;
532: case IJavaElement.IMPORT_CONTAINER:
533: element = element.getParent();
534: // fall through
535: case IJavaElement.COMPILATION_UNIT:
536: IType mainType = ((ICompilationUnit) element)
537: .findPrimaryType();
538: if (mainType == null) {
539: return null;
540: }
541: appendTypePath(mainType, pathBuffer);
542: break;
543: case IJavaElement.CLASS_FILE:
544: appendTypePath(((IClassFile) element).getType(), pathBuffer);
545: break;
546: case IJavaElement.TYPE:
547: appendTypePath((IType) element, pathBuffer);
548: break;
549: case IJavaElement.FIELD:
550: IField field = (IField) element;
551: appendTypePath(field.getDeclaringType(), pathBuffer);
552: if (includeMemberReference) {
553: appendFieldReference(field, pathBuffer);
554: }
555: break;
556: case IJavaElement.METHOD:
557: IMethod method = (IMethod) element;
558: appendTypePath(method.getDeclaringType(), pathBuffer);
559: if (includeMemberReference) {
560: appendMethodReference(method, pathBuffer);
561: }
562: break;
563: case IJavaElement.INITIALIZER:
564: appendTypePath(((IMember) element).getDeclaringType(),
565: pathBuffer);
566: break;
567: case IJavaElement.IMPORT_DECLARATION:
568: IImportDeclaration decl = (IImportDeclaration) element;
569:
570: if (decl.isOnDemand()) {
571: IJavaElement cont = JavaModelUtil.findTypeContainer(
572: element.getJavaProject(), Signature
573: .getQualifier(decl.getElementName()));
574: if (cont instanceof IType) {
575: appendTypePath((IType) cont, pathBuffer);
576: } else if (cont instanceof IPackageFragment) {
577: appendPackageSummaryPath((IPackageFragment) cont,
578: pathBuffer);
579: }
580: } else {
581: IType imp = element.getJavaProject().findType(
582: decl.getElementName());
583: appendTypePath(imp, pathBuffer);
584: }
585: break;
586: case IJavaElement.PACKAGE_DECLARATION:
587: IJavaElement pack = element
588: .getAncestor(IJavaElement.PACKAGE_FRAGMENT);
589: if (pack != null) {
590: appendPackageSummaryPath((IPackageFragment) pack,
591: pathBuffer);
592: } else {
593: return null;
594: }
595: break;
596: default:
597: return null;
598: }
599:
600: try {
601: return new URL(pathBuffer.toString());
602: } catch (MalformedURLException e) {
603: JavaPlugin.log(e);
604: }
605: return null;
606: }
607:
608: private static void appendPackageSummaryPath(IPackageFragment pack,
609: StringBuffer buf) {
610: String packPath = pack.getElementName().replace('.', '/');
611: buf.append(packPath);
612: buf.append("/package-summary.html"); //$NON-NLS-1$
613: }
614:
615: private static void appendIndexPath(StringBuffer buf) {
616: buf.append("index.html"); //$NON-NLS-1$
617: }
618:
619: private static void appendTypePath(IType type, StringBuffer buf) {
620: IPackageFragment pack = type.getPackageFragment();
621: String packPath = pack.getElementName().replace('.', '/');
622: String typePath = JavaModelUtil.getTypeQualifiedName(type);
623: buf.append(packPath);
624: buf.append('/');
625: buf.append(typePath);
626: buf.append(".html"); //$NON-NLS-1$
627: }
628:
629: private static void appendFieldReference(IField field,
630: StringBuffer buf) {
631: buf.append('#');
632: buf.append(field.getElementName());
633: }
634:
635: private static void appendMethodReference(IMethod meth,
636: StringBuffer buf) throws JavaModelException {
637: buf.append('#');
638: buf.append(meth.getElementName());
639:
640: buf.append('(');
641: String[] params = meth.getParameterTypes();
642: IType declaringType = meth.getDeclaringType();
643: boolean isVararg = Flags.isVarargs(meth.getFlags());
644: int lastParam = params.length - 1;
645: for (int i = 0; i <= lastParam; i++) {
646: if (i != 0) {
647: buf.append(", "); //$NON-NLS-1$
648: }
649: String curr = Signature.getTypeErasure(params[i]);
650: String fullName = JavaModelUtil.getResolvedTypeName(curr,
651: declaringType);
652: if (fullName != null) {
653: buf.append(fullName);
654: int dim = Signature.getArrayCount(curr);
655: if (i == lastParam && isVararg) {
656: dim--;
657: }
658: while (dim > 0) {
659: buf.append("[]"); //$NON-NLS-1$
660: dim--;
661: }
662: if (i == lastParam && isVararg) {
663: buf.append("..."); //$NON-NLS-1$
664: }
665: }
666: }
667: buf.append(')');
668: }
669:
670: }
|