Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions adapters/java/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?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>cn.uid9622.longhun</groupId>
<artifactId>lh-standard-adapter</artifactId>
<version>1.0.0</version>
<name>LongHun Standard Adapter</name>
<description>v∞ DNA traceability + seven-factor behavioral audit for Java/Kotlin</description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.3</version></dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.10.0</version><scope>test</scope></dependency>
</dependencies>
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.1.2</version></plugin></plugins></build>
</project>
85 changes: 85 additions & 0 deletions adapters/java/src/main/java/cn/uid9622/longhun/AuditWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cn.uid9622.longhun;

import java.security.MessageDigest;
import java.time.ZonedDateTime;
import java.time.ZoneOffset;
import java.util.*;

public class AuditWrapper {
private static final Map<String,Map<String,String>> LABEL_MAP = new LinkedHashMap<>();
static {
LABEL_MAP.put("P", Map.of("HasPromise","7F-P-有承诺","NoPromise","7F-P-无承诺"));
LABEL_MAP.put("F", Map.of("Fulfilled","7F-F-已兑现","Unfulfilled","7F-F-未兑现","Partial","7F-F-部分兑现"));
LABEL_MAP.put("E", Map.of("Willing","7F-E-心甘情愿","Perfunctory","7F-E-敷衍","Resentful","7F-E-怨恨","Numb","7F-E-麻木"));
LABEL_MAP.put("A", Map.of("Self","7F-A-自己","Partner","7F-A-伴侣","Family","7F-A-家庭","Outsider","7F-A-外人","Public","7F-A-公众"));
LABEL_MAP.put("X", Map.of("OverExplain","7F-X-过度解释","Silent","7F-X-沉默","Genuine","7F-X-真诚","Indifferent","7F-X-冷漠"));
LABEL_MAP.put("Y", Map.of("Changed","7F-Y-改正","Resisted","7F-Y-抗拒","Indifferent","7F-Y-无视","NoResponse","7F-Y-无响应"));
}

private final String uid;
public AuditWrapper(String uid) { this.uid = uid != null ? uid : "9622"; }

public Map<String,Object> wrap(Object payload, String taskType, String persona) {
if (taskType == null || taskType.isEmpty()) taskType = "default";
if (persona == null || persona.isEmpty()) persona = "P04";

Map<String,Object> sig = new LinkedHashMap<>();
sig.put("P","HasPromise"); sig.put("F","Fulfilled"); sig.put("T",0.0);
sig.put("E","Willing"); sig.put("C",0); sig.put("R",0);
sig.put("A","Self"); sig.put("X","Genuine"); sig.put("Y","NoResponse"); sig.put("Z",1.0);

String pattern = classify(sig);
List<String> labels = makeLabels(sig, pattern);
String color = determineColor(pattern, (int)sig.get("R"));

Map<String,Object> result = new LinkedHashMap<>();
result.put("audit_version","v1.0");
result.put("uid","UID"+uid);
result.put("persona",persona);
result.put("task_type",taskType);
result.put("behavior_signature",sig);
result.put("behavior_pattern",pattern);
result.put("behavior_labels",labels);
result.put("color",color);
result.put("timestamp", ZonedDateTime.now(ZoneOffset.UTC).toString());

try {
String pj = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(payload);
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] h = md.digest(pj.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : h) sb.append(String.format("%02x", b));
result.put("payload_hash", sb.toString().substring(0,16));
} catch (Exception e) { result.put("payload_hash","0000000000000000"); }
return result;
}

static String classify(Map<String,Object> sig) {
String f = (String)sig.get("F"), x = (String)sig.get("X");
String a = (String)sig.get("A"), y = (String)sig.get("Y");
double z = ((Number)sig.getOrDefault("Z",0.0)).doubleValue();
if ("Unfulfilled".equals(f) && "OverExplain".equals(x)) return "MODE-DefensiveDefaulter";
if ("Fulfilled".equals(f) && "Outsider".equals(a)) return "MODE-ExternalTrustSpender";
if ("Unfulfilled".equals(f) && "Indifferent".equals(y)) return "MODE-InternalDestroyer";
if (z > 2.0) return "MODE-Fluctuating";
return "MODE-StableDisciplined";
}

