| /******************************************************************************* |
| * Copyright (c) 2000, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.javadoc; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.io.UnsupportedEncodingException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.QualifiedName; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.Job; |
| |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| |
| import org.eclipse.jface.preference.IPreferenceStore; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.IClasspathAttribute; |
| import org.eclipse.jdt.core.IClasspathContainer; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IImportDeclaration; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| |
| import org.eclipse.jdt.internal.corext.CorextMessages; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| |
| import org.eclipse.jdt.ui.JavaUI; |
| import org.eclipse.jdt.ui.PreferenceConstants; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.JavaUIException; |
| import org.eclipse.jdt.internal.ui.JavaUIStatus; |
| import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter; |
| import org.eclipse.jdt.internal.ui.wizards.buildpaths.BuildPathSupport; |
| import org.eclipse.jdt.internal.ui.wizards.buildpaths.CPListElement; |
| |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| public class JavaDocLocations { |
| |
| public static final String ARCHIVE_PREFIX= "jar:file:/"; //$NON-NLS-1$ |
| private static final String PREF_JAVADOCLOCATIONS= "org.eclipse.jdt.ui.javadoclocations"; //$NON-NLS-1$ |
| public static final String PREF_JAVADOCLOCATIONS_MIGRATED= "org.eclipse.jdt.ui.javadoclocations.migrated"; //$NON-NLS-1$ |
| |
| |
| private static final String NODE_ROOT= "javadoclocation"; //$NON-NLS-1$ |
| private static final String NODE_ENTRY= "location_01"; //$NON-NLS-1$ |
| private static final String NODE_PATH= "path"; //$NON-NLS-1$ |
| private static final String NODE_URL= "url"; //$NON-NLS-1$ |
| |
| private static final QualifiedName PROJECT_JAVADOC= new QualifiedName(JavaUI.ID_PLUGIN, "project_javadoc_location"); //$NON-NLS-1$ |
| |
| public static void migrateToClasspathAttributes() { |
| final Map oldLocations= loadOldForCompatibility(); |
| if (oldLocations.isEmpty()) { |
| IPreferenceStore preferenceStore= PreferenceConstants.getPreferenceStore(); |
| preferenceStore.setValue(PREF_JAVADOCLOCATIONS, ""); //$NON-NLS-1$ |
| preferenceStore.setValue(PREF_JAVADOCLOCATIONS_MIGRATED, true); |
| return; |
| } |
| |
| Job job= new Job(CorextMessages.JavaDocLocations_migratejob_name) { |
| protected IStatus run(IProgressMonitor monitor) { |
| try { |
| IWorkspaceRunnable runnable= new IWorkspaceRunnable() { |
| public void run(IProgressMonitor pm) throws CoreException { |
| updateClasspathEntries(oldLocations, pm); |
| IPreferenceStore preferenceStore= PreferenceConstants.getPreferenceStore(); |
| preferenceStore.setValue(PREF_JAVADOCLOCATIONS, ""); //$NON-NLS-1$ |
| preferenceStore.setValue(PREF_JAVADOCLOCATIONS_MIGRATED, true); |
| } |
| }; |
| new WorkbenchRunnableAdapter(runnable).run(monitor); |
| } catch (InvocationTargetException e) { |
| JavaPlugin.log(e); |
| } catch (InterruptedException e) { |
| // should not happen, cannot cancel |
| } |
| return Status.OK_STATUS; |
| } |
| }; |
| job.schedule(); |
| } |
| |
| final static void updateClasspathEntries(Map oldLocationMap, IProgressMonitor monitor) throws JavaModelException { |
| IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); |
| IJavaProject[] javaProjects= JavaCore.create(root).getJavaProjects(); |
| try { |
| monitor.beginTask(CorextMessages.JavaDocLocations_migrate_operation, javaProjects.length); |
| for (int i= 0; i < javaProjects.length; i++) { |
| IJavaProject project= javaProjects[i]; |
| String projectJavadoc= (String) oldLocationMap.get(project.getPath()); |
| if (projectJavadoc != null) { |
| try { |
| setProjectJavadocLocation(project, projectJavadoc); |
| } catch (CoreException e) { |
| // ignore |
| } |
| } |
| |
| IClasspathEntry[] rawClasspath= project.getRawClasspath(); |
| boolean hasChange= false; |
| for (int k= 0; k < rawClasspath.length; k++) { |
| IClasspathEntry updated= getConvertedEntry(rawClasspath[k], project, oldLocationMap); |
| if (updated != null) { |
| rawClasspath[k]= updated; |
| hasChange= true; |
| } |
| } |
| if (hasChange) { |
| project.setRawClasspath(rawClasspath, new SubProgressMonitor(monitor, 1)); |
| } else { |
| monitor.worked(1); |
| } |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| private static IClasspathEntry getConvertedEntry(IClasspathEntry entry, IJavaProject project, Map oldLocationMap) { |
| IPath path= null; |
| switch (entry.getEntryKind()) { |
| case IClasspathEntry.CPE_SOURCE: |
| case IClasspathEntry.CPE_PROJECT: |
| return null; |
| case IClasspathEntry.CPE_CONTAINER: |
| convertContainer(entry, project, oldLocationMap); |
| return null; |
| case IClasspathEntry.CPE_LIBRARY: |
| path= entry.getPath(); |
| break; |
| case IClasspathEntry.CPE_VARIABLE: |
| path= JavaCore.getResolvedVariablePath(entry.getPath()); |
| break; |
| default: |
| return null; |
| } |
| if (path == null) { |
| return null; |
| } |
| IClasspathAttribute[] extraAttributes= entry.getExtraAttributes(); |
| for (int i= 0; i < extraAttributes.length; i++) { |
| if (IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME.equals(extraAttributes[i].getName())) { |
| return null; |
| } |
| } |
| String libraryJavadocLocation= (String) oldLocationMap.get(path); |
| if (libraryJavadocLocation != null) { |
| CPListElement element= CPListElement.createFromExisting(entry, project); |
| element.setAttribute(CPListElement.JAVADOC, libraryJavadocLocation); |
| return element.getClasspathEntry(); |
| } |
| return null; |
| } |
| |
| private static void convertContainer(IClasspathEntry entry, IJavaProject project, Map oldLocationMap) { |
| try { |
| IClasspathContainer container= JavaCore.getClasspathContainer(entry.getPath(), project); |
| if (container == null) { |
| return; |
| } |
| |
| IClasspathEntry[] entries= container.getClasspathEntries(); |
| boolean hasChange= false; |
| for (int i= 0; i < entries.length; i++) { |
| IClasspathEntry curr= entries[i]; |
| IClasspathEntry updatedEntry= getConvertedEntry(curr, project, oldLocationMap); |
| if (updatedEntry != null) { |
| entries[i]= updatedEntry; |
| hasChange= true; |
| } |
| } |
| if (hasChange) { |
| BuildPathSupport.requestContainerUpdate(project, container, entries); |
| } |
| } catch (CoreException e) { |
| // ignore |
| } |
| } |
| |
| /** |
| * Sets the Javadoc location for an archive with the given path. |
| */ |
| public static void setProjectJavadocLocation(IJavaProject project, URL url) { |
| try { |
| String location= url != null ? url.toExternalForm() : null; |
| setProjectJavadocLocation(project, location); |
| } catch (CoreException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| |
| private static void setProjectJavadocLocation(IJavaProject project, String url) throws CoreException { |
| project.getProject().setPersistentProperty(PROJECT_JAVADOC, url); |
| } |
| |
| public static URL getProjectJavadocLocation(IJavaProject project) { |
| try { |
| String prop= project.getProject().getPersistentProperty(PROJECT_JAVADOC); |
| if (prop == null) { |
| return null; |
| } |
| return new URL(prop); |
| } catch (CoreException e) { |
| JavaPlugin.log(e); |
| } catch (MalformedURLException e) { |
| JavaPlugin.log(e); |
| } |
| return null; |
| } |
| |
| |
| public static URL getLibraryJavadocLocation(IClasspathEntry entry) { |
| if (entry == null) { |
| throw new IllegalArgumentException("Entry must not be null"); //$NON-NLS-1$ |
| } |
| |
| int kind= entry.getEntryKind(); |
| if (kind != IClasspathEntry.CPE_LIBRARY && kind != IClasspathEntry.CPE_VARIABLE) { |
| throw new IllegalArgumentException("Entry must be of kind CPE_LIBRARY or CPE_VARIABLE"); //$NON-NLS-1$ |
| } |
| |
| IClasspathAttribute[] extraAttributes= entry.getExtraAttributes(); |
| for (int i= 0; i < extraAttributes.length; i++) { |
| IClasspathAttribute attrib= extraAttributes[i]; |
| if (IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME.equals(attrib.getName())) { |
| try { |
| return new URL(attrib.getValue()); |
| } catch (MalformedURLException e) { |
| return null; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static URL getJavadocBaseLocation(IJavaElement element) throws JavaModelException { |
| if (element.getElementType() == IJavaElement.JAVA_PROJECT) { |
| return getProjectJavadocLocation((IJavaProject) element); |
| } |
| |
| IPackageFragmentRoot root= JavaModelUtil.getPackageFragmentRoot(element); |
| if (root == null) { |
| return null; |
| } |
| |
| if (root.getKind() == IPackageFragmentRoot.K_BINARY) { |
| IClasspathEntry entry= root.getRawClasspathEntry(); |
| if (entry == null) { |
| return null; |
| } |
| if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { |
| entry= getRealClasspathEntry(root.getJavaProject(), entry.getPath(), root.getPath()); |
| if (entry == null) { |
| return null; |
| } |
| } |
| return getLibraryJavadocLocation(entry); |
| } else { |
| return getProjectJavadocLocation(root.getJavaProject()); |
| } |
| } |
| |
| private static IClasspathEntry getRealClasspathEntry(IJavaProject jproject, IPath containerPath, IPath libPath) throws JavaModelException { |
| IClasspathContainer container= JavaCore.getClasspathContainer(containerPath, jproject); |
| if (container != null) { |
| IClasspathEntry[] entries= container.getClasspathEntries(); |
| for (int i= 0; i < entries.length; i++) { |
| IClasspathEntry curr= entries[i]; |
| IClasspathEntry resolved= JavaCore.getResolvedClasspathEntry(curr); |
| if (resolved != null && libPath.equals(resolved.getPath())) { |
| return curr; // return the real entry |
| } |
| } |
| } |
| return null; // not found |
| } |
| |
| |
| // loading for compatibility |
| |
| private static JavaUIException createException(Throwable t, String message) { |
| return new JavaUIException(JavaUIStatus.createError(IStatus.ERROR, message, t)); |
| } |
| |
| private static Map/*<Path, String>*/ loadOldForCompatibility() { |
| HashMap resultingOldLocations= new HashMap(); |
| |
| // in 3.0, the javadoc locations were stored as one big string in the preferences |
| String string= PreferenceConstants.getPreferenceStore().getString(PREF_JAVADOCLOCATIONS); |
| if (string != null && string.length() > 0) { |
| byte[] bytes; |
| try { |
| bytes= string.getBytes("UTF-8"); //$NON-NLS-1$ |
| } catch (UnsupportedEncodingException e) { |
| bytes= string.getBytes(); |
| } |
| InputStream is= new ByteArrayInputStream(bytes); |
| try { |
| loadFromStream(new InputSource(is), resultingOldLocations); |
| PreferenceConstants.getPreferenceStore().setValue(PREF_JAVADOCLOCATIONS, ""); //$NON-NLS-1$ |
| return resultingOldLocations; |
| } catch (CoreException e) { |
| JavaPlugin.log(e); // log but ignore |
| } finally { |
| try { |
| is.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| |
| // in 2.1, the Javadoc locations were stored in a file in the meta data |
| // note that it is wrong to use a stream reader with XML declaring to be UTF-8 |
| try { |
| final String STORE_FILE= "javadoclocations.xml"; //$NON-NLS-1$ |
| File file= JavaPlugin.getDefault().getStateLocation().append(STORE_FILE).toFile(); |
| if (file.exists()) { |
| Reader reader= null; |
| try { |
| reader= new FileReader(file); |
| loadFromStream(new InputSource(reader), resultingOldLocations); |
| file.delete(); // remove file after successful store |
| return resultingOldLocations; |
| } catch (IOException e) { |
| JavaPlugin.log(e); // log but ignore |
| } finally { |
| try { |
| if (reader != null) { |
| reader.close(); |
| } |
| } catch (IOException e) {} |
| } |
| } |
| } catch (CoreException e) { |
| JavaPlugin.log(e); // log but ignore |
| } |
| |
| // in 2.0, the Javadoc locations were stored as one big string in the persistent properties |
| // note that it is wrong to use a stream reader with XML declaring to be UTF-8 |
| try { |
| final QualifiedName QUALIFIED_NAME= new QualifiedName(JavaUI.ID_PLUGIN, "jdoclocation"); //$NON-NLS-1$ |
| |
| IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); |
| String xmlString= root.getPersistentProperty(QUALIFIED_NAME); |
| if (xmlString != null) { // only set when workspace is old |
| Reader reader= new StringReader(xmlString); |
| try { |
| loadFromStream(new InputSource(reader), resultingOldLocations); |
| root.setPersistentProperty(QUALIFIED_NAME, null); // clear property |
| return resultingOldLocations; |
| } finally { |
| |
| try { |
| reader.close(); |
| } catch (IOException e) { |
| // error closing reader: ignore |
| } |
| } |
| } |
| } catch (CoreException e) { |
| JavaPlugin.log(e); // log but ignore |
| } |
| return resultingOldLocations; |
| } |
| |
| private static void loadFromStream(InputSource inputSource, Map/*<Path, String>*/ oldLocations) throws CoreException { |
| Element cpElement; |
| try { |
| DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| cpElement = parser.parse(inputSource).getDocumentElement(); |
| } catch (SAXException e) { |
| throw createException(e, CorextMessages.JavaDocLocations_error_readXML); |
| } catch (ParserConfigurationException e) { |
| throw createException(e, CorextMessages.JavaDocLocations_error_readXML); |
| } catch (IOException e) { |
| throw createException(e, CorextMessages.JavaDocLocations_error_readXML); |
| } |
| |
| if (cpElement == null) return; |
| if (!cpElement.getNodeName().equalsIgnoreCase(NODE_ROOT)) { |
| return; |
| } |
| NodeList list= cpElement.getChildNodes(); |
| int length= list.getLength(); |
| for (int i= 0; i < length; ++i) { |
| Node node= list.item(i); |
| short type= node.getNodeType(); |
| if (type == Node.ELEMENT_NODE) { |
| Element element= (Element) node; |
| if (element.getNodeName().equalsIgnoreCase(NODE_ENTRY)) { |
| String varPath = element.getAttribute(NODE_PATH); |
| String varURL = element.getAttribute(NODE_URL); |
| |
| oldLocations.put(Path.fromPortableString(varPath), varURL); |
| } |
| } |
| } |
| } |
| |
| public static URL getJavadocLocation(IJavaElement element, boolean includeMemberReference) throws JavaModelException { |
| URL baseLocation= getJavadocBaseLocation(element); |
| if (baseLocation == null) { |
| return null; |
| } |
| |
| String urlString= baseLocation.toExternalForm(); |
| |
| StringBuffer pathBuffer= new StringBuffer(urlString); |
| if (!urlString.endsWith("/")) { //$NON-NLS-1$ |
| pathBuffer.append('/'); |
| } |
| |
| switch (element.getElementType()) { |
| case IJavaElement.PACKAGE_FRAGMENT: |
| appendPackageSummaryPath((IPackageFragment) element, pathBuffer); |
| break; |
| case IJavaElement.JAVA_PROJECT: |
| case IJavaElement.PACKAGE_FRAGMENT_ROOT : |
| appendIndexPath(pathBuffer); |
| break; |
| case IJavaElement.IMPORT_CONTAINER : |
| element= element.getParent(); |
| // fall through |
| case IJavaElement.COMPILATION_UNIT : |
| IType mainType= ((ICompilationUnit) element).findPrimaryType(); |
| if (mainType == null) { |
| return null; |
| } |
| appendTypePath(mainType, pathBuffer); |
| break; |
| case IJavaElement.CLASS_FILE : |
| appendTypePath(((IClassFile) element).getType(), pathBuffer); |
| break; |
| case IJavaElement.TYPE : |
| appendTypePath((IType) element, pathBuffer); |
| break; |
| case IJavaElement.FIELD : |
| IField field= (IField) element; |
| appendTypePath(field.getDeclaringType(), pathBuffer); |
| if (includeMemberReference) { |
| appendFieldReference(field, pathBuffer); |
| } |
| break; |
| case IJavaElement.METHOD : |
| IMethod method= (IMethod) element; |
| appendTypePath(method.getDeclaringType(), pathBuffer); |
| if (includeMemberReference) { |
| appendMethodReference(method, pathBuffer); |
| } |
| break; |
| case IJavaElement.INITIALIZER : |
| appendTypePath(((IMember) element).getDeclaringType(), pathBuffer); |
| break; |
| case IJavaElement.IMPORT_DECLARATION : |
| IImportDeclaration decl= (IImportDeclaration) element; |
| |
| if (decl.isOnDemand()) { |
| IJavaElement cont= JavaModelUtil.findTypeContainer(element.getJavaProject(), Signature.getQualifier(decl.getElementName())); |
| if (cont instanceof IType) { |
| appendTypePath((IType) cont, pathBuffer); |
| } else if (cont instanceof IPackageFragment) { |
| appendPackageSummaryPath((IPackageFragment) cont, pathBuffer); |
| } |
| } else { |
| IType imp= element.getJavaProject().findType(decl.getElementName()); |
| appendTypePath(imp, pathBuffer); |
| } |
| break; |
| case IJavaElement.PACKAGE_DECLARATION : |
| IJavaElement pack= element.getAncestor(IJavaElement.PACKAGE_FRAGMENT); |
| if (pack != null) { |
| appendPackageSummaryPath((IPackageFragment) pack, pathBuffer); |
| } else { |
| return null; |
| } |
| break; |
| default : |
| return null; |
| } |
| |
| try { |
| return new URL(pathBuffer.toString()); |
| } catch (MalformedURLException e) { |
| JavaPlugin.log(e); |
| } |
| return null; |
| } |
| |
| private static void appendPackageSummaryPath(IPackageFragment pack, StringBuffer buf) { |
| String packPath= pack.getElementName().replace('.', '/'); |
| buf.append(packPath); |
| buf.append("/package-summary.html"); //$NON-NLS-1$ |
| } |
| |
| private static void appendIndexPath(StringBuffer buf) { |
| buf.append("index.html"); //$NON-NLS-1$ |
| } |
| |
| private static void appendTypePath(IType type, StringBuffer buf) { |
| IPackageFragment pack= type.getPackageFragment(); |
| String packPath= pack.getElementName().replace('.', '/'); |
| String typePath= JavaModelUtil.getTypeQualifiedName(type); |
| buf.append(packPath); |
| buf.append('/'); |
| buf.append(typePath); |
| buf.append(".html"); //$NON-NLS-1$ |
| } |
| |
| private static void appendFieldReference(IField field, StringBuffer buf) { |
| buf.append('#'); |
| buf.append(field.getElementName()); |
| } |
| |
| private static void appendMethodReference(IMethod meth, StringBuffer buf) throws JavaModelException { |
| buf.append('#'); |
| buf.append(meth.getElementName()); |
| |
| buf.append('('); |
| String[] params= meth.getParameterTypes(); |
| IType declaringType= meth.getDeclaringType(); |
| boolean isVararg= Flags.isVarargs(meth.getFlags()); |
| int lastParam= params.length - 1; |
| for (int i= 0; i <= lastParam; i++) { |
| if (i != 0) { |
| buf.append(", "); //$NON-NLS-1$ |
| } |
| String curr= Signature.getTypeErasure(params[i]); |
| String fullName= JavaModelUtil.getResolvedTypeName(curr, declaringType); |
| if (fullName != null) { |
| buf.append(fullName); |
| int dim= Signature.getArrayCount(curr); |
| if (i == lastParam && isVararg) { |
| dim--; |
| } |
| while (dim > 0) { |
| buf.append("[]"); //$NON-NLS-1$ |
| dim--; |
| } |
| if (i == lastParam && isVararg) { |
| buf.append("..."); //$NON-NLS-1$ |
| } |
| } |
| } |
| buf.append(')'); |
| } |
| |
| |
| } |