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