initial commit

This commit is contained in:
2025-12-09 15:40:47 +01:00
commit 91c731fef4
28 changed files with 690 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
package cz.jzitnik;
import cz.jzitnik.annotations.CommandImpl;
import cz.jzitnik.commands.models.Command;
import cz.jzitnik.services.ClassFactory;
import cz.jzitnik.services.CommandHistory;
import cz.jzitnik.services.CommandInvoker;
import org.reflections.Reflections;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;
public class Console implements Runnable {
private final CommandInvoker invoker = new CommandInvoker();
private final ClassFactory factory = new ClassFactory();
@Override
public void run() {
factory.load();
loadCommands();
Scanner sc = new Scanner(System.in);
System.out.println("Type 'help' for commands.");
boolean running = true;
while (running) {
System.out.print("-> ");
String input = sc.nextLine();
((CommandHistory) factory.get(CommandHistory.class)).add(input);
if (input.equals("stop")) {
running = false;
continue;
}
if (input.equals("help")) {
HashMap<String, Command> commands = invoker.getCommands();
System.out.println(commands.keySet());
continue;
}
String[] parts = input.split(" ", 2);
String cmd = parts[0];
String args2 = parts.length > 1 ? parts[1] : "";
String result = invoker.execute(cmd, args2);
System.out.println(result);
}
sc.close();
}
private void loadCommands() {
Reflections reflections = new Reflections("cz.jzitnik.commands");
List<? extends Class<? extends Command>> entityClasses =
reflections.getTypesAnnotatedWith(CommandImpl.class).stream()
.filter(Command.class::isAssignableFrom)
.map(i -> (Class<? extends Command>) i)
.toList();
for (Class<? extends Command> command : entityClasses) {
CommandImpl annotation = command.getAnnotation(CommandImpl.class);
for (var constructor : command.getDeclaredConstructors()) {
var paramTypes = constructor.getParameterTypes();
var params = new Object[paramTypes.length];
boolean suitable = true;
for (int i = 0; i < paramTypes.length; i++) {
Class<?> type = paramTypes[i];
if (factory.contains(type))
params[i] = factory.get(type);
else {
suitable = false;
break;
}
}
if (suitable) {
constructor.setAccessible(true);
try {
Command instance = (Command) constructor.newInstance(params);
invoker.register(annotation.value(), instance);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
break; // Found a matching constructor, go to next class
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
package cz.jzitnik;
public class Main {
public static void main(String[] args) {
Console console = new Console();
console.run();
}
}

View File

@@ -0,0 +1,12 @@
package cz.jzitnik.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CommandImpl {
String value();
}

View File

@@ -0,0 +1,11 @@
package cz.jzitnik.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Config {
}

View File

@@ -0,0 +1,11 @@
package cz.jzitnik.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Data {
}

View File

@@ -0,0 +1,52 @@
package cz.jzitnik.commands;
import cz.jzitnik.annotations.CommandImpl;
import cz.jzitnik.commands.models.Command;
import cz.jzitnik.factory.Contact;
import cz.jzitnik.repository.ContactRepository;
@CommandImpl("contacts <operation;parameters>")
public class ContactCommand implements Command {
private final ContactRepository cm;
public ContactCommand(ContactRepository cm) {
this.cm = cm;
}
@Override
public String execute(String args) {
// `args` ve formátu:
// add;jmeno;prijmeni;datum;email;telefon
// delete;telefon
// searchln;prijmeni
// searchph;telefon
// edit;staryTelefon;jmeno;prijmeni;datum;email;telefon
String[] p = args.split(";");
return switch (p[0]) {
case "add" -> {
Contact c = new Contact();
c.firstName = p[1];
c.lastName = p[2];
c.birthdate = p[3];
c.email = p[4];
c.phone = p[5];
yield cm.add(c);
}
case "delete" -> cm.delete(p[1]);
case "searchln" -> cm.searchLastName(p[1]);
case "searchph" -> cm.searchPhone(p[1]);
case "edit" -> {
Contact c = new Contact();
c.firstName = p[2];
c.lastName = p[3];
c.birthdate = p[4];
c.email = p[5];
c.phone = p[6];
yield cm.edit(p[1], c);
}
default -> String.join("\n", cm.getContacts().stream().map(Contact::toString).toList());
};
}
}

View File

@@ -0,0 +1,14 @@
package cz.jzitnik.commands;
import cz.jzitnik.annotations.CommandImpl;
import cz.jzitnik.commands.models.Command;
import java.time.LocalDateTime;
@CommandImpl("datetime")
public class DateTimeCommand implements Command {
@Override
public String execute(String args) {
return LocalDateTime.now().toString();
}
}

View File

@@ -0,0 +1,20 @@
package cz.jzitnik.commands;
import cz.jzitnik.annotations.CommandImpl;
import cz.jzitnik.commands.models.Command;
import cz.jzitnik.services.CommandHistory;
@CommandImpl("history")
public class HistoryCommand implements Command {
private final CommandHistory history;
public HistoryCommand(CommandHistory h) {
this.history = h;
}
@Override
public String execute(String args) {
return String.join("\n", history.getAll());
}
}

View File

@@ -0,0 +1,44 @@
package cz.jzitnik.commands;
import cz.jzitnik.annotations.CommandImpl;
import cz.jzitnik.commands.models.Command;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@CommandImpl("quadratic Ax^2+Bx+C")
public class QuadraticCommand implements Command {
@Override
public String execute(String eq) {
try {
eq = eq.replace(" ", "");
Pattern p = Pattern.compile("([+-]?\\d*)x\\^2([+-]\\d*)x([+-]\\d+)");
Matcher m = p.matcher(eq);
if (!m.matches()) {
return "Neplatný formát. Example: 4x^2+3x-2";
}
double A = m.group(1).equals("") || m.group(1).equals("+") ? 1 :
m.group(1).equals("-") ? -1 : Double.parseDouble(m.group(1));
double B = Double.parseDouble(m.group(2));
double C = Double.parseDouble(m.group(3));
double D = B * B - 4 * A * C;
if (D < 0) {
return "Žádné reálné kořeny.";
}
double x1 = (-B + Math.sqrt(D)) / (2 * A);
double x2 = (-B - Math.sqrt(D)) / (2 * A);
return "Kořeny: " + x1 + ", " + x2;
} catch (Exception e) {
return "Error parsing equation.";
}
}
}

View File

@@ -0,0 +1,32 @@
package cz.jzitnik.commands;
import cz.jzitnik.annotations.CommandImpl;
import cz.jzitnik.commands.models.Command;
import cz.jzitnik.config.models.QuoteConfig;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Random;
@CommandImpl("quotes")
public class QuoteCommand implements Command {
private final List<String> quotes;
public QuoteCommand(QuoteConfig config) {
List<String> temp;
try {
temp = Files.readAllLines(Paths.get(config.getFilePath()));
} catch (Exception e) {
temp = List.of("No quotes found.");
}
quotes = temp;
}
@Override
public String execute(String args) {
Random r = new Random();
return quotes.get(r.nextInt(quotes.size()));
}
}

View File

@@ -0,0 +1,5 @@
package cz.jzitnik.commands.models;
public interface Command {
String execute(String args);
}

View File

@@ -0,0 +1,12 @@
package cz.jzitnik.config;
import cz.jzitnik.annotations.Config;
import cz.jzitnik.config.models.ContactRepositoryConfig;
@Config
public class ContactRepositoryConfigImpl implements ContactRepositoryConfig {
@Override
public String getFilePath() {
return "kontakty.txt";
}
}

View File

@@ -0,0 +1,12 @@
package cz.jzitnik.config;
import cz.jzitnik.annotations.Config;
import cz.jzitnik.config.models.QuoteConfig;
@Config
public class QuoteConfigImpl implements QuoteConfig {
@Override
public String getFilePath() {
return "quotes.txt";
}
}

View File

@@ -0,0 +1,5 @@
package cz.jzitnik.config.models;
public interface ContactRepositoryConfig {
String getFilePath();
}

View File

@@ -0,0 +1,5 @@
package cz.jzitnik.config.models;
public interface QuoteConfig {
String getFilePath();
}

View File

@@ -0,0 +1,25 @@
package cz.jzitnik.factory;
public class Contact {
public String firstName;
public String lastName;
public String birthdate;
public String email;
public String phone;
@Override
public String toString() {
return firstName + ";" + lastName + ";" + birthdate + ";" + email + ";" + phone;
}
public static Contact fromString(String s) {
String[] p = s.split(";");
Contact c = new Contact();
c.firstName = p[0];
c.lastName = p[1];
c.birthdate = p[2];
c.email = p[3];
c.phone = p[4];
return c;
}
}

View File

@@ -0,0 +1,86 @@
package cz.jzitnik.repository;
import cz.jzitnik.annotations.Data;
import cz.jzitnik.config.models.ContactRepositoryConfig;
import cz.jzitnik.factory.Contact;
import java.io.*;
import java.util.*;
@Data
public class ContactRepository {
private final String filePath;
private final List<Contact> contacts = new ArrayList<>();
public List<Contact> getContacts() {
return contacts;
}
public ContactRepository(ContactRepositoryConfig config) {
this.filePath = config.getFilePath();
load();
}
private void load() {
try {
File f = new File(filePath);
if (!f.exists()) return;
try (Scanner sc = new Scanner(f)) {
while (sc.hasNextLine()) {
contacts.add(Contact.fromString(sc.nextLine()));
}
}
} catch (Exception ignored) {}
}
private void save() {
try (PrintWriter pw = new PrintWriter(filePath)) {
for (Contact c : contacts) pw.println(c);
} catch (Exception ignored) {}
}
public String add(Contact c) {
contacts.add(c);
save();
return "Contact added.";
}
public String edit(String phone, Contact updated) {
for (Contact c : contacts) {
if (c.phone.equals(phone)) {
c.firstName = updated.firstName;
c.lastName = updated.lastName;
c.birthdate = updated.birthdate;
c.email = updated.email;
c.phone = updated.phone;
save();
return "Contact updated.";
}
}
return "Contact not found.";
}
public String delete(String phone) {
boolean removed = contacts.removeIf(c -> c.phone.equals(phone));
save();
return removed ? "Contact removed." : "Contact not found.";
}
public String searchLastName(String ln) {
StringBuilder sb = new StringBuilder();
contacts.stream()
.filter(c -> c.lastName.equalsIgnoreCase(ln))
.forEach(c -> sb.append(c).append("\n"));
return sb.length() == 0 ? "Not found." : sb.toString();
}
public String searchPhone(String ph) {
StringBuilder sb = new StringBuilder();
contacts.stream()
.filter(c -> c.phone.equals(ph))
.forEach(c -> sb.append(c).append("\n"));
return sb.length() == 0 ? "Not found." : sb.toString();
}
}

View File

@@ -0,0 +1,78 @@
package cz.jzitnik.services;
import cz.jzitnik.annotations.Config;
import cz.jzitnik.annotations.Data;
import org.reflections.Reflections;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Set;
public class ClassFactory {
private final HashMap<Class<?>, Object> instances = new HashMap<>();
public HashMap<Class<?>, Object> getInstances() {
return instances;
}
public boolean contains(Class<?> clazz) {
return instances.keySet().stream().anyMatch(clazzIn -> clazz.isAssignableFrom(clazzIn));
}
public Object get(Class<?> clazz) {
for (Class<?> clazzInDb : instances.keySet()) {
if (clazz.isAssignableFrom(clazzInDb)) {
return instances.get(clazzInDb);
}
}
return null;
}
public void load() {
Reflections reflections = new Reflections("cz.jzitnik");
Set<Class<?>> configs = reflections.getTypesAnnotatedWith(Config.class);
for (Class<?> config : configs) {
try {
var instance = config.getDeclaredConstructor().newInstance();
instances.put(config, instance);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
Set<Class<?>> entityClasses = reflections.getTypesAnnotatedWith(Data.class);
for (Class<?> clazz : entityClasses) {
for (var constructor : clazz.getDeclaredConstructors()) {
var paramTypes = constructor.getParameterTypes();
var params = new Object[paramTypes.length];
boolean suitable = true;
for (int i = 0; i < paramTypes.length; i++) {
Class<?> type = paramTypes[i];
if (contains(type))
params[i] = get(type);
else {
suitable = false;
break;
}
}
if (suitable) {
constructor.setAccessible(true);
try {
Object instance = constructor.newInstance(params);
instances.put(clazz, instance);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
break; // Found a matching constructor, go to next class
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
package cz.jzitnik.services;
import cz.jzitnik.annotations.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class CommandHistory {
private final List<String> list = new ArrayList<>();
public void add(String cmd) {
list.add(cmd);
}
public List<String> getAll() {
return list;
}
}

View File

@@ -0,0 +1,35 @@
package cz.jzitnik.services;
import cz.jzitnik.commands.models.Command;
import java.util.HashMap;
public class CommandInvoker {
private final HashMap<String, Command> commands = new HashMap<>();
public HashMap<String, Command> getCommands() {
return commands;
}
private Command get(String name) {
for (String cmd : commands.keySet()) {
if ((cmd + " ").split(" ")[0].equals(name)) {
return commands.get(cmd);
}
}
return null;
}
public void register(String name, Command cmd) {
commands.put(name, cmd);
}
public String execute(String name, String args) {
Command cmd = get(name);
if (cmd != null) {
return cmd.execute(args);
}
return "Unknown command.";
}
}