1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.healthmarketscience.jackcess.impl;
18
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.EnumSet;
23 import java.util.HashSet;
24 import java.util.IdentityHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 import com.healthmarketscience.jackcess.ColumnBuilder;
30 import com.healthmarketscience.jackcess.DataType;
31 import com.healthmarketscience.jackcess.IndexBuilder;
32 import com.healthmarketscience.jackcess.PropertyMap;
33 import com.healthmarketscience.jackcess.TableBuilder;
34
35
36
37
38
39
40
41 public class TableCreator extends TableMutator
42 {
43 private String _name;
44 private List<ColumnBuilder> _columns;
45 private List<IndexBuilder> _indexes;
46 private final List<IndexDataState> _indexDataStates =
47 new ArrayList<>();
48 private final Map<ColumnBuilder,ColumnState> _columnStates =
49 new IdentityHashMap<>();
50 private final List<ColumnBuilder> _lvalCols = new ArrayList<>();
51 private int _tdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
52 private int _umapPageNumber = PageChannel.INVALID_PAGE_NUMBER;
53 private int _indexCount;
54 private int _logicalIndexCount;
55
56 public TableCreator(DatabaseImpl database) {
57 super(database);
58 }
59
60 public String getName() {
61 return _name;
62 }
63
64 @Override
65 String getTableName() {
66 return getName();
67 }
68
69 @Override
70 public int getTdefPageNumber() {
71 return _tdefPageNumber;
72 }
73
74 public int getUmapPageNumber() {
75 return _umapPageNumber;
76 }
77
78 public List<ColumnBuilder> getColumns() {
79 return _columns;
80 }
81
82 public List<IndexBuilder> getIndexes() {
83 return _indexes;
84 }
85
86 public boolean hasIndexes() {
87 return !_indexes.isEmpty();
88 }
89
90 public int getIndexCount() {
91 return _indexCount;
92 }
93
94 public int getLogicalIndexCount() {
95 return _logicalIndexCount;
96 }
97
98 @Override
99 public IndexDataState getIndexDataState(IndexBuilder idx) {
100 for(IndexDataState idxDataState : _indexDataStates) {
101 for(IndexBuilder curIdx : idxDataState.getIndexes()) {
102 if(idx == curIdx) {
103 return idxDataState;
104 }
105 }
106 }
107 throw new IllegalStateException(withErrorContext(
108 "could not find state for index"));
109 }
110
111 public List<IndexDataState> getIndexDataStates() {
112 return _indexDataStates;
113 }
114
115 @Override
116 public ColumnState getColumnState(ColumnBuilder col) {
117 return _columnStates.get(col);
118 }
119
120 public List<ColumnBuilder> getLongValueColumns() {
121 return _lvalCols;
122 }
123
124 @Override
125 short getColumnNumber(String colName) {
126 for(ColumnBuilder col : _columns) {
127 if(col.getName().equalsIgnoreCase(colName)) {
128 return col.getColumnNumber();
129 }
130 }
131 return IndexData.COLUMN_UNUSED;
132 }
133
134 @Override
135 boolean isComplexColumn(String colName) {
136 for(ColumnBuilder col : _columns) {
137 if(col.getName().equalsIgnoreCase(colName)) {
138 return (col.getType() == DataType.COMPLEX_TYPE);
139 }
140 }
141 return false;
142 }
143
144
145
146
147
148
149 public short countNonLongVariableLength() {
150 short rtn = 0;
151 for (ColumnBuilder col : _columns) {
152 if (col.isVariableLength() && !col.getType().isLongValue()) {
153 rtn++;
154 }
155 }
156 return rtn;
157 }
158
159
160
161
162
163
164 public TableImpl createTable(TableBuilder table) throws IOException {
165
166 _name = table.getName();
167 _columns = table.getColumns();
168 _indexes = table.getIndexes();
169 if(_indexes == null) {
170 _indexes = Collections.<IndexBuilder>emptyList();
171 }
172
173 validate();
174
175
176 short columnNumber = (short) 0;
177 for(ColumnBuilder col : _columns) {
178 col.setColumnNumber(columnNumber++);
179 if(col.getType().isLongValue()) {
180 _lvalCols.add(col);
181
182 _columnStates.put(col, new ColumnState());
183 }
184 }
185
186 if(hasIndexes()) {
187
188 for(IndexBuilder idx : _indexes) {
189 idx.setIndexNumber(_logicalIndexCount++);
190 findIndexDataState(idx);
191 }
192 }
193
194 getPageChannel().startExclusiveWrite();
195 try {
196
197
198 _tdefPageNumber = reservePageNumber();
199 _umapPageNumber = reservePageNumber();
200
201
202 TableImpl.writeTableDefinition(this);
203
204
205 getDatabase().addNewTable(_name, _tdefPageNumber, DatabaseImpl.TYPE_TABLE,
206 null, null);
207
208 TableImpl newTable = getDatabase().getTable(_name);
209
210
211 boolean addedProps = false;
212 Map<String,PropertyMap.Property> props = table.getProperties();
213 if(props != null) {
214 newTable.getProperties().putAll(props.values());
215 addedProps = true;
216 }
217 for(ColumnBuilder cb : _columns) {
218 Map<String,PropertyMap.Property> colProps = cb.getProperties();
219 if(colProps != null) {
220 newTable.getColumn(cb.getName()).getProperties()
221 .putAll(colProps.values());
222 addedProps = true;
223 }
224 }
225
226
227 if(addedProps) {
228 newTable.getProperties().save();
229 }
230
231 return newTable;
232
233 } finally {
234 getPageChannel().finishWrite();
235 }
236 }
237
238 private IndexDataState findIndexDataState(IndexBuilder idx) {
239
240
241
242 for(IndexDataState idxDataState : _indexDataStates) {
243 if(sameIndexData(idxDataState.getFirstIndex(), idx)) {
244 idxDataState.addIndex(idx);
245 return idxDataState;
246 }
247 }
248
249
250 IndexDataState idxDataState = new IndexDataState();
251 idxDataState.setIndexDataNumber(_indexCount++);
252 idxDataState.addIndex(idx);
253 _indexDataStates.add(idxDataState);
254 return idxDataState;
255 }
256
257
258
259
260 private void validate() throws IOException {
261
262 getDatabase().validateNewTableName(_name);
263
264 if((_columns == null) || _columns.isEmpty()) {
265 throw new IllegalArgumentException(withErrorContext(
266 "Cannot create table with no columns"));
267 }
268 if(_columns.size() > getFormat().MAX_COLUMNS_PER_TABLE) {
269 throw new IllegalArgumentException(withErrorContext(
270 "Cannot create table with more than " +
271 getFormat().MAX_COLUMNS_PER_TABLE + " columns"));
272 }
273
274 Set<String> colNames = new HashSet<>();
275
276 for(ColumnBuilder column : _columns) {
277 validateColumn(colNames, column);
278 }
279
280 List<ColumnBuilder> autoCols = getAutoNumberColumns();
281 if(autoCols.size() > 1) {
282
283 Set<DataType> autoTypes = EnumSet.noneOf(DataType.class);
284 for(ColumnBuilder c : autoCols) {
285 validateAutoNumberColumn(autoTypes, c);
286 }
287 }
288
289 if(hasIndexes()) {
290
291 if(_indexes.size() > getFormat().MAX_INDEXES_PER_TABLE) {
292 throw new IllegalArgumentException(withErrorContext(
293 "Cannot create table with more than " +
294 getFormat().MAX_INDEXES_PER_TABLE + " indexes"));
295 }
296
297
298 Set<String> idxNames = new HashSet<>();
299 boolean foundPk[] = new boolean[1];
300 for(IndexBuilder index : _indexes) {
301 validateIndex(colNames, idxNames, foundPk, index);
302 }
303 }
304 }
305
306 private List<ColumnBuilder> getAutoNumberColumns()
307 {
308 List<ColumnBuilder> autoCols = new ArrayList<>(1);
309 for(ColumnBuilder c : _columns) {
310 if(c.isAutoNumber()) {
311 autoCols.add(c);
312 }
313 }
314 return autoCols;
315 }
316
317 private static boolean sameIndexData(IndexBuilder/../../../com/healthmarketscience/jackcess/IndexBuilder.html#IndexBuilder">IndexBuilder idx1, IndexBuilder idx2) {
318
319
320 if(idx1.getFlags() != idx2.getFlags()) {
321 return false;
322 }
323
324 if(idx1.getColumns().size() != idx2.getColumns().size()) {
325 return false;
326 }
327
328 for(int i = 0; i < idx1.getColumns().size(); ++i) {
329 IndexBuilder.Column col1 = idx1.getColumns().get(i);
330 IndexBuilder.Column col2 = idx2.getColumns().get(i);
331
332 if(!sameIndexData(col1, col2)) {
333 return false;
334 }
335 }
336
337 return true;
338 }
339
340 private static boolean sameIndexData(
341 IndexBuilder.Column col1, IndexBuilder.Column col2) {
342 return (col1.getName().equals(col2.getName()) &&
343 (col1.getFlags() == col2.getFlags()));
344 }
345
346 @Override
347 protected String withErrorContext(String msg) {
348 return msg + "(Table=" + getName() + ")";
349 }
350 }