static List<String> makeLabels(Map<String,Object> sig, String pattern) {
List<String> labels = new ArrayList<>();
for (String factor : new String[]{"P","F","E","A","X","Y"}) {
String val = (String)sig.get(factor);
if (val != null && LABEL_MAP.containsKey(factor) && LABEL_MAP.get(factor).containsKey(val))
labels.add(LABEL_MAP.get(factor).get(val));
}
labels.add(pattern);
return labels;
}

static String determineColor(String pattern, int repeat) {
if ("MODE-InternalDestroyer".equals(pattern)) return "🔴";
if ("MODE-Fluctuating".equals(pattern) && repeat > 3) return "🟡";
if ("MODE-DefensiveDefaulter".equals(pattern) && repeat > 2) return "🟡";
return "🟢";
}
}
93 changes: 93 additions & 0 deletions adapters/java/src/main/java/cn/uid9622/longhun/DNAGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package cn.uid9622.longhun;

import java.security.MessageDigest;
import java.time.ZonedDateTime;
import java.time.ZoneOffset;
import java.util.*;

public class DNAGenerator {
private static final String[] TIAN_GAN = {"Jia","Yi","Bing","Ding","Wu","Ji","Geng","Xin","Ren","Gui"};
private static final String[] DI_ZHI = {"Zi","Chou","Yin","Mao","Chen","Si","Wu","Wei","Shen","You","Xu","Hai"};
private static final String[] SHI_CHEN = {"ZiShi","ChouShi","YinShi","MaoShi","ChenShi","SiShi","WuShi","WeiShi","ShenShi","YouShi","XuShi","HaiShi"};

private static final int[] CYCLE_MONTH = {2,4,6,8,10,0,2,4,6,8,10,0};
private static final int CYCLE_YEAR = 1984;

private final String uid, device, locale;

public DNAGenerator(String uid, String device, String locale) {
this.uid = uid != null ? uid : "9622";
this.device = device != null ? device : "HM-9622-001";
this.locale = locale != null ? locale : "Asia/Shanghai";
}

public String generate(String taskType, String action, String version) {
if (taskType == null || taskType.isEmpty()) taskType = "default";
if (action == null || action.isEmpty()) action = "WRAP";
if (version == null || version.isEmpty()) version = "V1.0";

ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
Map<String,String> stem = computeStemBranch(now);
String[] hex = selectHexagram(taskType);
String body = "ADAPTER-" + taskType.toUpperCase() + "-" + action.toUpperCase() + "-" + version;

String raw = stem.get("year") + stem.get("month") + stem.get("day") + stem.get("shichen")
+ hex[0] + hex[1] + body + device + now.toString();

try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(raw.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte b : hash) sb.append(String.format("%02x", b));
String hash8 = sb.toString().substring(0, 8);

return "#LongHun⚡️" + stem.get("year") + "·" + stem.get("month") + "·" + stem.get("day")
+ "·" + stem.get("shichen") + "·" + hex[0] + hex[1] + "-" + body + "-" + hash8;
} catch (Exception e) { return "#LongHun⚡️ERROR-" + e.getMessage(); }
}

private Map<String,String> computeStemBranch(ZonedDateTime dt) {
int y = dt.getYear(), m = dt.getMonthValue(), doy = dt.getDayOfYear(), h = dt.getHour();
int ys = ((y - CYCLE_YEAR) % 10 + 10) % 10;
int yb = ((y - CYCLE_YEAR) % 12 + 12) % 12;
int mb = CYCLE_MONTH[((y - CYCLE_YEAR) % 10 + 10) % 10];
int ms = ((mb + m - 1) % 10 + 10) % 10;
int mbr = ((m + 1) % 12 + 12) % 12;
int ds = ((y - 1900 + (y - 1900) / 4 + doy) % 10 + 10) % 10;
int db = ((y - 1900 + (y - 1900) / 4 + doy) % 12 + 12) % 12;
int si = h / 2;

Map<String,String> result = new LinkedHashMap<>();
result.put("year", TIAN_GAN[ys] + DI_ZHI[yb]);
result.put("month", TIAN_GAN[ms] + DI_ZHI[mbr]);
result.put("day", TIAN_GAN[ds] + DI_ZHI[db]);
result.put("shichen", SHI_CHEN[si]);
return result;
}

