blob: fde472a83318e0fbb656e249df011dac05eaee6a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.javadoc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
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;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.internal.corext.CorextMessages;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaUIException;
import org.eclipse.jdt.internal.ui.JavaUIStatus;
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$
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 boolean IS_CASE_SENSITIVE = !new File("Temp").equals(new File("temp")); //$NON-NLS-1$ //$NON-NLS-2$
private static Map fgJavadocLocations= null;
private static JavaDocVMInstallListener fgVMInstallListener= null;
private static Map getJavaDocLocations() {
if (fgJavadocLocations == null) {
fgJavadocLocations= new HashMap();
try {
initJavadocLocations(); //delayed initialization
} catch (CoreException e) {
JavaPlugin.log(e);
}
}
return fgJavadocLocations;
}
private static IPath canonicalizedPath(IPath externalPath) {
if (externalPath == null || IS_CASE_SENSITIVE)
return externalPath;
if (ResourcesPlugin.getWorkspace().getRoot().findMember(externalPath) != null) {
return externalPath;
}
try {
return new Path(externalPath.toFile().getCanonicalPath());
} catch (IOException e) {
}
return externalPath;
}
private static boolean setJavadocBaseLocation(IPath path, URL url, boolean save) {
boolean isModified;
if (url == null) {
Object old= getJavaDocLocations().remove(path);
isModified= (old != null);
} else {
URL old= (URL) getJavaDocLocations().put(path, url);
isModified= (old == null || !url.toExternalForm().equals(old.toExternalForm()));
}
if (save && isModified) {
try {
storeLocations();
} catch (CoreException e) {
JavaPlugin.log(e);
}
}
return isModified;
}
/**
* Gets the Javadoc location for an archive with the given path.
*/
private static URL getJavadocBaseLocation(IPath path) {
return (URL) getJavaDocLocations().get(path);
}
/**
* Sets the Javadoc location for an archive with the given path.
*/
public static void setLibraryJavadocLocation(IPath archivePath, URL url) {
setJavadocBaseLocation(canonicalizedPath(archivePath), url, true);
}
/**
* Sets the Javadocs locations for archives with given paths.
*/
public static void setLibraryJavadocLocations(IPath[] archivePaths, URL[] urls) {
boolean needsSave= false;
for (int i= urls.length - 1; i >= 0 ; i--) {
if (setJavadocBaseLocation(canonicalizedPath(archivePaths[i]), urls[i], false)) {
needsSave= true;
}
}
if (needsSave) {
try {
storeLocations();
} catch (CoreException e) {
JavaPlugin.log(e);
}
}
}
/**
* Sets the Javadoc location for an archive with the given path.
*/
public static void setProjectJavadocLocation(IJavaProject project, URL url) {
setJavadocBaseLocation(project.getProject().getFullPath(), url, true);
}
public static URL getProjectJavadocLocation(IJavaProject project) {
return getJavadocBaseLocation(project.getProject().getFullPath());
}
public static URL getLibraryJavadocLocation(IPath archivePath) {
return getJavadocBaseLocation(canonicalizedPath(archivePath));
}
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) {
return getLibraryJavadocLocation(root.getPath());
} else {
return getProjectJavadocLocation(root.getJavaProject());
}
}
// loading & storing
private static JavaUIException createException(Throwable t, String message) {
return new JavaUIException(JavaUIStatus.createError(IStatus.ERROR, message, t));
}
private static synchronized void storeLocations() throws CoreException {
ByteArrayOutputStream stream= new ByteArrayOutputStream(2000);
try {
saveToStream(fgJavadocLocations, stream);
byte[] bytes= stream.toByteArray();
String val;
try {
val= new String(bytes, "UTF-8"); //$NON-NLS-1$
} catch (UnsupportedEncodingException e) {
val= new String(bytes);
}
PreferenceConstants.getPreferenceStore().setValue(PREF_JAVADOCLOCATIONS, val);
JavaPlugin.getDefault().savePluginPreferences();
} finally {
try {
stream.close();
} catch (IOException e) {
// error closing reader: ignore
}
}
}
private static boolean loadOldForCompatibility() {
// 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));
storeLocations();
file.delete(); // remove file after successful store
return true;
} 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));
storeLocations();
root.setPersistentProperty(QUALIFIED_NAME, null); // clear property
return true;
} finally {
try {
reader.close();
} catch (IOException e) {
// error closing reader: ignore
}
}
}
} catch (CoreException e) {
JavaPlugin.log(e); // log but ignore
}
return false;
}
private static boolean loadFromPreferences() throws CoreException {
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));
return true;
} finally {
try {
is.close();
} catch (IOException e) {
// ignore
}
}
}
return false;
}
private static void saveToStream(Map locations, OutputStream stream) throws CoreException {
try {
DocumentBuilderFactory factory= DocumentBuilderFactory.newInstance();
DocumentBuilder builder= factory.newDocumentBuilder();
Document document= builder.newDocument();
Element rootElement = document.createElement(NODE_ROOT);
document.appendChild(rootElement);
Iterator iter= locations.keySet().iterator();
while (iter.hasNext()) {
IPath path= (IPath) iter.next();
URL url= getJavadocBaseLocation(path);
Element varElement= document.createElement(NODE_ENTRY);
varElement.setAttribute(NODE_PATH, path.toString());
varElement.setAttribute(NODE_URL, url.toExternalForm());
rootElement.appendChild(varElement);
}
JavaDocVMInstallListener.saveVMInstallJavadocLocations(document, rootElement);
Transformer transformer=TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(stream);
transformer.transform(source, result);
} catch (TransformerException e) {
throw createException(e, CorextMessages.getString("JavaDocLocations.error.serializeXML")); //$NON-NLS-1$
} catch (ParserConfigurationException e) {
throw createException(e, CorextMessages.getString("JavaDocLocations.error.serializeXML")); //$NON-NLS-1$
}
}
private static void loadFromStream(InputSource inputSource) throws CoreException {
Element cpElement;
try {
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
cpElement = parser.parse(inputSource).getDocumentElement();
} catch (SAXException e) {
throw createException(e, CorextMessages.getString("JavaDocLocations.error.readXML")); //$NON-NLS-1$
} catch (ParserConfigurationException e) {
throw createException(e, CorextMessages.getString("JavaDocLocations.error.readXML")); //$NON-NLS-1$
} catch (IOException e) {
throw createException(e, CorextMessages.getString("JavaDocLocations.error.readXML")); //$NON-NLS-1$
}
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);
try {
setJavadocBaseLocation(new Path(varPath), new URL(varURL), false);
} catch (MalformedURLException e) {
throw createException(e, CorextMessages.getString("JavaDocLocations.error.readXML")); //$NON-NLS-1$
}
}
}
}
updateVMInstallJavadocLocations(cpElement);
}
/**
* Get the javadoc locations from vminstalls and update them if they are different to stored vm installs
* @param cpElement The root node or null
*/
private static void updateVMInstallJavadocLocations(Element cpElement) {
// check for updates in the vm installs
ArrayList paths= new ArrayList(), urls= new ArrayList();
JavaDocVMInstallListener.collectChangedVMInstallJavadocLocations(cpElement, paths, urls);
for (int i= 0; i < paths.size(); i++) {
setJavadocBaseLocation(canonicalizedPath((IPath) paths.get(i)), (URL) urls.get(i), false);
}
}
public static void shutdownJavadocLocations() {
if (fgVMInstallListener == null) {
return;
}
fgVMInstallListener.remove();
fgVMInstallListener= null;
fgJavadocLocations= null;
}
private static synchronized void initJavadocLocations() throws CoreException {
try {
boolean res= loadFromPreferences();
if (!res) {
loadOldForCompatibility();
updateVMInstallJavadocLocations(null); // initialize all javadoc location from VM installs
}
} finally {
fgVMInstallListener= new JavaDocVMInstallListener();
fgVMInstallListener.init();
}
}
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();
for (int i= 0; i < params.length; i++) {
if (i != 0) {
buf.append(", "); //$NON-NLS-1$
}
String curr= params[i];
String fullName= JavaModelUtil.getResolvedTypeName(curr, declaringType);
if (fullName != null) {
buf.append(fullName);
int dim= Signature.getArrayCount(curr);
while (dim > 0) {
buf.append("[]"); //$NON-NLS-1$
dim--;
}
}
}
buf.append(')');
}
}