1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shardingsphere.infra.datanode;
19
20 import com.cedarsoftware.util.CaseInsensitiveMap.CaseInsensitiveString;
21 import com.google.common.base.Objects;
22 import com.google.common.base.Splitter;
23 import lombok.Getter;
24 import lombok.RequiredArgsConstructor;
25 import lombok.ToString;
26 import org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
27 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
28 import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
29 import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
30 import org.apache.shardingsphere.infra.exception.kernel.metadata.datanode.InvalidDataNodeFormatException;
31
32 import java.util.List;
33
34
35
36
37 @RequiredArgsConstructor
38 @Getter
39 @ToString
40 public final class DataNode {
41
42 private static final String DELIMITER = ".";
43
44 private static final String ASTERISK = "*";
45
46 private final String dataSourceName;
47
48 private final String schemaName;
49
50 private final String tableName;
51
52
53
54
55
56
57 public DataNode(final String dataNode) {
58 validateDataNodeFormat(dataNode);
59 List<String> segments = Splitter.on(DELIMITER).splitToList(dataNode);
60 boolean isIncludeSchema = 3 == segments.size();
61 dataSourceName = segments.get(0);
62 schemaName = isIncludeSchema ? segments.get(1) : null;
63 tableName = segments.get(isIncludeSchema ? 2 : 1);
64 }
65
66
67
68
69
70
71
72
73 public DataNode(final String databaseName, final DatabaseType databaseType, final String dataNode) {
74 ShardingSpherePreconditions.checkState(dataNode.contains(DELIMITER), () -> new InvalidDataNodeFormatException(dataNode));
75 DialectDatabaseMetaData dialectDatabaseMetaData = new DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData();
76 boolean containsSchema = dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable() && isValidDataNode(dataNode, 3);
77 List<String> segments = Splitter.on(DELIMITER).limit(containsSchema ? 3 : 2).splitToList(dataNode);
78 dataSourceName = segments.get(0);
79 schemaName = getSchemaName(databaseName, dialectDatabaseMetaData, containsSchema, segments);
80 tableName = containsSchema ? segments.get(2).toLowerCase() : segments.get(1).toLowerCase();
81 }
82
83 private String getSchemaName(final String databaseName, final DialectDatabaseMetaData dialectDatabaseMetaData, final boolean containsSchema, final List<String> segments) {
84 return dialectDatabaseMetaData.getSchemaOption().getDefaultSchema().map(optional -> containsSchema ? segments.get(1) : ASTERISK).orElse(databaseName);
85 }
86
87 private boolean isValidDataNode(final String dataNodeStr, final int tier) {
88 if (hasInvalidDelimiterStructure(dataNodeStr)) {
89 return false;
90 }
91 List<String> segments = Splitter.on(DELIMITER).splitToList(dataNodeStr);
92 return isAnySegmentIsEmptyOrContainsOnlyWhitespace(tier, segments);
93 }
94
95 private boolean hasInvalidDelimiterStructure(final String dataNodeStr) {
96 return !dataNodeStr.contains(DELIMITER) || hasLeadingOrTrailingDelimiter(dataNodeStr) || hasConsecutiveDelimiters(dataNodeStr) || hasWhitespaceAroundDelimiters(dataNodeStr);
97 }
98
99 private boolean hasLeadingOrTrailingDelimiter(final String dataNodeStr) {
100 return dataNodeStr.startsWith(DELIMITER) || dataNodeStr.endsWith(DELIMITER);
101 }
102
103 private boolean hasConsecutiveDelimiters(final String dataNodeStr) {
104 return dataNodeStr.contains(DELIMITER + DELIMITER);
105 }
106
107 private boolean hasWhitespaceAroundDelimiters(final String dataNodeStr) {
108 return dataNodeStr.contains(" " + DELIMITER) || dataNodeStr.contains(DELIMITER + " ");
109 }
110
111 private boolean isAnySegmentIsEmptyOrContainsOnlyWhitespace(final int tier, final List<String> segments) {
112 return segments.stream().noneMatch(each -> each.trim().isEmpty()) && tier == segments.size();
113 }
114
115
116
117
118
119
120
121 private void validateDataNodeFormat(final String dataNode) {
122 ShardingSpherePreconditions.checkState(isValidDataNode(dataNode, 2) || isValidDataNode(dataNode, 3), () -> new InvalidDataNodeFormatException(dataNode));
123 }
124
125
126
127
128
129
130 public String format() {
131 return null == schemaName ? formatWithoutSchema() : formatWithSchema();
132 }
133
134
135
136
137
138
139
140 public String format(final DatabaseType databaseType) {
141 return shouldIncludeSchema(databaseType) ? formatWithSchema() : formatWithoutSchema();
142 }
143
144 private boolean shouldIncludeSchema(final DatabaseType databaseType) {
145 return null != schemaName && new DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getSchemaOption().getDefaultSchema().isPresent();
146 }
147
148 private String formatWithSchema() {
149 return String.join(DELIMITER, dataSourceName, schemaName, tableName);
150 }
151
152 private String formatWithoutSchema() {
153 return String.join(DELIMITER, dataSourceName, tableName);
154 }
155
156 @Override
157 public boolean equals(final Object object) {
158 if (this == object) {
159 return true;
160 }
161 if (null == object || getClass() != object.getClass()) {
162 return false;
163 }
164 DataNode dataNode = (DataNode) object;
165 return Objects.equal(new CaseInsensitiveString(dataSourceName), new CaseInsensitiveString(dataNode.dataSourceName))
166 && Objects.equal(new CaseInsensitiveString(tableName), new CaseInsensitiveString(dataNode.tableName))
167 && Objects.equal(null == schemaName ? null : new CaseInsensitiveString(schemaName), null == dataNode.schemaName ? null : new CaseInsensitiveString(dataNode.schemaName));
168 }
169
170 @Override
171 public int hashCode() {
172 return Objects.hashCode(new CaseInsensitiveString(dataSourceName), new CaseInsensitiveString(tableName), null == schemaName ? null : new CaseInsensitiveString(schemaName));
173 }
174 }