import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.CookieManager; import java.net.HttpCookie; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.ArrayList; import java.util.regex.Pattern; import java.util.regex.Matcher; public class ClientLib { private final String baseUrl; private final HttpClient httpClient; private final CookieManager cookieManager; private String userId; private String username; private String authToken; public ClientLib(String serviceUrl) { this.baseUrl = serviceUrl.endsWith("/") ? serviceUrl.substring(0, serviceUrl.length() - 1) : serviceUrl; this.cookieManager = new CookieManager(); this.httpClient = HttpClient.newBuilder() .cookieHandler(cookieManager) .build(); } public Map registerUser(String username, String password) throws Exception { String body = "{\"username\":\"" + jsonEscape(username) + "\",\"password\":\"" + jsonEscape(password) + "\"}"; String response = post("/api/account/signup", body); Map result = parseJsonObject(response); if (result.containsKey("success") && Boolean.TRUE.equals(result.get("success"))) { this.username = (String) result.get("username"); Object userIdObj = result.get("userId"); this.userId = String.valueOf(userIdObj); extractCookies(); } return result; } public Map login(String username, String password) throws Exception { String body = "{\"username\":\"" + jsonEscape(username) + "\",\"password\":\"" + jsonEscape(password) + "\"}"; String response = post("/api/account/login", body); Map result = parseJsonObject(response); if (result.containsKey("success") && Boolean.TRUE.equals(result.get("success"))) { this.username = (String) result.get("username"); Object userIdObj = result.get("userId"); this.userId = String.valueOf(userIdObj); extractCookies(); } return result; } public Map getCurrentUser() throws Exception { String response = get("/api/account/me"); return parseJsonObject(response); } public Map logout() throws Exception { String response = post("/api/account/logout", "{}"); Map result = parseJsonObject(response); this.userId = null; this.username = null; this.authToken = null; return result; } public Map createForm(Map schema) throws Exception { String schemaJson = mapToJson(schema); String body = "{\"schema\":" + schemaJson + "}"; String response = post("/api/form/new", body); return parseJsonObject(response); } public Map getForm(String formId) throws Exception { String response = get("/api/form/" + formId); return parseJsonObject(response); } public Map updateForm(String formId, Map schema) throws Exception { String schemaJson = mapToJson(schema); String body = "{\"schema\":" + schemaJson + "}"; String response = put("/api/form/" + formId, body); return parseJsonObject(response); } public Map submitFormResponse(String formId, Map payload) throws Exception { String payloadJson = mapToJson(payload); String response = post("/api/form/" + formId + "/submit", payloadJson); return parseJsonObject(response); } public List> getMyForms() throws Exception { String response = get("/api/form/my-forms"); return parseJsonArray(response); } public List getFormResults(String formId) throws Exception { String response = get("/api/form/" + formId + "/results"); return parseStringArray(response); } private String get(String endpoint) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + endpoint)) .GET() .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); return response.body(); } private String post(String endpoint, String jsonBody) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + endpoint)) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); return response.body(); } private String put(String endpoint, String jsonBody) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + endpoint)) .header("Content-Type", "application/json") .PUT(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); return response.body(); } private void extractCookies() { List cookies = cookieManager.getCookieStore().getCookies(); for (HttpCookie cookie : cookies) { switch (cookie.getName()) { case "uid": this.userId = cookie.getValue(); break; case "u": this.username = cookie.getValue(); break; case "auth": this.authToken = cookie.getValue(); break; } } } public String getUserId() { return userId; } public String getUsername() { return username; } public String getAuthToken() { return authToken; } public String getBaseUrl() { return baseUrl; } public boolean isAuthenticated() { return username != null && authToken != null && userId != null; } public void setSessionCookies(String userId, String username, String authToken) { this.userId = userId; this.username = username; this.authToken = authToken; try { URI uri = new URI(baseUrl); HttpCookie uidCookie = new HttpCookie("uid", userId); uidCookie.setPath("/"); uidCookie.setDomain(uri.getHost()); HttpCookie uCookie = new HttpCookie("u", username); uCookie.setPath("/"); uCookie.setDomain(uri.getHost()); HttpCookie authCookie = new HttpCookie("auth", authToken); authCookie.setPath("/"); authCookie.setDomain(uri.getHost()); cookieManager.getCookieStore().add(uri, uidCookie); cookieManager.getCookieStore().add(uri, uCookie); cookieManager.getCookieStore().add(uri, authCookie); } catch (Exception e) { e.printStackTrace(); } } private String jsonEscape(String str) { return str.replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t"); } private String mapToJson(Map map) { StringBuilder sb = new StringBuilder("{"); boolean first = true; for (Map.Entry entry : map.entrySet()) { if (!first) sb.append(","); first = false; sb.append("\"").append(jsonEscape(entry.getKey())).append("\":"); sb.append(valueToJson(entry.getValue())); } sb.append("}"); return sb.toString(); } private String valueToJson(Object value) { if (value == null) { return "null"; } else if (value instanceof String) { return "\"" + jsonEscape((String) value) + "\""; } else if (value instanceof Number || value instanceof Boolean) { return value.toString(); } else if (value instanceof Map) { return mapToJson((Map) value); } else if (value instanceof List) { StringBuilder sb = new StringBuilder("["); boolean first = true; for (Object item : (List) value) { if (!first) sb.append(","); first = false; sb.append(valueToJson(item)); } sb.append("]"); return sb.toString(); } else { return "\"" + jsonEscape(value.toString()) + "\""; } } private Map parseJsonObject(String json) { Map result = new HashMap<>(); json = json.trim(); if (!json.startsWith("{") || !json.endsWith("}")) { return result; } json = json.substring(1, json.length() - 1).trim(); Pattern pattern = Pattern.compile("\"([^\"]+)\"\\s*:\\s*([^,}]+|\\{[^}]*\\}|\\[[^\\]]*\\]|\"[^\"]*\")"); Matcher matcher = pattern.matcher(json); while (matcher.find()) { String key = matcher.group(1); String value = matcher.group(2).trim(); result.put(key, parseValue(value)); } return result; } private List> parseJsonArray(String json) { List> result = new ArrayList<>(); json = json.trim(); if (!json.startsWith("[") || !json.endsWith("]")) { return result; } json = json.substring(1, json.length() - 1).trim(); if (json.isEmpty()) { return result; } int depth = 0; StringBuilder current = new StringBuilder(); for (char c : json.toCharArray()) { if (c == '{') depth++; else if (c == '}') depth--; if (c == ',' && depth == 0) { result.add(parseJsonObject(current.toString())); current = new StringBuilder(); } else { current.append(c); } } if (current.length() > 0) { result.add(parseJsonObject(current.toString())); } return result; } private List parseStringArray(String json) { List result = new ArrayList<>(); json = json.trim(); if (!json.startsWith("[") || !json.endsWith("]")) { return result; } json = json.substring(1, json.length() - 1).trim(); if (json.isEmpty()) { return result; } int depth = 0; boolean inString = false; StringBuilder current = new StringBuilder(); for (int i = 0; i < json.length(); i++) { char c = json.charAt(i); if (c == '"' && (i == 0 || json.charAt(i - 1) != '\\')) { inString = !inString; } if (!inString) { if (c == '{' || c == '[') depth++; else if (c == '}' || c == ']') depth--; } if (c == ',' && depth == 0 && !inString) { String item = current.toString().trim(); if (item.startsWith("\"") && item.endsWith("\"")) { item = item.substring(1, item.length() - 1); } result.add(item); current = new StringBuilder(); } else { current.append(c); } } if (current.length() > 0) { String item = current.toString().trim(); if (item.startsWith("\"") && item.endsWith("\"")) { item = item.substring(1, item.length() - 1); } result.add(item); } return result; } private Object parseValue(String value) { value = value.trim(); if (value.equals("null")) { return null; } else if (value.equals("true")) { return Boolean.TRUE; } else if (value.equals("false")) { return Boolean.FALSE; } else if (value.startsWith("\"") && value.endsWith("\"")) { return value.substring(1, value.length() - 1); } else if (value.matches("-?\\d+")) { return Long.parseLong(value); } else if (value.matches("-?\\d+\\.\\d+")) { return Double.parseDouble(value); } else { return value; } } }