private String[] selectHexagram(String taskType) {
String domain;
switch (taskType) {
case "code": domain = "engine"; break;
case "deploy": domain = "deploy"; break;
case "audit": domain = "audit"; break;
case "security": domain = "security"; break;
case "archive": domain = "archive"; break;
case "init": domain = "init"; break;
case "learn": domain = "learn"; break;
case "legal": domain = "legal"; break;
case "privacy": domain = "privacy"; break;
case "trust": domain = "trust"; break;
case "complete": domain = "complete"; break;
case "progress": domain = "progress"; break;
default: domain = "governance";
}
String[][] all = {{"䷀","Qian","governance"},{"䷁","Kun","archive"},{"䷂","Zhun","init"},
{"䷃","Meng","learn"},{"䷄","Xu","async"},{"䷅","Song","legal"},
{"䷜","Kan","engine"},{"䷝","Li","audit"},{"䷲","Zhen","security"},
{"䷳","Gen","privacy"},{"䷸","Xun","deploy"},{"䷹","Dui","trust"},
{"䷾","JiJi","complete"},{"䷿","WeiJi","progress"}};
for (String[] h : all) if (h[2].equals(domain)) return new String[]{h[0], h[1]};
return new String[]{"䷀","Qian"};
}
}
51 changes: 51 additions & 0 deletions adapters/java/src/main/java/cn/uid9622/longhun/LongHunAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cn.uid9622.longhun;

import java.time.ZonedDateTime;
import java.time.ZoneOffset;
import java.util.*;

