diff --git a/.idea/misc.xml b/.idea/misc.xml index e122dea..234fdd3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,10 @@ + + + + + \ No newline at end of file diff --git a/src/main/java/org/lumijiez/Main.java b/src/main/java/org/lumijiez/Main.java index c07936b..4d6c5f3 100644 --- a/src/main/java/org/lumijiez/Main.java +++ b/src/main/java/org/lumijiez/Main.java @@ -1,17 +1,35 @@ package org.lumijiez; -//TIP To Run code, press or -// click the icon in the gutter. -public class Main { - public static void main(String[] args) { - //TIP Press with your caret at the highlighted text - // to see how IntelliJ IDEA suggests fixing it. - System.out.printf("Hello and welcome!"); +import org.lumijiez.models.FilteredProducts; +import org.lumijiez.models.Product; +import org.lumijiez.network.TCPClient; +import org.lumijiez.parsers.DarwinParser; +import org.lumijiez.parsers.DarwinProductParser; +import org.lumijiez.parsers.DarwinProductProcessor; +import org.lumijiez.serializer.Lumi; - for (int i = 1; i <= 5; i++) { - //TIP Press to start debugging your code. We have set one breakpoint - // for you, but you can always add more by pressing . - System.out.println("i = " + i); - } +import java.util.List; + +public class Main { + public static void main(String[] args) throws Exception { + String host = "https://darwin.md/search?search=apple&page=1"; + + String response = TCPClient.getHttps(host); + String productList = DarwinParser.extractProductList(response); + List products = DarwinProductParser.parseProducts(productList); + + FilteredProducts filtered = DarwinProductProcessor.processProducts(products, 0, 1000000); + + Lumi.jsonToFile(filtered, "filtered.json"); + Lumi.xmlToFile(filtered, "filtered.xml"); + + System.out.println(TCPClient.checkJson(Lumi.toJson(filtered))); + System.out.println(TCPClient.checkXml(Lumi.toXml(filtered))); + +// Serialization/Deserialization Test + System.out.println( + Lumi.toXml( + Lumi.fromJson( + Lumi.toJson(products.getFirst()), Product.class))); } } \ No newline at end of file diff --git a/src/main/java/org/lumijiez/models/FilteredProducts.java b/src/main/java/org/lumijiez/models/FilteredProducts.java index 2a465af..5bdfc04 100644 --- a/src/main/java/org/lumijiez/models/FilteredProducts.java +++ b/src/main/java/org/lumijiez/models/FilteredProducts.java @@ -1,4 +1,19 @@ package org.lumijiez.models; +import org.lumijiez.serializer.LumiSerializable; +import org.lumijiez.serializer.LumiSerializeField; + +import java.util.Date; +import java.util.List; + +@LumiSerializable public class FilteredProducts { + @LumiSerializeField + public List products; + + @LumiSerializeField + public Date date; + + @LumiSerializeField + public float price; } diff --git a/src/main/java/org/lumijiez/models/Product.java b/src/main/java/org/lumijiez/models/Product.java index 15b0445..9545351 100644 --- a/src/main/java/org/lumijiez/models/Product.java +++ b/src/main/java/org/lumijiez/models/Product.java @@ -1,4 +1,45 @@ package org.lumijiez.models; +import org.lumijiez.serializer.LumiSerializable; +import org.lumijiez.serializer.LumiSerializeField; + +@LumiSerializable public class Product { + + @LumiSerializeField + public String id; + + @LumiSerializeField + public String name; + + @LumiSerializeField + public String imageUrl; + + @LumiSerializeField + public String specifications; + + @LumiSerializeField + public String oldPrice; + + @LumiSerializeField + public String newPrice; + + @LumiSerializeField + public String discount; + + @LumiSerializeField + public String productLink; + + public Product() {} + + public Product(String id, String name, String imageUrl, String specifications, String oldPrice, String newPrice, String discount, String productLink) { + this.id = id; + this.name = name; + this.imageUrl = imageUrl; + this.specifications = specifications; + this.oldPrice = oldPrice; + this.newPrice = newPrice; + this.discount = discount; + this.productLink = productLink; + } } diff --git a/src/main/java/org/lumijiez/network/TCPClient.java b/src/main/java/org/lumijiez/network/TCPClient.java index 670bf60..c3c5cdd 100644 --- a/src/main/java/org/lumijiez/network/TCPClient.java +++ b/src/main/java/org/lumijiez/network/TCPClient.java @@ -1,4 +1,93 @@ package org.lumijiez.network; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import java.io.*; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + public class TCPClient { -} + public static String getHttps(String url) { + try { + URI uri = new URI(url); + String host = uri.getHost(); + String path = uri.getRawPath(); + String query = uri.getRawQuery(); + int port = uri.getPort() == -1 ? 443 : uri.getPort(); + + SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); + SSLSocket socket = (SSLSocket) factory.createSocket(host, port); + socket.setEnabledProtocols(socket.getSupportedProtocols()); + socket.startHandshake(); + + String requestPath = path + (query != null ? "?" + query : ""); + + String request = + "GET " + requestPath + " HTTP/1.1\r\n" + + "Host: " + host + "\r\n" + + "Connection: close\r\n\r\n"; + + return socketIo(socket, request); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException(e); + } + } + + public static String checkJson(String jsonData) { + return sendData("application/json", jsonData); + } + + public static String checkXml(String xmlData) { + return sendData("application/xml", xmlData); + } + + private static String sendData(String contentType, String data) { + try { + URI uri = new URI("http://localhost:8000/upload"); + String host = uri.getHost(); + int port = uri.getPort() == -1 ? 80 : uri.getPort(); + + Socket socket = new Socket(host, port); + + String username = "500"; + String password = "408"; + String auth = username + ":" + password; + String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes()); + + String request = + "POST " + uri.getPath() + " HTTP/1.1\r\n" + + "Host: " + host + "\r\n" + + "Content-Type: " + contentType + "\r\n" + + "Content-Length: " + data.getBytes(StandardCharsets.UTF_8).length + "\r\n" + + "Authorization: Basic " + encodedAuth + "\r\n" + + "Connection: close\r\n\r\n" + + data; + + String response = socketIo(socket, request); + + return "Status Code: " + response.split("\n")[0].split(" ")[1]; + } catch (IOException | URISyntaxException e) { + throw new RuntimeException(e); + } + } + + private static String socketIo(Socket socket, String request) throws IOException { + OutputStream outputStream = socket.getOutputStream(); + outputStream.write(request.getBytes()); + outputStream.flush(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + String line; + StringBuilder response = new StringBuilder(); + while ((line = reader.readLine()) != null) { + response.append(line).append("\n"); + } + reader.close(); + socket.close(); + + return response.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/lumijiez/parsers/DarwinParser.java b/src/main/java/org/lumijiez/parsers/DarwinParser.java index 9ad13b9..d6a8a88 100644 --- a/src/main/java/org/lumijiez/parsers/DarwinParser.java +++ b/src/main/java/org/lumijiez/parsers/DarwinParser.java @@ -1,4 +1,44 @@ package org.lumijiez.parsers; public class DarwinParser { -} + public static String extractProductList(String html) { + String divStart = "
"; + String divEnd = "
"; + + int startIndex = html.indexOf(divStart); + if (startIndex == -1) { + return "Product list div not found"; + } + + int endIndex = -1; + int nestedLevel = 1; + int searchStartIndex = startIndex + divStart.length(); + + while (nestedLevel > 0 && searchStartIndex < html.length()) { + int nextDivStart = html.indexOf(" parseProducts(String html) { + List products = new ArrayList<>(); + String productPattern = "
.*?
\\s*\\s*"; + Pattern pattern = Pattern.compile(productPattern, Pattern.DOTALL); + Matcher matcher = pattern.matcher(html); + + while (matcher.find()) { + String productHtml = matcher.group(); + Product product = extractProductInfo(productHtml); + if (product != null) { + products.add(product); + } + } + + return products; + } + + private static Product extractProductInfo(String productHtml) { + String name = extractWithRegex(productHtml, "title=\"([^\"]+)\"\\s+class=\"ga-item\""); + String imageUrl = extractWithRegex(productHtml, "src=\"([^\"]+)\""); + String specifications = extractWithRegex(productHtml, "([^<]+)"); + String oldPrice = extractWithRegex(productHtml, "([^<]+)"); + String newPrice = extractWithRegex(productHtml, "([^<]+)"); + String discount = extractWithRegex(productHtml, ""); + String productLink = extractWithRegex(productHtml, "href=\"([^\"]+)\"\\s+title=\"[^\"]+\"\\s+class=\"ga-item\""); + String id = extractId(productLink); + if (name == null || newPrice == null) { + return null; + } + + return new Product(id, name, imageUrl, specifications, oldPrice, newPrice, discount, productLink); + } + + private static String extractWithRegex(String input, String regex) { + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + return matcher.group(1).trim(); + } + return null; + } + + private static String extractId(String productLink) { + String body = TCPClient.getHttps(productLink); + + String regex = "ID produs:\\s*]*>\\s*(\\d{2}\\.\\d{8}\\.\\d{3})\\s*"; + + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(body); + + if (matcher.find()) { + return matcher.group(1); + } + + return "0"; + } } diff --git a/src/main/java/org/lumijiez/parsers/DarwinProductProcessor.java b/src/main/java/org/lumijiez/parsers/DarwinProductProcessor.java index 41be993..ad3f9f4 100644 --- a/src/main/java/org/lumijiez/parsers/DarwinProductProcessor.java +++ b/src/main/java/org/lumijiez/parsers/DarwinProductProcessor.java @@ -1,4 +1,81 @@ package org.lumijiez.parsers; +import org.lumijiez.models.FilteredProducts; +import org.lumijiez.models.Product; + +import java.text.NumberFormat; +import java.text.ParseException; +import java.time.Instant; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + public class DarwinProductProcessor { -} + + public static FilteredProducts processProducts(List products, float minPrice, float maxPrice) { + List filteredProducts = products.stream() + .map(DarwinProductProcessor::convertPricesToEUR) + .filter(product -> isInPriceRange(product, minPrice, maxPrice)) + .collect(Collectors.toList()); + + float totalPrice = filteredProducts.stream() + .map(DarwinProductProcessor::getPrice) + .reduce(0f, Float::sum); + + FilteredProducts result = new FilteredProducts(); + result.products = filteredProducts; + result.date = Date.from(Instant.now()); + result.price = totalPrice; + + return result; + } + + private static Product convertPricesToEUR(Product product) { + product.oldPrice = convertToEUR(product.oldPrice); + product.newPrice = convertToEUR(product.newPrice); + product.discount = convertToEUR(product.discount); + return product; + } + + private static String convertToEUR(String priceInMDL) { + if (priceInMDL == null || priceInMDL.isEmpty()) { + return null; + } + try { + NumberFormat format = NumberFormat.getInstance(Locale.forLanguageTag("ro-RO")); + Number number = format.parse(priceInMDL.replaceAll("[^\\d]", "")); + float price = number.floatValue(); + return String.format("%.2f EUR", price / 19); + } catch (ParseException e) { + System.out.println(Arrays.toString(e.getStackTrace())); + return null; + } + } + + private static boolean isInPriceRange(Product product, float minPrice, float maxPrice) { + float price = getPrice(product); + return price >= minPrice && price <= maxPrice; + } + + private static float getPrice(Product product) { + if (product.newPrice != null) { + return parsePrice(product.newPrice); + } + return 0f; + } + + private static float parsePrice(String priceInEUR) { + if (priceInEUR == null) { + return 0f; + } + try { + String numericPart = priceInEUR.replaceAll("[^\\d.]", ""); + return Float.parseFloat(numericPart); + } catch (NumberFormatException e) { + System.out.println(Arrays.toString(e.getStackTrace())); + return 0f; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/lumijiez/serializer/JsonFileWriter.java b/src/main/java/org/lumijiez/serializer/JsonFileWriter.java index 50821a1..522f9b6 100644 --- a/src/main/java/org/lumijiez/serializer/JsonFileWriter.java +++ b/src/main/java/org/lumijiez/serializer/JsonFileWriter.java @@ -1,4 +1,32 @@ package org.lumijiez.serializer; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; + public class JsonFileWriter { -} + public static void saveJsonToFile(Object objectToSerialize, String filePath) throws IllegalAccessException { + String jsonOutput = Lumi.toJson(objectToSerialize); + + File file = new File(filePath); + BufferedWriter writer = null; + + try { + writer = new BufferedWriter(new FileWriter(file)); + writer.write(jsonOutput); + System.out.println("JSON saved to " + filePath); + } catch (IOException e) { + System.out.println(Arrays.toString(e.getStackTrace())); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + System.out.println(Arrays.toString(e.getStackTrace())); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/org/lumijiez/serializer/Lumi.java b/src/main/java/org/lumijiez/serializer/Lumi.java new file mode 100644 index 0000000..76d9243 --- /dev/null +++ b/src/main/java/org/lumijiez/serializer/Lumi.java @@ -0,0 +1,217 @@ +package org.lumijiez.serializer; + +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Lumi { + public static String toJson(Object obj) throws IllegalAccessException { + if (!obj.getClass().isAnnotationPresent(LumiSerializable.class)) { + throw new IllegalArgumentException("Class is not marked as LumiSerializable"); + } + + StringBuilder json = new StringBuilder("{"); + Field[] fields = obj.getClass().getDeclaredFields(); + List serializedFields = new ArrayList<>(); + + for (Field field : fields) { + if (field.isAnnotationPresent(LumiSerializeField.class)) { + field.setAccessible(true); + String fieldName = field.getName(); + Object value = field.get(obj); + serializedFields.add("\"" + fieldName + "\":" + valueToJson(value)); + } + } + + json.append(String.join(",", serializedFields)); + json.append("}"); + return json.toString(); + } + + private static String valueToJson(Object value) { + if (value == null) { + return "null"; + } else if (value instanceof String) { + return "\"" + escapeJsonString((String) value) + "\""; + } else if (value instanceof Number || value instanceof Boolean) { + return value.toString(); + } else if (value instanceof List) { + return listToJson((List) value); + } else if (value instanceof Date) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + return "\"" + dateFormat.format((Date) value) + "\""; + } else { + try { + return toJson(value); + } catch (IllegalAccessException e) { + return "\"" + value + "\""; + } + } + } + + private static String listToJson(List list) { + List jsonItems = new ArrayList<>(); + for (Object item : list) { + jsonItems.add(valueToJson(item)); + } + return "[" + String.join(",", jsonItems) + "]"; + } + + private static String escapeJsonString(String input) { + return input.replace("\"", "\\\"") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } + + public static String toXml(Object obj) throws IllegalAccessException { + if (!obj.getClass().isAnnotationPresent(LumiSerializable.class)) { + throw new IllegalArgumentException("Class is not marked as LumiSerializable"); + } + + StringBuilder xml = new StringBuilder("<" + obj.getClass().getSimpleName() + ">"); + Field[] fields = obj.getClass().getDeclaredFields(); + + for (Field field : fields) { + if (field.isAnnotationPresent(LumiSerializeField.class)) { + field.setAccessible(true); + String fieldName = field.getName(); + Object value = field.get(obj); + xml.append(valueToXml(fieldName, value)); + } + } + + xml.append(""); + return xml.toString(); + } + + private static String valueToXml(String fieldName, Object value) { + StringBuilder xml = new StringBuilder("<" + fieldName + ">"); + + if (value == null) { + xml.append("null"); + } else if (value instanceof String || value instanceof Number || value instanceof Boolean) { + xml.append(escapeXmlString(value.toString())); + } else if (value instanceof List) { + xml.append(listToXml((List) value)); + } else if (value instanceof Date) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + xml.append(escapeXmlString(dateFormat.format((Date) value))); + } else if (value instanceof java.time.LocalDate) { + xml.append(escapeXmlString(((java.time.LocalDate) value).toString())); + } else if (value instanceof java.time.LocalDateTime) { + xml.append(escapeXmlString(((java.time.LocalDateTime) value).toString())); + } else if (value instanceof java.time.ZonedDateTime) { + xml.append(escapeXmlString(((java.time.ZonedDateTime) value).toString())); + } else { + try { + xml.append(toXml(value)); + } catch (IllegalAccessException e) { + xml.append(escapeXmlString(value.toString())); + } + } + + xml.append(""); + return xml.toString(); + } + + private static String listToXml(List list) { + StringBuilder xml = new StringBuilder(); + for (Object item : list) { + xml.append(""); + xml.append(valueToXml("value", item)); + xml.append(""); + } + return xml.toString(); + } + + private static String escapeXmlString(String input) { + return input.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'"); + } + + public static T fromJson(String json, Class clazz) throws Exception { + if (!clazz.isAnnotationPresent(LumiSerializable.class)) { + throw new IllegalArgumentException("Class is not marked as LumiSerializable"); + } + + T obj = clazz.getDeclaredConstructor().newInstance(); + Pattern pattern = Pattern.compile("\"(\\w+)\"\\s*:\\s*(.+?)(?=,\\s*\"|\\})"); + Matcher matcher = pattern.matcher(json); + + while (matcher.find()) { + String fieldName = matcher.group(1); + String fieldValue = matcher.group(2).trim(); + + Field field = getFieldByName(clazz, fieldName); + if (field != null && field.isAnnotationPresent(LumiSerializeField.class)) { + field.setAccessible(true); + field.set(obj, parseValue(fieldValue, field.getType())); + } + } + return obj; + } + + public static T fromXml(String xml, Class clazz) throws Exception { + if (!clazz.isAnnotationPresent(LumiSerializable.class)) { + throw new IllegalArgumentException("Class is not marked as LumiSerializable"); + } + + T obj = clazz.getDeclaredConstructor().newInstance(); + Pattern pattern = Pattern.compile("<(\\w+)>(.*?)"); + Matcher matcher = pattern.matcher(xml); + + while (matcher.find()) { + String fieldName = matcher.group(1); + String fieldValue = matcher.group(2); + + Field field = getFieldByName(clazz, fieldName); + if (field != null && field.isAnnotationPresent(LumiSerializeField.class)) { + field.setAccessible(true); + field.set(obj, parseValue(fieldValue, field.getType())); + } + } + + return obj; + } + + private static Field getFieldByName(Class clazz, String fieldName) { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + return null; + } + } + + private static Object parseValue(String value, Class type) { + if (type == String.class) { + return value.replaceAll("^\"|\"$", ""); + } else if (type == int.class || type == Integer.class) { + return Integer.parseInt(value); + } else if (type == long.class || type == Long.class) { + return Long.parseLong(value); + } else if (type == double.class || type == Double.class) { + return Double.parseDouble(value); + } else if (type == boolean.class || type == Boolean.class) { + return Boolean.parseBoolean(value); + } else if (type == float.class || type == Float.class) { + return Float.parseFloat(value); + } + return value; + } + + public static void xmlToFile(Object obj, String fileName) throws IllegalAccessException { + XmlFileWriter.saveXmlToFile(obj, fileName); + } + + public static void jsonToFile(Object obj, String fileName) throws IllegalAccessException { + JsonFileWriter.saveJsonToFile(obj, fileName); + } +} diff --git a/src/main/java/org/lumijiez/serializer/LumiSerializable.java b/src/main/java/org/lumijiez/serializer/LumiSerializable.java index e69de29..09dc214 100644 --- a/src/main/java/org/lumijiez/serializer/LumiSerializable.java +++ b/src/main/java/org/lumijiez/serializer/LumiSerializable.java @@ -0,0 +1,10 @@ +package org.lumijiez.serializer; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface LumiSerializable {} diff --git a/src/main/java/org/lumijiez/serializer/LumiSerializeField.java b/src/main/java/org/lumijiez/serializer/LumiSerializeField.java index e69de29..4db1e6d 100644 --- a/src/main/java/org/lumijiez/serializer/LumiSerializeField.java +++ b/src/main/java/org/lumijiez/serializer/LumiSerializeField.java @@ -0,0 +1,10 @@ +package org.lumijiez.serializer; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface LumiSerializeField {} diff --git a/src/main/java/org/lumijiez/serializer/LumiSerializer.java b/src/main/java/org/lumijiez/serializer/LumiSerializer.java deleted file mode 100644 index bb35fba..0000000 --- a/src/main/java/org/lumijiez/serializer/LumiSerializer.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.lumijiez.serializer; - -public class LumiSerializer { -} diff --git a/src/main/java/org/lumijiez/serializer/XmlFileWriter.java b/src/main/java/org/lumijiez/serializer/XmlFileWriter.java new file mode 100644 index 0000000..e0176e7 --- /dev/null +++ b/src/main/java/org/lumijiez/serializer/XmlFileWriter.java @@ -0,0 +1,33 @@ +package org.lumijiez.serializer; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; + +public class XmlFileWriter { + public static void saveXmlToFile(Object objectToSerialize, String filePath) throws IllegalAccessException { + String xmlOutput = Lumi.toXml(objectToSerialize); + + File file = new File(filePath); + BufferedWriter writer = null; + + try { + writer = new BufferedWriter(new FileWriter(file)); + writer.write(xmlOutput); + System.out.println("XML saved to " + filePath); + } catch (IOException e) { + System.out.println(Arrays.toString(e.getStackTrace())); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + System.out.println(Arrays.toString(e.getStackTrace())); + } + } + } + } +} +