com.codenvy.ide.ext.java.server.javadoc.JavaDocLocations.java Source code

Java tutorial

Introduction

Here is the source code for com.codenvy.ide.ext.java.server.javadoc.JavaDocLocations.java

Source

/*******************************************************************************
 * Copyright (c) 2012-2014 Codenvy, S.A.
 * 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:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package com.codenvy.ide.ext.java.server.javadoc;

import com.codenvy.ide.ext.java.server.corext.util.JavaModelUtil;
import com.codenvy.ide.ext.java.server.internal.corext.CorextMessages;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IOpenable;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;

public class JavaDocLocations {
    private static final Logger LOG = LoggerFactory.getLogger(JavaDocLocations.class);
    private static final String JAR_PROTOCOL = "jar"; //$NON-NLS-1$
    public static final String ARCHIVE_PREFIX = "jar:"; //$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", //$NON-NLS-1$
            "project_javadoc_location");

    //   public static void migrateToClasspathAttributes() {
    //      final Map<IPath, String> 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) {
    //         @Override
    //         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<IPath, String> 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= 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<IPath, String> 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= 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<IPath, String> 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.
    //    * @param project the Java project
    //    * @param url the Javadoc location
    //    */
    //   public static void setProjectJavadocLocation(IJavaProject project, URL url) {
    //      try {
    //         String location= url != null ? url.toExternalForm() : null;
    //         setProjectJavadocLocation(project, location);
    //      } catch (CoreException e) {
    //         LOG.error(e.getMessage(), e);
    //      }
    //   }

    private static void setProjectJavadocLocation(IJavaProject project, String url) throws CoreException {
        project.getProject().setPersistentProperty(PROJECT_JAVADOC, url);
    }

    public static URL getProjectJavadocLocation(IJavaProject project) {
        if (!project.getProject().isAccessible()) {
            return null;
        }
        try {
            String prop = project.getProject().getPersistentProperty(PROJECT_JAVADOC);
            if (prop == null) {
                return null;
            }
            return parseURL(prop);
        } catch (CoreException e) {
            LOG.error(e.getMessage(), 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())) {
                return parseURL(attrib.getValue());
            }
        }
        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.getResolvedClasspathEntry();
            URL javadocLocation = getLibraryJavadocLocation(entry);
            if (javadocLocation != null) {
                return getLibraryJavadocLocation(entry);
            }
            entry = root.getRawClasspathEntry();
            switch (entry.getEntryKind()) {
            case IClasspathEntry.CPE_LIBRARY:
            case IClasspathEntry.CPE_VARIABLE:
                return getLibraryJavadocLocation(entry);
            default:
                return null;
            }
        } else {
            return getProjectJavadocLocation(root.getJavaProject());
        }
    }

    //   private static JavaUIException createException(Throwable t, String message) {
    //      return new JavaUIException(JavaUIStatus.createError(IStatus.ERROR, message, t));
    //   }

    //   private static Map<IPath, String> loadOldForCompatibility() {
    //      HashMap<IPath, String> resultingOldLocations= new HashMap<IPath, String>();
    //
    //      // 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<IPath, String> oldLocations) throws CoreException {
    //      Element cpElement;
    //      try {
    //         DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    //         parser.setErrorHandler(new DefaultHandler());
    //         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 = parseURL(element.getAttribute(NODE_URL)).toExternalForm();
    //
    //               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 urlBuffer= new StringBuffer(urlString);
    //      if (!urlString.endsWith("/")) { //$NON-NLS-1$
    //         urlBuffer.append('/');
    //      }
    //
    //      StringBuffer pathBuffer= new StringBuffer();
    //      StringBuffer fragmentBuffer= new StringBuffer();
    //
    //      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, fragmentBuffer);
    //            }
    //            break;
    //         case IJavaElement.METHOD :
    //            IMethod method= (IMethod) element;
    //            appendTypePath(method.getDeclaringType(), pathBuffer);
    //            if (includeMemberReference) {
    //               appendMethodReference(method, fragmentBuffer);
    //            }
    //            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 {
    //         String fragment= fragmentBuffer.length() == 0 ? null : fragmentBuffer.toString();
    //         try {
    //            URI relativeURI= new URI(null, null, pathBuffer.toString(), fragment);
    //            urlBuffer.append(relativeURI.toString());
    //            return new URL(urlBuffer.toString());
    //         } catch (URISyntaxException e) {
    //            JavaPlugin.log(e);
    //            return new URL(urlBuffer.append(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 = type.getTypeQualifiedName('.');
        if (packPath.length() > 0) {
            buf.append(packPath);
            buf.append('/');
        }
        buf.append(typePath);
        buf.append(".html"); //$NON-NLS-1$
    }

    private static void appendFieldReference(IField field, StringBuffer buf) {
        buf.append(field.getElementName());
    }

    private static void appendMethodReference(IMethod meth, StringBuffer buf) throws JavaModelException {
        buf.append(meth.getElementName());

        /*
         * The Javadoc tool for Java SE 8 changed the anchor syntax and now tries to avoid "strange" characters in URLs.
         * This breaks all clients that directly create such URLs.
         * We can't know what format is required, so we just guess by the project's compiler compliance.
         */
        boolean is18OrHigher = JavaModelUtil.is18OrHigher(meth.getJavaProject());
        buf.append(is18OrHigher ? '-' : '(');
        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(is18OrHigher ? "-" : ", "); //$NON-NLS-1$ //$NON-NLS-2$
            }
            String curr = Signature.getTypeErasure(params[i]);
            String fullName = JavaModelUtil.getResolvedTypeName(curr, declaringType);
            if (fullName == null) { // e.g. a type parameter "QE;"
                fullName = Signature.toString(Signature.getElementType(curr));
            }
            if (fullName != null) {
                buf.append(fullName);
                int dim = Signature.getArrayCount(curr);
                if (i == lastParam && isVararg) {
                    dim--;
                }
                while (dim > 0) {
                    buf.append(is18OrHigher ? ":A" : "[]"); //$NON-NLS-1$ //$NON-NLS-2$
                    dim--;
                }
                if (i == lastParam && isVararg) {
                    buf.append("..."); //$NON-NLS-1$
                }
            }
        }
        buf.append(is18OrHigher ? '-' : ')');
    }

    //   /**
    //    * Returns the location of the Javadoc.
    //    *
    //    * @param element whose Javadoc location has to be found
    //    * @param isBinary <code>true</code> if the Java element is from a binary container
    //    * @return the location URL of the Javadoc or <code>null</code> if the location cannot be found
    //    * @throws org.eclipse.jdt.core.JavaModelException thrown when the Java element cannot be accessed
    //    * @since 3.9
    //    */
    //   public static String getBaseURL(IJavaElement element, boolean isBinary) throws JavaModelException {
    //      if (isBinary) {
    //         // Source attachment usually does not include Javadoc resources
    //         // => Always use the Javadoc location as base:
    //         URL baseURL= JavaUI.getJavadocLocation(element, false);
    //         if (baseURL != null) {
    //            if (baseURL.getProtocol().equals(JAR_PROTOCOL)) {
    //               // It's a JarURLConnection, which is not known to the browser widget.
    //               // Let's start the help web server:
    //               URL baseURL2= PlatformUI.getWorkbench().getHelpSystem().resolve(baseURL.toExternalForm(), true);
    //               if (baseURL2 != null) { // can be null if org.eclipse.help.ui is not available
    //                  baseURL= baseURL2;
    //               }
    //            }
    //            return baseURL.toExternalForm();
    //         }
    //      } else {
    //         IResource resource= element.getResource();
    //         if (resource != null) {
    //            /*
    //             * Too bad: Browser widget knows nothing about EFS and custom URL handlers,
    //             * so IResource#getLocationURI() does not work in all cases.
    //             * We only support the local file system for now.
    //             * A solution could be https://bugs.eclipse.org/bugs/show_bug.cgi?id=149022 .
    //             */
    //            IPath location= resource.getLocation();
    //            if (location != null)
    //               return location.toFile().toURI().toString();
    //         }
    //      }
    //      return null;
    //   }

    /**
     * Returns the reason for why the Javadoc of the Java element could not be retrieved.
     *
     * @param element whose Javadoc could not be retrieved
     * @param root the root of the Java element
     * @return the String message for why the Javadoc could not be retrieved for the Java element or
     *         <code>null</code> if the Java element is from a source container
     * @since 3.9
     */
    public static String getExplanationForMissingJavadoc(IJavaElement element, IPackageFragmentRoot root) {
        String message = null;
        try {
            boolean isBinary = (root.exists() && root.getKind() == IPackageFragmentRoot.K_BINARY);
            if (isBinary) {
                boolean hasAttachedJavadoc = JavaDocLocations.getJavadocBaseLocation(element) != null;
                boolean hasAttachedSource = root.getSourceAttachmentPath() != null;
                IOpenable openable = element.getOpenable();
                boolean hasSource = openable.getBuffer() != null;

                // Provide hint why there's no Java doc
                if (!hasAttachedSource && !hasAttachedJavadoc)
                    message = CorextMessages.JavaDocLocations_noAttachments;
                else if (!hasAttachedJavadoc && !hasSource)
                    message = CorextMessages.JavaDocLocations_noAttachedJavadoc;
                else if (!hasAttachedSource)
                    message = CorextMessages.JavaDocLocations_noAttachedSource;
                else if (!hasSource)
                    message = CorextMessages.JavaDocLocations_noInformation;

            }
        } catch (JavaModelException e) {
            message = CorextMessages.JavaDocLocations_error_gettingJavadoc;
            LOG.error(message, e);
        }
        return message;
    }

    /**
     * Handles the exception thrown from JDT Core when the attached Javadoc
     * cannot be retrieved due to accessibility issues or location URL issue. This exception is not
     * logged but the exceptions occurred due to other reasons are logged.
     *
     * @param e the exception thrown when retrieving the Javadoc fails
     * @return the String message for why the Javadoc could not be retrieved
     * @since 3.9
     */
    public static String handleFailedJavadocFetch(CoreException e) {
        IStatus status = e.getStatus();
        if (JavaCore.PLUGIN_ID.equals(status.getPlugin())) {
            Throwable cause = e.getCause();
            int code = status.getCode();
            // See bug 120559, bug 400060 and bug 400062
            if (code == IJavaModelStatusConstants.CANNOT_RETRIEVE_ATTACHED_JAVADOC_TIMEOUT
                    || (code == IJavaModelStatusConstants.CANNOT_RETRIEVE_ATTACHED_JAVADOC
                            && (cause instanceof FileNotFoundException || cause instanceof SocketException
                                    || cause instanceof UnknownHostException
                                    || cause instanceof ProtocolException)))
                return CorextMessages.JavaDocLocations_error_gettingAttachedJavadoc;
        }
        LOG.error(e.getMessage(), e);
        return CorextMessages.JavaDocLocations_error_gettingJavadoc;
    }

    /**
     * Parse a URL from a String. This method first tries to treat <code>url</code> as a valid, encoded URL.
     * If that didn't work, it tries to recover from bad URLs, e.g. the unencoded form we used to use in persistent storage.
     *
     * @param url a URL
     * @return the parsed URL or <code>null</code> if the URL couldn't be parsed
     * @since 3.9
     */
    public static URL parseURL(String url) {
        try {
            try {
                return new URI(url).toURL();
            } catch (URISyntaxException e) {
                try {
                    // don't log, since we used to store bad (unencoded) URLs
                    if (url.startsWith("file:/")) { //$NON-NLS-1$
                        // workaround for a bug in the 3-arg URI constructor for paths that contain '[' or ']':
                        return new URI("file", null, url.substring(5), null).toURL(); //$NON-NLS-1$
                    } else {
                        return URIUtil.fromString(url).toURL();
                    }
                } catch (URISyntaxException e1) {
                    // last try, not expected to happen
                    LOG.error(e.getMessage(), e);
                    return new URL(url);
                }
            }
        } catch (MalformedURLException e) {
            LOG.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * Returns the {@link java.io.File} of a <code>file:</code> URL. This method tries to recover from bad URLs,
     * e.g. the unencoded form we used to use in persistent storage.
     * 
     * @param url a <code>file:</code> URL
     * @return the file
     * @since 3.9
     */
    public static File toFile(URL url) {
        try {
            return URIUtil.toFile(url.toURI());
        } catch (URISyntaxException e) {
            LOG.error(e.getMessage(), e);
            return new File(url.getFile());
        }
    }
}