Skip to content

Commit

Permalink
In Surround with try-catch hint add/default to System.Logger.
Browse files Browse the repository at this point in the history
This default is also used by `Generate > Logger...`.
Addresses some issues in raised in #8240.
New feature `Use existing declared Logger`.
Fix "Logging" hints to handle System.Logger.
  • Loading branch information
errael committed Feb 16, 2025
1 parent 102f069 commit 7d026d2
Show file tree
Hide file tree
Showing 13 changed files with 460 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,27 @@
*/
package org.netbeans.modules.java.editor.codegen;

import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
Expand All @@ -37,18 +50,9 @@
import javax.lang.model.util.ElementFilter;
import javax.swing.text.JTextComponent;

import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;

import org.netbeans.api.java.source.CodeStyle;
import org.netbeans.api.java.source.CompilationController;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.ModificationResult;
import org.netbeans.api.java.source.Task;
Expand All @@ -58,9 +62,12 @@
import org.netbeans.modules.java.completion.Utilities;
import org.netbeans.modules.java.editor.codegen.ui.ElementNode;
import org.netbeans.spi.editor.codegen.CodeGenerator;
import org.netbeans.spi.editor.hints.settings.FileHintPreferences;
import org.openide.filesystems.FileObject;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;

/**
*
Expand Down Expand Up @@ -94,24 +101,29 @@ public List<? extends CodeGenerator> create(Lookup context) {
if (typeElement == null || !typeElement.getKind().isClass()) {
return ret;
}
boolean isSystemLogger = isUseSystemLogger(controller);
String loggerFQN = isSystemLogger ? "java.lang.System.Logger" : Logger.class.getName();
for (VariableElement ve : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
TypeMirror type = ve.asType();
if (type.getKind() == TypeKind.DECLARED && ((TypeElement)((DeclaredType)type).asElement()).getQualifiedName().contentEquals(Logger.class.getName())) {
if (type.getKind() == TypeKind.DECLARED && ((TypeElement)((DeclaredType)type).asElement()).getQualifiedName().contentEquals(loggerFQN)) {
return ret;
}
}
List<ElementNode.Description> descriptions = new ArrayList<>();
ret.add(new LoggerGenerator(component, ElementNode.Description.create(controller, typeElement, descriptions, false, false)));
ret.add(new LoggerGenerator(component, ElementNode.Description.create(controller, typeElement, descriptions, false, false), isSystemLogger));
return ret;
}
}

private final JTextComponent component;
private final ElementNode.Description description;
private final boolean isSystemLogger;

/** Creates a new instance of ToStringGenerator */
private LoggerGenerator(JTextComponent component, ElementNode.Description description) {
private LoggerGenerator(JTextComponent component, ElementNode.Description description, boolean isSystemLogger) {
this.component = component;
this.description = description;
this.isSystemLogger = isSystemLogger;
}

@Override
Expand Down Expand Up @@ -139,8 +151,8 @@ public void run(WorkingCopy copy) throws IOException {
ClassTree cls = (ClassTree) path.getLeaf();
CodeStyle cs = CodeStyle.getDefault(component.getDocument());
Set<Modifier> mods = EnumSet.of(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL);
List<String> names = Utilities.varNamesSuggestions(null, ElementKind.FIELD, mods, "LOG", null, copy.getTypes(), copy.getElements(), e.getEnclosedElements(), cs);
VariableTree var = createLoggerField(copy.getTreeMaker(), cls, names.size() > 0 ? names.get(0) : "LOG", mods); //NOI18N
List<String> names = Utilities.varNamesSuggestions(null, ElementKind.FIELD, mods, getBaseLoggerName(), null, copy.getTypes(), copy.getElements(), e.getEnclosedElements(), cs);
VariableTree var = createLoggerField(copy.getTreeMaker(), cls, names.size() > 0 ? names.get(0) : getBaseLoggerName(), mods, isSystemLogger); //NOI18N
copy.rewrite(cls, GeneratorUtils.insertClassMembers(copy, cls, Collections.singletonList(var), caretOffset));
}
}
Expand All @@ -152,15 +164,65 @@ public void run(WorkingCopy copy) throws IOException {
}
}

public static String getBaseLoggerName() {
// Undocumented feature/property subject to change/removal. Need a UI.
String name = System.getProperty("LOGGER_GENERATOR_BASE_LOGGER_NAME");
return name == null ? "LOG" : name;
}

