001package gudusoft.gsqlparser.dlineage.util;
002
003import java.io.IOException;
004import java.io.StringWriter;
005import java.util.ArrayList;
006import java.util.Arrays;
007import java.util.Collections;
008import java.util.Comparator;
009import java.util.HashMap;
010import java.util.HashSet;
011import java.util.Iterator;
012import java.util.List;
013import java.util.Map;
014import java.util.Objects;
015import java.util.Set;
016import java.util.TreeMap;
017import java.util.TreeSet;
018
019import gudusoft.gsqlparser.EDbVendor;
020import gudusoft.gsqlparser.dlineage.DataFlowAnalyzer;
021import gudusoft.gsqlparser.dlineage.dataflow.model.ModelBindingManager;
022import gudusoft.gsqlparser.dlineage.dataflow.model.xml.column;
023import gudusoft.gsqlparser.dlineage.dataflow.model.xml.dataflow;
024import gudusoft.gsqlparser.dlineage.dataflow.model.xml.relationship;
025import gudusoft.gsqlparser.dlineage.dataflow.model.xml.sourceColumn;
026import gudusoft.gsqlparser.dlineage.dataflow.model.xml.table;
027import gudusoft.gsqlparser.dlineage.dataflow.model.xml.targetColumn;
028import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
029import gudusoft.gsqlparser.sqlenv.IdentifierService;
030import gudusoft.gsqlparser.util.Logger;
031import gudusoft.gsqlparser.util.LoggerFactory;
032import gudusoft.gsqlparser.util.SQLUtil;
033import gudusoft.gsqlparser.util.csv.CsvWriter;
034import gudusoft.gsqlparser.util.json.JSON;
035
036public class TableFlowUtility {
037
038        private static final Logger logger = LoggerFactory.getLogger(TableFlowUtility.class);
039
040        public static class LeftMostSourceTable {
041                private Table[] targetTables;
042                private TableColumn[] attributes;
043
044                public TableColumn[] getAttributes() {
045                        return attributes;
046                }
047
048                public void setAttributes(TableColumn[] attributes) {
049                        this.attributes = attributes;
050                }
051
052                public Table[] getTargetTables() {
053                        return targetTables;
054                }
055
056                public void setTargetTables(Table[] targetTables) {
057                        this.targetTables = targetTables;
058                }
059
060                public void appendAttribute(TableColumn attribute) {
061                        if (attribute == null) {
062                                return;
063                        }
064                        if (attributes == null) {
065                                attributes = new TableColumn[] { attribute };
066                        } else {
067                                TableColumn[] newAttributes = new TableColumn[attributes.length + 1];
068                                System.arraycopy(attributes, 0, newAttributes, 0, attributes.length);
069                                newAttributes[attributes.length] = attribute;
070                                this.attributes = newAttributes;
071                        }
072                }
073
074                public void appendTargetTable(Table targetTable) {
075                        if (targetTable == null) {
076                                return;
077                        }
078                        if (targetTables == null) {
079                                targetTables = new Table[] { targetTable };
080                        } else {
081                                Table[] newTargetTables = new Table[targetTables.length + 1];
082                                System.arraycopy(targetTables, 0, newTargetTables, 0, targetTables.length);
083                                newTargetTables[targetTables.length] = targetTable;
084                                this.targetTables = newTargetTables;
085                        }
086                }
087
088        }
089
090        public static class ColumnUpstream {
091                private List<TableColumn> nodes;
092
093                public List<TableColumn> getNodes() {
094                        return nodes;
095                }
096
097                public void setNodes(List<TableColumn> nodes) {
098                        this.nodes = nodes;
099                }
100
101                public void appendNode(TableColumn node) {
102                        if (node == null) {
103                                return;
104                        }
105                        if (nodes == null) {
106                                nodes = new ArrayList<TableFlowUtility.TableColumn>();
107                                nodes.add(node);
108                        } else {
109                                if (!nodes.contains(node)) {
110                                        nodes.add(node);
111                                }
112                        }
113                }
114
115                public void appendNode(Table node) {
116                        if (node == null) {
117                                return;
118                        }
119                        TableColumn tableColumn = new TableColumn(node);
120                        if (nodes == null) {
121                                nodes = new ArrayList<TableColumn>();
122                                nodes.add(tableColumn);
123                        } else {
124                                if (!nodes.contains(tableColumn)) {
125                                        nodes.add(tableColumn);
126                                }
127                        }
128                }
129
130        }
131
132        public static class Table implements Comparable<Table> {
133                private String server;
134                private String userName;
135                private String databaseName;
136                private String schemaName;
137                private String relationName;
138                private ColumnUpstream upstreamAttributesIndirect;
139
140                public Table() {
141                }
142
143                public Table(String server, String userName, String databaseName, String schemaName, String relationName) {
144                        this.server = server;
145                        this.userName = userName;
146                        this.databaseName = databaseName;
147                        this.schemaName = schemaName;
148                        this.relationName = relationName;
149                }
150
151                public String getServer() {
152                        return server;
153                }
154
155                public void setServer(String server) {
156                        this.server = server;
157                }
158
159                public String getUserName() {
160                        return userName;
161                }
162
163                public void setUserName(String userName) {
164                        this.userName = userName;
165                }
166
167                public String getDatabaseName() {
168                        return databaseName;
169                }
170
171                public void setDatabaseName(String databaseName) {
172                        this.databaseName = databaseName;
173                }
174
175                public String getSchemaName() {
176                        return schemaName;
177                }
178
179                public void setSchemaName(String schemaName) {
180                        this.schemaName = schemaName;
181                }
182
183                public String getRelationName() {
184                        return relationName;
185                }
186
187                public void setRelationName(String relationName) {
188                        this.relationName = relationName;
189                }
190
191                public ColumnUpstream getUpstreamAttributesIndirect() {
192                        return upstreamAttributesIndirect;
193                }
194
195                public void setUpstreamAttributesIndirect(ColumnUpstream upstreamAttributesIndirect) {
196                        this.upstreamAttributesIndirect = upstreamAttributesIndirect;
197                }
198
199                @Override
200                public boolean equals(Object o) {
201                        if (this == o) return true;
202                        if (o == null || getClass() != o.getClass()) return false;
203                        Table table = (Table) o;
204                        return Objects.equals(server, table.server) &&
205                                        Objects.equals(userName, table.userName) &&
206                                        Objects.equals(databaseName, table.databaseName) &&
207                                        Objects.equals(schemaName, table.schemaName) &&
208                                        Objects.equals(relationName, table.relationName) &&
209                                        Objects.equals(upstreamAttributesIndirect, table.upstreamAttributesIndirect);
210                }
211
212                @Override
213                public int hashCode() {
214                        return Objects.hash(server, userName, databaseName, schemaName, relationName, upstreamAttributesIndirect);
215                }
216
217                @Override
218                public int compareTo(Table table) {
219                        return (getDatabaseName() + "." + getSchemaName() + "." + getRelationName())
220                                        .compareTo(table.getDatabaseName() + "." + table.getSchemaName() + "." + table.getRelationName());
221                }
222        }
223
224        public static class TableColumn {
225                private String server;
226                private String userName;
227                private String databaseName;
228                private String schemaName;
229                private String relationName;
230                private String attributeName;
231                private String timestampMin;
232                private String timestampMax;
233                private ColumnUpstream upstreamAttributesIndirect;
234
235                public TableColumn() {
236                }
237
238                public TableColumn(Table node) {
239                        this.server = node.getServer();
240                        this.userName = node.getUserName();
241                        this.databaseName = node.getDatabaseName();
242                        this.schemaName = node.getSchemaName();
243                        this.relationName = node.getRelationName();
244                }
245
246                public String getServer() {
247                        return server;
248                }
249
250                public void setServer(String server) {
251                        this.server = server;
252                }
253
254                public String getUserName() {
255                        return userName;
256                }
257
258                public void setUserName(String userName) {
259                        this.userName = userName;
260                }
261
262                public String getDatabaseName() {
263                        return databaseName;
264                }
265
266                public void setDatabaseName(String databaseName) {
267                        this.databaseName = databaseName;
268                }
269
270                public String getSchemaName() {
271                        return schemaName;
272                }
273
274                public void setSchemaName(String schemaName) {
275                        this.schemaName = schemaName;
276                }
277
278                public String getRelationName() {
279                        return relationName;
280                }
281
282                public void setRelationName(String relationName) {
283                        this.relationName = relationName;
284                }
285
286                public String getAttributeName() {
287                        return attributeName;
288                }
289
290                public void setAttributeName(String attributeName) {
291                        this.attributeName = attributeName;
292                }
293
294                public ColumnUpstream getUpstreamAttributesIndirect() {
295                        return upstreamAttributesIndirect;
296                }
297
298                public void setUpstreamAttributesIndirect(ColumnUpstream upstreamAttributesIndirect) {
299                        this.upstreamAttributesIndirect = upstreamAttributesIndirect;
300                }
301
302                public String getTimestampMin() {
303                        return timestampMin;
304                }
305
306                public void setTimestampMin(String timestampMin) {
307                        this.timestampMin = timestampMin;
308                }
309
310                public String getTimestampMax() {
311                        return timestampMax;
312                }
313
314                public void setTimestampMax(String timestampMax) {
315                        this.timestampMax = timestampMax;
316                }
317
318                @Override
319                public boolean equals(Object o) {
320                        if (this == o) return true;
321                        if (o == null || getClass() != o.getClass()) return false;
322                        TableColumn that = (TableColumn) o;
323                        return Objects.equals(server, that.server) &&
324                                        Objects.equals(userName, that.userName) &&
325                                        Objects.equals(databaseName, that.databaseName) &&
326                                        Objects.equals(schemaName, that.schemaName) &&
327                                        Objects.equals(relationName, that.relationName) &&
328                                        Objects.equals(attributeName, that.attributeName) &&
329                                        Objects.equals(timestampMin, that.timestampMin) &&
330                                        Objects.equals(timestampMax, that.timestampMax) &&
331                                        Objects.equals(upstreamAttributesIndirect, that.upstreamAttributesIndirect);
332                }
333
334                @Override
335                public int hashCode() {
336                        return Objects.hash(server, userName, databaseName, schemaName, relationName, attributeName, timestampMin, timestampMax, upstreamAttributesIndirect);
337                }
338
339                public Table parentTable() {
340                        return new Table(server, userName, databaseName, schemaName, relationName);
341                }
342        }
343
344        public static String generateLeftMostTable(DataFlowAnalyzer analyzer, dataflow instance, String tableNamePattern,
345                                                                                           String tableColumn, boolean isSimple) {
346                return generateLeftMostTable(analyzer, instance, tableNamePattern, tableColumn, isSimple, "json", null);
347        }
348
349        public static String generateLeftMostTable(DataFlowAnalyzer analyzer, dataflow instance, String tableNamePattern,
350                                                                                           String tableColumn, boolean isSimple, String outputType, String delimiter) {
351                dataflow dataflow = generateLeftMostTableFlow(analyzer, instance, tableNamePattern, tableColumn, isSimple);
352                LeftMostSourceTable leftMostSourceTable = exportToLeftMostTableModel(dataflow);
353
354                if ("json".equals(outputType)) {
355                        return JSON.toJSONString(leftMostSourceTable);
356                } else if ("csv".equals(outputType)) {
357                        return exportModelToCsv(leftMostSourceTable, delimiter);
358                } else {
359                        throw new UnsupportedOperationException("Unsupported output type: " + outputType);
360                }
361        }
362
363        static LeftMostSourceTable exportToLeftMostTableModel(dataflow dataflow) {
364                LeftMostSourceTable leftMostSourceTable = new LeftMostSourceTable();
365
366                Map<String, Set<relationship>> targetIdRelationMap = new HashMap<String, Set<relationship>>();
367                List<relationship> relations = dataflow.getRelationships();
368                for (relationship relation : relations) {
369                        if (relation.getTarget() != null) {
370                                String key = relation.getTarget().getParent_id() + ":" + relation.getTarget().getId();
371                                if (!targetIdRelationMap.containsKey(key)) {
372                                        targetIdRelationMap.put(key, new HashSet<relationship>());
373                                }
374                                targetIdRelationMap.get(key).add(relation);
375                        }
376                }
377
378                List<table> tables = new ArrayList<>();
379                if (dataflow.getTables() != null) {
380                        tables.addAll(dataflow.getTables());
381                }
382                if (dataflow.getPaths() != null) {
383                        tables.addAll(dataflow.getPaths());
384                }
385                if (dataflow.getStages() != null) {
386                        tables.addAll(dataflow.getStages());
387                }
388                if (dataflow.getSequences() != null) {
389                        tables.addAll(dataflow.getSequences());
390                }
391                if (dataflow.getDatasources() != null) {
392                        tables.addAll(dataflow.getDatasources());
393                }
394                if (dataflow.getDatabases() != null) {
395                        tables.addAll(dataflow.getDatabases());
396                }
397                if (dataflow.getSchemas() != null) {
398                        tables.addAll(dataflow.getSchemas());
399                }
400                if (dataflow.getVariables() != null) {
401                        tables.addAll(dataflow.getVariables());
402                }
403                if (dataflow.getViews() != null) {
404                        tables.addAll(dataflow.getViews());
405                }
406                Map<String, table> tableMap = new HashMap<String, table>();
407                Map<String, List<table>> duplicateTableMap = new HashMap<String, List<table>>();
408                Iterator<table> tableIter = tables.iterator();
409                while (tableIter.hasNext()) {
410                        table table = tableIter.next();
411                        tableMap.put(table.getId(), table);
412                        String tableName = table.getName();
413                        if(!duplicateTableMap.containsKey(tableName)) {
414                                duplicateTableMap.put(tableName, new ArrayList<table>());
415                        }
416                        duplicateTableMap.get(tableName).add(table);
417                }
418
419                Map<String, TableColumn> tableColumnMap = new TreeMap<String, TableColumn>();
420                Map<String, Table> targetTableMap = new TreeMap<String, Table>();
421                for (String key : targetIdRelationMap.keySet()) {
422                        String targetTableId = key.split(":")[0];
423                        String targetColumnId = key.split(":")[1];
424                        boolean matches = false;
425                        if (!tableColumnMap.containsKey(key)) {
426                                table targetTable = tableMap.get(targetTableId);
427                                for (column column : targetTable.getColumns()) {
428                                        if (column.getId().equals(targetColumnId)) {
429                                                TableColumn targetTableColumn = new TableColumn();
430                                                targetTableColumn.setServer(targetTable.getServer());
431                                                targetTableColumn.setUserName(targetTable.getUserName());
432                                                targetTableColumn.setDatabaseName(targetTable.getDatabase());
433                                                targetTableColumn.setSchemaName(targetTable.getSchema());
434                                                targetTableColumn.setRelationName(targetTable.getTableNameOnly());
435                                                targetTableColumn.setAttributeName(column.getName());
436                                                ColumnUpstream columnUpstream = new ColumnUpstream();
437                                                targetTableColumn.setUpstreamAttributesIndirect(columnUpstream);
438                                                tableColumnMap.put(key, targetTableColumn);
439                                                matches = true;
440                                                break;
441                                        }
442                                }
443                        }
444
445                        if (!matches) {
446                                continue;
447                        }
448                        ColumnUpstream columnUpstream = tableColumnMap.get(key).getUpstreamAttributesIndirect();
449                        Set<relationship> columnRelations = targetIdRelationMap.get(key);
450                        for (relationship columnRelation : columnRelations) {
451                                List<sourceColumn> sourceColumns = columnRelation.getSources();
452                                for (sourceColumn sourceColumn : sourceColumns) {
453                                        table sourceTable = tableMap.get(sourceColumn.getParent_id());
454                                        if (sourceTable == null)
455                                                continue;
456                                        TableColumn sourceTableColumn = new TableColumn();
457                                        sourceTableColumn.setServer(sourceTable.getServer());
458                                        sourceTableColumn.setUserName(sourceTable.getUserName());
459                                        sourceTableColumn.setDatabaseName(sourceTable.getDatabase());
460                                        sourceTableColumn.setSchemaName(sourceTable.getSchema());
461                                        sourceTableColumn.setRelationName(sourceTable.getTableNameOnly());
462                                        sourceTableColumn.setAttributeName(sourceColumn.getColumn());
463                                        sourceTableColumn.setTimestampMax(columnRelation.getTimestampMax());
464                                        sourceTableColumn.setTimestampMin(columnRelation.getTimestampMin());
465                                        columnUpstream.appendNode(sourceTableColumn);
466                                }
467                        }
468                        if (columnUpstream.getNodes() != null && !columnUpstream.getNodes().isEmpty()) {
469                                leftMostSourceTable.appendAttribute(tableColumnMap.get(key));
470                                if (!targetTableMap.containsKey(targetTableId)) {
471                                        Table targetTable = new Table();
472                                        table table = tableMap.get(targetTableId);
473                                        targetTable.setServer(table.getServer());
474                                        targetTable.setUserName(table.getUserName());
475                                        targetTable.setDatabaseName(table.getDatabase());
476                                        targetTable.setSchemaName(table.getSchema());
477                                        targetTable.setRelationName(table.getTableNameOnly());
478                                        leftMostSourceTable.appendTargetTable(targetTable);
479                                        targetTableMap.put(targetTableId, targetTable);
480                                }
481                        }
482                }
483
484                if (leftMostSourceTable.getAttributes() != null) {
485                        Arrays.sort(leftMostSourceTable.getAttributes(), new Comparator<TableColumn>() {
486
487                                @Override
488                                public int compare(TableColumn o1, TableColumn o2) {
489                                        int result = (o1.getDatabaseName() + "." + o1.getSchemaName() + "." + o1.getRelationName())
490                                                        .compareTo(o2.getDatabaseName() + "." + o2.getSchemaName() + "." + o2.getRelationName());
491                                        if (result == 0) {
492                                                return o1.getAttributeName().compareTo(o2.getAttributeName());
493                                        } else {
494                                                return result;
495                                        }
496                                }
497                        });
498
499                        Map<Table, TreeSet<Table>> targetSourceTableMap = new HashMap<TableFlowUtility.Table, TreeSet<Table>>();
500                        for (TableColumn attribute : leftMostSourceTable.getAttributes()) {
501                                if (attribute.getUpstreamAttributesIndirect() != null
502                                                && attribute.getUpstreamAttributesIndirect().getNodes() != null) {
503                                        Collections.sort(attribute.getUpstreamAttributesIndirect().getNodes(),
504                                                        new Comparator<TableColumn>() {
505                                                                @Override
506                                                                public int compare(TableColumn o1, TableColumn o2) {
507                                                                        int result = (o1.getDatabaseName() + "." + o1.getSchemaName() + "."
508                                                                                        + o1.getRelationName())
509                                                                                        .compareTo(o2.getDatabaseName() + "." + o2.getSchemaName() + "."
510                                                                                                        + o2.getRelationName());
511                                                                        if (result == 0) {
512                                                                                return o1.getAttributeName().compareTo(o2.getAttributeName());
513                                                                        } else {
514                                                                                return result;
515                                                                        }
516                                                                }
517                                                        });
518
519                                        if (!targetSourceTableMap.containsKey(attribute.parentTable())) {
520                                                targetSourceTableMap.put(attribute.parentTable(), new TreeSet<Table>());
521                                        }
522                                        for (TableColumn sourceTableColumn : attribute.getUpstreamAttributesIndirect().getNodes()) {
523                                                targetSourceTableMap.get(attribute.parentTable()).add(sourceTableColumn.parentTable());
524                                        }
525                                }
526                        }
527
528                        if (leftMostSourceTable.getTargetTables() != null) {
529                                for (Table targetTable : leftMostSourceTable.getTargetTables()) {
530                                        TreeSet<Table> sourceTableTree = targetSourceTableMap.get(targetTable);
531                                        if (sourceTableTree != null) {
532                                                ColumnUpstream columnUpstream = new ColumnUpstream();
533                                                targetTable.setUpstreamAttributesIndirect(columnUpstream);
534                                                for (Table table : sourceTableTree) {
535                                                        columnUpstream.appendNode(table);
536                                                }
537                                        }
538                                }
539                        }
540                }
541
542                if (leftMostSourceTable.getTargetTables() != null) {
543                        Arrays.sort(leftMostSourceTable.getTargetTables(), new Comparator<Table>() {
544                                @Override
545                                public int compare(Table o1, Table o2) {
546                                        int result = (o1.getDatabaseName() + "." + o1.getSchemaName() + "." + o1.getRelationName())
547                                                        .compareTo(o2.getDatabaseName() + "." + o2.getSchemaName() + "." + o2.getRelationName());
548                                        return result;
549                                }
550                        });
551                }
552                return leftMostSourceTable;
553        }
554
555        private static String exportModelToCsv(LeftMostSourceTable leftMostSourceTable, String delimiter) {
556                if (delimiter == null || delimiter.length() == 0) {
557                        delimiter = ",";
558                }
559                StringBuilder buffer = new StringBuilder();
560                buffer.append("SOURCE_TABLE"+delimiter+"SOURCE_COLUMN"+delimiter+"TARGET_TABLE"+delimiter+"TARGET_COLUMN").append(System.getProperty("line.separator"));
561                try {
562                        Set<String> lines = exportModelToLines(leftMostSourceTable, delimiter);
563                        for (String line : lines) {
564                                buffer.append(line).append(System.getProperty("line.separator"));
565                        }
566                } catch (Exception e) {
567                        logger.error("Generate column level csv failed.", e);
568                }
569                return buffer.toString();
570        }
571
572        public static Set<String> exportModelToLines(LeftMostSourceTable leftMostSourceTable, String delimiter){
573                TreeSet<String> lines = new TreeSet<String>(new Comparator<String>() {
574                        @Override
575                        public int compare(String o1, String o2) {
576                                return o1.toLowerCase(java.util.Locale.ROOT).compareTo(o2.toLowerCase(java.util.Locale.ROOT));
577                        }
578                });
579                if (leftMostSourceTable.getAttributes() != null) {
580                        StringWriter sw = new StringWriter();
581                        CsvWriter csvWriter = new CsvWriter(sw, delimiter.charAt(0));
582                        try {
583                                for (TableColumn targetColumn : leftMostSourceTable.getAttributes()) {
584                                        if (targetColumn.getUpstreamAttributesIndirect() != null
585                                                        && targetColumn.getUpstreamAttributesIndirect().getNodes() != null) {
586                                                String targetTableName = getTableFullName(targetColumn);
587                                                String targetColumnName = targetColumn.getAttributeName();
588                                                for (TableColumn sourceColumn : targetColumn.getUpstreamAttributesIndirect().getNodes()) {
589                                                        String sourceTableName = getTableFullName(sourceColumn);
590                                                        String sourceColumnName = sourceColumn.getAttributeName();
591                                                        csvWriter.writeRecord(new String[]{sourceTableName, sourceColumnName, targetTableName, targetColumnName});
592                                                }
593                                        }
594                                }
595                        } catch (Exception e) {
596                                logger.error("write csv failed.", e);
597                        }
598                        csvWriter.close();
599                        String systemRecordDelimiter = System.getProperty("line.separator");
600                        lines.addAll(Arrays.asList(sw.toString().split(systemRecordDelimiter)));
601                }
602                return lines;
603        }
604
605        private static String getTableFullName(TableColumn targetColumn) {
606                String tableName = targetColumn.getRelationName();
607                if (!SQLUtil.isEmpty(targetColumn.getSchemaName())) {
608                        tableName = targetColumn.getSchemaName() + "." + tableName;
609                }
610                if (!SQLUtil.isEmpty(targetColumn.getDatabaseName())) {
611                        tableName = targetColumn.getDatabaseName() + "." + tableName;
612                }
613                return tableName;
614        }
615
616        public static dataflow generateLeftMostTableFlow(DataFlowAnalyzer analyzer, dataflow instance, String tableNamePattern, String tableColumn, boolean isSimple) {
617                EDbVendor vendor = analyzer.getOption().getVendor();
618
619                if (tableNamePattern != null) {
620                        tableNamePattern = tableNamePattern.replace("[", "\\[").replace("]", "\\]");
621                }
622                try {
623                        dataflow simple = instance;
624                        if (!isSimple && instance.getResultsets() != null && instance.getResultsets().size() > 0) {
625                                simple = analyzer.getSimpleDataflow(instance, true);
626                        }
627                        if (simple.getTables() == null && simple.getViews() == null) {
628                                return simple;
629                        }
630                        List<relationship> relations = simple.getRelationships();
631                        if (relations == null || relations.size() == 0) {
632                                return simple;
633                        }
634                        long maxId = Long.parseLong(relations.get(relations.size() - 1).getId().split("\\-")[0].replace("_", ""))
635                                        * 100;
636                        
637                        List<table> tables = new ArrayList<>();
638                        if (simple.getTables() != null) {
639                                tables.addAll(simple.getTables());
640                        }
641                        if (simple.getPaths() != null) {
642                                tables.addAll(simple.getPaths());
643                        }
644                        if (simple.getStages() != null) {
645                                tables.addAll(simple.getStages());
646                        }
647                        if (simple.getSequences() != null) {
648                                tables.addAll(simple.getSequences());
649                        }
650                        if (simple.getDatasources() != null) {
651                                tables.addAll(simple.getDatasources());
652                        }
653                        if (simple.getDatabases() != null) {
654                                tables.addAll(simple.getDatabases());
655                        }
656                        if (simple.getSchemas() != null) {
657                                tables.addAll(simple.getSchemas());
658                        }
659                        if (simple.getVariables() != null) {
660                                tables.addAll(simple.getVariables());
661                        }
662                        if (simple.getViews() != null) {
663                                tables.addAll(simple.getViews());
664                        }
665                        Map<String, table> tableMap = new HashMap<String, table>();
666                        Iterator<table> tableIter = tables.iterator();
667                        while (tableIter.hasNext()) {
668                                table table = tableIter.next();
669                                tableMap.put(table.getId(), table);
670                        }
671                        
672                        Iterator<relationship> iter = simple.getRelationships().iterator();
673                        while (iter.hasNext()) {
674                                relationship relation = iter.next();
675                                if (!"fdd".equals(relation.getType())) {
676                                        iter.remove();
677                                }
678                                if(relation.getSources() != null && relation.getSources().size()>0) {
679                                        Iterator<sourceColumn> sourceIter = relation.getSources().iterator();
680                                        while(sourceIter.hasNext()) {
681                                                sourceColumn source = sourceIter.next();
682                                                if(source.getId().equals(relation.getTarget().getId())) {
683                                                        sourceIter.remove();
684                                                }
685                                                
686                                                table sourceTable = tableMap.get(source.getParent_id());
687                                                if(sourceTable.isFile()) {
688                                                        sourceIter.remove();
689                                                }
690                                        }
691                                        if(relation.getSources().isEmpty()) {
692                                                iter.remove();
693                                        }
694                                }
695                        }
696
697
698                        Map<String, Set<relationship>> targetIdRelationMap = new HashMap<String, Set<relationship>>();
699                        for (relationship relation : relations) {
700                                if (relation.getTarget() != null) {
701                                        String key = relation.getTarget().getParent_id() + "." + relation.getTarget().getId();
702                                        if (!targetIdRelationMap.containsKey(key)) {
703                                                targetIdRelationMap.put(key, new HashSet<relationship>());
704                                        }
705                                        targetIdRelationMap.get(key).add(relation);
706                                }
707                        }
708
709                        List<relationship> simpleRelations = new ArrayList<relationship>();
710                        Set<table> tableSet = new HashSet<table>();
711                        iter = simple.getRelationships().iterator();
712                        while (iter.hasNext()) {
713                                relationship relation = iter.next();
714                                targetColumn target = relation.getTarget();
715                                String targetParentId = target.getParent_id();
716                                table targetTable = tableMap.get(targetParentId);
717                                if (targetTable == null) {
718                                        continue;
719                                }
720                                if (!matchTableName(tableNamePattern, targetTable, SQLUtil.isEmpty(tableColumn), vendor)) {
721                                        continue;
722                                }
723                                if (!SQLUtil.isEmpty(tableColumn) && !storedName(vendor, ESQLDataObjectType.dotColumn, target.getColumn())
724                                                .equalsIgnoreCase(storedName(vendor, ESQLDataObjectType.dotColumn, tableColumn))) { // non-identifier-compare: lenient user filter
725                                        continue;
726                                }
727                                tableSet.add(targetTable);
728
729                                Set<sourceColumn> sourceColumnSet = new HashSet<sourceColumn>();
730                                Set<relationship> relationSet = new HashSet<relationship>();
731
732                                findSourceColumns(targetIdRelationMap, relation, sourceColumnSet, relationSet, tableMap);
733                                if (!sourceColumnSet.isEmpty()) {
734                                        relationship simpleRelation = (relationship) relation.clone();
735                                        simpleRelation.setSources(new ArrayList<sourceColumn>(sourceColumnSet));
736                                        simpleRelation.setId(String.valueOf(++maxId));
737                                        simpleRelations.add(simpleRelation);
738                                        for (sourceColumn sourceColumn : sourceColumnSet) {
739                                                table sourceTable = tableMap.get(sourceColumn.getParent_id());
740                                                if (sourceTable != null) {
741                                                        tableSet.add(sourceTable);
742                                                }
743                                        }
744                                }
745                        }
746                        simple.setRelationships(simpleRelations);
747
748                        if (simple.getTables() != null) {
749                                simple.getTables().retainAll(tableSet);
750                        }
751                        if (simple.getPaths() != null) {
752                                simple.getPaths().retainAll(tableSet);
753                        }
754                        if (simple.getStages() != null) {
755                                simple.getStages().retainAll(tableSet);
756                        }
757                        if (simple.getSequences() != null) {
758                                simple.getSequences().retainAll(tableSet);
759                        }
760                        if (simple.getDatasources() != null) {
761                                simple.getDatasources().retainAll(tableSet);
762                        }
763                        if (simple.getDatabases() != null) {
764                                simple.getDatabases().retainAll(tableSet);
765                        }
766                        if (simple.getSchemas() != null) {
767                                simple.getSchemas().retainAll(tableSet);
768                        }
769                        if (simple.getVariables() != null) {
770                                simple.getVariables().retainAll(tableSet);
771                        }
772                        if (simple.getViews() != null) {
773                                simple.getViews().retainAll(tableSet);
774                        }
775                        simple.getProcedures().clear();
776                        simple.getProcesses().clear();
777                        simple.getResultsets().clear();
778                        simple.getErrors().clear();
779                        return simple;
780                } catch (Exception e) {
781                        logger.error("Generate left most table flow failed.", e);
782                }
783                return new dataflow();
784        }
785
786        private static void findSourceColumns(Map<String, Set<relationship>> targetIdRelationMap, relationship relation,
787                                                                                  Set<sourceColumn> sourceColumnSet, Set<relationship> relationSet, Map<String, table> tableMap) {
788                if (relationSet.contains(relation)) {
789                        List<sourceColumn> sourceColumns = relation.getSources();
790                        for (sourceColumn sourceColumn : sourceColumns) {
791                                if (sourceColumn.getId().equals(relation.getTarget().getId())) {
792                                        sourceColumnSet.add(sourceColumn);
793                                        break;
794                                }
795                        }
796                        return;
797                } else {
798                        relationSet.add(relation);
799                }
800                List<sourceColumn> sourceColumns = relation.getSources();
801                for (sourceColumn sourceColumn : sourceColumns) {
802                        String columnId = sourceColumn.getParent_id() + "." + sourceColumn.getId();
803                        if (sourceColumnSet.contains(sourceColumn)) {
804                                continue;
805                        } else if (!targetIdRelationMap.containsKey(columnId)) {
806                                sourceColumnSet.add(sourceColumn);
807                        } else {
808                                Set<relationship> sourceRelations = targetIdRelationMap.get(columnId);
809                                for (relationship sourceRelation : sourceRelations) {
810                                        findSourceColumns(targetIdRelationMap, sourceRelation, sourceColumnSet, relationSet, tableMap);
811                                }
812                        }
813                }
814        }
815
816        public static boolean matchTableName(String tableNamePattern, table targetTable, boolean regex, EDbVendor vendor) {
817                if (SQLUtil.isEmpty(tableNamePattern))
818                        return false;
819                String tableNameOnly = storedName(vendor, ESQLDataObjectType.dotTable, targetTable.getTableNameOnly());
820                if (regex) {
821                        tableNamePattern = SQLUtil.trimColumnStringQuote(tableNamePattern);
822                        if (SQLUtil.trimColumnStringQuote(tableNameOnly).matches("(?i)" + tableNamePattern))
823                                return true;
824                        String tableName = targetTable.getName();
825                        if (SQLUtil.trimColumnStringQuote(tableName).matches("(?i)" + tableNamePattern))
826                                return true;
827                        if (vendor == EDbVendor.dbvmssql || vendor == EDbVendor.dbvazuresql) {
828                                if (tableName.indexOf("..") != -1) {
829                                        if (ModelBindingManager.getGlobalSchema() != null) {
830                                                tableName = tableName.replace("..", "." + ModelBindingManager.getGlobalSchema() + ".");
831                                        } else {
832                                                tableName = tableName.replace("..", ".dbo.");
833                                        }
834                                        if (SQLUtil.trimColumnStringQuote(tableName).matches("(?i)" + tableNamePattern))
835                                                return true;
836                                }
837                        }
838                } else {
839                        String pattern = storedName(vendor, ESQLDataObjectType.dotTable, tableNamePattern);
840                        return tableNameOnly.equalsIgnoreCase(pattern) // non-identifier-compare: lenient user filter
841                                        || storedName(vendor, ESQLDataObjectType.dotTable, targetTable.getName()).equalsIgnoreCase(pattern); // non-identifier-compare: lenient user filter
842                }
843                return false;
844        }
845
846        /**
847         * The name as the vendor stores it, segment by segment, with an omitted SQL Server schema filled
848         * in, upper-cased. The table-flow filters match a user's name against it ignoring case, as they
849         * always have: a lookup the user types is not an identifier comparison.
850         */
851        private static String storedName(EDbVendor vendor, ESQLDataObjectType objectType, String name) {
852                if (name == null || vendor == null) {
853                        return name;
854                }
855                // The legacy filter upper-cased with Locale.ROOT, so ß matched SS; a user's lookup keeps that.
856                return IdentifierService.defaultServiceFor(vendor)
857                                .normalizeQualifiedName(DlineageUtil.withSessionSchema(vendor, name), objectType)
858                                .toUpperCase(java.util.Locale.ROOT); // non-identifier-compare: lenient user filter
859        }
860}