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