001package gudusoft.gsqlparser.dlineage.dataflow.model; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.common.structured.StructuredPathSegment; 005import gudusoft.gsqlparser.common.structured.StructuredPathSegmentKind; 006import gudusoft.gsqlparser.dlineage.util.DlineageUtil; 007 008import java.util.ArrayList; 009import java.util.Collections; 010import java.util.List; 011 012/** 013 * The identity of a field node of a STRUCT tree (plan section 4.7): the column the tree hangs below, by object 014 * identity, and the steps below it, each FIELD step keyed by the analysis's identifier rules from the name as written, 015 * every other step by its kind. Two reads of one field of one column give equal keys whatever their spelling the 016 * rules equate ({@code s.`Foo`} and {@code s.foo} in Hive); a field named {@code "a.b"} (one step) and the path 017 * {@code a.b} (two steps) give different keys although they display alike. 018 * <p> 019 * In-process only: the owner is compared by identity, so a key never leaves the analysis that made it. 020 */ 021public final class FieldNodeKey { 022 023 /** One step of the path: its kind, and for a FIELD the name's identity key. */ 024 public static final class StepKey { 025 private final StructuredPathSegmentKind kind; 026 private final Object nameKey; 027 028 private StepKey(StructuredPathSegmentKind kind, Object nameKey) { 029 this.kind = kind; 030 this.nameKey = nameKey; 031 } 032 033 /** The key of {@code segment} under the analysis's identifier rules. */ 034 public static StepKey of(StructuredPathSegment segment) { 035 if (segment.getKind() != StructuredPathSegmentKind.FIELD) { 036 return new StepKey(segment.getKind(), null); 037 } 038 EDbVendor vendor = ModelBindingManager.getGlobalVendor(); 039 Object key = vendor == null ? null : DlineageUtil.lexicalColumnKey(vendor, segment.getName()); 040 if (key == null) { 041 // no rules bound (a model used outside an analysis): DlineageUtil.sameName then compares ignoring 042 // case, and so does this key 043 key = "\u0000" + segment.getName().toUpperCase(java.util.Locale.ROOT).toLowerCase(java.util.Locale.ROOT); 044 } 045 return new StepKey(segment.getKind(), key); 046 } 047 048 @Override 049 public boolean equals(Object o) { 050 if (this == o) { 051 return true; 052 } 053 if (!(o instanceof StepKey)) { 054 return false; 055 } 056 StepKey other = (StepKey) o; 057 return kind == other.kind && (nameKey == null ? other.nameKey == null : nameKey.equals(other.nameKey)); 058 } 059 060 @Override 061 public int hashCode() { 062 return 31 * kind.hashCode() + (nameKey == null ? 0 : nameKey.hashCode()); 063 } 064 } 065 066 private final Object owner; 067 private final List<StepKey> steps; 068 069 private FieldNodeKey(Object owner, List<StepKey> steps) { 070 this.owner = owner; 071 this.steps = Collections.unmodifiableList(steps); 072 } 073 074 /** The key of the node {@code segments} below {@code owner} (a column, or a result column). */ 075 public static FieldNodeKey of(Object owner, List<StructuredPathSegment> segments) { 076 List<StepKey> steps = new ArrayList<StepKey>(segments.size()); 077 for (StructuredPathSegment segment : segments) { 078 steps.add(StepKey.of(segment)); 079 } 080 return new FieldNodeKey(owner, steps); 081 } 082 083 /** The key of a table's field path node: its declared column and its path. */ 084 public static FieldNodeKey of(TableColumn node) { 085 return of(node.getDeclaredColumn(), node.getFieldPath().getSegments()); 086 } 087 088 public Object getOwner() { 089 return owner; 090 } 091 092 public List<StepKey> getSteps() { 093 return steps; 094 } 095 096 @Override 097 public boolean equals(Object o) { 098 if (this == o) { 099 return true; 100 } 101 if (!(o instanceof FieldNodeKey)) { 102 return false; 103 } 104 FieldNodeKey other = (FieldNodeKey) o; 105 return owner == other.owner && steps.equals(other.steps); 106 } 107 108 @Override 109 public int hashCode() { 110 return 31 * System.identityHashCode(owner) + steps.hashCode(); 111 } 112}