001package gudusoft.gsqlparser.dlineage.dataflow.model.xml;
002
003import java.util.Comparator;
004import java.util.LinkedHashSet;
005import java.util.Set;
006
007import javax.xml.bind.annotation.XmlAttribute;
008import javax.xml.bind.annotation.XmlElement;
009import javax.xml.bind.annotation.XmlElementWrapper;
010import javax.xml.bind.annotation.XmlTransient;
011import javax.xml.bind.annotation.XmlType;
012
013import gudusoft.gsqlparser.TSourceToken;
014import gudusoft.gsqlparser.dlineage.dataflow.model.ModelBindingManager;
015import gudusoft.gsqlparser.dlineage.dataflow.model.Transform;
016import gudusoft.gsqlparser.dlineage.util.DlineageUtil;
017import gudusoft.gsqlparser.dlineage.util.LegacyDlineageWireNamesV1;
018import gudusoft.gsqlparser.dlineage.util.Pair3;
019import gudusoft.gsqlparser.util.SQLUtil;
020
021@XmlType(propOrder = { "id", "name", "column", "value", "source_id", "source_name", "column_type", "parent_id", "parent_name", "parent_alias",
022                "coordinate", "clauseType", "source", "type", "transforms", "candidateParents"})
023public class sourceColumn {
024
025        private String id;
026
027        private String name;
028        
029        private String column;
030
031        private String value;
032
033        private String source_id;
034
035        private String source_name;
036
037        private String column_type;
038
039        private String parent_id;
040
041        private String parent_name;
042        
043        private String parent_alias;
044
045        private String coordinate;
046
047        private String clauseType;
048
049        private String source;
050        
051        private String type;
052        
053        private Set<transform> transforms;
054        
055        private Set<candidateTable> candidateParents;
056        
057        @XmlTransient
058        private Boolean struct;
059
060        @XmlTransient
061        private boolean positionUnproven;
062
063        private String dedupCoordinate;
064
065        @XmlElementWrapper(name="candidateTables") 
066        @XmlElement(name="candidateTable", required = false)
067        public Set<candidateTable> getCandidateParents() {
068                if (ModelBindingManager.getGlobalOption()!=null && !ModelBindingManager.getGlobalOption().isShowCandidateTable()) {
069                        return null;
070                }
071                return candidateParents;
072        }
073        
074        public void setCandidateParents(Set<candidateTable> candidateParents) {
075                if (ModelBindingManager.getGlobalOption()!=null && !ModelBindingManager.getGlobalOption().isShowCandidateTable()) {
076                        return;
077                }
078                this.candidateParents = candidateParents;
079        }
080        
081        public void addCandidateParent(candidateTable candidateParent) {
082                if (ModelBindingManager.getGlobalOption()!=null && !ModelBindingManager.getGlobalOption().isShowCandidateTable()) {
083                        return;
084                }
085                if(candidateParent == null)
086                        return;
087                if(candidateParents == null) {
088                        candidateParents = new LinkedHashSet<candidateTable>();
089                }
090                candidateParents.add(candidateParent);
091        }
092        
093        @XmlElementWrapper(name="transforms") 
094        @XmlElement(name="transform", required = false)
095        public Set<transform> getTransforms() {
096                if (ModelBindingManager.getGlobalOption()!=null && !ModelBindingManager.getGlobalOption().isTransform()) {
097                        return null;
098                }
099                return transforms;
100        }
101        
102        public void setTransforms(Set<transform> transforms) {
103                if (ModelBindingManager.getGlobalOption()!=null && !ModelBindingManager.getGlobalOption().isTransform()) {
104                        return;
105                }
106                this.transforms = transforms;
107        }
108        
109        public void addTransform(Transform transform) {
110                if (ModelBindingManager.getGlobalOption()!=null && !ModelBindingManager.getGlobalOption().isTransform()) {
111                        return;
112                }
113                if(transform == null)
114                        return;
115                if(transforms == null) {
116                        transforms = new LinkedHashSet<transform>();
117                }
118                
119                transform t = new transform();
120                if (transform.getCode() != null) {
121                        if (ModelBindingManager.getGlobalOption().isTransformCoordinate()) {
122                                TSourceToken startToken = transform.getCode().getStartToken();
123                                TSourceToken endToken = transform.getCode().getEndToken();
124                                Pair3<Long, Long, String> startPosition = new Pair3<Long, Long, String>(startToken.lineNo,
125                                                startToken.columnNo, ModelBindingManager.getGlobalHash());
126                                Pair3<Long, Long, String> endPosition = new Pair3<Long, Long, String>(endToken.lineNo,
127                                                endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash());
128                                t.setCoordinate(convertCoordinate(startPosition) + "," + convertCoordinate(endPosition));
129                        }
130                        t.setCode(transform.getCode().toString());
131                } else if (!SQLUtil.isEmpty(transform.getCodeString())) {
132                        if (ModelBindingManager.getGlobalOption().isTransformCoordinate()) {
133                                TSourceToken startToken = transform.getStartToken();
134                                TSourceToken endToken = transform.getEndToken();
135                                Pair3<Long, Long, String> startPosition = new Pair3<Long, Long, String>(startToken.lineNo,
136                                                startToken.columnNo, ModelBindingManager.getGlobalHash());
137                                Pair3<Long, Long, String> endPosition = new Pair3<Long, Long, String>(endToken.lineNo,
138                                                endToken.columnNo + SQLUtil.endTrim(endToken.getAstext()).length(), ModelBindingManager.getGlobalHash());
139                                t.setCoordinate(convertCoordinate(startPosition) + "," + convertCoordinate(endPosition));
140                        }
141                        t.setCode(transform.getCodeString());
142                }
143                t.setType(transform.getType());
144                transforms.add(t);
145        }
146        
147        public void addTransform(transform transform) {
148                if (ModelBindingManager.getGlobalOption()!=null && !ModelBindingManager.getGlobalOption().isTransform()) {
149                        return;
150                }
151                if(transform == null)
152                        return;
153                if(transforms == null) {
154                        transforms = new LinkedHashSet<transform>();
155                }
156                transforms.add(transform);
157        }
158        
159        private Pair3<Long, Long, Integer> convertCoordinate(Pair3<Long, Long, String> position) {
160                return new Pair3<Long, Long, Integer>(position.first, position.second,
161                                ModelBindingManager.getGlobalSqlInfo().getIndexOf(position.third));
162        }
163        
164        @XmlAttribute(required = false)
165        public String getCoordinate() {
166//              if (ModelBindingManager.getGlobalOption()!=null && ModelBindingManager.getGlobalOption().isIgnoreCoordinate()) {
167//                      return null;
168//              }
169                return coordinate;
170        }
171
172        public void setCoordinate(String coordinate) {
173//              if (ModelBindingManager.getGlobalOption()!=null && ModelBindingManager.getGlobalOption().isIgnoreCoordinate()) {
174//                      return;
175//              }
176                this.coordinate = coordinate;
177        }
178
179        @XmlAttribute(required = false)
180        public String getColumn() {
181                return column;
182        }
183
184        public void setColumn(String column) {
185                this.column = column;
186        }
187
188        @XmlAttribute(required = false)
189        public String getId() {
190                return id;
191        }
192
193        public void setId(String id) {
194                this.id = id;
195        }
196
197        @XmlAttribute(name = "parent_id", required = false)
198        public String getParent_id() {
199                return parent_id;
200        }
201
202        public void setParent_id(String parent_id) {
203                this.parent_id = parent_id;
204        }
205
206        @XmlAttribute(name = "parent_name", required = false)
207        public String getParent_name() {
208                return parent_name;
209        }
210
211        public void setParent_name(String parent_name) {
212                this.parent_name = parent_name;
213        }
214        
215        @XmlAttribute(name = "parent_alias", required = false)
216        public String getParent_alias() {
217                return parent_alias;
218        }
219
220        public void setParent_alias(String parent_alias) {
221                this.parent_alias = parent_alias;
222        }
223
224        @XmlAttribute(required = false)
225        public String getValue() {
226                return value;
227        }
228
229        public void setValue(String value) {
230                this.value = value;
231        }
232
233        @XmlAttribute(name = "source_name", required = false)
234        public String getSource_name() {
235                return source_name;
236        }
237
238        public void setSource_name(String source_name) {
239                this.source_name = source_name;
240        }
241
242        @XmlAttribute(name = "source_id", required = false)
243        public String getSource_id() {
244                return source_id;
245        }
246
247        public void setSource_id(String source_id) {
248                this.source_id = source_id;
249        }
250
251        @XmlAttribute(required = false)
252        public String getClauseType() {
253                return clauseType;
254        }
255
256        public void setClauseType(String clauseType) {
257                this.clauseType = clauseType;
258        }
259
260        @XmlAttribute(name = "column_type", required = false)
261        public String getColumn_type() {
262                return column_type;
263        }
264
265        public void setColumn_type(String column_type) {
266                this.column_type = column_type;
267        }
268
269        @XmlAttribute(required = false)
270        public String getSource() {
271                return source;
272        }
273
274        public void setSource(String source) {
275                this.source = source;
276        }
277        
278        @XmlAttribute(required = false)
279        public String getType() {
280                return type;
281        }
282
283        public void setType(String type) {
284                this.type = type;
285        }
286
287        @Override
288        public int hashCode() {
289                final int prime = 31;
290                int result = 1;
291                String columnName = getColumnName(this);
292                result = prime * result + ((columnName == null) ? 0 : columnName.hashCode());
293                result = prime * result + ((coordinate == null) ? 0 : coordinate.hashCode());
294                result = prime * result + ((parent_id == null) ? 0 : parent_id.hashCode());
295                return result;
296        }
297
298        private String getColumnName(sourceColumn column) {
299                if ("constant".equals(column.getColumn_type())) {
300                        return column.column;
301                }
302                if (ModelBindingManager.getGlobalVendor() != null) {
303                        // equal published endpoints: the label's output-v1 bucket, beside the coordinate and the parent
304                        return LegacyDlineageWireNamesV1.columnBucket(column.column);
305                }
306                return column.column;
307        }
308
309        @Override
310        public boolean equals(Object obj) {
311                if (this == obj)
312                        return true;
313                if (obj == null)
314                        return false;
315                if (getClass() != obj.getClass())
316                        return false;
317                sourceColumn other = (sourceColumn) obj;
318                if (column == null) {
319                        if (other.column != null)
320                                return false;
321                } else if (!getColumnName(this).equals(getColumnName(other)))
322                        return false;
323                if (coordinate == null) {
324                        if (other.coordinate != null)
325                                return false;
326                } else if (coordinate.hashCode() != other.coordinate.hashCode())
327                        return false;
328                if (parent_id == null) {
329                        if (other.parent_id != null)
330                                return false;
331                } else if (!parent_id.equals(other.parent_id))
332                        return false;
333                return true;
334        }
335
336        @XmlAttribute(required = false)
337        public String getName() {
338                return name;
339        }
340
341        public void setName(String name) {
342                this.name = name;
343        }
344
345        /**
346         * Whether this source is a {@code *} read at a position its members' unknown order cannot tell (Mantis 4856):
347         * the simple output follows it to the {@code *} of the table that fills it, never to the column named like the
348         * target. Never serialized.
349         */
350        @XmlTransient
351        public boolean isPositionUnproven() {
352                return positionUnproven;
353        }
354
355        public void setPositionUnproven(boolean positionUnproven) {
356                this.positionUnproven = positionUnproven;
357        }
358
359        @XmlTransient
360        public Boolean isStruct() {
361                return struct;
362        }
363
364        public void setStruct(Boolean struct) {
365                this.struct = struct;
366        }
367
368        public String toDedupKey() {
369                StringBuilder sb = new StringBuilder(128);
370                appendDedupKey(sb);
371                return sb.toString();
372        }
373
374        public long toDedupHash() {
375                StringBuilder sb = new StringBuilder(128);
376                appendDedupKey(sb);
377                return SQLUtil.hash64(sb);
378        }
379
380        /**
381         * The coordinate this source would have carried before reference-site
382         * publishing — the column model's definition span. The table-merge pass
383         * stamps it in for its dedup steps (equals/hashCode and toDedupHash include
384         * the coordinate), so sources and relations keep collapsing exactly as they
385         * did when every reference was rendered with the model span, then restores
386         * the published reference-site coordinate on the survivors. Never
387         * serialized.
388         */
389        @XmlTransient
390        public String getDedupCoordinate() {
391                return dedupCoordinate;
392        }
393
394        public void setDedupCoordinate(String dedupCoordinate) {
395                this.dedupCoordinate = dedupCoordinate;
396        }
397
398        void appendDedupKey(StringBuilder sb) {
399                if (name != null) { sb.append("name=").append(name).append('|'); }
400                if (column != null) { sb.append("column=").append(column).append('|'); }
401                if (parent_id != null) { sb.append("parent_id=").append(parent_id).append('|'); }
402                if (source_id != null) { sb.append("source_id=").append(source_id).append('|'); }
403                if (value != null) { sb.append("value=").append(value).append('|'); }
404                if (source_name != null) { sb.append("source_name=").append(source_name).append('|'); }
405                if (column_type != null) { sb.append("column_type=").append(column_type).append('|'); }
406                if (parent_name != null) { sb.append("parent_name=").append(parent_name).append('|'); }
407                if (parent_alias != null) { sb.append("parent_alias=").append(parent_alias).append('|'); }
408                if (coordinate != null) { sb.append("coordinate=").append(coordinate).append('|'); }
409                if (clauseType != null) { sb.append("clauseType=").append(clauseType).append('|'); }
410                if (source != null) { sb.append("source=").append(source).append('|'); }
411                if (type != null) { sb.append("type=").append(type).append('|'); }
412                if (transforms != null && !transforms.isEmpty()) {
413                        sb.append("transforms=[");
414                        transforms.stream().sorted(Comparator.comparing(transform::getType, Comparator.nullsFirst(Comparator.<String>naturalOrder())))
415                                        .forEach(t -> { sb.append('{'); t.appendDedupKey(sb); sb.append("},"); });
416                        sb.setLength(sb.length() - 1);
417                        sb.append("]|");
418                }
419                if (candidateParents != null && !candidateParents.isEmpty()) {
420                        sb.append("candidateParents=[");
421                        candidateParents.stream().sorted(Comparator.comparing(candidateTable::getId, Comparator.nullsFirst(Comparator.<String>naturalOrder())))
422                                        .forEach(c -> { sb.append('{'); c.appendDedupKey(sb); sb.append("},"); });
423                        sb.setLength(sb.length() - 1);
424                        sb.append("]|");
425                }
426        }
427}