001package gudusoft.gsqlparser.dlineage.dataflow.model; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.LinkedHashMap; 006import java.util.List; 007import java.util.Map; 008 009/** 010 * Immutable semantic evidence captured while the parser AST and catalog 011 * bindings are still available. The legacy XML/JSON contract deliberately 012 * does not serialize this object; consumers correlate it to the completed 013 * legacy POJO through document-local relationship, routine, and field IDs. 014 */ 015public final class AuthoritativeLineageEvidence { 016 017 private static final AuthoritativeLineageEvidence EMPTY = new AuthoritativeLineageEvidence( 018 Collections.<TypedTransform>emptyList(), Collections.<RoutineBinding>emptyList(), 019 Collections.<CompositeForeignKey>emptyList()); 020 021 private final Map<String, TypedTransform> typedTransformsByRelationshipId; 022 private final Map<String, RoutineBinding> routineBindingsByRoutineId; 023 private final List<CompositeForeignKey> compositeForeignKeys; 024 025 public AuthoritativeLineageEvidence(List<TypedTransform> typedTransforms, 026 List<RoutineBinding> routineBindings, List<CompositeForeignKey> compositeForeignKeys) { 027 this.typedTransformsByRelationshipId = indexTypedTransforms(typedTransforms); 028 this.routineBindingsByRoutineId = indexRoutineBindings(routineBindings); 029 this.compositeForeignKeys = immutable(compositeForeignKeys); 030 } 031 032 public static AuthoritativeLineageEvidence empty() { return EMPTY; } 033 034 public TypedTransform typedTransform(String relationshipId) { 035 return typedTransformsByRelationshipId.get(relationshipId); 036 } 037 038 public RoutineBinding routineBinding(String routineId) { 039 return routineBindingsByRoutineId.get(routineId); 040 } 041 042 public List<TypedTransform> typedTransforms() { 043 return Collections.unmodifiableList(new ArrayList<TypedTransform>( 044 typedTransformsByRelationshipId.values())); 045 } 046 047 public List<RoutineBinding> routineBindings() { 048 return Collections.unmodifiableList(new ArrayList<RoutineBinding>( 049 routineBindingsByRoutineId.values())); 050 } 051 052 public List<CompositeForeignKey> compositeForeignKeys() { return compositeForeignKeys; } 053 054 public boolean isEmpty() { 055 return typedTransformsByRelationshipId.isEmpty() && routineBindingsByRoutineId.isEmpty() 056 && compositeForeignKeys.isEmpty(); 057 } 058 059 public static final class TypedTransform { 060 private final String relationshipId; 061 private final String operationFamily; 062 private final String operationCode; 063 private final List<TypedOperand> operands; 064 065 public TypedTransform(String relationshipId, String operationFamily, String operationCode, 066 List<TypedOperand> operands) { 067 this.relationshipId = required(relationshipId, "relationshipId"); 068 this.operationFamily = required(operationFamily, "operationFamily"); 069 this.operationCode = required(operationCode, "operationCode"); 070 this.operands = immutableRequired(operands, "operands"); 071 } 072 073 public String relationshipId() { return relationshipId; } 074 public String operationFamily() { return operationFamily; } 075 public String operationCode() { return operationCode; } 076 public List<TypedOperand> operands() { return operands; } 077 } 078 079 public static final class TypedOperand { 080 private final String sourceLegacyId; 081 private final String roleCode; 082 083 public TypedOperand(String sourceLegacyId, String roleCode) { 084 this.sourceLegacyId = required(sourceLegacyId, "sourceLegacyId"); 085 this.roleCode = required(roleCode, "roleCode"); 086 } 087 088 public String sourceLegacyId() { return sourceLegacyId; } 089 public String roleCode() { return roleCode; } 090 } 091 092 public static final class RoutineBinding { 093 private final String routineLegacyId; 094 private final String opaqueIdentity; 095 private final List<RoutineParameter> parameters; 096 097 public RoutineBinding(String routineLegacyId, String opaqueIdentity, 098 List<RoutineParameter> parameters) { 099 this.routineLegacyId = required(routineLegacyId, "routineLegacyId"); 100 this.opaqueIdentity = required(opaqueIdentity, "opaqueIdentity"); 101 this.parameters = immutable(parameters); 102 } 103 104 public String routineLegacyId() { return routineLegacyId; } 105 public String opaqueIdentity() { return opaqueIdentity; } 106 public List<RoutineParameter> parameters() { return parameters; } 107 } 108 109 public static final class RoutineParameter { 110 private final String bindingKey; 111 private final String legacyArgumentId; 112 private final String boundLegacyEndpointId; 113 private final String displayName; 114 private final String role; 115 private final int ordinal; 116 private final String declaredType; 117 private final String opaqueIdentity; 118 119 public RoutineParameter(String bindingKey, String legacyArgumentId, 120 String boundLegacyEndpointId, String displayName, String role, int ordinal, 121 String declaredType, String opaqueIdentity) { 122 this.bindingKey = required(bindingKey, "bindingKey"); 123 this.legacyArgumentId = optional(legacyArgumentId); 124 this.boundLegacyEndpointId = optional(boundLegacyEndpointId); 125 this.displayName = required(displayName, "displayName"); 126 this.role = required(role, "role"); 127 if (ordinal < 1) throw new IllegalArgumentException("ordinal must be positive"); 128 this.ordinal = ordinal; 129 this.declaredType = optional(declaredType); 130 this.opaqueIdentity = required(opaqueIdentity, "opaqueIdentity"); 131 } 132 133 public String bindingKey() { return bindingKey; } 134 public String legacyArgumentId() { return legacyArgumentId; } 135 public String boundLegacyEndpointId() { return boundLegacyEndpointId; } 136 public String displayName() { return displayName; } 137 public String role() { return role; } 138 public int ordinal() { return ordinal; } 139 public String declaredType() { return declaredType; } 140 public String opaqueIdentity() { return opaqueIdentity; } 141 } 142 143 public static final class CompositeForeignKey { 144 private final List<String> evidenceRelationshipIds; 145 private final List<ForeignKeyPair> keyPairs; 146 147 public CompositeForeignKey(List<String> evidenceRelationshipIds, 148 List<ForeignKeyPair> keyPairs) { 149 this.evidenceRelationshipIds = immutableRequired(evidenceRelationshipIds, 150 "evidenceRelationshipIds"); 151 this.keyPairs = immutableRequired(keyPairs, "keyPairs"); 152 } 153 154 public List<String> evidenceRelationshipIds() { return evidenceRelationshipIds; } 155 public List<ForeignKeyPair> keyPairs() { return keyPairs; } 156 } 157 158 public static final class ForeignKeyPair { 159 private final String foreignKeyFieldLegacyId; 160 private final String referencedFieldLegacyId; 161 162 public ForeignKeyPair(String foreignKeyFieldLegacyId, String referencedFieldLegacyId) { 163 this.foreignKeyFieldLegacyId = required(foreignKeyFieldLegacyId, 164 "foreignKeyFieldLegacyId"); 165 this.referencedFieldLegacyId = required(referencedFieldLegacyId, 166 "referencedFieldLegacyId"); 167 } 168 169 public String foreignKeyFieldLegacyId() { return foreignKeyFieldLegacyId; } 170 public String referencedFieldLegacyId() { return referencedFieldLegacyId; } 171 } 172 173 private static Map<String, TypedTransform> indexTypedTransforms(List<TypedTransform> values) { 174 Map<String, TypedTransform> result = new LinkedHashMap<String, TypedTransform>(); 175 if (values != null) for (TypedTransform value : values) { 176 if (value == null) throw new IllegalArgumentException("typedTransforms must not contain null"); 177 if (result.put(value.relationshipId(), value) != null) { 178 throw new IllegalArgumentException("duplicate typed transform relationship ID: " 179 + value.relationshipId()); 180 } 181 } 182 return Collections.unmodifiableMap(result); 183 } 184 185 private static Map<String, RoutineBinding> indexRoutineBindings(List<RoutineBinding> values) { 186 Map<String, RoutineBinding> result = new LinkedHashMap<String, RoutineBinding>(); 187 if (values != null) for (RoutineBinding value : values) { 188 if (value == null) throw new IllegalArgumentException("routineBindings must not contain null"); 189 if (result.put(value.routineLegacyId(), value) != null) { 190 throw new IllegalArgumentException("duplicate routine binding ID: " 191 + value.routineLegacyId()); 192 } 193 } 194 return Collections.unmodifiableMap(result); 195 } 196 197 private static <T> List<T> immutable(List<T> values) { 198 if (values == null || values.isEmpty()) return Collections.emptyList(); 199 List<T> copy = new ArrayList<T>(values.size()); 200 for (T value : values) { 201 if (value == null) throw new IllegalArgumentException("evidence list must not contain null"); 202 copy.add(value); 203 } 204 return Collections.unmodifiableList(copy); 205 } 206 207 private static <T> List<T> immutableRequired(List<T> values, String field) { 208 List<T> result = immutable(values); 209 if (result.isEmpty()) throw new IllegalArgumentException(field + " must not be empty"); 210 return result; 211 } 212 213 private static String optional(String value) { 214 return value == null || value.length() == 0 ? null : value; 215 } 216 217 private static String required(String value, String field) { 218 if (value == null || value.length() == 0) { 219 throw new IllegalArgumentException(field + " must not be empty"); 220 } 221 return value; 222 } 223}