commit
c34817cf50
12 changed files with 403 additions and 0 deletions
@ -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 |
|||
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<groupId>internal.hw.crawler</groupId> |
|||
<artifactId>homework-final</artifactId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
<properties> |
|||
<maven.compiler.source>11</maven.compiler.source> |
|||
<maven.compiler.target>11</maven.compiler.target> |
|||
</properties> |
|||
|
|||
</project> |
|||
@ -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()); |
|||
} |
|||
} |
|||
} |
|||
@ -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<String, Command> commands = new HashMap<>(); |
|||
private final ConsoleView view; |
|||
|
|||
public MainController(ConsoleView view) { |
|||
this.view = view; |
|||
} |
|||
|
|||
public Map<String, Command> 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<CommandArg> 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<CommandArg> 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(); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
package internal.hw.crawler.commands; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface Command { |
|||
String getName(); |
|||
|
|||
default List<CommandArg> getArgs() { |
|||
return List.of(); |
|||
} |
|||
|
|||
void execute(String[] args); |
|||
} |
|||
@ -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 + "]"; |
|||
} |
|||
} |
|||
@ -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<CommandArg> 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]); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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<String, Command> commands; |
|||
|
|||
public HelpCommand(Map<String, Command> commands) { |
|||
this.commands = commands; |
|||
} |
|||
|
|||
@Override |
|||
public String getName() { |
|||
return "help"; |
|||
} |
|||
|
|||
@Override |
|||
public List<CommandArg> 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 <command>` 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<CommandArg> 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()); |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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<Article> articles = new ArrayList<>(); |
|||
|
|||
public void add(Article article) { |
|||
if (article == null) { |
|||
throw new IllegalArgumentException("Article cannot be null"); |
|||
} |
|||
articles.add(article); |
|||
} |
|||
|
|||
public List<Article> getAll() { |
|||
return Collections.unmodifiableList(articles); |
|||
} |
|||
|
|||
public int size() { |
|||
return articles.size(); |
|||
} |
|||
|
|||
public void clear() { |
|||
articles.clear(); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue