package bluej.pkgmgr;

import bluej.debugger.gentype.JavaType;
import bluej.debugger.gentype.MethodReflective;
import bluej.debugger.gentype.Reflective;
import bluej.extensions.SourceType;
import bluej.parser.ConstructorOrMethodReflective;
import bluej.parser.JavadocParser;
import bluej.parser.entity.EntityResolver;
import bluej.parser.entity.PackageResolver;
import bluej.parser.symtab.ClassInfo;
import bluej.utility.Debug;
import bluej.utility.JavaNames;
import bluej.utility.Utility;
import bluej.views.CallableView;
import bluej.views.Comment;
import bluej.views.View;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;


| Resolves javadoc from classes within a project. | | @author Davin McCall | public class ProjectJavadocResolver implements JavadocResolver{ private Project project; private CommentCache commentCache = new CommentCache(); public ProjectJavadocResolver(Project project) { this.project = project; }
| Retrieve the javadoc for the specified method, if possible. The javadoc and | method parameter names will be added to the supplied MethodReflective(s). | The collection of methods must all come from the same declaring type. | @Override public void getJavadoc(Reflective declaring, Collection<? extends ConstructorOrMethodReflective> targetMethods) { if (targetMethods.isEmpty()) { return; } String declName = declaring.getName(); Map<String, ConstructorOrMethodReflective> methodSigs = targetMethods.stream().collect( Collectors.toMap(ProjectJavadocResolver::buildSig, m -> m, (a,b) -> a) ); try { Class<?> cl = project.getClassLoader().loadClass(declName); View clView = View.getView(cl); List<CallableView> methods = Utility.concat(Arrays.asList(clView.getAllMethods()), Arrays.asList(clView.getConstructors())); for (CallableView method : methods) { String signature = method.getSignature(); ConstructorOrMethodReflective methodReflective = methodSigs.get(signature); if (methodReflective != null) { Comment comment = method.getComment(); if (comment != null) { methodReflective.setJavaDoc(comment.getText()); List<String> paramNames = new ArrayList<String>(comment.getParamCount()); for (int j = 0; j < comment.getParamCount(); j++) { paramNames.add(comment.getParamName(j)); } methodReflective.setParamNames(paramNames); methodSigs.remove(signature); } } } } catch (ClassNotFoundException cnfe) { } catch (LinkageError e) { } if (methodSigs.isEmpty()) { return; } Properties comments = commentCache.get(declName); if (comments == null) { ClassInfo classInfo = getClassInfoFromSource(declaring.getModuleName(), declName); if (classInfo != null) comments = classInfo.getComments(); if (comments == null) { commentCache.put(declName, new Properties()); return; } commentCache.put(declName, comments); } for (int i = 0; ; i++) { String comtarget = comments.getProperty("comment" + i + ".target"); if (comtarget == null) { break; } ConstructorOrMethodReflective methodReflective = methodSigs.get(comtarget); if (methodReflective != null) { methodReflective.setJavaDoc(comments.getProperty("comment" + i + ".text")); String paramNames = comments.getProperty("comment" + i + ".params"); StringTokenizer tokenizer = new StringTokenizer(paramNames); List<String> paramNamesList = new ArrayList<String>(); while (tokenizer.hasMoreTokens()){ paramNamesList.add(tokenizer.nextToken()); } methodReflective.setParamNames(paramNamesList); methodSigs.remove(comtarget); } } for (ConstructorOrMethodReflective methodReflective : methodSigs.values()) { methodReflective.setJavaDoc(""); } } @Override public boolean getJavadocAsync(final ConstructorOrMethodReflective method, final AsyncCallback callback, Executor executor) { Reflective declaring = method.getDeclaringType(); final String declName = declaring.getName(); final String methodSig = buildSig(method); try { Class<?> cl = project.getClassLoader().loadClass(declName); View clView = View.getView(cl); CallableView[] methods = method instanceof MethodReflective ? clView.getAllMethods() : clView.getConstructors(); for (int i = 0; i < methods.length; i++) { if (methodSig.equals(methods[i].getSignature())) { Comment comment = methods[i].getComment(); if (comment != null) { method.setJavaDoc(comment.getText()); List<String> paramNames = new ArrayList<String>(comment.getParamCount()); for (int j = 0; j < comment.getParamCount(); j++) { paramNames.add(comment.getParamName(j)); } method.setParamNames(paramNames); callback.gotJavadoc(method); return true; } break; } } } catch (ClassNotFoundException cnfe) { } catch (LinkageError e) { } Properties comments = commentCache.get(declName); if (comments == null) { if (comments == null) { callback.gotJavadoc(method); } findMethodComment(comments, callback, method, methodSig, true); return false; } else { findMethodComment(comments, callback, method, methodSig, false); return true; } }
| Search a set of comments for different targets to find the target we want. | Apply the found comment/parameter names to the method reflective, and | optionally notify the callback. | | @param comments The set of comments to search | @param callback The callback to notify (if postOnQueue is true) | @param method The method reflective to update | @param methodSig The method signature to search for | @param postOnQueue Whether to notify the callback | private void findMethodComment(final Properties comments, final AsyncCallback callback, final ConstructorOrMethodReflective method, String methodSig, boolean postOnQueue) { for (int i = 0; ; i++) { String comtarget = comments.getProperty("comment" + i + ".target"); if (comtarget == null) { break; } if (comtarget.equals(methodSig)) { String paramNames = comments.getProperty("comment" + i + ".params"); String javadoc = comments.getProperty("comment" + i + ".text"); StringTokenizer tokenizer = new StringTokenizer(paramNames); List<String> paramNamesList = new ArrayList<String>(); while (tokenizer.hasMoreTokens()){ paramNamesList.add(tokenizer.nextToken()); } method.setJavaDoc(javadoc); method.setParamNames(paramNamesList); break; } } if (postOnQueue) { callback.gotJavadoc(method); } }
| Find the javadoc for a given class (target) by searching the project source path. | In particular, this normally includes the JDK source. When source for the required | class is found, it is parsed to extract comments. | | @param moduleName The module name if known and applicable. May be null. | @param target The fully-qualified class name. | @return The discovered class info, or null if not found. | private ClassInfo getClassInfoFromSource(String moduleName, String target) { List<DocPathEntry> sourcePath = project.getSourcePath(); String pkg = JavaNames.getPrefix(target); String entName = target.replace('.', '/') + "." + SourceType.Java.toString().toLowerCase(); String entNameFs = target.replace('.', File.separatorChar) + "." + SourceType.Java.toString().toLowerCase(); EntityResolver resolver = new PackageResolver(project.getEntityResolver(), pkg); for (DocPathEntry pathEntry : sourcePath) { File jarFile = pathEntry.getFile(); if (jarFile.isFile()) { String fullEntryName = pathEntry.getPathPrefix(); if (fullEntryName.length() != 0 && !fullEntryName.endsWith("/")) { fullEntryName += "/"; } fullEntryName += entName; Reader r = null; try (ZipFile zipFile = new ZipFile(jarFile)) { List<String> possibleEntries = new ArrayList<>(); possibleEntries.add(fullEntryName); if (moduleName != null) { possibleEntries.add(moduleName + "/" + fullEntryName); } for (String entryName : possibleEntries) { ZipEntry zipEnt = zipFile.getEntry(entryName); if (zipEnt != null) { InputStream zeis = zipFile.getInputStream(zipEnt); r = new InputStreamReader(zeis, project.getProjectCharset()); ClassInfo info = JavadocParser.parse(r, resolver, null); if (info == null) { return null; } return info; } } } catch (IOException ioe) { } finally { if (r != null) { try { r.close(); } catch (IOException e) { } } } } else if (jarFile.isDirectory()) { File base = jarFile; String prefix = pathEntry.getPathPrefix(); if (prefix != null && !prefix.isEmpty()) { base = new File(base, prefix); } File srcFile = new File(base, entNameFs); FileInputStream fis = null; try { if (srcFile.canRead()) { fis = new FileInputStream(srcFile); Reader r = new InputStreamReader(fis, project.getProjectCharset()); ClassInfo info = JavadocParser.parse(r, resolver, null); r.close(); if (info == null) { return null; } return info; } } catch (IOException ioe) { if (fis != null) { try { fis.close(); } catch (IOException e) { } } } } } String targetName = target.replace('.', '/') + "." + SourceType.Java.toString().toLowerCase(); URL srcUrl = project.getClassLoader().findResource(targetName); if (srcUrl != null) { try { Reader r = new InputStreamReader(srcUrl.openStream(), project.getProjectCharset()); ClassInfo info = JavadocParser.parse(r, resolver, null); if (info != null) { return info; } } catch (IOException ioe) { Debug.message("I/O exception while trying to retrieve javadoc for " + target); } } return null; }
| Build a method signature from a MethodReflective. | private static String buildSig(ConstructorOrMethodReflective method) { String sig = ""; if (method instanceof MethodReflective) { sig = ((MethodReflective)method).getReturnType().getErasedType().toString(); sig = sig.replace('$', '.'); sig += ' ' + ((MethodReflective)method).getName(); } else { sig = method.getDeclaringType().getSimpleName(); sig = sig.replace('$', '.'); int lastDot = sig.lastIndexOf('.'); if (lastDot != -1) sig = sig.substring(lastDot + 1); } sig += '('; Iterator<JavaType> i = method.getParamTypes().iterator(); while (i.hasNext()){ JavaType ptype = i.next(); sig += ptype.getErasedType().toString().replace('$', '.'); if (i.hasNext()) { sig += ", "; } } sig += ')'; return sig; }
| Find the javadoc for a given class (target) by searching the project source path. | In particular, this normally includes the JDK source. When source for the required | class is found, it is parsed to extract comments. | private Properties getCommentsFromSource(String target) { List<DocPathEntry> sourcePath = project.getSourcePath(); String pkg = JavaNames.getPrefix(target); String entName = target.replace('.', '/') + ".java"; String entNameFs = target.replace('.', File.separatorChar) + ".java"; EntityResolver resolver = new PackageResolver(project.getEntityResolver(), pkg); for (DocPathEntry pathEntry : sourcePath) { File jarFile = pathEntry.getFile(); if (jarFile.isFile()) { String fullEntryName = pathEntry.getPathPrefix(); if (fullEntryName.length() != 0 && !fullEntryName.endsWith("/")) { fullEntryName += "/"; } fullEntryName += entName; Reader r = null; try (ZipFile zipFile = new ZipFile(jarFile)) { ZipEntry zipEnt = zipFile.getEntry(fullEntryName); if (zipEnt != null) { InputStream zeis = zipFile.getInputStream(zipEnt); r = new InputStreamReader(zeis, project.getProjectCharset()); ClassInfo info = JavadocParser.parse(r, resolver, null); if (info == null) { return null; } return info.getComments(); } } catch (IOException ioe) { } finally { if (r != null) { try { r.close(); } catch (IOException e) { } } } } else if (jarFile.isDirectory()) { File base = jarFile; String prefix = pathEntry.getPathPrefix(); if (prefix != null && !prefix.isEmpty()) { base = new File(base, prefix); } File srcFile = new File(base, entNameFs); FileInputStream fis = null; try { if (srcFile.canRead()) { fis = new FileInputStream(srcFile); Reader r = new InputStreamReader(fis, project.getProjectCharset()); ClassInfo info = JavadocParser.parse(r, resolver, null); r.close(); if (info == null) { return null; } return info.getComments(); } } catch (IOException ioe) { if (fis != null) { try { fis.close(); } catch (IOException e) { } } } } } String targetName = target.replace('.', '/') + ".java"; URL srcUrl = project.getClassLoader().findResource(targetName); if (srcUrl != null) { try { Reader r = new InputStreamReader(srcUrl.openStream(), project.getProjectCharset()); ClassInfo info = JavadocParser.parse(r, resolver, null); if (info != null) { return info.getComments(); } } catch (IOException ioe) { Debug.message("I/O exception while trying to retrieve javadoc for " + target); } } return null; } @Override public String getJavadoc(String moduleName, String className) { ClassInfo ci = getClassInfoFromSource(moduleName, className); if (ci == null) return null; return ci.getCommentsAsList().stream() .filter(sc -> ci.getName().equals(sc.target)) .map(sc -> sc.comment) .filter(c -> c != null) .findFirst().orElse(null); } }
top, use, map, class ProjectJavadocResolver

.   ProjectJavadocResolver
.   getJavadoc
.   getJavadocAsync
.   findMethodComment
.   getClassInfoFromSource
.   buildSig
.   getCommentsFromSource
.   getJavadoc




649 neLoCode + 23 LoComm