/** if info is null return the default, not project, setting. */
public static boolean isUseSystemLogger(CompilationInfo info) {
if (info != null && info.getSourceVersion().compareTo(SourceVersion.RELEASE_9) < 0)
return false;

FileObject fo = info != null ? info.getFileObject() : null;
String keyHack = "surround-try-catch-java-lang-System-Logger";
boolean v = true; // default
try {
Preferences root = null;
String nodeHack = "org.netbeans.modules.java.hints.errors.ErrorFixesFakeHintSURROUND_WITH_TRY_CATCH";
if (fo == null) {
// Can't get the project value, get the default.
nodeHack = "org/netbeans/modules/java/hints/default/" + nodeHack;
root = NbPreferences.root();
} else {
// Project value.
root = FileHintPreferences.getFilePreferences(fo, "text/x-java");
}
Preferences tryCatch = null;
if (root.nodeExists(nodeHack)) {
tryCatch = root.node(nodeHack);
}

if (tryCatch != null) {
v = tryCatch.getBoolean(keyHack, v);
}
} catch(BackingStoreException ex) {
}
return v;
}

/** Use the default logger. */
public static VariableTree createLoggerField(TreeMaker make, ClassTree cls, CharSequence name, Set<Modifier> mods) {
return createLoggerField(make, cls, name, mods, isUseSystemLogger(null));
}

/** Use the project's default logger. */
public static VariableTree createLoggerField(TreeMaker make, ClassTree cls, CharSequence name, Set<Modifier> mods, CompilationInfo info) {
return createLoggerField(make, cls, name, mods, isUseSystemLogger(info));
}

