From 7fa964ff89b8a44a708c7f63a40733bf9948a5d4 Mon Sep 17 00:00:00 2001 From: MartinWitt Date: Fri, 28 Oct 2022 20:03:15 +0200 Subject: [PATCH] feat: add inner_class_may_be_static rule (#239) --- .../analyzer/qodana/QodanaRules.java | 4 +- .../qodana/rules/InnerClassMayBeStatic.java | 77 ++++ .../spoon/code_solver/history/Change.java | 12 +- .../qodana/InnerClassMayBeStaticTest.java | 23 ++ .../InnerClassMayBeStatic/Landlordbase.java | 328 ++++++++++++++++++ .../Landlordbase.java.correct | 328 ++++++++++++++++++ .../api/BadSmellGraphQLTest.java | 8 +- settings.gradle | 2 +- 8 files changed, 775 insertions(+), 7 deletions(-) create mode 100644 code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/analyzer/qodana/rules/InnerClassMayBeStatic.java create mode 100644 code-transformation/src/test/java/xyz/keksdose/spoon/code_solver/transformations/qodana/InnerClassMayBeStaticTest.java create mode 100644 code-transformation/src/test/resources/projects/refactorings/InnerClassMayBeStatic/Landlordbase.java create mode 100644 code-transformation/src/test/resources/projects/refactorings/InnerClassMayBeStatic/Landlordbase.java.correct diff --git a/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/analyzer/qodana/QodanaRules.java b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/analyzer/qodana/QodanaRules.java index e31d2d23e..4b0067cf7 100644 --- a/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/analyzer/qodana/QodanaRules.java +++ b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/analyzer/qodana/QodanaRules.java @@ -4,6 +4,7 @@ import java.util.function.Function; import xyz.keksdose.spoon.code_solver.analyzer.AnalyzerRule; import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.AbstractRefactoring; +import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.InnerClassMayBeStatic; import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.MethodMayBeStatic; import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.NonProtectedConstructorInAbstractClass; import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.NonStrictComparisonCanBeEquality; @@ -35,7 +36,8 @@ public enum QodanaRules implements AnalyzerRule { UNUSED_IMPORT("UnusedImport", UnusedImport::new), PROTECTED_MEMBER_IN_FINAL_CLASS("ProtectedMemberInFinalClass", ProtectedMemberInFinalClass::new), UNNECESSARY_MODIFIER("UnnecessaryModifier", UnnecessaryModifier::new), - POINTLESS_BOOLEAN_EXPRESSION("PointlessBooleanExpression", PointlessBooleanExpression::new); + POINTLESS_BOOLEAN_EXPRESSION("PointlessBooleanExpression", PointlessBooleanExpression::new), + INNER_CLASS_MAY_BE_STATIC("InnerClassMayBeStatic", InnerClassMayBeStatic::new); private final String ruleId; private final Function refactoring; diff --git a/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/analyzer/qodana/rules/InnerClassMayBeStatic.java b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/analyzer/qodana/rules/InnerClassMayBeStatic.java new file mode 100644 index 000000000..3f7f40096 --- /dev/null +++ b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/analyzer/qodana/rules/InnerClassMayBeStatic.java @@ -0,0 +1,77 @@ +package xyz.keksdose.spoon.code_solver.analyzer.qodana.rules; + +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.ModifierKind; +import xyz.keksdose.spoon.code_solver.analyzer.PositionScanner; +import xyz.keksdose.spoon.code_solver.api.analyzer.AnalyzerResult; +import xyz.keksdose.spoon.code_solver.history.Change; +import xyz.keksdose.spoon.code_solver.history.ChangeListener; +import xyz.keksdose.spoon.code_solver.history.MarkdownString; +import xyz.keksdose.spoon.code_solver.transformations.BadSmell; + +public class InnerClassMayBeStatic extends AbstractRefactoring { + + private static final BadSmell badSmell = new BadSmell() { + @Override + public MarkdownString getName() { + return MarkdownString.fromMarkdown("InnerClassMayBeStatic"); + } + + @Override + public MarkdownString getDescription() { + return MarkdownString.fromMarkdown( + """ + Inner classes that do not reference their enclosing instances can be made static. + This prevents a common cause of memory leaks and uses less memory per instance of the class. + """); + } + }; + + public InnerClassMayBeStatic(AnalyzerResult result) { + super(result); + } + + @Override + public void refactor(ChangeListener listener, CtType type) { + if (!isSameType(type, Path.of(result.filePath()))) { + return; + } + for (CtType ctType : filterMatches(PositionScanner.findLineOnly(type, result.position()))) { + var modifiers = new HashSet<>(ctType.getModifiers()); + modifiers.add(ModifierKind.STATIC); + ctType.setModifiers(modifiers); + String format = "Add to inner class %s modifier static"; + String markdown = String.format(format, "`" + ctType.getSimpleName() + "`"); + String text = String.format(format, ctType.getQualifiedName()); + MarkdownString markdownString = MarkdownString.fromMarkdown(text, markdown); + Change change = new Change(badSmell, markdownString, getMostOuterType(type), result); + listener.setChanged(getMostOuterType(type), change); + } + } + + private Iterable> filterMatches(List findLineOnly) { + return findLineOnly.stream() + .filter(v -> v instanceof CtType) + .map(v -> (CtType) v) + .filter(v -> !v.isTopLevel()) + .filter(v -> !v.getModifiers().contains(ModifierKind.STATIC)) + .toList(); + } + + @Override + public List getHandledBadSmells() { + return List.of(badSmell); + } + + private CtType getMostOuterType(CtType inner) { + if (inner.getDeclaringType() == null) { + return inner; + } else { + return getMostOuterType(inner.getDeclaringType()); + } + } +} diff --git a/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/history/Change.java b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/history/Change.java index 13d54a319..04d47db42 100644 --- a/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/history/Change.java +++ b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/history/Change.java @@ -17,14 +17,14 @@ public class Change { public Change(String text, String issue, CtType affectedType) { this.text = MarkdownString.fromRaw(text); this.issue = issue; - this.affectedType = affectedType; + this.affectedType = getMostOuterType(affectedType); } public Change(BadSmell badSmell, MarkdownString text, CtType affectedType) { this.text = text; this.issue = badSmell.getName().asText(); this.badsmell = badSmell; - this.affectedType = affectedType; + this.affectedType = getMostOuterType(affectedType); } public Change(BadSmell badSmell, MarkdownString text, CtType affectedType, AnalyzerResult analyzerResult) { @@ -85,4 +85,12 @@ public boolean equals(Object obj) { public @Nullable AnalyzerResult getAnalyzerResult() { return analyzerResult; } + + private CtType getMostOuterType(CtType inner) { + if (inner.getDeclaringType() == null) { + return inner; + } else { + return getMostOuterType(inner.getDeclaringType()); + } + } } diff --git a/code-transformation/src/test/java/xyz/keksdose/spoon/code_solver/transformations/qodana/InnerClassMayBeStaticTest.java b/code-transformation/src/test/java/xyz/keksdose/spoon/code_solver/transformations/qodana/InnerClassMayBeStaticTest.java new file mode 100644 index 000000000..9337900d7 --- /dev/null +++ b/code-transformation/src/test/java/xyz/keksdose/spoon/code_solver/transformations/qodana/InnerClassMayBeStaticTest.java @@ -0,0 +1,23 @@ +package xyz.keksdose.spoon.code_solver.transformations.qodana; + +import java.io.File; +import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.InnerClassMayBeStatic; +import xyz.keksdose.spoon.code_solver.api.analyzer.AnalyzerResult; +import xyz.keksdose.spoon.code_solver.api.analyzer.Position; +import xyz.keksdose.spoon.code_solver.transformations.TestAnalyzerResult; +import xyz.keksdose.spoon.code_solver.transformations.TransformationTestUtils; + +public class InnerClassMayBeStaticTest { + + @Test + void landLordInnerClassMabyBeStatic(@TempDir File dir) throws IOException { + Position position = new Position(296, 0, 0, 0, 0, 80); + AnalyzerResult result = new TestAnalyzerResult("Landlordbase.java", position); + String resourcePath = "projects/refactorings/InnerClassMayBeStatic/Landlordbase.java"; + var copy = TransformationTestUtils.transform(new InnerClassMayBeStatic(result), resourcePath, dir); + TransformationTestUtils.compareContent(copy, resourcePath); + } +} diff --git a/code-transformation/src/test/resources/projects/refactorings/InnerClassMayBeStatic/Landlordbase.java b/code-transformation/src/test/resources/projects/refactorings/InnerClassMayBeStatic/Landlordbase.java new file mode 100644 index 000000000..f12d18865 --- /dev/null +++ b/code-transformation/src/test/resources/projects/refactorings/InnerClassMayBeStatic/Landlordbase.java @@ -0,0 +1,328 @@ +package biz.princeps.landlord.commands; + +import biz.princeps.landlord.api.ILandLord; +import biz.princeps.landlord.api.ILangManager; +import biz.princeps.landlord.api.Options; +import biz.princeps.landlord.commands.admin.AdminClaim; +import biz.princeps.landlord.commands.admin.AdminTeleport; +import biz.princeps.landlord.commands.admin.Clear; +import biz.princeps.landlord.commands.admin.ClearInactive; +import biz.princeps.landlord.commands.admin.Debug; +import biz.princeps.landlord.commands.admin.GiveClaims; +import biz.princeps.landlord.commands.admin.Reload; +import biz.princeps.landlord.commands.admin.Update; +import biz.princeps.landlord.commands.claiming.Claim; +import biz.princeps.landlord.commands.claiming.Claims; +import biz.princeps.landlord.commands.claiming.MultiClaim; +import biz.princeps.landlord.commands.claiming.MultiUnclaim; +import biz.princeps.landlord.commands.claiming.Shop; +import biz.princeps.landlord.commands.claiming.Unclaim; +import biz.princeps.landlord.commands.claiming.UnclaimAll; +import biz.princeps.landlord.commands.claiming.adv.Advertise; +import biz.princeps.landlord.commands.claiming.adv.RemoveAdvertise; +import biz.princeps.landlord.commands.friends.Addfriend; +import biz.princeps.landlord.commands.friends.AddfriendAll; +import biz.princeps.landlord.commands.friends.ListFriends; +import biz.princeps.landlord.commands.friends.MultiAddfriend; +import biz.princeps.landlord.commands.friends.MultiRemovefriend; +import biz.princeps.landlord.commands.friends.Unfriend; +import biz.princeps.landlord.commands.friends.UnfriendAll; +import biz.princeps.landlord.commands.homes.Home; +import biz.princeps.landlord.commands.homes.SetHome; +import biz.princeps.landlord.commands.management.Info; +import biz.princeps.landlord.commands.management.LandMap; +import biz.princeps.landlord.commands.management.ListLands; +import biz.princeps.landlord.commands.management.Manage; +import biz.princeps.landlord.commands.management.ManageAll; +import biz.princeps.landlord.commands.management.MultiListLands; +import biz.princeps.landlord.commands.management.MultiManage; +import biz.princeps.landlord.commands.management.Regenerate; +import biz.princeps.landlord.commands.management.borders.Borders; +import biz.princeps.landlord.multi.MultiMode; +import biz.princeps.lib.chat.MultiPagedMessage; +import biz.princeps.lib.command.Arguments; +import biz.princeps.lib.command.MainCommand; +import biz.princeps.lib.command.Properties; +import biz.princeps.lib.command.SubCommand; +import com.google.common.collect.Sets; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Project: LandLord + * Created by Alex D. (SpatiumPrinceps) + * Date: 16/07/17 + *

+ * This command protection may look a bit unfamiliar. It is based on shitty system I programmed a long time ago (PrincepsLib) + * Basically a single command is created by extending MainCommand. For example you would do: + * class HealCommand extends MainCommand {...} // introduces a heal command + * Landlordbase describes the base command ./landlord + * Subcommands are created by creating a class, that extends SubCommand. Add that one as subcmd. + * Always call super(cmdname, description, usage, permissions, aliases) to initialize the (sub)command with everything + * it needs to work. + */ +public class Landlordbase extends MainCommand { + + private final ILandLord plugin; + + public Landlordbase(ILandLord plugin) { + super(plugin.getConfig().getString("CommandSettings.Main.name"), + plugin.getConfig().getString("CommandSettings.Main.description"), + plugin.getConfig().getString("CommandSettings.Main.usage"), + Sets.newHashSet(plugin.getConfig().getStringList("CommandSettings.Main.permissions")), + plugin.getConfig().getStringList("CommandSettings.Main.aliases").toArray(new String[]{})); + this.plugin = plugin; + reloadCommands(); + } + + /** + * Reloads all commands. Reinitialisation, to pick up changed config variables + */ + private void reloadCommands() { + this.clearSubcommands(); + this.addSubcommand(new Version()); + this.addSubcommand(new Confirm()); + + this.addSubcommand(new Info(plugin)); + this.addSubcommand(new Claim(plugin, false)); + this.addSubcommand(new Unclaim(plugin)); + this.addSubcommand(new UnclaimAll(plugin)); + this.addSubcommand(new Addfriend(plugin)); + this.addSubcommand(new AddfriendAll(plugin)); + this.addSubcommand(new Unfriend(plugin)); + this.addSubcommand(new UnfriendAll(plugin)); + this.addSubcommand(new Advertise(plugin)); + this.addSubcommand(new RemoveAdvertise(plugin)); + this.addSubcommand(new ListFriends(plugin)); + this.addSubcommand(new ListLands(plugin)); + this.addSubcommand(new Claims(plugin)); + this.addSubcommand(new Shop(plugin)); + this.addSubcommand(new GiveClaims(plugin)); + this.addSubcommand(new Update(plugin)); + this.addSubcommand(new AdminTeleport(plugin)); + this.addSubcommand(new AdminClaim(plugin)); + this.addSubcommand(new MultiClaim(plugin)); + this.addSubcommand(new Borders(plugin)); + this.addSubcommand(new Home(plugin)); + this.addSubcommand(new SetHome(plugin)); + this.addSubcommand(new Manage(plugin)); + this.addSubcommand(new ManageAll(plugin)); + this.addSubcommand(new Clear(plugin)); + this.addSubcommand(new ClearInactive(plugin)); + this.addSubcommand(new LandMap(plugin)); + this.addSubcommand(new Reload(plugin)); + this.addSubcommand(new Regenerate(plugin)); + this.addSubcommand(new MultiUnclaim(plugin)); + this.addSubcommand(new MultiAddfriend(plugin)); + this.addSubcommand(new MultiRemovefriend(plugin)); + this.addSubcommand(new MultiListLands(plugin)); + this.addSubcommand(new MultiManage(plugin)); + this.addSubcommand(new Debug(plugin)); + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + List tabReturn = new ArrayList<>(); + + // Length == 1 means there is just the first thing like /ll typed + if (args.length == 1) { + for (SubCommand subCommand : this.subCommandMap.values()) { + if (subCommand.hasPermission(sender)) { + if (subCommand instanceof Borders) { + if (Options.enabled_borders()) { + tabReturn.add(subCommand.getName()); + } + } else if (subCommand instanceof LandMap) { + if (Options.enabled_map()) { + tabReturn.add(subCommand.getName()); + } + } else if (subCommand instanceof Shop || subCommand instanceof GiveClaims) { + if (Options.enabled_shop()) { + tabReturn.add(subCommand.getName()); + } + } else if (subCommand instanceof Home || subCommand instanceof SetHome) { + if (Options.enabled_homes()) { + tabReturn.add(subCommand.getName()); + } + } else { + tabReturn.add(subCommand.getName()); + } + } + } + + if (!args[0].isEmpty()) { + tabReturn.removeIf(next -> !next.startsWith(args[0])); + } + } else if (args.length == 2) { + for (SubCommand subCommand : subCommandMap.values()) { + if (subCommand.matches(args[0])) { + if (subCommand instanceof GiveClaims) { + tabReturn.add(""); + for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { + tabReturn.add(onlinePlayer.getName()); + } + return tabReturn; + } + + if (subCommand instanceof AdminTeleport) { + for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { + tabReturn.add(onlinePlayer.getName()); + } + return tabReturn; + } + + if (subCommand instanceof LandMap) { + tabReturn.add("on"); + tabReturn.add("off"); + return tabReturn; + } + + if (subCommand instanceof Addfriend || subCommand instanceof AddfriendAll || + subCommand instanceof Unfriend || subCommand instanceof UnfriendAll) { + for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { + if (!onlinePlayer.getName().startsWith(args[1]) || + (sender instanceof Player && !((Player) sender).canSee(onlinePlayer))) + continue; + + tabReturn.add(onlinePlayer.getName()); + } + return tabReturn; + } + + if (subCommand instanceof MultiClaim || subCommand instanceof MultiUnclaim || + subCommand instanceof MultiAddfriend || subCommand instanceof MultiRemovefriend || + subCommand instanceof MultiListLands || subCommand instanceof MultiManage) { + for (MultiMode multiMode : MultiMode.values()) { + tabReturn.add(multiMode.name()); + } + return tabReturn; + } + + if (subCommand instanceof UnclaimAll) { + for (World world : plugin.getServer().getWorlds()) { + tabReturn.add(world.getName()); + } + return tabReturn; + } + + if (subCommand instanceof Update) { + tabReturn.add("-u"); + tabReturn.add("-r"); + tabReturn.add("-c"); + return tabReturn; + } + } + } + } else if (args.length == 3) { + for (SubCommand subCommand : subCommandMap.values()) { + if (subCommand.matches(args[0])) { + if (subCommand instanceof GiveClaims) { + tabReturn.add(""); + tabReturn.add(""); + return tabReturn; + } + + if (subCommand instanceof MultiAddfriend || subCommand instanceof MultiRemovefriend) { + for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { + if (!onlinePlayer.getName().startsWith(args[2]) || + (sender instanceof Player && !((Player) sender).canSee(onlinePlayer))) + continue; + + tabReturn.add(onlinePlayer.getName()); + } + return tabReturn; + } + } + } + } else if (args.length == 4) { + for (SubCommand subCommand : subCommandMap.values()) { + if (subCommand.matches(args[0])) { + if (subCommand instanceof GiveClaims) { + tabReturn.add(""); + } + } + } + } + return tabReturn; + } + + /** + * Main onCommand function of ./landlord. + * Display the help menu here. + * This function is not called for subcommands like ./ll claim + * + * @param properties a cool properties object, that contains stuff like isPlayer, isConsole + * @param arguments the arguments passed here. + */ + @Override + public void onCommand(Properties properties, Arguments arguments) { + if (properties.isConsole()) + return; + + ILangManager lm = plugin.getLangManager(); + List playersList = lm.getStringList("Commands.Help.players"); + List adminList = lm.getStringList("Commands.Help.admins"); + + int perSite = plugin.getConfig().getInt("HelpCommandPerSite"); + + String[] argsN = new String[1]; + if (arguments.get().length == 1) { + argsN[0] = (arguments.get(0) == null ? "0" : arguments.get(0)); + } + + List toDisplay = new ArrayList<>(); + if (properties.getPlayer().hasPermission("landlord.admin.help")) + toDisplay.addAll(adminList); + toDisplay.addAll(playersList); + + // plugin.getLogger().log(Level.INFO, String.valueOf(toDisplay.size())); + + MultiPagedMessage msg = new MultiPagedMessage.Builder() + .setElements(toDisplay) + .setPerSite(perSite) + .setHeaderString(lm.getRawString("Commands.Help.header")) + .setNextString(lm.getRawString("Commands.Help.next")) + .setPreviousString(lm.getRawString("Commands.Help.previous")) + .setCommand(plugin.getConfig().getString("CommandSettings.Main.name"), argsN).build(); + plugin.getUtilsManager().sendBasecomponent(properties.getPlayer(), msg.create()); + } + + public class Confirm extends SubCommand { + + public Confirm() { + super("confirm", + "/ll help", + Sets.newHashSet(Collections.singleton("landlord.use")), + Sets.newHashSet()); + } + + @Override + public void onCommand(Properties properties, Arguments arguments) { + // just a placeholder for the confirmationmanager, this is on purpose! Check PrincepsLib for more info. + } + } + + public class Version extends SubCommand { + + public Version() { + super("version", + "/ll version", + Sets.newHashSet(Collections.singleton("landlord.admin")), + Sets.newHashSet()); + } + + @Override + public void onCommand(Properties properties, Arguments arguments) { + String msg = plugin.getLangManager().getTag() + " &aLandLord version: &7%version%" + .replace("%version%", plugin.getDescription().getVersion()); + properties.sendMessage(ChatColor.translateAlternateColorCodes('&', msg)); + } + } + +} \ No newline at end of file diff --git a/code-transformation/src/test/resources/projects/refactorings/InnerClassMayBeStatic/Landlordbase.java.correct b/code-transformation/src/test/resources/projects/refactorings/InnerClassMayBeStatic/Landlordbase.java.correct new file mode 100644 index 000000000..1c27417ad --- /dev/null +++ b/code-transformation/src/test/resources/projects/refactorings/InnerClassMayBeStatic/Landlordbase.java.correct @@ -0,0 +1,328 @@ +package biz.princeps.landlord.commands; + +import biz.princeps.landlord.api.ILandLord; +import biz.princeps.landlord.api.ILangManager; +import biz.princeps.landlord.api.Options; +import biz.princeps.landlord.commands.admin.AdminClaim; +import biz.princeps.landlord.commands.admin.AdminTeleport; +import biz.princeps.landlord.commands.admin.Clear; +import biz.princeps.landlord.commands.admin.ClearInactive; +import biz.princeps.landlord.commands.admin.Debug; +import biz.princeps.landlord.commands.admin.GiveClaims; +import biz.princeps.landlord.commands.admin.Reload; +import biz.princeps.landlord.commands.admin.Update; +import biz.princeps.landlord.commands.claiming.Claim; +import biz.princeps.landlord.commands.claiming.Claims; +import biz.princeps.landlord.commands.claiming.MultiClaim; +import biz.princeps.landlord.commands.claiming.MultiUnclaim; +import biz.princeps.landlord.commands.claiming.Shop; +import biz.princeps.landlord.commands.claiming.Unclaim; +import biz.princeps.landlord.commands.claiming.UnclaimAll; +import biz.princeps.landlord.commands.claiming.adv.Advertise; +import biz.princeps.landlord.commands.claiming.adv.RemoveAdvertise; +import biz.princeps.landlord.commands.friends.Addfriend; +import biz.princeps.landlord.commands.friends.AddfriendAll; +import biz.princeps.landlord.commands.friends.ListFriends; +import biz.princeps.landlord.commands.friends.MultiAddfriend; +import biz.princeps.landlord.commands.friends.MultiRemovefriend; +import biz.princeps.landlord.commands.friends.Unfriend; +import biz.princeps.landlord.commands.friends.UnfriendAll; +import biz.princeps.landlord.commands.homes.Home; +import biz.princeps.landlord.commands.homes.SetHome; +import biz.princeps.landlord.commands.management.Info; +import biz.princeps.landlord.commands.management.LandMap; +import biz.princeps.landlord.commands.management.ListLands; +import biz.princeps.landlord.commands.management.Manage; +import biz.princeps.landlord.commands.management.ManageAll; +import biz.princeps.landlord.commands.management.MultiListLands; +import biz.princeps.landlord.commands.management.MultiManage; +import biz.princeps.landlord.commands.management.Regenerate; +import biz.princeps.landlord.commands.management.borders.Borders; +import biz.princeps.landlord.multi.MultiMode; +import biz.princeps.lib.chat.MultiPagedMessage; +import biz.princeps.lib.command.Arguments; +import biz.princeps.lib.command.MainCommand; +import biz.princeps.lib.command.Properties; +import biz.princeps.lib.command.SubCommand; +import com.google.common.collect.Sets; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Project: LandLord + * Created by Alex D. (SpatiumPrinceps) + * Date: 16/07/17 + *

+ * This command protection may look a bit unfamiliar. It is based on shitty system I programmed a long time ago (PrincepsLib) + * Basically a single command is created by extending MainCommand. For example you would do: + * class HealCommand extends MainCommand {...} // introduces a heal command + * Landlordbase describes the base command ./landlord + * Subcommands are created by creating a class, that extends SubCommand. Add that one as subcmd. + * Always call super(cmdname, description, usage, permissions, aliases) to initialize the (sub)command with everything + * it needs to work. + */ +public class Landlordbase extends MainCommand { + + private final ILandLord plugin; + + public Landlordbase(ILandLord plugin) { + super(plugin.getConfig().getString("CommandSettings.Main.name"), + plugin.getConfig().getString("CommandSettings.Main.description"), + plugin.getConfig().getString("CommandSettings.Main.usage"), + Sets.newHashSet(plugin.getConfig().getStringList("CommandSettings.Main.permissions")), + plugin.getConfig().getStringList("CommandSettings.Main.aliases").toArray(new String[]{})); + this.plugin = plugin; + reloadCommands(); + } + + /** + * Reloads all commands. Reinitialisation, to pick up changed config variables + */ + private void reloadCommands() { + this.clearSubcommands(); + this.addSubcommand(new Version()); + this.addSubcommand(new Confirm()); + + this.addSubcommand(new Info(plugin)); + this.addSubcommand(new Claim(plugin, false)); + this.addSubcommand(new Unclaim(plugin)); + this.addSubcommand(new UnclaimAll(plugin)); + this.addSubcommand(new Addfriend(plugin)); + this.addSubcommand(new AddfriendAll(plugin)); + this.addSubcommand(new Unfriend(plugin)); + this.addSubcommand(new UnfriendAll(plugin)); + this.addSubcommand(new Advertise(plugin)); + this.addSubcommand(new RemoveAdvertise(plugin)); + this.addSubcommand(new ListFriends(plugin)); + this.addSubcommand(new ListLands(plugin)); + this.addSubcommand(new Claims(plugin)); + this.addSubcommand(new Shop(plugin)); + this.addSubcommand(new GiveClaims(plugin)); + this.addSubcommand(new Update(plugin)); + this.addSubcommand(new AdminTeleport(plugin)); + this.addSubcommand(new AdminClaim(plugin)); + this.addSubcommand(new MultiClaim(plugin)); + this.addSubcommand(new Borders(plugin)); + this.addSubcommand(new Home(plugin)); + this.addSubcommand(new SetHome(plugin)); + this.addSubcommand(new Manage(plugin)); + this.addSubcommand(new ManageAll(plugin)); + this.addSubcommand(new Clear(plugin)); + this.addSubcommand(new ClearInactive(plugin)); + this.addSubcommand(new LandMap(plugin)); + this.addSubcommand(new Reload(plugin)); + this.addSubcommand(new Regenerate(plugin)); + this.addSubcommand(new MultiUnclaim(plugin)); + this.addSubcommand(new MultiAddfriend(plugin)); + this.addSubcommand(new MultiRemovefriend(plugin)); + this.addSubcommand(new MultiListLands(plugin)); + this.addSubcommand(new MultiManage(plugin)); + this.addSubcommand(new Debug(plugin)); + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + List tabReturn = new ArrayList<>(); + + // Length == 1 means there is just the first thing like /ll typed + if (args.length == 1) { + for (SubCommand subCommand : this.subCommandMap.values()) { + if (subCommand.hasPermission(sender)) { + if (subCommand instanceof Borders) { + if (Options.enabled_borders()) { + tabReturn.add(subCommand.getName()); + } + } else if (subCommand instanceof LandMap) { + if (Options.enabled_map()) { + tabReturn.add(subCommand.getName()); + } + } else if (subCommand instanceof Shop || subCommand instanceof GiveClaims) { + if (Options.enabled_shop()) { + tabReturn.add(subCommand.getName()); + } + } else if (subCommand instanceof Home || subCommand instanceof SetHome) { + if (Options.enabled_homes()) { + tabReturn.add(subCommand.getName()); + } + } else { + tabReturn.add(subCommand.getName()); + } + } + } + + if (!args[0].isEmpty()) { + tabReturn.removeIf(next -> !next.startsWith(args[0])); + } + } else if (args.length == 2) { + for (SubCommand subCommand : subCommandMap.values()) { + if (subCommand.matches(args[0])) { + if (subCommand instanceof GiveClaims) { + tabReturn.add(""); + for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { + tabReturn.add(onlinePlayer.getName()); + } + return tabReturn; + } + + if (subCommand instanceof AdminTeleport) { + for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { + tabReturn.add(onlinePlayer.getName()); + } + return tabReturn; + } + + if (subCommand instanceof LandMap) { + tabReturn.add("on"); + tabReturn.add("off"); + return tabReturn; + } + + if (subCommand instanceof Addfriend || subCommand instanceof AddfriendAll || + subCommand instanceof Unfriend || subCommand instanceof UnfriendAll) { + for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { + if (!onlinePlayer.getName().startsWith(args[1]) || + (sender instanceof Player && !((Player) sender).canSee(onlinePlayer))) + continue; + + tabReturn.add(onlinePlayer.getName()); + } + return tabReturn; + } + + if (subCommand instanceof MultiClaim || subCommand instanceof MultiUnclaim || + subCommand instanceof MultiAddfriend || subCommand instanceof MultiRemovefriend || + subCommand instanceof MultiListLands || subCommand instanceof MultiManage) { + for (MultiMode multiMode : MultiMode.values()) { + tabReturn.add(multiMode.name()); + } + return tabReturn; + } + + if (subCommand instanceof UnclaimAll) { + for (World world : plugin.getServer().getWorlds()) { + tabReturn.add(world.getName()); + } + return tabReturn; + } + + if (subCommand instanceof Update) { + tabReturn.add("-u"); + tabReturn.add("-r"); + tabReturn.add("-c"); + return tabReturn; + } + } + } + } else if (args.length == 3) { + for (SubCommand subCommand : subCommandMap.values()) { + if (subCommand.matches(args[0])) { + if (subCommand instanceof GiveClaims) { + tabReturn.add(""); + tabReturn.add(""); + return tabReturn; + } + + if (subCommand instanceof MultiAddfriend || subCommand instanceof MultiRemovefriend) { + for (Player onlinePlayer : plugin.getServer().getOnlinePlayers()) { + if (!onlinePlayer.getName().startsWith(args[2]) || + (sender instanceof Player && !((Player) sender).canSee(onlinePlayer))) + continue; + + tabReturn.add(onlinePlayer.getName()); + } + return tabReturn; + } + } + } + } else if (args.length == 4) { + for (SubCommand subCommand : subCommandMap.values()) { + if (subCommand.matches(args[0])) { + if (subCommand instanceof GiveClaims) { + tabReturn.add(""); + } + } + } + } + return tabReturn; + } + + /** + * Main onCommand function of ./landlord. + * Display the help menu here. + * This function is not called for subcommands like ./ll claim + * + * @param properties a cool properties object, that contains stuff like isPlayer, isConsole + * @param arguments the arguments passed here. + */ + @Override + public void onCommand(Properties properties, Arguments arguments) { + if (properties.isConsole()) + return; + + ILangManager lm = plugin.getLangManager(); + List playersList = lm.getStringList("Commands.Help.players"); + List adminList = lm.getStringList("Commands.Help.admins"); + + int perSite = plugin.getConfig().getInt("HelpCommandPerSite"); + + String[] argsN = new String[1]; + if (arguments.get().length == 1) { + argsN[0] = (arguments.get(0) == null ? "0" : arguments.get(0)); + } + + List toDisplay = new ArrayList<>(); + if (properties.getPlayer().hasPermission("landlord.admin.help")) + toDisplay.addAll(adminList); + toDisplay.addAll(playersList); + + // plugin.getLogger().log(Level.INFO, String.valueOf(toDisplay.size())); + + MultiPagedMessage msg = new MultiPagedMessage.Builder() + .setElements(toDisplay) + .setPerSite(perSite) + .setHeaderString(lm.getRawString("Commands.Help.header")) + .setNextString(lm.getRawString("Commands.Help.next")) + .setPreviousString(lm.getRawString("Commands.Help.previous")) + .setCommand(plugin.getConfig().getString("CommandSettings.Main.name"), argsN).build(); + plugin.getUtilsManager().sendBasecomponent(properties.getPlayer(), msg.create()); + } + + public static class Confirm extends SubCommand { + + public Confirm() { + super("confirm", + "/ll help", + Sets.newHashSet(Collections.singleton("landlord.use")), + Sets.newHashSet()); + } + + @Override + public void onCommand(Properties properties, Arguments arguments) { + // just a placeholder for the confirmationmanager, this is on purpose! Check PrincepsLib for more info. + } + } + + public class Version extends SubCommand { + + public Version() { + super("version", + "/ll version", + Sets.newHashSet(Collections.singleton("landlord.admin")), + Sets.newHashSet()); + } + + @Override + public void onCommand(Properties properties, Arguments arguments) { + String msg = plugin.getLangManager().getTag() + " &aLandLord version: &7%version%" + .replace("%version%", plugin.getDescription().getVersion()); + properties.sendMessage(ChatColor.translateAlternateColorCodes('&', msg)); + } + } + +} \ No newline at end of file diff --git a/github-bot/src/test/java/io/github/martinwitt/laughing_train/api/BadSmellGraphQLTest.java b/github-bot/src/test/java/io/github/martinwitt/laughing_train/api/BadSmellGraphQLTest.java index da7fa11cd..868d9681c 100644 --- a/github-bot/src/test/java/io/github/martinwitt/laughing_train/api/BadSmellGraphQLTest.java +++ b/github-bot/src/test/java/io/github/martinwitt/laughing_train/api/BadSmellGraphQLTest.java @@ -16,6 +16,7 @@ import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient; import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClientBuilder; import java.util.regex.Pattern; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import xyz.keksdose.spoon.code_solver.api.analyzer.Position; @@ -46,7 +47,7 @@ private BadSmell createWithMessage(String ruleID) { } @Test - // @Disabled("Only for local testing") + @Disabled("Only for local testing") void getBadSmellFromLive() throws Exception { client = DynamicGraphQLClientBuilder.newBuilder() .url("https://laughing-train.keksdose.xyz/graphql") @@ -56,11 +57,12 @@ void getBadSmellFromLive() throws Exception { OperationType.QUERY, field( "byRuleID", - Argument.args(Argument.arg("ruleID", "UnnecessaryToStringCall")), + Argument.args(Argument.arg("ruleID", "InnerClassMayBeStatic")), field("ruleID"), field("message"), field("projectName"), - field("filePath")))); + field("filePath"), + field("position", field("startLine"))))); Response response = client.executeSync(document); System.out.println(response.getData().toString().replaceAll(Pattern.quote("},{"), "\n")); } diff --git a/settings.gradle b/settings.gradle index d7fad541e..906f9be59 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,5 +7,5 @@ * in the user manual at https://docs.gradle.org/7.4.2/userguide/multi_project_builds.html * This project uses @Incubating APIs which are subject to change. */ -rootProject.name = 'laughing-train' +rootProject.name = 'laughing-train-project' include('code-transformation', "github-bot") \ No newline at end of file