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<IndexDataState>();
48 private final Map<ColumnBuilder,ColumnState> _columnStates =
49 new IdentityHashMap<ColumnBuilder,ColumnState>();
50 private final List<ColumnBuilder> _lvalCols = new ArrayList<ColumnBuilder>();
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
135
136
137
138
139 public short countNonLongVariableLength() {
140 short rtn = 0;
141 for (ColumnBuilder col : _columns) {
142 if (col.isVariableLength() && !col.getType().isLongValue()) {
143 rtn++;
144 }
145 }
146 return rtn;
147 }
148
149
150
151
152
153
154 public TableImpl createTable(TableBuilder table) throws IOException {
155
156 _name = table.getName();
157 _columns = table.getColumns();
158 _indexes = table.getIndexes();
159 if(_indexes == null) {
160 _indexes = Collections.<IndexBuilder>emptyList();
161 }
162
163 validate();
164
165
166 short columnNumber = (short) 0;
167 for(ColumnBuilder col : _columns) {
168 col.setColumnNumber(columnNumber++);
169 if(col.getType().isLongValue()) {
170 _lvalCols.add(col);
171
172 _columnStates.put(col, new ColumnState());
173 }
174 }
175
176 if(hasIndexes()) {
177
178 for(IndexBuilder idx : _indexes) {
179 idx.setIndexNumber(_logicalIndexCount++);
180 findIndexDataState(idx);
181 }
182 }
183
184 getPageChannel().startExclusiveWrite();
185 try {
186
187
188 _tdefPageNumber = reservePageNumber();
189 _umapPageNumber = reservePageNumber();
190
191
192 TableImpl.writeTableDefinition(this);
193
194
195 getDatabase().addNewTable(_name, _tdefPageNumber, DatabaseImpl.TYPE_TABLE,
196 null, null);
197
198 TableImpl newTable = getDatabase().getTable(_name);
199
200
201 boolean addedProps = false;
202 Map<String,PropertyMap.Property> props = table.getProperties();
203 if(props != null) {
204 newTable.getProperties().putAll(props.values());
205 addedProps = true;
206 }
207 for(ColumnBuilder cb : _columns) {
208 Map<String,PropertyMap.Property> colProps = cb.getProperties();
209 if(colProps != null) {
210 newTable.getColumn(cb.getName()).getProperties()
211 .putAll(colProps.values());
212 addedProps = true;
213 }
214 }
215
216
217 if(addedProps) {
218 newTable.getProperties().save();
219 }
220
221 return newTable;
222
223 } finally {
224 getPageChannel().finishWrite();
225 }
226 }
227
228 private IndexDataState findIndexDataState(IndexBuilder idx) {
229
230
231
232 for(IndexDataState idxDataState : _indexDataStates) {
233 if(sameIndexData(idxDataState.getFirstIndex(), idx)) {
234 idxDataState.addIndex(idx);
235 return idxDataState;
236 }
237 }
238
239
240 IndexDataState idxDataState = new IndexDataState();
241 idxDataState.setIndexDataNumber(_indexCount++);
242 idxDataState.addIndex(idx);
243 _indexDataStates.add(idxDataState);
244 return idxDataState;
245 }
246
247
248
249
250 private void validate() throws IOException {
251
252 getDatabase().validateNewTableName(_name);
253
254 if((_columns == null) || _columns.isEmpty()) {
255 throw new IllegalArgumentException(withErrorContext(
256 "Cannot create table with no columns"));
257 }
258 if(_columns.size() > getFormat().MAX_COLUMNS_PER_TABLE) {
259 throw new IllegalArgumentException(withErrorContext(
260 "Cannot create table with more than " +
261 getFormat().MAX_COLUMNS_PER_TABLE + " columns"));
262 }
263
264 Set<String> colNames = new HashSet<String>();
265
266 for(ColumnBuilder column : _columns) {
267 validateColumn(colNames, column);
268 }
269
270 List<ColumnBuilder> autoCols = getAutoNumberColumns();
271 if(autoCols.size() > 1) {
272
273 Set<DataType> autoTypes = EnumSet.noneOf(DataType.class);
274 for(ColumnBuilder c : autoCols) {
275 validateAutoNumberColumn(autoTypes, c);
276 }
277 }
278
279 if(hasIndexes()) {
280
281 if(_indexes.size() > getFormat().MAX_INDEXES_PER_TABLE) {
282 throw new IllegalArgumentException(withErrorContext(
283 "Cannot create table with more than " +
284 getFormat().MAX_INDEXES_PER_TABLE + " indexes"));
285 }
286
287
288 Set<String> idxNames = new HashSet<String>();
289 boolean foundPk[] = new boolean[1];
290 for(IndexBuilder index : _indexes) {
291 validateIndex(colNames, idxNames, foundPk, index);
292 }
293 }
294 }
295
296 private List<ColumnBuilder> getAutoNumberColumns()
297 {
298 List<ColumnBuilder> autoCols = new ArrayList<ColumnBuilder>(1);
299 for(ColumnBuilder c : _columns) {
300 if(c.isAutoNumber()) {
301 autoCols.add(c);
302 }
303 }
304 return autoCols;
305 }
306
307 private static boolean sameIndexData(IndexBuilder/../../../com/healthmarketscience/jackcess/IndexBuilder.html#IndexBuilder">IndexBuilder idx1, IndexBuilder idx2) {
308
309
310 if(idx1.getFlags() != idx2.getFlags()) {
311 return false;
312 }
313
314 if(idx1.getColumns().size() != idx2.getColumns().size()) {
315 return false;
316 }
317
318 for(int i = 0; i < idx1.getColumns().size(); ++i) {
319 IndexBuilder.Column col1 = idx1.getColumns().get(i);
320 IndexBuilder.Column col2 = idx2.getColumns().get(i);
321
322 if(!sameIndexData(col1, col2)) {
323 return false;
324 }
325 }
326
327 return true;
328 }
329
330 private static boolean sameIndexData(
331 IndexBuilder.Column col1, IndexBuilder.Column col2) {
332 return (col1.getName().equals(col2.getName()) &&
333 (col1.getFlags() == col2.getFlags()));
334 }
335
336 @Override
337 protected String withErrorContext(String msg) {
338 return msg + "(Table=" + getName() + ")";
339 }
340 }