001package gudusoft.gsqlparser.dlineage.dataflow.model.xml;
002
003import gudusoft.gsqlparser.dlineage.dataflow.model.ModelBindingManager;
004import gudusoft.gsqlparser.dlineage.dataflow.model.json.Coordinate;
005import gudusoft.gsqlparser.dlineage.util.DlineageUtil;
006import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
007import gudusoft.gsqlparser.sqlenv.TSQLEnv;
008import gudusoft.gsqlparser.util.SQLUtil;
009
010import javax.xml.bind.annotation.XmlAttribute;
011import javax.xml.bind.annotation.XmlElement;
012import javax.xml.bind.annotation.XmlTransient;
013import javax.xml.bind.annotation.XmlType;
014import java.util.ArrayList;
015import java.util.LinkedHashSet;
016import java.util.List;
017
018@XmlType(
019        propOrder = {"id", "server", "userName", "database", "schema", "name", "displayName", "alias", "uri", "type", "subType",
020                "processIds", "fileType", "fileFormat", "location", "namespace", "isTarget", "coordinate", "columns", "parent", "more", "fromDDL", "candidateTables",
021                "endpointKind", "endpointIntroduction", "createdInSql", "normalizedName"}
022)
023public class table implements Cloneable {
024
025    private String id;
026
027    private String server;
028
029    private String userName;
030
031    private String database;
032
033    private String schema;
034
035    private String name;
036
037    private String displayName;
038
039    private String alias;
040
041    private String type;
042
043    private String subType;
044
045    private String uri;
046
047    private List<String> processIds;
048
049    private String isTarget;
050
051    private StringBuffer coordinate = new StringBuffer();
052
053    private List<column> columns;
054
055    private String parent;
056
057    private String fileType;
058
059    private String fileFormat;
060
061    private String location;
062
063    private String namespace;
064
065    @XmlTransient
066    private String starStmt;
067
068    private Boolean more;
069    
070    @XmlTransient
071    private String isDetermined;
072
073    private String fromDDL;
074
075    /**
076     * Qualified candidate table names when this one-part reference is ambiguous
077     * across two or more schemas of the default catalog (e.g. {@code testdb.s1.orders},
078     * {@code testdb.s2.orders}) and no object of that name exists in the default
079     * schema. Empty/absent for normally-resolved references. See
080     * docs/tmp/unqualified-name-default-schema-masks-cross-schema-ambiguity.md.
081     */
082    private List<String> candidateTables;
083
084    /**
085     * Authoritative endpoint classification (additive). Carried as Strings so a null
086     * value is omitted from XML, keeping existing output byte-identical for endpoints
087     * that aren't classified. Populated from the internal model only when non-default.
088     * See docs/tmp/dlineage-authoritative-endpoint-classification.md.
089     */
090    private String endpointKind;
091
092    private String endpointIntroduction;
093
094    private String createdInSql;
095
096    private String normalizedName;
097
098    @XmlTransient
099    private LinkedHashSet<String> coordinateItems = new LinkedHashSet<String>();
100
101    @XmlAttribute(required = false)
102    public String getAlias() {
103        return alias;
104    }
105
106    public void setAlias(String alias) {
107        this.alias = alias;
108    }
109
110    @XmlElement(name = "column", required = false)
111    public List<column> getColumns() {
112        if (this.columns == null) {
113            this.columns = new ArrayList<column>();
114        }
115        return columns;
116    }
117
118    public void setColumns(List<column> columns) {
119        this.columns = columns;
120    }
121
122    @XmlAttribute(required = false)
123    public String getCoordinate() {
124        String result = coordinate.toString();
125        if (SQLUtil.isEmpty(result))
126            return null;
127        return result;
128    }
129
130    public void appendCoordinate(String coordinate) {
131        if (!coordinateItems.contains(coordinate)) {
132            coordinateItems.add(coordinate);
133            rebuildCoordinate();
134        }
135    }
136    
137    private void rebuildCoordinate() {
138        this.coordinate.setLength(0);
139        
140        List<String> itemsList = new ArrayList<>(coordinateItems);
141        boolean hasMultiplePairs = itemsList.size() > 1;
142        
143        String separator = "";
144        for (String coordPair : itemsList) {
145            // 只有多对坐标时才过滤无效对
146            if (!hasMultiplePairs || !isBothCoordsInvalid(coordPair)) {
147                this.coordinate.append(separator).append(coordPair);
148                separator = ",";
149            }
150        }
151    }
152    
153    /**
154     * 判断一对坐标是否两个都是无效坐标([-1,-1,x] 格式)
155     */
156    private static boolean isBothCoordsInvalid(String coordPair) {
157        return coordPair != null 
158            && coordPair.contains("[-1,-1,") 
159            && countOccurrences(coordPair, "[-1,-1,") >= 2;
160    }
161    
162    private static int countOccurrences(String str, String target) {
163        if (str == null || target == null || target.isEmpty()) {
164            return 0;
165        }
166        int count = 0;
167        int idx = 0;
168        while ((idx = str.indexOf(target, idx)) != -1) {
169            count++;
170            idx += target.length();
171        }
172        return count;
173    }
174
175    public void setCoordinate(String coordinate) {
176        if (SQLUtil.isEmpty(coordinate)) {
177                return;
178        }
179        this.coordinate.setLength(0);
180        this.coordinateItems.clear();
181        
182        // 先分割出所有坐标 [line,col,fileIdx]
183        List<String> allCoords = new ArrayList<>();
184        int start = 0;
185        while (start < coordinate.length()) {
186            int open = coordinate.indexOf('[', start);
187            if (open < 0) break;
188            int close = coordinate.indexOf(']', open);
189            if (close < 0) break;
190            allCoords.add(coordinate.substring(open, close + 1));
191            start = close + 1;
192        }
193        
194        // 计算坐标对数量(每两个坐标组成一对)
195        int pairCount = (allCoords.size() + 1) / 2;
196        
197        // 如果只有一对坐标,直接保留
198        if (pairCount <= 1) {
199            this.coordinate.append(coordinate);
200            this.coordinateItems.add(coordinate);
201            return;
202        }
203        
204        // 有多对坐标时,过滤掉无效的坐标对(两个坐标都是 [-1,-1,x] 格式)
205        StringBuilder sb = new StringBuilder();
206        for (int i = 0; i < allCoords.size(); i += 2) {
207            String coord1 = allCoords.get(i);
208            String coord2 = (i + 1 < allCoords.size()) ? allCoords.get(i + 1) : "";
209            
210            // 检查这一对是否都是无效坐标
211            boolean pairIsInvalid = isInvalidCoord(coord1) && isInvalidCoord(coord2);
212            
213            if (!pairIsInvalid) {
214                if (sb.length() > 0) {
215                    sb.append(",");
216                }
217                sb.append(coord1);
218                this.coordinateItems.add(coord1);
219                if (!coord2.isEmpty()) {
220                    sb.append(",").append(coord2);
221                    this.coordinateItems.add(coord2);
222                }
223            }
224        }
225        this.coordinate.append(sb.toString());
226    }
227    
228    private static boolean isInvalidCoord(String coord) {
229        if (coord == null || coord.isEmpty()) {
230            return false;
231        }
232        String trimmed = coord.trim();
233        if (trimmed.indexOf("-1") != -1 && trimmed.startsWith("[") && trimmed.endsWith("]")) {
234            String inner = trimmed.substring(1, trimmed.length() - 1);
235            String[] coords = inner.split(",");
236            if (coords.length >= 2 && "-1".equals(coords[0].trim()) && "-1".equals(coords[1].trim())) {
237                return true;
238            }
239        }
240        return false;
241    }
242    
243    public void clearCoordinate() {
244        this.coordinate = new StringBuffer();
245    }
246
247    @XmlAttribute(required = false)
248    public String getUserName() {
249        return userName;
250    }
251
252    public void setUserName(String userName) {
253        this.userName = userName;
254    }
255
256    @XmlAttribute(required = false)
257    public String getServer() {
258        return server;
259    }
260
261    public void setServer(String server) {
262        this.server = server;
263    }
264
265    @XmlAttribute(required = false)
266    public String getName() {
267        return name;
268    }
269
270    public void setName(String name) {
271        this.name = name;
272    }
273
274    @XmlAttribute(required = false)
275    public String getDisplayName() {
276        return displayName;
277    }
278
279    public void setDisplayName(String displayName) {
280        this.displayName = displayName;
281    }
282
283    @XmlAttribute(required = false)
284    public String getId() {
285        return id;
286    }
287
288    public void setId(String id) {
289        this.id = id;
290    }
291
292    @XmlAttribute(required = false)
293    public List<String> getProcessIds() {
294        return processIds;
295    }
296
297    public void setProcessIds(List<String> processIds) {
298        this.processIds = processIds;
299    }
300
301    @XmlElement(name = "candidate", required = false)
302    public List<String> getCandidateTables() {
303        return candidateTables;
304    }
305
306    public void setCandidateTables(List<String> candidateTables) {
307        this.candidateTables = candidateTables;
308    }
309
310    @XmlAttribute(required = false)
311    public String getType() {
312        return type;
313    }
314
315    public void setType(String type) {
316        this.type = type;
317    }
318
319    @XmlAttribute(required = false)
320    public String getUri() {
321        return uri;
322    }
323
324    public void setUri(String uri) {
325        this.uri = uri;
326    }
327
328    @XmlAttribute(required = false)
329    public String getFileType() {
330        return fileType;
331    }
332
333    public void setFileType(String fileType) {
334        this.fileType = fileType;
335    }
336
337    @XmlAttribute(required = false)
338    public String getFileFormat() {
339        return fileFormat;
340    }
341
342    public void setFileFormat(String fileFormat) {
343        this.fileFormat = fileFormat;
344    }
345
346    @XmlAttribute(required = false)
347    public String getLocation() {
348        return location;
349    }
350
351    public void setLocation(String location) {
352        this.location = location;
353    }
354
355    @XmlAttribute(required = false)
356    public String getNamespace() {
357        return namespace;
358    }
359
360    public void setNamespace(String namespace) {
361        this.namespace = namespace;
362    }
363
364    @XmlTransient
365    public String getStarStmt() {
366        return starStmt;
367    }
368
369    public void setStarStmt(String starStmt) {
370        this.starStmt = starStmt;
371    }
372
373    @XmlTransient
374    public String getIsDetermined() {
375                return isDetermined;
376        }
377
378        public void setIsDetermined(String isDetermined) {
379                this.isDetermined = isDetermined;
380        }
381
382        public boolean isFunction() {
383        return "function".equals(type) || "function".equals(subType);
384    }
385
386    public boolean isView() {
387        return "view".equals(type);
388    }
389
390    public boolean isDatabaseType() {
391        return "database".equals(type);
392    }
393
394    public boolean isSchemaType() {
395        return "schema".equals(type);
396    }
397
398    public boolean isSequence() {
399        return "sequence".equals(type);
400    }
401
402    public boolean isStage() {
403        return "stage".equals(type);
404    }
405
406    public boolean isDataSource() {
407        return "dataSource".equals(type);
408    }
409
410    public boolean isStream() {
411        return "stream".equals(type);
412    }
413
414    public boolean isVariable() {
415        return "variable".equals(type);
416    }
417
418    public boolean isCursor() {
419        return "cursor".equals(type);
420    }
421
422    public boolean isFile() {
423        return "file".equals(type) || "path".equals(type);
424    }
425
426    public boolean isTable() {
427        return "table".equals(type) || "pseudoTable".equals(type) || "constantTable".equals(type);
428    }
429
430    public boolean isPseudoTable() {
431        return "pseudoTable".equals(type);
432    }
433
434    public boolean isConstantTable() {
435        return "pseudoTable".equals(type);
436    }
437
438    public boolean isResultSet() {
439        return type != null && !isView() && !isCursor() && !isTable() && !isStage() && !isSequence() && !isDataSource() && !isDatabaseType() && !isSchemaType() && !isStream() && !isVariable() && !isFile();
440    }
441
442    @XmlAttribute(name = "isTarget", required = false)
443    public String getIsTarget() {
444        return isTarget;
445    }
446
447    public boolean isTarget() {
448        return "true".equals(isTarget);
449    }
450
451    @XmlAttribute(required = false)
452    public String getParent() {
453        return parent;
454    }
455
456    public void setParent(String parent) {
457        this.parent = parent;
458    }
459
460    @XmlAttribute(required = false)
461    public String getDatabase() {
462        return database;
463    }
464
465    public void setDatabase(String database) {
466        if (SQLUtil.parseNames(database).size() > 1) {
467            database = "\"" + database + "\"";
468        }
469        this.database = database;
470    }
471
472    @XmlAttribute(required = false)
473    public String getSchema() {
474        return schema;
475    }
476
477    public void setSchema(String schema) {
478        if (SQLUtil.parseNames(schema).size() > 1) {
479            schema = "\"" + schema + "\"";
480        }
481        this.schema = schema;
482    }
483
484    @XmlAttribute(required = false)
485    public String getSubType() {
486        return subType;
487    }
488
489    public void setSubType(String subType) {
490        this.subType = subType;
491    }
492
493    public String getFullName() {
494        if (isDatabaseType()) {
495            return database;
496        }
497        StringBuilder fullName = new StringBuilder();
498        if (!SQLUtil.isEmpty(database)) {
499            fullName.append(database).append(".");
500        }
501        if (!SQLUtil.isEmpty(schema)) {
502            fullName.append(schema).append(".");
503        }
504        if (fullName.length() > 0) {
505            fullName.append(getTableNameOnly());
506        } else {
507            fullName.append(name);
508        }
509        return fullName.toString();
510    }
511
512    public String getFullSchemaName() {
513        StringBuilder fullName = new StringBuilder();
514        if (!SQLUtil.isEmpty(database)) {
515            if(ModelBindingManager.getGlobalVendor()!=null) {
516                fullName.append(DlineageUtil.getIdentifierNormalName(database, ESQLDataObjectType.dotCatalog)).append(".");
517            }
518            else{
519                fullName.append(database).append(".");
520            }
521        }
522        if (!SQLUtil.isEmpty(schema)) {
523            if(ModelBindingManager.getGlobalVendor()!=null) {
524                fullName.append(DlineageUtil.getIdentifierNormalName(schema, ESQLDataObjectType.dotSchema));
525            }
526            else {
527                fullName.append(schema).append(".");
528            }
529        }
530        String fullSchemaName = fullName.toString();
531        if (fullSchemaName.endsWith(".")) {
532            fullSchemaName = fullSchemaName.substring(0, fullSchemaName.length() - 1);
533        }
534        if (fullSchemaName.length() == 0) {
535            fullSchemaName = TSQLEnv.DEFAULT_SCHEMA_NAME;
536        }
537        return fullSchemaName;
538    }
539
540    public String getTableNameOnly() {
541        if (name.indexOf("@") != -1 && SQLUtil.trimColumnStringQuote(name.substring(name.lastIndexOf("@") + 1).trim()).equals(SQLUtil.trimColumnStringQuote(database))) {
542            List<String> segments = SQLUtil.parseNames(name.substring(0, name.lastIndexOf("@")).trim());
543            if (segments.size() > 2) {
544                return SQLUtil.mergeSegments(segments, 2);
545            }
546            return segments.get(segments.size() - 1);
547        } else {
548            List<String> segments = SQLUtil.parseNames(name);
549            if (segments.size() > 2) {
550                return SQLUtil.mergeSegments(segments, 2);
551            }
552            return segments.get(segments.size() - 1);
553        }
554    }
555
556    public void setIsTarget(String isTarget) {
557        this.isTarget = isTarget;
558    }
559
560    public int getOccurrencesNumber() {
561        return PositionUtil.getOccurrencesNumber(coordinate.toString());
562    }
563
564    public Coordinate getStartPos(int index) {
565        return PositionUtil.getStartPos(coordinate.toString(), index);
566    }
567
568    public Coordinate getEndPos(int index) {
569        return PositionUtil.getEndPos(coordinate.toString(), index);
570    }
571
572    public Boolean getMore() {
573        return more;
574    }
575
576    public void setMore(Boolean more) {
577        this.more = more;
578    }
579
580    @XmlAttribute(required = false)
581    public String getFromDDL() {
582        return fromDDL;
583    }
584
585    public void setFromDDL(String fromDDL) {
586        this.fromDDL = fromDDL;
587    }
588
589    @XmlAttribute(required = false)
590    public String getEndpointKind() {
591        return endpointKind;
592    }
593
594    public void setEndpointKind(String endpointKind) {
595        this.endpointKind = endpointKind;
596    }
597
598    @XmlAttribute(required = false)
599    public String getEndpointIntroduction() {
600        return endpointIntroduction;
601    }
602
603    public void setEndpointIntroduction(String endpointIntroduction) {
604        this.endpointIntroduction = endpointIntroduction;
605    }
606
607    @XmlAttribute(required = false)
608    public String getCreatedInSql() {
609        return createdInSql;
610    }
611
612    public void setCreatedInSql(String createdInSql) {
613        this.createdInSql = createdInSql;
614    }
615
616    @XmlAttribute(required = false)
617    public String getNormalizedName() {
618        return normalizedName;
619    }
620
621    public void setNormalizedName(String normalizedName) {
622        this.normalizedName = normalizedName;
623    }
624
625    @Override
626    public Object clone() throws CloneNotSupportedException {
627        return super.clone();
628    }
629}