public class LongHunAdapter {
private final String uid, device, locale;
private final DNAGenerator dnaGen;
private final AuditWrapper audit;
private final Validator validator;

public LongHunAdapter(String uid, String device, String locale) {
this.uid = uid != null ? uid : "9622";
this.device = device != null ? device : "HM-9622-001";
this.locale = locale != null ? locale : "Asia/Shanghai";
this.dnaGen = new DNAGenerator(this.uid, this.device, this.locale);
this.audit = new AuditWrapper(this.uid);
this.validator = new Validator();
}

public Map<String,Object> wrap(Object data, String taskType, String persona, String action, String version) {
if (taskType == null || taskType.isEmpty()) taskType = "default";
if (persona == null || persona.isEmpty()) persona = "P04";
if (action == null || action.isEmpty()) action = "WRAP";
Map<String,Object> result = new LinkedHashMap<>();
result.put("dna", dnaGen.generate(taskType, action, version));
result.put("audit", audit.wrap(data, taskType, persona));
result.put("payload", data);
Map<String,Object> meta = new LinkedHashMap<>();
meta.put("adapter_version","1.0.0");
meta.put("uid",uid);
meta.put("device",device);
meta.put("task_type",taskType);
meta.put("persona",persona);
meta.put("generated_at", ZonedDateTime.now(ZoneOffset.UTC).toString());
meta.put("format","longhun-v∞");
result.put("meta", meta);
return result;
}

public ValidationResult validate(Map<String,Object> wrapped) { return validator.validate(wrapped); }

public Map<String,Object> getSchemas() {
Map<String,Object> schemas = new LinkedHashMap<>();
schemas.put("dnaSchema", Map.of("type","string","description","v∞ DNA traceability code"));
schemas.put("auditSchema", Map.of("type","object","required",
List.of("audit_version","uid","behavior_signature","behavior_pattern","behavior_labels","color")));
return schemas;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cn.uid9622.longhun;

import java.util.List;

public class ValidationResult {
private final boolean valid;
private final List<String> errors, warnings;
private final String summary;

public ValidationResult(boolean valid, List<String> errors, List<String> warnings, String summary) {
this.valid = valid; this.errors = errors; this.warnings = warnings; this.summary = summary;
}
public boolean isValid() { return valid; }
public List<String> getErrors() { return errors; }
public List<String> getWarnings() { return warnings; }
public String getSummary() { return summary; }
}
65 changes: 65 additions & 0 deletions adapters/java/src/main/java/cn/uid9622/longhun/Validator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package cn.uid9622.longhun;

import java.util.*;
import java.util.regex.Pattern;

public class Validator {
private static final Pattern DNA_REGEX = Pattern.compile(
"^#LongHun⚡️([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([\\u4DC0-\\u4DFF][A-Za-z]+)-(.+)-([a-f0-9]{8})$");
private static final Set<String> REQUIRED_TOP = Set.of("dna","audit","payload","meta");
private static final Set<String> REQUIRED_AUDIT = Set.of("audit_version","uid","behavior_signature","behavior_pattern","behavior_labels","color");
private static final Set<String> REQUIRED_SIG = Set.of("P","F","T","E","C","R","A","X","Y","Z");

private List<String> errors = new ArrayList<>();
private List<String> warnings = new ArrayList<>();

public ValidationResult validate(Map<String,Object> wrapped) {
errors.clear(); warnings.clear();
if (wrapped == null || wrapped.isEmpty()) { errors.add("Input null/empty"); return result(); }

for (String k : REQUIRED_TOP) if (!wrapped.containsKey(k)) errors.add("Missing top key: "+k);

Object dnaObj = wrapped.get("dna");
if (dnaObj == null || "".equals(dnaObj)) errors.add("DNA empty");
else if (!DNA_REGEX.matcher((String)dnaObj).matches())
errors.add("DNA regex fail: "+((String)dnaObj).substring(0, Math.min(60, ((String)dnaObj).length())));

Object auditObj = wrapped.get("audit");
if (!(auditObj instanceof Map)) errors.add("Audit not object");
else validateAudit((Map<String,Object>)auditObj);

return result();
}

private void validateAudit(Map<String,Object> audit) {
for (String k : REQUIRED_AUDIT) if (!audit.containsKey(k)) errors.add("Missing audit key: "+k);
Object sigObj = audit.get("behavior_signature");
if (!(sigObj instanceof Map)) errors.add("sig not object");
else {
Map<String,Object> sig = (Map<String,Object>)sigObj;
for (String k : REQUIRED_SIG) if (!sig.containsKey(k)) errors.add("Missing sig key: "+k);
validateSigValues(sig);
}
}

private void validateSigValues(Map<String,Object> sig) {
Set<String> vp = Set.of("HasPromise","NoPromise");
Set<String> vf = Set.of("Fulfilled","Unfulfilled","Partial");
Set<String> ve = Set.of("Willing","Perfunctory","Resentful","Numb");
Set<String> va = Set.of("Self","Partner","Family","Outsider","Public");
Set<String> vx = Set.of("OverExplain","Silent","Genuine","Indifferent");
Set<String> vy = Set.of("Changed","Resisted","Indifferent","NoResponse");

if (!vp.contains(sig.get("P"))) warnings.add("Invalid P: "+sig.get("P"));
if (!vf.contains(sig.get("F"))) warnings.add("Invalid F: "+sig.get("F"));
if (!ve.contains(sig.get("E"))) warnings.add("Invalid E: "+sig.get("E"));
if (!va.contains(sig.get("A"))) warnings.add("Invalid A: "+sig.get("A"));
if (!vx.contains(sig.get("X"))) warnings.add("Invalid X: "+sig.get("X"));
if (!vy.contains(sig.get("Y"))) warnings.add("Invalid Y: "+sig.get("Y"));
}

private ValidationResult result() {
boolean valid = errors.isEmpty();
return new ValidationResult(valid, errors, warnings, valid ? "✅ VALID" : "❌ INVALID");
}
}