1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess;
18
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Set;
24
25 import com.healthmarketscience.jackcess.impl.DatabaseImpl;
26 import com.healthmarketscience.jackcess.impl.IndexData;
27 import com.healthmarketscience.jackcess.impl.IndexImpl;
28 import com.healthmarketscience.jackcess.impl.JetFormat;
29 import com.healthmarketscience.jackcess.impl.TableImpl;
30 import com.healthmarketscience.jackcess.impl.TableUpdater;
31 import com.healthmarketscience.jackcess.impl.ToStringBuilder;
32
33
34
35
36
37
38
39
40
41
42 public class IndexBuilder
43 {
44
45 public static final String PRIMARY_KEY_NAME = "PrimaryKey";
46
47
48 private String _name;
49
50 private byte _type;
51
52
53 private byte _flags = IndexData.UNKNOWN_INDEX_FLAG;
54
55 private final List<Column> _columns = new ArrayList<>();
56
57 private int _indexNumber;
58
59 public IndexBuilder(String name) {
60 _name = name;
61 }
62
63 public String getName() {
64 return _name;
65 }
66
67 public byte getType() {
68 return _type;
69 }
70
71 public byte getFlags() {
72 return _flags;
73 }
74
75 public boolean isPrimaryKey() {
76 return (getType() == IndexImpl.PRIMARY_KEY_INDEX_TYPE);
77 }
78
79 public boolean isUnique() {
80 return ((getFlags() & IndexData.UNIQUE_INDEX_FLAG) != 0);
81 }
82
83 public boolean isIgnoreNulls() {
84 return ((getFlags() & IndexData.IGNORE_NULLS_INDEX_FLAG) != 0);
85 }
86
87 public List<Column> getColumns() {
88 return _columns;
89 }
90
91
92
93
94 public IndexBuilder setName(String name) {
95 _name = name;
96 return this;
97 }
98
99
100
101
102 public IndexBuilder addColumns(String... names) {
103 return addColumns(true, names);
104 }
105
106
107
108
109 public IndexBuilder addColumns(boolean ascending, String... names) {
110 if(names != null) {
111 for(String name : names) {
112 _columns.add(new Column(name, ascending));
113 }
114 }
115 return this;
116 }
117
118
119
120
121
122 public IndexBuilder setPrimaryKey() {
123 _type = IndexImpl.PRIMARY_KEY_INDEX_TYPE;
124 setRequired();
125 return setUnique();
126 }
127
128
129
130
131 public IndexBuilder setType(byte type) {
132 _type = type;
133 return this;
134 }
135
136
137
138
139 public IndexBuilder setUnique() {
140 _flags |= IndexData.UNIQUE_INDEX_FLAG;
141 return this;
142 }
143
144
145
146
147 public IndexBuilder setRequired() {
148 _flags |= IndexData.REQUIRED_INDEX_FLAG;
149 return this;
150 }
151
152
153
154
155 public IndexBuilder setIgnoreNulls() {
156 _flags |= IndexData.IGNORE_NULLS_INDEX_FLAG;
157 return this;
158 }
159
160
161
162
163 public int getIndexNumber() {
164 return _indexNumber;
165 }
166
167
168
169
170 public void setIndexNumber(int newIndexNumber) {
171 _indexNumber = newIndexNumber;
172 }
173
174
175
176
177
178
179
180 public void validate(Set<String> tableColNames, JetFormat format) {
181
182 DatabaseImpl.validateIdentifierName(
183 getName(), format.MAX_INDEX_NAME_LENGTH, "index");
184
185 if(getColumns().isEmpty()) {
186 throw new IllegalArgumentException(withErrorContext(
187 "index has no columns"));
188 }
189 if(getColumns().size() > IndexData.MAX_COLUMNS) {
190 throw new IllegalArgumentException(withErrorContext(
191 "index has too many columns, max " + IndexData.MAX_COLUMNS));
192 }
193
194 Set<String> idxColNames = new HashSet<>();
195 for(Column col : getColumns()) {
196 String idxColName = DatabaseImpl.toLookupName(col.getName());
197 if(!idxColNames.add(idxColName)) {
198 throw new IllegalArgumentException(withErrorContext(
199 "duplicate column name " + col.getName() + " in index"));
200 }
201 if(!tableColNames.contains(idxColName)) {
202 throw new IllegalArgumentException(withErrorContext(
203 "column named " + col.getName() + " not found in table"));
204 }
205 }
206 }
207
208
209
210
211
212 public Index addToTable(Table table) throws IOException {
213 return addToTableDefinition(table);
214 }
215
216
217
218
219
220 public Index addToTableDefinition(TableDefinition table) throws IOException {
221 return new TableUpdater((TableImpl)table).addIndex(this);
222 }
223
224 @Override
225 public String toString() {
226 return ToStringBuilder.builder(this)
227 .append("name", _name)
228 .append("number", _indexNumber)
229 .append("type", _type)
230 .append("isPrimaryKey", isPrimaryKey())
231 .append("isUnique", isUnique())
232 .append("ignoreNulls", isIgnoreNulls())
233 .append("columns", _columns)
234 .toString();
235 }
236
237 private String withErrorContext(String msg) {
238 return msg + "(Index=" + getName() + ")";
239 }
240
241
242
243
244 public static class Column
245 {
246
247 private String _name;
248
249 private final byte _flags;
250
251 private Column(String name, boolean ascending) {
252 _name = name;
253 _flags = (ascending ? IndexData.ASCENDING_COLUMN_FLAG : 0);
254 }
255
256 public String getName() {
257 return _name;
258 }
259
260 public Column setName(String name) {
261 _name = name;
262 return this;
263 }
264
265 public boolean isAscending() {
266 return ((getFlags() & IndexData.ASCENDING_COLUMN_FLAG) != 0);
267 }
268
269 public byte getFlags() {
270 return _flags;
271 }
272
273 @Override
274 public String toString() {
275 return ToStringBuilder.valueBuilder(this)
276 .append("name", _name)
277 .append("flags", getFlags() + " " + (isAscending() ? "(ASC)" : "(DSC)"))
278 .toString();
279 }
280 }
281
282 }