You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jl...@apache.org on 2019/05/05 13:42:26 UTC
[netbeans] branch master updated: Inject parameter names lazily
This is an automated email from the ASF dual-hosted git repository.
jlahoda pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 16f195d Inject parameter names lazily
16f195d is described below
commit 16f195de95bf1aee45f592f300b4af6869e2dae2
Author: Jan Lahoda <jl...@netbeans.org>
AuthorDate: Sun May 5 15:42:20 2019 +0200
Inject parameter names lazily
---
java/java.source.base/manifest.mf | 2 +-
.../org/netbeans/api/java/source/JavaSource.java | 3 +-
.../org/netbeans/api/java/source/SourceUtils.java | 227 +-------
.../modules/java/source/parsing/JavacParser.java | 1 +
.../source/parsing/ParameterNameProviderImpl.java | 620 +++++++++++++++++++++
.../netbeans/api/java/source/SourceUtilsTest.java | 3 +-
.../java/source/nbjavac/parsing/TreeLoader.java | 236 +-------
7 files changed, 646 insertions(+), 446 deletions(-)
diff --git a/java/java.source.base/manifest.mf b/java/java.source.base/manifest.mf
index 2f062f8..d18a404 100644
--- a/java/java.source.base/manifest.mf
+++ b/java/java.source.base/manifest.mf
@@ -1,5 +1,5 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.modules.java.source.base
-OpenIDE-Module-Implementation-Version: 4
+OpenIDE-Module-Implementation-Version: 5
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/source/base/Bundle.properties
OpenIDE-Module-Layer: org/netbeans/modules/java/source/base/layer.xml
diff --git a/java/java.source.base/src/org/netbeans/api/java/source/JavaSource.java b/java/java.source.base/src/org/netbeans/api/java/source/JavaSource.java
index 89acf16..58c5c2e 100644
--- a/java/java.source.base/src/org/netbeans/api/java/source/JavaSource.java
+++ b/java/java.source.base/src/org/netbeans/api/java/source/JavaSource.java
@@ -58,6 +58,7 @@ import org.netbeans.modules.java.source.parsing.JavacParser;
import org.netbeans.modules.java.source.parsing.JavacParserFactory;
import org.netbeans.modules.java.source.parsing.MimeTask;
import org.netbeans.modules.java.source.parsing.NewComilerTask;
+import org.netbeans.modules.java.source.parsing.ParameterNameProviderImpl;
import org.netbeans.modules.java.source.save.ElementOverlay;
import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor;
import org.netbeans.modules.parsing.api.Embedding;
@@ -802,7 +803,7 @@ public final class JavaSource {
@Override
public @NonNull String generateReadableParameterName (@NonNull String typeName, @NonNull Set<String> used) {
- return SourceUtils.generateReadableParameterName(typeName, used);
+ return ParameterNameProviderImpl.generateReadableParameterName(typeName, used);
}
@Override
diff --git a/java/java.source.base/src/org/netbeans/api/java/source/SourceUtils.java b/java/java.source.base/src/org/netbeans/api/java/source/SourceUtils.java
index 2e82805..8e136e2 100644
--- a/java/java.source.base/src/org/netbeans/api/java/source/SourceUtils.java
+++ b/java/java.source.base/src/org/netbeans/api/java/source/SourceUtils.java
@@ -49,6 +49,7 @@ import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Kinds;
import com.sun.tools.javac.code.Scope.NamedImportScope;
import com.sun.tools.javac.code.Scope.StarImportScope;
import com.sun.tools.javac.code.Symbol;
@@ -60,9 +61,18 @@ import com.sun.tools.javac.comp.Check;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.util.Context;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.io.Reader;
import java.util.function.Predicate;
import javax.swing.SwingUtilities;
+import javax.swing.text.ChangedCharSetException;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.parser.ParserDelegator;
import javax.tools.JavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.Diagnostic;
@@ -1310,223 +1320,6 @@ public class SourceUtils {
// --------------- End of getFile () helper methods ------------------------------
- private static final int MAX_LEN = 6;
- /**
- * Utility method for generating method parameter names based on incoming
- * class name when source is unavailable.
- * <p/>
- * This method uses both subjective heuristics to follow common patterns
- * for common JDK classes, acronym creation for bicapitalized names, and
- * vowel and repeated character elision if that fails, to generate
- * readable, programmer-friendly method names.
- *
- * @param typeName The fqn of the parameter class
- * @param used A set of names that have already been used for parameters
- * and should not be reused, to avoid creating uncompilable code
- * @return A programmer-friendly parameter name (i.e. not arg0, arg1...)
- */
- static @NonNull String generateReadableParameterName (@NonNull String typeName, @NonNull Set<String> used) {
- boolean arr = typeName.indexOf ("[") > 0 || typeName.endsWith("..."); //NOI18N
- typeName = trimToSimpleName (typeName);
- String result = typeName.toLowerCase();
- //First, do some common, sane substitutions that are common java parlance
- if ( typeName.endsWith ( "Listener" ) ) { //NOI18N
- result = Character.toLowerCase(typeName.charAt(0)) + "l"; //NOI18N
- } else if ( "Object".equals (typeName)) { //NOI18N
- result = "o"; //NOI18N
- } else if ("Class".equals(typeName)) { //NOI18N
- result = "type"; //NOI18N
- } else if ( "InputStream".equals(typeName)) { //NOI18N
- result = "in"; //NOI18N
- } else if ( "OutputStream".equals(typeName)) {
- result = "out"; //NOI18N
- } else if ( "Runnable".equals(typeName)) {
- result = "r"; //NOI18N
- } else if ( "Lookup".equals(typeName)) {
- result = "lkp"; //NOI18N
- } else if ( typeName.endsWith ( "Stream" )) { //NOI18N
- result = "stream"; //NOI18N
- } else if ( typeName.endsWith ("Writer")) { //NOI18N
- result = "writer"; //NOI18N
- } else if ( typeName.endsWith ("Reader")) { //NOI18N
- result = "reader"; //NOI18N
- } else if ( typeName.endsWith ( "Panel" )) { //NOI18N
- result = "pnl"; //NOI18N
- } else if ( typeName.endsWith ( "Action" )) { //NOI18N
- result = "action"; //NOI18N
- }
- //Now see if we've made a large and unwieldy variable - people
- //usually prefer reasonably short but legible arguments
- if ( result.length () > MAX_LEN ) {
- //See if we can turn, say, NoClassDefFoundError into "ncdfe"
- result = tryToMakeAcronym ( typeName );
- //No luck? We've probably got one long word like Component or Runnable
- if (result.length() > MAX_LEN) {
- //First, strip out vowels - people easily figure out words
- //missing vowels - common in abbreviations and spam mails
- result = elideVowelsAndRepetitions(result);
- if (result.length() > MAX_LEN) {
- //Still too long? Give up and give them a 1 character var name
- result = new StringBuilder().append(
- result.charAt(0)).toString().toLowerCase();
- }
- }
- }
- //Make sure we haven't killed everything - if so, use a generic version
- if ( result.trim ().length () == 0 ) {
- result = "value"; //NOI18N
- }
- //If it's an array, pluralize it (english language style - but better than nothing)
- if (arr) {
- result += "s"; //NOI18N
- }
- //Now make sure it's legal; if not, make it a single letter
- if ( isPrimitiveTypeName ( result ) || !BaseUtilities.isJavaIdentifier ( result ) ) {
- StringBuilder sb = new StringBuilder();
- sb.append (result.charAt(0));
- result = sb.toString();
- }
- //Now make sure we're not duplicating a variable name we already used
- String test = result;
- int revs = 0;
- while ( used.contains ( test ) ) {
- revs++;
- test = result + revs;
- }
- result = test;
- used.add ( result );
- return result;
- }
-
- /**
- * Trims to the simple class name and removes and generics
- *
- * @param typeName The class name
- * @return A simplified class name
- */
- private static String trimToSimpleName (String typeName) {
- String result = typeName;
- int ix = result.indexOf ("<"); //NOI18N
- if (ix > 0 && ix != typeName.length() - 1) {
- result = typeName.substring(0, ix);
- }
- if (result.endsWith ("...")) { //NOI18N
- result = result.substring (0, result.length() - 3);
- }
- ix = result.lastIndexOf ("$"); //NOI18N
- if (ix > 0 && ix != result.length() - 1) {
- result = result.substring(ix + 1);
- } else {
- ix = result.lastIndexOf("."); //NOI18N
- if (ix > 0 && ix != result.length() - 1) {
- result = result.substring(ix + 1);
- }
- }
- ix = result.indexOf ( "[" ); //NOI18N
- if ( ix > 0 ) {
- result = result.substring ( 0, ix );
- }
- return result;
- }
-
- /**
- * Removes vowels and repeated letters. This is used to generate names
- * where the class name a single long word - e.g. abbreviate
- * Runnable to rnbl
- * @param name The name
- * @return A shortened version of it
- */
- private static String elideVowelsAndRepetitions (String name) {
- char[] chars = name.toCharArray();
- StringBuilder sb = new StringBuilder();
- char last = 0;
- char lastUsed = 0;
- for (int i = 0; i < chars.length; i++) {
- char c = chars[i];
- if (Character.isDigit(c)) {
- continue;
- }
- if (i == 0 || Character.isUpperCase(c)) {
- if (lastUsed != c) {
- sb.append (c);
- lastUsed = c;
- }
- } else if (c != last && !isVowel(c)) {
- if (lastUsed != c) {
- sb.append (c);
- lastUsed = c;
- }
- }
- last = c;
- }
- return sb.toString();
- }
-
- private static boolean isVowel(char c) {
- return Arrays.binarySearch(VOWELS, c) >= 0;
- }
-
- /**
- * Vowels in various indo-european-based languages
- */
- private static final char[] VOWELS = new char[] {
- //IMPORTANT: This array is sorted. If you add to it,
- //add in the correct place or Arrays.binarySearch will break on it
- '\u0061', '\u0065', '\u0069', '\u006f', '\u0075', '\u0079', '\u00e9', '\u00ea', //NOI18N
- '\u00e8', '\u00e1', '\u00e2', '\u00e6', '\u00e0', '\u03b1', '\u00e3', //NOI18N
- '\u00e5', '\u00e4', '\u00eb', '\u00f3', '\u00f4', '\u0153', '\u00f2', //NOI18N
- '\u03bf', '\u00f5', '\u00f6', '\u00ed', '\u00ee', '\u00ec', '\u03b9', //NOI18N
- '\u00ef', '\u00fa', '\u00fb', '\u00f9', '\u03d2', '\u03c5', '\u00fc', //NOI18N
- '\u0430', '\u043e', '\u044f', '\u0438', '\u0439', '\u0435', '\u044b', //NOI18N
- '\u044d', '\u0443', '\u044e', };
-
- //PENDING: The below would be much prettier; whether it survives
- //cross-platform encoding issues in hg is another question; the hg diff generated
- //was incorrect
-/*
- 'a', 'e', 'i', 'o', 'u', 'y', 'à', 'á', //NOI18N
- 'â', 'ã', 'ä', 'å', 'æ', 'è', 'é', //NOI18N
- 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ò', //NOI18N
- 'ó', 'ô', 'õ', 'ö', 'ù', 'ú', 'û', //NOI18N
- 'ü', 'œ', 'α', 'ι', 'ο', 'υ', 'ϒ', //NOI18N
- 'а', 'е', 'и', 'й', 'о', 'у', 'ы', //NOI18N
- 'э', 'ю', 'я'}; //NOI18N
-*/
- /**
- * Determine if a string matches a java primitive type. Used in generating reasonable variable names.
- */
- private static boolean isPrimitiveTypeName (String typeName) {
- return (
- //Whoa, ascii art!
- "void".equals ( typeName ) || //NOI18N
- "int".equals ( typeName ) || //NOI18N
- "long".equals ( typeName ) || //NOI18N
- "float".equals ( typeName ) || //NOI18N
- "double".equals ( typeName ) || //NOI18N
- "short".equals ( typeName ) || //NOI18N
- "char".equals ( typeName ) || //NOI18N
- "boolean".equals ( typeName ) ); //NOI18N
- }
-
- /**
- * Try to create an acronym-style variable name from a string - i.e.,
- * "JavaDataObject" becomes "jdo".
- */
- private static String tryToMakeAcronym (String s) {
- char[] c = s.toCharArray ();
- StringBuilder sb = new StringBuilder ();
- for ( int i = 0; i < c.length; i++ ) {
- if ( Character.isUpperCase (c[i])) {
- sb.append ( c[ i ] );
- }
- }
- if ( sb.length () > 1 ) {
- return sb.toString ().toLowerCase ();
- } else {
- return s.toLowerCase();
- }
- }
-
@NonNull
private static Set<URL> mapToURLs(
@NonNull final Collection<? extends FileObject> fos) {
diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/JavacParser.java b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/JavacParser.java
index 10dba1d..40a4091 100644
--- a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/JavacParser.java
+++ b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/JavacParser.java
@@ -921,6 +921,7 @@ public class JavacParser extends Parser {
}
NBEnter.preRegister(context);
NBMemberEnter.preRegister(context, backgroundCompilation);
+ ParameterNameProviderImpl.register(task, cpInfo);
TIME_LOGGER.log(Level.FINE, "JavaC", context);
return task;
}
diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/ParameterNameProviderImpl.java b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/ParameterNameProviderImpl.java
new file mode 100644
index 0000000..decc660
--- /dev/null
+++ b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/ParameterNameProviderImpl.java
@@ -0,0 +1,620 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source.parsing;
+
+import com.sun.source.tree.MethodTree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TreePathScanner;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.util.Context;
+import java.awt.EventQueue;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.io.Reader;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.swing.text.ChangedCharSetException;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.parser.ParserDelegator;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.SourceUtils;
+import org.netbeans.modules.java.source.JavadocHelper;
+import org.openide.filesystems.FileObject;
+import org.openide.util.BaseUtilities;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author lahvac
+ */
+public class ParameterNameProviderImpl {
+ public static void register(JavacTask task, ClasspathInfo cpInfo) {
+ try {
+ Class<?> c = Class.forName("com.sun.source.util.ParameterNameProvider");
+ ParameterNameProviderImpl impl = new ParameterNameProviderImpl(cpInfo, task);
+ InvocationHandler h = new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getName().equals("getParameterName")) {
+ return impl.getParameterName((VariableElement) args[0]);
+ }
+ return null;
+ }
+ };
+ Object proxy = Proxy.newProxyInstance(ParameterNameProviderImpl.class.getClassLoader(), new Class[] {c}, h);
+ JavacTask.class.getDeclaredMethod("setParameterNameProvider", c).invoke(task, proxy);
+ } catch (ClassNotFoundException ex) {
+ //ok
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ private static final int MAX_CACHE_SIZE = 100;
+ private static final LinkedHashMap<String, Map<String, List<String>>> source_toplevelClass2method2Parameters = new LinkedHashMap<>();
+ private static final LinkedHashMap<String, Map<String, List<String>>> javadoc_class2method2Parameters = new LinkedHashMap<>();
+ private static final LinkedHashMap<String, List<String>> artificial_method2Parameters = new LinkedHashMap<>();
+
+ private final ClasspathInfo cpInfo;
+ private final JavacTask task;
+
+ public ParameterNameProviderImpl(ClasspathInfo cpInfo, JavacTask task) {
+ this.cpInfo = cpInfo;
+ this.task = task;
+ }
+
+ public CharSequence getParameterName(VariableElement parameter) {
+ Element method = parameter.getEnclosingElement();
+ String methodKey = computeKey(method);
+ List<String> names;
+
+ //from sources:
+ {
+ Element topLevel = parameter;
+ while (topLevel.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
+ topLevel = topLevel.getEnclosingElement();
+ }
+ ElementHandle<?> topLevelHandle = ElementHandle.create(topLevel);
+
+ names = source_toplevelClass2method2Parameters.computeIfAbsent(computeKey(topLevel), d -> {
+ Map<String, List<String>> parametersInClass = new HashMap<>();
+ FileObject source = SourceUtils.getFile(topLevelHandle, cpInfo);
+ JavaSource javaSource = source != null ? JavaSource.forFileObject(source) : null;
+ if (javaSource != null) {
+ try {
+ javaSource.runUserActionTask(cc -> {
+ cc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
+ new TreePathScanner<Void, Void>() {
+ public Void visitMethod(MethodTree mt, Void v) {
+ Element el = cc.getTrees().getElement(getCurrentPath());
+ if (el != null && el.getKind() == ElementKind.METHOD) {
+ parametersInClass.put(computeKey(el), ((ExecutableElement) el).getParameters().stream().map(p -> p.getSimpleName().toString()).collect(Collectors.toList()));
+ }
+ return super.visitMethod(mt, v);
+ }
+ }.scan(cc.getCompilationUnit(), null);
+ }, true);
+ } catch (IOException ex) {
+ //ignore
+ }
+ }
+ return parametersInClass;
+ }).get(methodKey);
+ }
+
+ if (names == null) {
+ Element clazzCandidate = method.getEnclosingElement();
+ if (clazzCandidate != null && (clazzCandidate.getKind().isClass() || clazzCandidate.getKind().isInterface())) {
+ TypeElement clazz = (TypeElement) clazzCandidate;
+ names = javadoc_class2method2Parameters.computeIfAbsent(clazz.getQualifiedName().toString(), clz -> {
+ Map<String, List<String>> result = new HashMap<>();
+ fillInParameterNamesFromJavadoc(((JavacTaskImpl) task).getContext(), clazz, (m, n) -> {
+ result.put(computeKey(m), n);
+ });
+ return result;
+ }).get(methodKey);
+ }
+ }
+
+ if (names == null) {
+ names = artificial_method2Parameters.computeIfAbsent(methodKey, mk -> {
+ Set<String> usedNames = new HashSet<>();
+ return ((ExecutableElement) method).getParameters().stream().map(p -> generateReadableParameterName(p.asType().toString(), usedNames)).collect(Collectors.toList());
+ });
+ }
+
+ capCache(source_toplevelClass2method2Parameters);
+ capCache(javadoc_class2method2Parameters);
+ capCache(artificial_method2Parameters);
+
+ int idx = ((ExecutableElement) method).getParameters().indexOf(parameter);
+ return idx != (-1) && idx < names.size() ? names.get(idx) : null;
+ }
+
+ private static String computeKey(Element el) {
+ return Arrays.stream(SourceUtils.getJVMSignature(ElementHandle.create(el))).collect(Collectors.joining(":"));
+ }
+
+ private void capCache(LinkedHashMap<String, ?> map) {
+ Iterator<String> it = map.keySet().iterator();
+ while (map.size() > MAX_CACHE_SIZE) {
+ it.remove();
+ }
+ }
+
+ //from javadoc:
+ private static final boolean ALWAYS_ALLOW_JDOC_ARG_NAMES = Boolean.getBoolean("java.source.args.from.http.jdoc"); //NOI18N
+
+ public static boolean fillInParameterNamesFromJavadoc(Context ctx, TypeElement type, BiConsumer<ExecutableElement, List<String>> setNamesToMethod) {
+ JavadocHelper.TextStream page = JavadocHelper.getJavadoc(type, ALWAYS_ALLOW_JDOC_ARG_NAMES, null);
+ if (page != null && (!page.isRemote() || !EventQueue.isDispatchThread())) {
+ getParamNamesFromJavadocText(ctx, page, (ClassSymbol) type, setNamesToMethod);
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean getParamNamesFromJavadocText(Context context, final JavadocHelper.TextStream page, ClassSymbol clazz, BiConsumer<ExecutableElement, List<String>> setNamesToMethod) {
+ HTMLEditorKit.Parser parser;
+ InputStream is = null;
+ String charset = null;
+ for (;;) {
+ try{
+ is = page.openStream();
+ Reader reader = charset == null ? new InputStreamReader(is): new InputStreamReader(is, charset);
+ parser = new ParserDelegator();
+ parser.parse(reader, new HTMLEditorKit.ParserCallback() {
+
+ private int state = 0; //init
+ private String signature = null;
+ private StringBuilder sb = null;
+
+ @Override
+ public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
+ if (t == HTML.Tag.A) {
+ String attrName = (String)a.getAttribute(HTML.Attribute.NAME);
+ if (attrName != null && ctor_summary_name.matcher(attrName).matches()) {
+ // we have found desired javadoc constructor info anchor
+ state = 10; //ctos open
+ } else if (attrName != null && method_summary_name.matcher(attrName).matches()) {
+ // we have found desired javadoc method info anchor
+ state = 20; //methods open
+ } else if (attrName != null && field_detail_name.matcher(attrName).matches()) {
+ state = 30; //end
+ } else if (attrName != null && ctor_detail_name.matcher(attrName).matches()) {
+ state = 30; //end
+ } else if (attrName != null && method_detail_name.matcher(attrName).matches()) {
+ state = 30; //end
+ } else if (state == 12 || state == 22) {
+ String attrHref = (String)a.getAttribute(HTML.Attribute.HREF);
+ if (attrHref != null) {
+ int idx = attrHref.indexOf('#');
+ if (idx >= 0) {
+ signature = attrHref.substring(idx + 1);
+ sb = new StringBuilder();
+ }
+ }
+ }
+ } else if (t == HTML.Tag.TABLE) {
+ if (state == 10 || state == 20)
+ state++;
+ } else if (t == HTML.Tag.CODE) {
+ if (state == 11 || state == 21)
+ state++;
+ } else if (t == HTML.Tag.DIV && a.containsAttribute(HTML.Attribute.CLASS, "block")) { //NOI18N
+ if (state == 11 && signature != null && sb != null) {
+ setParamNames(signature, sb.toString().trim(), true);
+ signature = null;
+ sb = null;
+ } else if (state == 21 && signature != null && sb != null) {
+ setParamNames(signature, sb.toString().trim(), false);
+ signature = null;
+ sb = null;
+ }
+ }
+ }
+
+ @Override
+ public void handleEndTag(HTML.Tag t, int pos) {
+ if (t == HTML.Tag.CODE) {
+ if (state == 12 || state == 22)
+ state--;
+ } else if (t == HTML.Tag.TABLE) {
+ if (state == 11 || state == 21)
+ state--;
+ }
+ }
+
+ @Override
+ public void handleText(char[] data, int pos) {
+ if (signature != null && sb != null && (state == 12 || state == 22))
+ sb.append(data);
+ }
+
+ @Override
+ public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
+ if (t == HTML.Tag.BR) {
+ if (state == 11 && signature != null && sb != null) {
+ setParamNames(signature, sb.toString().trim(), true);
+ signature = null;
+ sb = null;
+ } else if (state == 21 && signature != null && sb != null) {
+ setParamNames(signature, sb.toString().trim(), false);
+ signature = null;
+ sb = null;
+ }
+ }
+ }
+
+ private void setParamNames(String signature, String names, boolean isCtor) {
+ ArrayList<String> paramTypes = new ArrayList<String>();
+ int idx = -1;
+ for(int i = 0; i < signature.length(); i++) {
+ switch(signature.charAt(i)) {
+ case '-':
+ case '(':
+ case ')':
+ case ',':
+ if (idx > -1 && idx < i - 1) {
+ String typeName = signature.substring(idx + 1, i).trim();
+ if (typeName.endsWith("...")) //NOI18N
+ typeName = typeName.substring(0, typeName.length() - 3) + "[]"; //NOI18N
+ paramTypes.add(typeName);
+ }
+ idx = i;
+ break;
+ }
+ }
+ String methodName = null;
+ ArrayList<String> paramNames = new ArrayList<String>();
+ idx = -1;
+ for(int i = 0; i < names.length(); i++) {
+ switch(names.charAt(i)) {
+ case '(':
+ methodName = names.substring(0, i);
+ break;
+ case ')':
+ case ',':
+ if (idx > -1) {
+ paramNames.add(names.substring(idx + 1, i));
+ idx = -1;
+ }
+ break;
+ case 160: //
+ idx = i;
+ break;
+ }
+ }
+ assert methodName != null : "Null methodName. Signature: [" + signature + "], Names: [" + names + "]";
+ assert paramTypes.size() == paramNames.size() : "Inconsistent param types/names. Signature: [" + signature + "], Names: [" + names + "]";
+ if (paramNames.size() > 0) {
+ for (Symbol s : clazz.members().getSymbolsByName(isCtor
+ ? clazz.name.table.names.init
+ : clazz.name.table.fromString(methodName))) {
+ if (s.kind == Kinds.Kind.MTH && s.owner == clazz) {
+ MethodSymbol sym = (MethodSymbol)s;
+ com.sun.tools.javac.util.List<VarSymbol> params = sym.params;
+ if (checkParamTypes(params, paramTypes)) {
+ setNamesToMethod.accept(sym, paramNames);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean checkParamTypes(com.sun.tools.javac.util.List<VarSymbol> params, ArrayList<String> paramTypes) {
+ Types types = Types.instance(context);
+ for (String typeName : paramTypes) {
+ if (params.isEmpty())
+ return false;
+ Type type = params.head.type;
+ if (type.isParameterized())
+ type = types.erasure(type);
+ if (!typeName.equals(type.toString()))
+ return false;
+ params = params.tail;
+ }
+ return params.isEmpty();
+ }
+ }, charset != null);
+ return true;
+ } catch (ChangedCharSetException e) {
+ if (charset == null) {
+ charset = JavadocHelper.getCharSet(e);
+ //restart with valid charset
+ } else {
+ e.printStackTrace();
+ break;
+ }
+ } catch (InterruptedIOException x) {
+ //Http javadoc timeout
+ break;
+ } catch(IOException ioe){
+ ioe.printStackTrace();
+ break;
+ }finally{
+ parser = null;
+ if (is!=null) {
+ try{
+ is.close();
+ }catch(IOException ioe){
+ ioe.printStackTrace();
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static final Pattern ctor_summary_name = Pattern.compile("constructor[_.]summary"); //NOI18N
+ private static final Pattern method_summary_name = Pattern.compile("method[_.]summary"); //NOI18N
+ private static final Pattern field_detail_name = Pattern.compile("field[_.]detail"); //NOI18N
+ private static final Pattern ctor_detail_name = Pattern.compile("constructor[_.]detail"); //NOI18N
+ private static final Pattern method_detail_name = Pattern.compile("method[_.]detail"); //NOI18N
+
+ private static final int MAX_LEN = 6;
+
+ /**
+ * Utility method for generating method parameter names based on incoming
+ * class name when source is unavailable.
+ * <p/>
+ * This method uses both subjective heuristics to follow common patterns
+ * for common JDK classes, acronym creation for bicapitalized names, and
+ * vowel and repeated character elision if that fails, to generate
+ * readable, programmer-friendly method names.
+ *
+ * @param typeName The fqn of the parameter class
+ * @param used A set of names that have already been used for parameters
+ * and should not be reused, to avoid creating uncompilable code
+ * @return A programmer-friendly parameter name (i.e. not arg0, arg1...)
+ *///public so that TreeLoader can use it:
+ public static @NonNull String generateReadableParameterName (@NonNull String typeName, @NonNull Set<String> used) {
+ boolean arr = typeName.indexOf ("[") > 0 || typeName.endsWith("..."); //NOI18N
+ typeName = trimToSimpleName (typeName);
+ String result = typeName.toLowerCase();
+ //First, do some common, sane substitutions that are common java parlance
+ if ( typeName.endsWith ( "Listener" ) ) { //NOI18N
+ result = Character.toLowerCase(typeName.charAt(0)) + "l"; //NOI18N
+ } else if ( "Object".equals (typeName)) { //NOI18N
+ result = "o"; //NOI18N
+ } else if ("Class".equals(typeName)) { //NOI18N
+ result = "type"; //NOI18N
+ } else if ( "InputStream".equals(typeName)) { //NOI18N
+ result = "in"; //NOI18N
+ } else if ( "OutputStream".equals(typeName)) {
+ result = "out"; //NOI18N
+ } else if ( "Runnable".equals(typeName)) {
+ result = "r"; //NOI18N
+ } else if ( "Lookup".equals(typeName)) {
+ result = "lkp"; //NOI18N
+ } else if ( typeName.endsWith ( "Stream" )) { //NOI18N
+ result = "stream"; //NOI18N
+ } else if ( typeName.endsWith ("Writer")) { //NOI18N
+ result = "writer"; //NOI18N
+ } else if ( typeName.endsWith ("Reader")) { //NOI18N
+ result = "reader"; //NOI18N
+ } else if ( typeName.endsWith ( "Panel" )) { //NOI18N
+ result = "pnl"; //NOI18N
+ } else if ( typeName.endsWith ( "Action" )) { //NOI18N
+ result = "action"; //NOI18N
+ }
+ //Now see if we've made a large and unwieldy variable - people
+ //usually prefer reasonably short but legible arguments
+ if ( result.length () > MAX_LEN ) {
+ //See if we can turn, say, NoClassDefFoundError into "ncdfe"
+ result = tryToMakeAcronym ( typeName );
+ //No luck? We've probably got one long word like Component or Runnable
+ if (result.length() > MAX_LEN) {
+ //First, strip out vowels - people easily figure out words
+ //missing vowels - common in abbreviations and spam mails
+ result = elideVowelsAndRepetitions(result);
+ if (result.length() > MAX_LEN) {
+ //Still too long? Give up and give them a 1 character var name
+ result = new StringBuilder().append(
+ result.charAt(0)).toString().toLowerCase();
+ }
+ }
+ }
+ //Make sure we haven't killed everything - if so, use a generic version
+ if ( result.trim ().length () == 0 ) {
+ result = "value"; //NOI18N
+ }
+ //If it's an array, pluralize it (english language style - but better than nothing)
+ if (arr) {
+ result += "s"; //NOI18N
+ }
+ //Now make sure it's legal; if not, make it a single letter
+ if ( isPrimitiveTypeName ( result ) || !BaseUtilities.isJavaIdentifier ( result ) ) {
+ StringBuilder sb = new StringBuilder();
+ sb.append (result.charAt(0));
+ result = sb.toString();
+ }
+ //Now make sure we're not duplicating a variable name we already used
+ String test = result;
+ int revs = 0;
+ while ( used.contains ( test ) ) {
+ revs++;
+ test = result + revs;
+ }
+ result = test;
+ used.add ( result );
+ return result;
+ }
+
+ /**
+ * Trims to the simple class name and removes and generics
+ *
+ * @param typeName The class name
+ * @return A simplified class name
+ */
+ private static String trimToSimpleName (String typeName) {
+ String result = typeName;
+ int ix = result.indexOf ("<"); //NOI18N
+ if (ix > 0 && ix != typeName.length() - 1) {
+ result = typeName.substring(0, ix);
+ }
+ if (result.endsWith ("...")) { //NOI18N
+ result = result.substring (0, result.length() - 3);
+ }
+ ix = result.lastIndexOf ("$"); //NOI18N
+ if (ix > 0 && ix != result.length() - 1) {
+ result = result.substring(ix + 1);
+ } else {
+ ix = result.lastIndexOf("."); //NOI18N
+ if (ix > 0 && ix != result.length() - 1) {
+ result = result.substring(ix + 1);
+ }
+ }
+ ix = result.indexOf ( "[" ); //NOI18N
+ if ( ix > 0 ) {
+ result = result.substring ( 0, ix );
+ }
+ return result;
+ }
+
+ /**
+ * Removes vowels and repeated letters. This is used to generate names
+ * where the class name a single long word - e.g. abbreviate
+ * Runnable to rnbl
+ * @param name The name
+ * @return A shortened version of it
+ */
+ private static String elideVowelsAndRepetitions (String name) {
+ char[] chars = name.toCharArray();
+ StringBuilder sb = new StringBuilder();
+ char last = 0;
+ char lastUsed = 0;
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ if (Character.isDigit(c)) {
+ continue;
+ }
+ if (i == 0 || Character.isUpperCase(c)) {
+ if (lastUsed != c) {
+ sb.append (c);
+ lastUsed = c;
+ }
+ } else if (c != last && !isVowel(c)) {
+ if (lastUsed != c) {
+ sb.append (c);
+ lastUsed = c;
+ }
+ }
+ last = c;
+ }
+ return sb.toString();
+ }
+
+ private static boolean isVowel(char c) {
+ return Arrays.binarySearch(VOWELS, c) >= 0;
+ }
+
+ /**
+ * Vowels in various indo-european-based languages
+ */
+ private static final char[] VOWELS = new char[] {
+ //IMPORTANT: This array is sorted. If you add to it,
+ //add in the correct place or Arrays.binarySearch will break on it
+ '\u0061', '\u0065', '\u0069', '\u006f', '\u0075', '\u0079', '\u00e9', '\u00ea', //NOI18N
+ '\u00e8', '\u00e1', '\u00e2', '\u00e6', '\u00e0', '\u03b1', '\u00e3', //NOI18N
+ '\u00e5', '\u00e4', '\u00eb', '\u00f3', '\u00f4', '\u0153', '\u00f2', //NOI18N
+ '\u03bf', '\u00f5', '\u00f6', '\u00ed', '\u00ee', '\u00ec', '\u03b9', //NOI18N
+ '\u00ef', '\u00fa', '\u00fb', '\u00f9', '\u03d2', '\u03c5', '\u00fc', //NOI18N
+ '\u0430', '\u043e', '\u044f', '\u0438', '\u0439', '\u0435', '\u044b', //NOI18N
+ '\u044d', '\u0443', '\u044e', };
+
+ //PENDING: The below would be much prettier; whether it survives
+ //cross-platform encoding issues in hg is another question; the hg diff generated
+ //was incorrect
+/*
+ 'a', 'e', 'i', 'o', 'u', 'y', 'à', 'á', //NOI18N
+ 'â', 'ã', 'ä', 'å', 'æ', 'è', 'é', //NOI18N
+ 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ò', //NOI18N
+ 'ó', 'ô', 'õ', 'ö', 'ù', 'ú', 'û', //NOI18N
+ 'ü', 'œ', 'α', 'ι', 'ο', 'υ', 'ϒ', //NOI18N
+ 'а', 'е', 'и', 'й', 'о', 'у', 'ы', //NOI18N
+ 'э', 'ю', 'я'}; //NOI18N
+*/
+ /**
+ * Determine if a string matches a java primitive type. Used in generating reasonable variable names.
+ */
+ private static boolean isPrimitiveTypeName (String typeName) {
+ return (
+ //Whoa, ascii art!
+ "void".equals ( typeName ) || //NOI18N
+ "int".equals ( typeName ) || //NOI18N
+ "long".equals ( typeName ) || //NOI18N
+ "float".equals ( typeName ) || //NOI18N
+ "double".equals ( typeName ) || //NOI18N
+ "short".equals ( typeName ) || //NOI18N
+ "char".equals ( typeName ) || //NOI18N
+ "boolean".equals ( typeName ) ); //NOI18N
+ }
+
+ /**
+ * Try to create an acronym-style variable name from a string - i.e.,
+ * "JavaDataObject" becomes "jdo".
+ */
+ private static String tryToMakeAcronym (String s) {
+ char[] c = s.toCharArray ();
+ StringBuilder sb = new StringBuilder ();
+ for ( int i = 0; i < c.length; i++ ) {
+ if ( Character.isUpperCase (c[i])) {
+ sb.append ( c[ i ] );
+ }
+ }
+ if ( sb.length () > 1 ) {
+ return sb.toString ().toLowerCase ();
+ } else {
+ return s.toLowerCase();
+ }
+ }
+}
diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTest.java
index 78bb6f8..7e9a61a 100644
--- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTest.java
+++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTest.java
@@ -47,6 +47,7 @@ import org.netbeans.api.java.queries.SourceForBinaryQuery.Result;
import org.netbeans.api.java.source.ClasspathInfo.PathKind;
import org.netbeans.modules.java.source.ClassIndexTestCase;
import org.netbeans.modules.java.source.TestUtil;
+import org.netbeans.modules.java.source.parsing.ParameterNameProviderImpl;
import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor;
import org.netbeans.modules.parsing.api.indexing.IndexingManager;
import org.netbeans.modules.parsing.impl.RunWhenScanFinishedSupport;
@@ -612,7 +613,7 @@ public class SourceUtilsTest extends ClassIndexTestCase {
for (int i = 0; i < fqns.length; i++) {
String fqn = fqns[i];
String expected = names[i];
- String got = SourceUtils.generateReadableParameterName(fqn, used);
+ String got = ParameterNameProviderImpl.generateReadableParameterName(fqn, used);
String msg = "For " + Arrays.asList(fqns) + " expected " + Arrays.asList(names);
assertEquals (msg, expected, got);
}
diff --git a/java/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TreeLoader.java b/java/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TreeLoader.java
index 9067e40..93b0c5a 100644
--- a/java/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TreeLoader.java
+++ b/java/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TreeLoader.java
@@ -22,7 +22,6 @@ package org.netbeans.modules.java.source.nbjavac.parsing;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
-import com.sun.source.tree.PackageTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TypeParameterTree;
import com.sun.source.tree.VariableTree;
@@ -31,15 +30,11 @@ import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.api.DuplicateClassChecker;
import com.sun.tools.javac.api.JavacTaskImpl;
-import com.sun.tools.javac.code.Kinds;
-import com.sun.tools.javac.code.Scope;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Symtab;
-import com.sun.tools.javac.code.Type;
-import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Enter;
import com.sun.tools.javac.comp.Env;
@@ -55,19 +50,13 @@ import com.sun.tools.javac.util.CouplingAbort;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
-import java.awt.EventQueue;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
-import java.io.Reader;
import java.net.URL;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -76,15 +65,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.regex.Pattern;
import javax.lang.model.element.Name;
-import javax.swing.text.ChangedCharSetException;
-import javax.swing.text.MutableAttributeSet;
-import javax.swing.text.html.HTML;
-import javax.swing.text.html.HTML.Tag;
-import javax.swing.text.html.HTMLEditorKit;
-import javax.swing.text.html.HTMLEditorKit.ParserCallback;
-import javax.swing.text.html.parser.ParserDelegator;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
@@ -94,16 +75,15 @@ import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.ElementUtilities;
import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.modules.java.source.JavaSourceAccessor;
-import org.netbeans.modules.java.source.JavadocHelper;
import org.netbeans.modules.java.source.indexing.FQN2Files;
import org.netbeans.modules.java.source.indexing.JavaBinaryIndexer;
import org.netbeans.modules.java.source.indexing.JavaIndex;
import org.netbeans.modules.java.source.parsing.FileManagerTransaction;
import org.netbeans.modules.java.source.parsing.FileObjects;
-import org.netbeans.modules.java.source.parsing.JavacParser;
import org.netbeans.modules.java.source.parsing.JavacParser.DuplicateClassRegistry;
import org.netbeans.modules.java.source.parsing.JavacParser.TreeLoaderRegistry;
import org.netbeans.modules.java.source.parsing.OutputFileManager.InvalidSourcePath;
+import org.netbeans.modules.java.source.parsing.ParameterNameProviderImpl;
import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor;
import org.netbeans.modules.parsing.impl.indexing.IndexingUtils;
import org.openide.filesystems.FileObject;
@@ -118,11 +98,6 @@ import org.openide.util.lookup.ServiceProvider;
public class TreeLoader extends LazyTreeLoader {
private static final String OPTION_OUTPUT_ROOT = "output-root"; //NOI18N
- private static final Pattern ctor_summary_name = Pattern.compile("constructor[_.]summary"); //NOI18N
- private static final Pattern method_summary_name = Pattern.compile("method[_.]summary"); //NOI18N
- private static final Pattern field_detail_name = Pattern.compile("field[_.]detail"); //NOI18N
- private static final Pattern ctor_detail_name = Pattern.compile("constructor[_.]detail"); //NOI18N
- private static final Pattern method_detail_name = Pattern.compile("method[_.]detail"); //NOI18N
private static final ThreadLocal<Boolean> isTreeLoading = new ThreadLocal<Boolean>();
public static void preRegister(final Context context, final ClasspathInfo cpInfo, final boolean detached) {
@@ -139,7 +114,6 @@ public class TreeLoader extends LazyTreeLoader {
}
private static final Logger LOGGER = Logger.getLogger(TreeLoader.class.getName());
- private static final boolean ALWAYS_ALLOW_JDOC_ARG_NAMES = Boolean.getBoolean("java.source.args.from.http.jdoc"); //NOI18N
public static boolean DISABLE_CONFINEMENT_TEST = false; //Only for tests!
public static boolean DISABLE_ARTIFICAL_PARAMETER_NAMES = false; //Only for tests!
@@ -259,11 +233,16 @@ public class TreeLoader extends LazyTreeLoader {
try {
assert DISABLE_CONFINEMENT_TEST || JavaSourceAccessor.getINSTANCE().isJavaCompilerLocked() || !contended;
if (clazz != null) {
- JavadocHelper.TextStream page = JavadocHelper.getJavadoc(clazz, ALWAYS_ALLOW_JDOC_ARG_NAMES, null);
- if (page != null && (!page.isRemote() || !EventQueue.isDispatchThread())) {
- if (getParamNamesFromJavadocText(page, clazz)) {
- return true;
+ boolean javadocParams = ParameterNameProviderImpl.fillInParameterNamesFromJavadoc(context, clazz, (method, names) -> {
+ MethodSymbol sym = (MethodSymbol)method;
+ List<VarSymbol> params = sym.params;
+ for (String name : names) {
+ params.head.setName(clazz.name.table.fromString(name));
+ params = params.tail;
}
+ });
+ if (javadocParams) {
+ return true;
}
if (!DISABLE_ARTIFICAL_PARAMETER_NAMES) {
fillArtificalParamNames(clazz);
@@ -529,201 +508,6 @@ public class TreeLoader extends LazyTreeLoader {
return info;
}
- private boolean getParamNamesFromJavadocText(final JavadocHelper.TextStream page, final ClassSymbol clazz) {
- HTMLEditorKit.Parser parser;
- InputStream is = null;
- String charset = null;
- for (;;) {
- try{
- is = page.openStream();
- Reader reader = charset == null ? new InputStreamReader(is): new InputStreamReader(is, charset);
- parser = new ParserDelegator();
- parser.parse(reader, new ParserCallback() {
-
- private int state = 0; //init
- private String signature = null;
- private StringBuilder sb = null;
-
- @Override
- public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
- if (t == HTML.Tag.A) {
- String attrName = (String)a.getAttribute(HTML.Attribute.NAME);
- if (attrName != null && ctor_summary_name.matcher(attrName).matches()) {
- // we have found desired javadoc constructor info anchor
- state = 10; //ctos open
- } else if (attrName != null && method_summary_name.matcher(attrName).matches()) {
- // we have found desired javadoc method info anchor
- state = 20; //methods open
- } else if (attrName != null && field_detail_name.matcher(attrName).matches()) {
- state = 30; //end
- } else if (attrName != null && ctor_detail_name.matcher(attrName).matches()) {
- state = 30; //end
- } else if (attrName != null && method_detail_name.matcher(attrName).matches()) {
- state = 30; //end
- } else if (state == 12 || state == 22) {
- String attrHref = (String)a.getAttribute(HTML.Attribute.HREF);
- if (attrHref != null) {
- int idx = attrHref.indexOf('#');
- if (idx >= 0) {
- signature = attrHref.substring(idx + 1);
- sb = new StringBuilder();
- }
- }
- }
- } else if (t == HTML.Tag.TABLE) {
- if (state == 10 || state == 20)
- state++;
- } else if (t == HTML.Tag.CODE) {
- if (state == 11 || state == 21)
- state++;
- } else if (t == HTML.Tag.DIV && a.containsAttribute(HTML.Attribute.CLASS, "block")) { //NOI18N
- if (state == 11 && signature != null && sb != null) {
- setParamNames(signature, sb.toString().trim(), true);
- signature = null;
- sb = null;
- } else if (state == 21 && signature != null && sb != null) {
- setParamNames(signature, sb.toString().trim(), false);
- signature = null;
- sb = null;
- }
- }
- }
-
- @Override
- public void handleEndTag(Tag t, int pos) {
- if (t == HTML.Tag.CODE) {
- if (state == 12 || state == 22)
- state--;
- } else if (t == HTML.Tag.TABLE) {
- if (state == 11 || state == 21)
- state--;
- }
- }
-
- @Override
- public void handleText(char[] data, int pos) {
- if (signature != null && sb != null && (state == 12 || state == 22))
- sb.append(data);
- }
-
- @Override
- public void handleSimpleTag(Tag t, MutableAttributeSet a, int pos) {
- if (t == HTML.Tag.BR) {
- if (state == 11 && signature != null && sb != null) {
- setParamNames(signature, sb.toString().trim(), true);
- signature = null;
- sb = null;
- } else if (state == 21 && signature != null && sb != null) {
- setParamNames(signature, sb.toString().trim(), false);
- signature = null;
- sb = null;
- }
- }
- }
-
- private void setParamNames(String signature, String names, boolean isCtor) {
- ArrayList<String> paramTypes = new ArrayList<String>();
- int idx = -1;
- for(int i = 0; i < signature.length(); i++) {
- switch(signature.charAt(i)) {
- case '-':
- case '(':
- case ')':
- case ',':
- if (idx > -1 && idx < i - 1) {
- String typeName = signature.substring(idx + 1, i).trim();
- if (typeName.endsWith("...")) //NOI18N
- typeName = typeName.substring(0, typeName.length() - 3) + "[]"; //NOI18N
- paramTypes.add(typeName);
- }
- idx = i;
- break;
- }
- }
- String methodName = null;
- ArrayList<String> paramNames = new ArrayList<String>();
- idx = -1;
- for(int i = 0; i < names.length(); i++) {
- switch(names.charAt(i)) {
- case '(':
- methodName = names.substring(0, i);
- break;
- case ')':
- case ',':
- if (idx > -1) {
- paramNames.add(names.substring(idx + 1, i));
- idx = -1;
- }
- break;
- case 160: //
- idx = i;
- break;
- }
- }
- assert methodName != null : "Null methodName. Signature: [" + signature + "], Names: [" + names + "]";
- assert paramTypes.size() == paramNames.size() : "Inconsistent param types/names. Signature: [" + signature + "], Names: [" + names + "]";
- if (paramNames.size() > 0) {
- for (Symbol s : clazz.members().getSymbolsByName(isCtor
- ? clazz.name.table.names.init
- : clazz.name.table.fromString(methodName))) {
- if (s.kind == Kinds.Kind.MTH && s.owner == clazz) {
- MethodSymbol sym = (MethodSymbol)s;
- List<VarSymbol> params = sym.params;
- if (checkParamTypes(params, paramTypes)) {
- for (String name : paramNames) {
- params.head.setName(clazz.name.table.fromString(name));
- params = params.tail;
- }
- }
- }
- }
- }
- }
-
- private boolean checkParamTypes(List<VarSymbol> params, ArrayList<String> paramTypes) {
- Types types = Types.instance(context);
- for (String typeName : paramTypes) {
- if (params.isEmpty())
- return false;
- Type type = params.head.type;
- if (type.isParameterized())
- type = types.erasure(type);
- if (!typeName.equals(type.toString()))
- return false;
- params = params.tail;
- }
- return params.isEmpty();
- }
- }, charset != null);
- return true;
- } catch (ChangedCharSetException e) {
- if (charset == null) {
- charset = JavadocHelper.getCharSet(e);
- //restart with valid charset
- } else {
- e.printStackTrace();
- break;
- }
- } catch (InterruptedIOException x) {
- //Http javadoc timeout
- break;
- } catch(IOException ioe){
- ioe.printStackTrace();
- break;
- }finally{
- parser = null;
- if (is!=null) {
- try{
- is.close();
- }catch(IOException ioe){
- ioe.printStackTrace();
- }
- }
- }
- }
- return false;
- }
-
private void fillArtificalParamNames(final ClassSymbol clazz) {
for (Symbol s : clazz.getEnclosedElements()) {
if (s instanceof MethodSymbol) {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists