From 80dbd5d4432a5fcec64cb3efc589b12506b075ea Mon Sep 17 00:00:00 2001 From: wwerlosh Date: Wed, 24 Apr 2024 20:19:08 +0300 Subject: [PATCH 1/2] init --- .gitignore | 38 ++++++ .idea/.gitignore | 8 ++ .idea/encodings.xml | 7 + .idea/misc.xml | 15 +++ .idea/uiDesigner.xml | 124 ++++++++++++++++++ README.md | 27 ++++ pom.xml | 88 +++++++++++++ src/main/java/ru/wwerlosh/App.java | 26 ++++ .../converter/DomainJsonConverter.java | 23 ++++ .../ru/wwerlosh/converter/JsonConverter.java | 13 ++ .../ru/wwerlosh/crawler/DomainExtractor.java | 14 ++ .../wwerlosh/crawler/HtmlDomainExtractor.java | 42 ++++++ .../ru/wwerlosh/service/DomainProcessor.java | 14 ++ .../wwerlosh/service/HtmlDomainProcessor.java | 22 ++++ .../converter/DomainJsonConverterTest.java | 33 +++++ .../crawler/HtmlDomainExtractorTest.java | 27 ++++ .../service/HtmlDomainProcessorTest.java | 33 +++++ 17 files changed, 554 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 pom.xml create mode 100644 src/main/java/ru/wwerlosh/App.java create mode 100644 src/main/java/ru/wwerlosh/converter/DomainJsonConverter.java create mode 100644 src/main/java/ru/wwerlosh/converter/JsonConverter.java create mode 100644 src/main/java/ru/wwerlosh/crawler/DomainExtractor.java create mode 100644 src/main/java/ru/wwerlosh/crawler/HtmlDomainExtractor.java create mode 100644 src/main/java/ru/wwerlosh/service/DomainProcessor.java create mode 100644 src/main/java/ru/wwerlosh/service/HtmlDomainProcessor.java create mode 100644 src/test/java/ru/wwerlosh/converter/DomainJsonConverterTest.java create mode 100644 src/test/java/ru/wwerlosh/crawler/HtmlDomainExtractorTest.java create mode 100644 src/test/java/ru/wwerlosh/service/HtmlDomainProcessorTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### 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/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5d6b03c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 4754fc7..20589f9 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,34 @@ 4. Найденные в соответствии с условием задачи домены должны выводиться в нижнем регистре без указания протокола и «www» в алфавитном порядке. ## Автор решения +Шверло Игорь Сергеевич, студент 2 курса Санкт-Петербургского государственного университета Телекоммуникаций им. профессора М. А. Бонч-Бруевича +Направление обучения: программная инженерия ## Описание реализации +#### [App](src/main/java/ru/wwerlosh/App.java) (Main): +- Метод _**main(String[] args)**_: Принимает путь к HTML-файлу в качестве аргумента командной строки, создает экземпляры `DomainExtractor`, `JsonConverter` и `DomainProcessor`, и выводит результат обработки на консоль. + +#### [DomainExtractor](src/main/java/ru/wwerlosh/crawler/DomainExtractor.java) (Interface): +- Метод **_extractDomains(File doc)_**: Извлекает домены из файла. + +#### [HtmlDomainExtractor](src/main/java/ru/wwerlosh/crawler/HtmlDomainExtractor.java) (Class): +- Метод **_extractDomains(File doc)_**: Извлекает домены из HTML-файла. + +#### [JsonConverter](src/main/java/ru/wwerlosh/converter/JsonConverter.java) (Interface): +- Метод **_convert(Collection objects)_**: Преобразует коллекцию объектов в строковое представление в формате JSON. + +#### [DomainJsonConverter](src/main/java/ru/wwerlosh/converter/DomainJsonConverter.java) (Class): +- Метод **_convert(Collection objects)_**: Преобразует коллекцию доменов в строковое представление в формате JSON. Домены сортируются по алфавиту без учета регистра. + +#### [DomainProcessor](src/main/java/ru/wwerlosh/service/DomainProcessor.java) (Interface): +- Метод **_process(File doc)_**: Обрабатывает домены в файле и возвращает результат обработки в виде JSON строки. + +#### [HtmlDomainProcessor](src/main/java/ru/wwerlosh/service/HtmlDomainProcessor.java) (Class): +- Метод **_process(File doc)_**: Обрабатывает домены в файле с использованием `DomainExtractor` и `JsonConverter`, и возвращает результат в формате JSON. ## Инструкция по сборке и запуску решения + +### Build +```mvn clean compile assembly:single``` +### Launch +```java -jar .\target\demo-jar-with-dependencies.jar <путь-к-HTML-файлу>``` diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ec28888 --- /dev/null +++ b/pom.xml @@ -0,0 +1,88 @@ + + 4.0.0 + + ru.wwerlosh + croc-mvp-testcase + 1.0-SNAPSHOT + jar + + croc-mvp-testcase + https://maven.apache.org + + + 17 + 17 + UTF-8 + + + + + junit + junit + 4.13.2 + test + + + + org.junit.jupiter + junit-jupiter + test + + + + org.mockito + mockito-core + 4.11.0 + test + + + + org.jsoup + jsoup + 1.17.2 + + + + + + + org.junit + junit-bom + 5.10.2 + pom + import + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + + + ru.wwerlosh.App + + + demo + + + + make-assembly + package + + single + + + + + + + + diff --git a/src/main/java/ru/wwerlosh/App.java b/src/main/java/ru/wwerlosh/App.java new file mode 100644 index 0000000..2f9e1f2 --- /dev/null +++ b/src/main/java/ru/wwerlosh/App.java @@ -0,0 +1,26 @@ +package ru.wwerlosh; + +import java.io.File; +import ru.wwerlosh.converter.DomainJsonConverter; +import ru.wwerlosh.converter.JsonConverter; +import ru.wwerlosh.crawler.DomainExtractor; +import ru.wwerlosh.crawler.HtmlDomainExtractor; +import ru.wwerlosh.service.DomainProcessor; +import ru.wwerlosh.service.HtmlDomainProcessor; + +public class App { + public static void main( String[] args ) { + if (args.length != 1) { + System.out.println("Usage: java App "); + return; + } + + String filePath = args[0]; + + JsonConverter converter = new DomainJsonConverter<>(); + DomainExtractor extractor = new HtmlDomainExtractor(); + DomainProcessor processor = new HtmlDomainProcessor(extractor, converter); + + System.out.println(processor.process(new File(filePath))); + } +} diff --git a/src/main/java/ru/wwerlosh/converter/DomainJsonConverter.java b/src/main/java/ru/wwerlosh/converter/DomainJsonConverter.java new file mode 100644 index 0000000..25e87a9 --- /dev/null +++ b/src/main/java/ru/wwerlosh/converter/DomainJsonConverter.java @@ -0,0 +1,23 @@ +package ru.wwerlosh.converter; + +import java.util.Collection; +import java.util.Set; +import java.util.TreeSet; + +public class DomainJsonConverter> implements JsonConverter { + + @Override + public String convert(Collection objects) { + Set sortedDomains = new TreeSet<>(objects); + StringBuilder jsonOutput = new StringBuilder(); + jsonOutput.append("{\n\"sites\": ["); + for (T domain : sortedDomains) { + jsonOutput.append("\"").append(domain).append("\", "); + } + if (!sortedDomains.isEmpty()) { + jsonOutput.delete(jsonOutput.length() - 2, jsonOutput.length()); + } + jsonOutput.append("]\n}"); + return jsonOutput.toString(); + } +} diff --git a/src/main/java/ru/wwerlosh/converter/JsonConverter.java b/src/main/java/ru/wwerlosh/converter/JsonConverter.java new file mode 100644 index 0000000..5fc12c0 --- /dev/null +++ b/src/main/java/ru/wwerlosh/converter/JsonConverter.java @@ -0,0 +1,13 @@ +package ru.wwerlosh.converter; + +import java.util.Collection; + +public interface JsonConverter { + /** + * Преобразует коллекцию объектов в строковое представление в формате JSON. + * + * @param objects Коллекция объектов для конвертации. + * @return Строковое представление объектов в формате JSON. + */ + String convert(Collection objects); +} diff --git a/src/main/java/ru/wwerlosh/crawler/DomainExtractor.java b/src/main/java/ru/wwerlosh/crawler/DomainExtractor.java new file mode 100644 index 0000000..f138707 --- /dev/null +++ b/src/main/java/ru/wwerlosh/crawler/DomainExtractor.java @@ -0,0 +1,14 @@ +package ru.wwerlosh.crawler; + +import java.io.File; +import java.util.Set; + +public interface DomainExtractor { + /** + * Извлекает домены из файла. + * + * @param doc Файл для извлечения доменов. + * @return Множество строк, представляющих извлеченные домены. + */ + Set extractDomains(File doc); +} diff --git a/src/main/java/ru/wwerlosh/crawler/HtmlDomainExtractor.java b/src/main/java/ru/wwerlosh/crawler/HtmlDomainExtractor.java new file mode 100644 index 0000000..079fb4d --- /dev/null +++ b/src/main/java/ru/wwerlosh/crawler/HtmlDomainExtractor.java @@ -0,0 +1,42 @@ +package ru.wwerlosh.crawler; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +public class HtmlDomainExtractor implements DomainExtractor { + @Override + public Set extractDomains(File doc) { + Document document = null; + try { + document = Jsoup.parse(doc, "UTF-8", ""); + } catch (IOException e) { + System.err.println("Ошибка: Файл не найден или не может быть прочитан."); + System.exit(1); + } + + + Set domains = new HashSet<>(); + Elements links = document.select("a[href]"); + for (Element link : links) { + String url = link.absUrl("href"); + String domain = extractDomain(url); + domains.add(domain.toLowerCase()); + } + return domains; + } + + private String extractDomain(String url) { + String[] parts = url.split("/"); + String domainPart = parts[2]; + if (domainPart.startsWith("www.")) { + domainPart = domainPart.substring(4); + } + return domainPart; + } +} diff --git a/src/main/java/ru/wwerlosh/service/DomainProcessor.java b/src/main/java/ru/wwerlosh/service/DomainProcessor.java new file mode 100644 index 0000000..0b4cce4 --- /dev/null +++ b/src/main/java/ru/wwerlosh/service/DomainProcessor.java @@ -0,0 +1,14 @@ +package ru.wwerlosh.service; + +import java.io.File; +import org.jsoup.nodes.Document; + +public interface DomainProcessor { + /** + * Обрабатывает домены в файле. + * + * @param doc Файл для обработки доменов. + * @return Результат обработки в виде JSON строки. + */ + String process(File doc); +} diff --git a/src/main/java/ru/wwerlosh/service/HtmlDomainProcessor.java b/src/main/java/ru/wwerlosh/service/HtmlDomainProcessor.java new file mode 100644 index 0000000..e067375 --- /dev/null +++ b/src/main/java/ru/wwerlosh/service/HtmlDomainProcessor.java @@ -0,0 +1,22 @@ +package ru.wwerlosh.service; + +import java.io.File; +import java.util.Set; +import ru.wwerlosh.converter.JsonConverter; +import ru.wwerlosh.crawler.DomainExtractor; + +public class HtmlDomainProcessor implements DomainProcessor { + private final DomainExtractor domainExtractor; + private final JsonConverter jsonConverter; + + public HtmlDomainProcessor(DomainExtractor domainExtractor, JsonConverter jsonConverter) { + this.domainExtractor = domainExtractor; + this.jsonConverter = jsonConverter; + } + + @Override + public String process(File doc) { + Set domains = domainExtractor.extractDomains(doc); + return jsonConverter.convert(domains); + } +} \ No newline at end of file diff --git a/src/test/java/ru/wwerlosh/converter/DomainJsonConverterTest.java b/src/test/java/ru/wwerlosh/converter/DomainJsonConverterTest.java new file mode 100644 index 0000000..618e4bb --- /dev/null +++ b/src/test/java/ru/wwerlosh/converter/DomainJsonConverterTest.java @@ -0,0 +1,33 @@ +package ru.wwerlosh.converter; + +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import ru.wwerlosh.converter.DomainJsonConverter; +import ru.wwerlosh.converter.JsonConverter; + +import static org.junit.jupiter.api.Assertions.*; + +public class DomainJsonConverterTest { + + private JsonConverter jsonConverter; + private Set data; + + @BeforeEach + public void init() { + jsonConverter = new DomainJsonConverter<>(); + data = new HashSet<>(); + data.add("ex.com"); + data.add("xe.com"); + data.add("de.eu"); + } + + @Test + public void domainJsonConverter_test() { + String expected = "{\n" + "\"sites\": " + "[\"de.eu\", \"ex.com\", \"xe.com\"]\n" + "}"; + String json = jsonConverter.convert(data); + + assertEquals(expected, json); + } +} diff --git a/src/test/java/ru/wwerlosh/crawler/HtmlDomainExtractorTest.java b/src/test/java/ru/wwerlosh/crawler/HtmlDomainExtractorTest.java new file mode 100644 index 0000000..ebdfd04 --- /dev/null +++ b/src/test/java/ru/wwerlosh/crawler/HtmlDomainExtractorTest.java @@ -0,0 +1,27 @@ +package ru.wwerlosh.crawler; + +import java.io.File; +import java.util.Set; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.parser.Tag; +import org.jsoup.select.Elements; +import org.junit.jupiter.api.Test; +import ru.wwerlosh.crawler.HtmlDomainExtractor; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class HtmlDomainExtractorTest { + @Test + void testExtractDomains() { + File doc = new File("page.html"); + + HtmlDomainExtractor domainExtractor = new HtmlDomainExtractor(); + + Set domains = domainExtractor.extractDomains(doc); + assertTrue(domains.contains("ru.wikipedia.org")); + assertTrue(domains.contains("tutorials.jenkov.com")); + } +} diff --git a/src/test/java/ru/wwerlosh/service/HtmlDomainProcessorTest.java b/src/test/java/ru/wwerlosh/service/HtmlDomainProcessorTest.java new file mode 100644 index 0000000..b3b0056 --- /dev/null +++ b/src/test/java/ru/wwerlosh/service/HtmlDomainProcessorTest.java @@ -0,0 +1,33 @@ +package ru.wwerlosh.service; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import org.jsoup.nodes.Document; +import org.junit.jupiter.api.Test; +import ru.wwerlosh.converter.DomainJsonConverter; +import ru.wwerlosh.converter.JsonConverter; +import ru.wwerlosh.crawler.DomainExtractor; +import ru.wwerlosh.crawler.HtmlDomainExtractor; +import ru.wwerlosh.exception.InvalidDocumentTypeException; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class HtmlDomainProcessorTest { + + @Test + void process() { + JsonConverter converter = new DomainJsonConverter<>(); + DomainExtractor extractor = new HtmlDomainExtractor(); + DomainProcessor processor = new HtmlDomainProcessor(extractor, converter); + + String expectedJson = "{\n" + + "\"sites\": [\"blogs.oracle.com\", \"career.habr.com\", \"company.habr.com\", \"docs.oracle.com\", \"dzen.ru\", \"effect.habr.com\", \"facebook.com\", \"google.com\", \"habr.com\", \"jcp.org\", \"ru.wikipedia.org\", \"telegram.me\", \"translit.net\", \"tutorials.jenkov.com\", \"twitter.com\", \"vk.com\", \"youtube.com\"]\n" + + "}"; + + String result = processor.process(new File("page.html")); + assertEquals(expectedJson, result); + } +} \ No newline at end of file From 44fd7fcaefa7cc49c8cabf1405422454c2891df5 Mon Sep 17 00:00:00 2001 From: wwerlosh Date: Wed, 24 Apr 2024 22:37:34 +0300 Subject: [PATCH 2/2] method explanation added --- src/main/java/ru/wwerlosh/App.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/ru/wwerlosh/App.java b/src/main/java/ru/wwerlosh/App.java index 2f9e1f2..911aeb3 100644 --- a/src/main/java/ru/wwerlosh/App.java +++ b/src/main/java/ru/wwerlosh/App.java @@ -9,9 +9,14 @@ import ru.wwerlosh.service.HtmlDomainProcessor; public class App { + /** + * Метод для запуска приложения. + * + * @param args Аргументы командной строки. Ожидается путь к файлу HTML-страницы. + */ public static void main( String[] args ) { if (args.length != 1) { - System.out.println("Usage: java App "); + System.out.println("Usage: java App "); return; }