private static VariableTree createLoggerField(TreeMaker make, ClassTree cls, CharSequence name, Set<Modifier> mods, boolean useSystemLogger) {
ModifiersTree modifiers = make.Modifiers(mods, Collections.<AnnotationTree>emptyList());
final List<ExpressionTree> none = Collections.<ExpressionTree>emptyList();
IdentifierTree className = make.Identifier(cls.getSimpleName());
MemberSelectTree classType = make.MemberSelect(className, "class"); // NOI18N
MemberSelectTree getName = make.MemberSelect(classType, "getName"); // NOI18N
MethodInvocationTree initClass = make.MethodInvocation(none, getName, none);
final ExpressionTree logger = make.QualIdent(Logger.class.getName());
MemberSelectTree getLogger = make.MemberSelect(logger, "getLogger"); // NOI18N
final ExpressionTree logger = make.QualIdent(useSystemLogger ? "java.lang.System.Logger" : Logger.class.getName());
final ExpressionTree loggerProvider = useSystemLogger ? make.QualIdent("java.lang.System") : logger;

MemberSelectTree getLogger = make.MemberSelect(loggerProvider, "getLogger"); // NOI18N
MethodInvocationTree initField = make.MethodInvocation(none, getLogger, Collections.nCopies(1, initClass));
return make.Variable(modifiers, name, logger, initField); // NOI18N
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;

import java.util.EnumSet;
import java.util.Set;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;

import org.netbeans.api.java.source.TreePathHandle;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.spi.editor.hints.ErrorDescription;
Expand All @@ -47,7 +50,9 @@ public class LoggerNotStaticFinal {

@TriggerPatterns({
@TriggerPattern(value="$mods$ java.util.logging.Logger $LOG;"), //NOI18N
@TriggerPattern(value="$mods$ java.util.logging.Logger $LOG = $init;") //NOI18N
@TriggerPattern(value="$mods$ java.util.logging.Logger $LOG = $init;"), //NOI18N
@TriggerPattern(value="$mods$ java.lang.System.Logger $LOG;"), //NOI18N
@TriggerPattern(value="$mods$ java.lang.System.Logger $LOG = $init;") //NOI18N
})
public static ErrorDescription checkLoggerDeclaration(HintContext ctx) {
Element e = ctx.getInfo().getTrees().getElement(ctx.getPath());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@

import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;

import java.util.LinkedList;
import java.util.List;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
Expand All @@ -31,6 +33,7 @@
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;

import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
import org.netbeans.spi.java.hints.Hint;
Expand Down Expand Up @@ -67,14 +70,25 @@ public static Iterable<ErrorDescription> checkMultipleLoggers(HintContext ctx) {
return null;
}

// "sysLoggerTypeElement" may be null if pre Java9; so not a problem.
TypeElement sysLoggerTypeElement = ctx.getInfo().getElements().getTypeElement("java.lang.System.Logger"); // NOI18N
TypeMirror sysLoggerTypeElementAsType = null;
if (sysLoggerTypeElement != null) {
sysLoggerTypeElementAsType = sysLoggerTypeElement.asType();
if (sysLoggerTypeElementAsType == null || sysLoggerTypeElementAsType.getKind() != TypeKind.DECLARED) {
return null;
}
}

List<VariableElement> loggerFields = new LinkedList<VariableElement>();
List<VariableElement> fields = ElementFilter.fieldsIn(cls.getEnclosedElements());
for(VariableElement f : fields) {
if (f.getKind() != ElementKind.FIELD) {
continue;
}

if (f.asType().equals(loggerTypeElementAsType)) {
if (f.asType().equals(loggerTypeElementAsType)
|| f.asType().equals(sysLoggerTypeElementAsType)) {
loggerFields.add(f);
}
}
Expand Down
38 changes: 29 additions & 9 deletions java/java.hints/src/org/netbeans/modules/java/hints/NoLoggers.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.EnumSet;
import java.util.prefs.Preferences;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
Expand All @@ -45,9 +46,11 @@
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.swing.JComponent;

import org.netbeans.api.java.source.TreeMaker;
import org.netbeans.api.java.source.TreePathHandle;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.modules.java.editor.codegen.LoggerGenerator;
import org.netbeans.modules.java.hints.NoLoggers.NoLoggersCustomizer;
import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.java.hints.CustomizerProvider;
Expand Down Expand Up @@ -86,6 +89,16 @@ public static Iterable<ErrorDescription> checkNoLoggers(HintContext ctx) {
return null;
}

// "sysLoggerTypeElement" may be null if pre Java9; so not a problem.
TypeElement sysLoggerTypeElement = ctx.getInfo().getElements().getTypeElement("java.lang.System.Logger"); // NOI18N
TypeMirror sysLoggerTypeElementAsType = null;
if (sysLoggerTypeElement != null) {
sysLoggerTypeElementAsType = sysLoggerTypeElement.asType();
if (sysLoggerTypeElementAsType == null || sysLoggerTypeElementAsType.getKind() != TypeKind.DECLARED) {
return null;
}
}

List<TypeMirror> customLoggersList = new ArrayList<>();
if (isCustomEnabled(ctx.getPreferences())) {
List<String> customLoggerClasses = getCustomLoggers(ctx.getPreferences());
Expand All @@ -111,7 +124,8 @@ public static Iterable<ErrorDescription> checkNoLoggers(HintContext ctx) {
continue;
}

if (f.asType().equals(loggerTypeElementAsType)) {
if (f.asType().equals(loggerTypeElementAsType)
|| f.asType().equals(sysLoggerTypeElementAsType)) {
loggerFields.add(f);
} else if (customLoggersList.contains(f.asType())) {
loggerFields.add(f);
Expand Down Expand Up @@ -179,17 +193,20 @@ protected void performRewrite(TransformationContext ctx) {
return;
}

boolean useSystemLogger = LoggerGenerator.isUseSystemLogger(wc);

// find free field name
String baseLoggerFieldName = LoggerGenerator.getBaseLoggerName();
String loggerFieldName = null;
List<VariableElement> fields = ElementFilter.fieldsIn(cls.getEnclosedElements());
if (!contains(fields, "LOG")) { //NOI18N
loggerFieldName = "LOG"; //NOI18N
if (!contains(fields, baseLoggerFieldName)) { //NOI18N
loggerFieldName = baseLoggerFieldName; //NOI18N
} else {
if (!contains(fields, "LOGGER")) { //NOI18N
loggerFieldName = "LOGGER"; //NOI18N
} else {
for(int i = 1; i < Integer.MAX_VALUE; i++) {
String n = "LOG" + i; //NOI18N
String n = baseLoggerFieldName + i; //NOI18N
if (!contains(fields, n)) {
loggerFieldName = n;
break;
Expand All @@ -207,15 +224,18 @@ protected void performRewrite(TransformationContext ctx) {
ModifiersTree mt = m.Modifiers(mods);

// logger type
TypeElement loggerTypeElement = wc.getElements().getTypeElement("java.util.logging.Logger"); // NOI18N
if (loggerTypeElement == null) {
TypeElement loggerTypeElement = wc.getElements().getTypeElement(
useSystemLogger ? "java.lang.System.Logger" : "java.util.logging.Logger"); // NOI18N
TypeElement loggerFactoryTypeElement = !useSystemLogger ? loggerTypeElement : wc.getElements().getTypeElement("java.lang.System");
if (loggerTypeElement == null || loggerFactoryTypeElement == null) {
// TODO: report to the user
return;
}
ExpressionTree loggerClassQualIdent = m.QualIdent(loggerTypeElement);
ExpressionTree loggerFactoryClassQualIdent = m.QualIdent(loggerFactoryTypeElement);

// initializer
MemberSelectTree getLogger = m.MemberSelect(loggerClassQualIdent, "getLogger"); //NOI18N
MemberSelectTree getLogger = m.MemberSelect(loggerFactoryClassQualIdent, "getLogger"); //NOI18N
ExpressionTree initializer = m.MethodInvocation(
Collections.<ExpressionTree>emptyList(),
getLogger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,6 @@ FIX_EnablePreviewFeature=Enable Preview Feature
FIX_EnablePreviewFeatureSetSourceLevel=Enable Preview Feature and Set Source Level To {0}
# {0} - source level
FIX_EnablePreviewFeatureSetSourceLevelManual=Enable Preview Feature (Set Source Level To {0} Manually)
SurroundWithTryCatchLog.jLabel2.text=When possible:
SurroundWithTryCatchLog.systemLogger.text=Use java.lang.System.Logger
SurroundWithTryCatchLog.existing.text=Use existing declared logger
Loading

0 comments on commit 7d026d2

Please sign in to comment.