commit c34817cf5027e17a20542151c7152328f2c6e862 Author: 283375 Date: Sat May 2 19:36:00 2026 +0800 basic structure diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c5ca92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +.kotlin + +### IntelliJ IDEA ### +.idea/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..42c2374 --- /dev/null +++ b/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + internal.hw.crawler + homework-final + 1.0-SNAPSHOT + + 11 + 11 + + + \ No newline at end of file diff --git a/src/main/java/internal/hw/crawler/Main.java b/src/main/java/internal/hw/crawler/Main.java new file mode 100644 index 0000000..7c3d405 --- /dev/null +++ b/src/main/java/internal/hw/crawler/Main.java @@ -0,0 +1,24 @@ +package internal.hw.crawler; + +import internal.hw.crawler.commands.CrawlCommand; +import internal.hw.crawler.commands.ExitCommand; +import internal.hw.crawler.commands.HelpCommand; +import internal.hw.crawler.repositories.ArticleRepository; +import internal.hw.crawler.views.ConsoleView; + +public class Main { + public static void main(String[] args) { + ConsoleView view = new ConsoleView(); + MainController controller = new MainController(view); + ArticleRepository repository = new ArticleRepository(); + + controller.registerCommand(new ExitCommand()); + controller.registerCommand(new CrawlCommand(repository)); + controller.registerCommand(new HelpCommand(controller.getCommands())); + + view.printSuccess("Welcome to crawler. Type `help` for a list of available commands."); + while (true) { + controller.handleInput(view.readLine()); + } + } +} \ No newline at end of file diff --git a/src/main/java/internal/hw/crawler/MainController.java b/src/main/java/internal/hw/crawler/MainController.java new file mode 100644 index 0000000..fdbf603 --- /dev/null +++ b/src/main/java/internal/hw/crawler/MainController.java @@ -0,0 +1,72 @@ +package internal.hw.crawler; + +import internal.hw.crawler.commands.Command; +import internal.hw.crawler.commands.CommandArg; +import internal.hw.crawler.views.ConsoleView; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MainController { + private final Map commands = new HashMap<>(); + private final ConsoleView view; + + public MainController(ConsoleView view) { + this.view = view; + } + + public Map getCommands() { + return Collections.unmodifiableMap(commands); + } + + public void registerCommand(Command command) { + commands.put(command.getName(), command); + } + + public void handleInput(String input) { + String text = input == null ? "" : input.trim(); + if (text.isEmpty()) { + return; + } + + String[] args = text.split("\\s+"); + String cmdName = args[0].toLowerCase(); + Command command = commands.get(cmdName); + if (command == null) { + view.printError("Unknown command: " + cmdName); + return; + } + + if (!validateArgs(command, args)) { + return; + } + + command.execute(args); + } + + private boolean validateArgs(Command command, String[] args) { + List cmdArgs = command.getArgs(); + long required = cmdArgs.stream().filter(CommandArg::required).count(); + int provided = args.length - 1; + + if (provided < required) { + view.printError("Usage: " + command.getName() + " " + formatUsage(cmdArgs)); + return false; + } + return true; + } + + private String formatUsage(List cmdArgs) { + StringBuilder sb = new StringBuilder(); + for (CommandArg arg : cmdArgs) { + if (arg.required()) { + sb.append("<").append(arg.name()).append("> "); + } else { + sb.append("[").append(arg.name()).append("] "); + } + } + return sb.toString().trim(); + } +} diff --git a/src/main/java/internal/hw/crawler/commands/Command.java b/src/main/java/internal/hw/crawler/commands/Command.java new file mode 100644 index 0000000..e461cc4 --- /dev/null +++ b/src/main/java/internal/hw/crawler/commands/Command.java @@ -0,0 +1,13 @@ +package internal.hw.crawler.commands; + +import java.util.List; + +public interface Command { + String getName(); + + default List getArgs() { + return List.of(); + } + + void execute(String[] args); +} diff --git a/src/main/java/internal/hw/crawler/commands/CommandArg.java b/src/main/java/internal/hw/crawler/commands/CommandArg.java new file mode 100644 index 0000000..f7d29dc --- /dev/null +++ b/src/main/java/internal/hw/crawler/commands/CommandArg.java @@ -0,0 +1,39 @@ +package internal.hw.crawler.commands; + +import java.util.Objects; + +public final class CommandArg { + private final String name; + private final String description; + private final boolean required; + + public CommandArg(String name, String description, boolean required) { + this.name = Objects.requireNonNull(name); + this.description = Objects.requireNonNull(description); + this.required = required; + } + + public String name() { return name; } + public String description() { return description; } + public boolean required() { return required; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CommandArg)) return false; + CommandArg that = (CommandArg) o; + return required == that.required + && name.equals(that.name) + && description.equals(that.description); + } + + @Override + public int hashCode() { + return Objects.hash(name, description, required); + } + + @Override + public String toString() { + return "CommandArg[name=" + name + ", description=" + description + ", required=" + required + "]"; + } +} diff --git a/src/main/java/internal/hw/crawler/commands/CrawlCommand.java b/src/main/java/internal/hw/crawler/commands/CrawlCommand.java new file mode 100644 index 0000000..d4e0218 --- /dev/null +++ b/src/main/java/internal/hw/crawler/commands/CrawlCommand.java @@ -0,0 +1,28 @@ +package internal.hw.crawler.commands; + +import internal.hw.crawler.repositories.ArticleRepository; + +import java.util.List; + +public class CrawlCommand implements Command { + private ArticleRepository repository; + + public CrawlCommand(ArticleRepository repository) { + this.repository = repository; + } + + @Override + public String getName() { + return "crawl"; + } + + @Override + public List getArgs() { + return List.of(new CommandArg("url", "The website to crawl", true)); + } + + @Override + public void execute(String[] args) { + System.out.printf("Will crawl %s%n", args[1]); + } +} diff --git a/src/main/java/internal/hw/crawler/commands/ExitCommand.java b/src/main/java/internal/hw/crawler/commands/ExitCommand.java new file mode 100644 index 0000000..5c80548 --- /dev/null +++ b/src/main/java/internal/hw/crawler/commands/ExitCommand.java @@ -0,0 +1,13 @@ +package internal.hw.crawler.commands; + +public class ExitCommand implements Command { + @Override + public String getName() { + return "exit"; + } + + @Override + public void execute(String[] args) { + System.exit(0); + } +} diff --git a/src/main/java/internal/hw/crawler/commands/HelpCommand.java b/src/main/java/internal/hw/crawler/commands/HelpCommand.java new file mode 100644 index 0000000..60d7557 --- /dev/null +++ b/src/main/java/internal/hw/crawler/commands/HelpCommand.java @@ -0,0 +1,65 @@ +package internal.hw.crawler.commands; + +import java.util.List; +import java.util.Map; + +public class HelpCommand implements Command { + private final Map commands; + + public HelpCommand(Map commands) { + this.commands = commands; + } + + @Override + public String getName() { + return "help"; + } + + @Override + public List getArgs() { + return List.of( + new CommandArg("command", "Command name to show detailed help for", false) + ); + } + + @Override + public void execute(String[] args) { + if (args.length >= 2) { + showDetail(args[1]); + } else { + showAll(); + } + } + + private void showAll() { + System.out.println("Available commands:"); + for (Command cmd : commands.values()) { + System.out.printf(" %s", cmd.getName()); + System.out.println(); + } + System.out.println("Type `help ` to show detailed help for a specific command."); + } + + private void showDetail(String name) { + Command cmd = commands.get(name); + if (cmd == null) { + System.out.println("Unknown command: " + name); + return; + } + + System.out.println("Command: " + cmd.getName()); + List cmdArgs = cmd.getArgs(); + if (cmdArgs.isEmpty()) { + System.out.println(" No arguments."); + return; + } + + System.out.println("Arguments:"); + for (CommandArg arg : cmdArgs) { + System.out.printf(" %s %s %s%n", + arg.required() ? "[R]" : " ", + arg.name(), + arg.description()); + } + } +} diff --git a/src/main/java/internal/hw/crawler/models/Article.java b/src/main/java/internal/hw/crawler/models/Article.java new file mode 100644 index 0000000..b37c916 --- /dev/null +++ b/src/main/java/internal/hw/crawler/models/Article.java @@ -0,0 +1,42 @@ +package internal.hw.crawler.models; + +public class Article { + private String url; + private String title; + private String content; + + public Article(String url, String title, String content) { + this.url = url; + this.title = title; + this.content = content; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + @Override + public String toString() { + return String.format("Article{%s}", url); + } +} diff --git a/src/main/java/internal/hw/crawler/repositories/ArticleRepository.java b/src/main/java/internal/hw/crawler/repositories/ArticleRepository.java new file mode 100644 index 0000000..79931cd --- /dev/null +++ b/src/main/java/internal/hw/crawler/repositories/ArticleRepository.java @@ -0,0 +1,30 @@ +package internal.hw.crawler.repositories; + +import internal.hw.crawler.models.Article; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ArticleRepository { + private final List
articles = new ArrayList<>(); + + public void add(Article article) { + if (article == null) { + throw new IllegalArgumentException("Article cannot be null"); + } + articles.add(article); + } + + public List
getAll() { + return Collections.unmodifiableList(articles); + } + + public int size() { + return articles.size(); + } + + public void clear() { + articles.clear(); + } +} diff --git a/src/main/java/internal/hw/crawler/views/ConsoleView.java b/src/main/java/internal/hw/crawler/views/ConsoleView.java new file mode 100644 index 0000000..6caac9d --- /dev/null +++ b/src/main/java/internal/hw/crawler/views/ConsoleView.java @@ -0,0 +1,29 @@ +package internal.hw.crawler.views; + +import java.util.Scanner; + +public class ConsoleView { + private static final String ANSI_RESET = "\u001B[0m"; + private static final String ANSI_GREEN = "\u001B[32m"; + private static final String ANSI_RED = "\u001B[31m"; + private static final String ANSI_BLUE = "\u001B[34m"; + + private final Scanner scanner = new Scanner(System.in); + + public String readLine() { + System.out.print("> "); + return scanner.nextLine(); + } + + public void printSuccess(String msg) { + System.out.println(ANSI_GREEN + msg + ANSI_RESET); + } + + public void printError(String msg) { + System.out.println(ANSI_RED + msg + ANSI_RESET); + } + + public void printInfo(String msg) { + System.out.println(ANSI_BLUE + msg + ANSI_RESET); + } +}