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.HashMap;
21 import java.util.Map;
22
23 import com.healthmarketscience.jackcess.impl.ColumnImpl;
24 import com.healthmarketscience.jackcess.impl.DatabaseImpl;
25 import com.healthmarketscience.jackcess.impl.JetFormat;
26 import com.healthmarketscience.jackcess.impl.PropertyMapImpl;
27 import com.healthmarketscience.jackcess.impl.TableImpl;
28 import com.healthmarketscience.jackcess.impl.TableUpdater;
29 import com.healthmarketscience.jackcess.impl.ToStringBuilder;
30
31
32
33
34
35
36
37
38
39
40 public class ColumnBuilder {
41
42
43 private static final short UNSET_COLUMN_ID = (short)-1;
44
45
46 private String _name;
47
48 private DataType _type;
49
50 private Short _length;
51
52 private Byte _precision;
53
54 private Byte _scale;
55
56 private boolean _autoNumber;
57
58 private boolean _compressedUnicode;
59
60 private boolean _calculated;
61
62 private boolean _hyperlink;
63
64 private short _columnNumber;
65
66
67 private short _columnId = UNSET_COLUMN_ID;
68
69 private ColumnImpl.SortOrder _sortOrder;
70
71 private Map<String,PropertyMap.Property> _props;
72
73
74 public ColumnBuilder(String name) {
75 this(name, null);
76 }
77
78 public ColumnBuilder(String name, DataType type) {
79 _name = name;
80 _type = type;
81 }
82
83 public String getName() {
84 return _name;
85 }
86
87
88
89
90 public ColumnBuilder setType(DataType type) {
91 _type = type;
92 return this;
93 }
94
95 public DataType getType() {
96 return _type;
97 }
98
99
100
101
102 public ColumnBuilder setSQLType(int type) throws IOException {
103 return setSQLType(type, 0, null);
104 }
105
106
107
108
109
110 public ColumnBuilder setSQLType(int type, int lengthInUnits)
111 throws IOException
112 {
113 return setSQLType(type, lengthInUnits, null);
114 }
115
116
117
118
119
120 public ColumnBuilder setSQLType(int type, int lengthInUnits,
121 Database.FileFormat fileFormat)
122 throws IOException
123 {
124 return setType(DataType.fromSQLType(type, lengthInUnits, fileFormat));
125 }
126
127
128
129
130 public ColumnBuilder setPrecision(int newPrecision) {
131 _precision = (byte)newPrecision;
132 return this;
133 }
134
135 public byte getPrecision() {
136 return ((_precision != null) ? _precision : (byte)_type.getDefaultPrecision());
137 }
138
139
140
141
142
143 public ColumnBuilder setMaxPrecision() {
144 if(_type.getHasScalePrecision()) {
145 setPrecision(_type.getMaxPrecision());
146 }
147 return this;
148 }
149
150
151
152
153 public ColumnBuilder setScale(int newScale) {
154 _scale = (byte)newScale;
155 return this;
156 }
157
158 public byte getScale() {
159 return ((_scale != null) ? _scale : (byte)_type.getDefaultScale());
160 }
161
162
163
164
165
166 public ColumnBuilder setMaxScale() {
167 if(_type.getHasScalePrecision()) {
168 setScale(_type.getMaxScale());
169 }
170 return this;
171 }
172
173
174
175
176 public ColumnBuilder setLength(int length) {
177 _length = (short)length;
178 return this;
179 }
180
181 public short getLength() {
182 return ((_length != null) ? _length :
183 (short)(!_type.isVariableLength() ? _type.getFixedSize() :
184 _type.getDefaultSize()));
185 }
186
187
188
189
190 public ColumnBuilder setLengthInUnits(int unitLength) {
191 return setLength(_type.fromUnitSize(unitLength));
192 }
193
194
195
196
197
198 public ColumnBuilder setMaxLength() {
199
200 if(_type.isVariableLength()) {
201 setLength(_type.getMaxSize());
202 }
203 return this;
204 }
205
206
207
208
209 public ColumnBuilder setAutoNumber(boolean autoNumber) {
210 _autoNumber = autoNumber;
211 return this;
212 }
213
214 public boolean isAutoNumber() {
215 return _autoNumber;
216 }
217
218
219
220
221 public ColumnBuilder setCompressedUnicode(boolean compressedUnicode) {
222 _compressedUnicode = compressedUnicode;
223 return this;
224 }
225
226 public boolean isCompressedUnicode() {
227 return _compressedUnicode;
228 }
229
230
231
232
233 public ColumnBuilder setCalculated(boolean calculated) {
234 _calculated = calculated;
235 return this;
236 }
237
238 public boolean isCalculated() {
239 return _calculated;
240 }
241
242
243
244
245
246 public ColumnBuilder setCalculatedInfo(String expression) {
247 setCalculated(true);
248 putProperty(PropertyMap.EXPRESSION_PROP, expression);
249 return putProperty(PropertyMap.RESULT_TYPE_PROP, getType().getValue());
250 }
251
252 public boolean isVariableLength() {
253
254 return(getType().isVariableLength() || isCalculated());
255 }
256
257
258
259
260 public ColumnBuilder setHyperlink(boolean hyperlink) {
261 _hyperlink = hyperlink;
262 return this;
263 }
264
265 public boolean isHyperlink() {
266 return _hyperlink;
267 }
268
269
270
271
272
273
274
275 public ColumnBuilder putProperty(String name, Object value) {
276 return putProperty(name, null, value);
277 }
278
279
280
281
282 public ColumnBuilder putProperty(String name, DataType type, Object value) {
283 setProperty(name, PropertyMapImpl.createProperty(name, type, value));
284 return this;
285 }
286
287 public Map<String,PropertyMap.Property> getProperties() {
288 return _props;
289 }
290
291 private void setProperty(String name, PropertyMap.Property prop) {
292 if(prop == null) {
293 return;
294 }
295 if(_props == null) {
296 _props = new HashMap<>();
297 }
298 _props.put(name, prop);
299 }
300
301 private PropertyMap.Property getProperty(String name) {
302 return ((_props != null) ? _props.get(name) : null);
303 }
304
305
306
307
308
309 public ColumnBuilder setFromColumn(Column template)
310 throws IOException
311 {
312 DataType type = template.getType();
313 setType(type);
314 setLengthInUnits(template.getLengthInUnits());
315 setAutoNumber(template.isAutoNumber());
316 if(type.getHasScalePrecision()) {
317 setScale(template.getScale());
318 setPrecision(template.getPrecision());
319 }
320 setCalculated(template.isCalculated());
321 setCompressedUnicode(template.isCompressedUnicode());
322 setHyperlink(template.isHyperlink());
323 if(template instanceof ColumnImpl) {
324 setTextSortOrder(((ColumnImpl)template).getTextSortOrder());
325 }
326
327 PropertyMap colProps = template.getProperties();
328 for(PropertyMap.Property colProp : colProps) {
329
330 if(!PropertyMap.GUID_PROP.equalsIgnoreCase(colProp.getName())) {
331 setProperty(colProp.getName(), colProp);
332 }
333 }
334
335 return this;
336 }
337
338
339
340
341 public ColumnBuilderm/healthmarketscience/jackcess/ColumnBuilder.html#ColumnBuilder">ColumnBuilder setFromColumn(ColumnBuilder template) {
342 DataType type = template.getType();
343 _type = type;
344 _length = template._length;
345 _autoNumber = template._autoNumber;
346 if(type.getHasScalePrecision()) {
347 _scale = template._scale;
348 _precision = template._precision;
349 }
350 _calculated = template._calculated;
351 _compressedUnicode = template._compressedUnicode;
352 _hyperlink = template._hyperlink;
353 _sortOrder = template._sortOrder;
354
355 if(template._props != null) {
356 _props = new HashMap<>(template._props);
357 }
358
359 return this;
360 }
361
362
363
364
365 public ColumnBuilder escapeName() {
366 _name = TableBuilder.escapeIdentifier(_name);
367 return this;
368 }
369
370
371
372
373 public short getColumnNumber() {
374 return _columnNumber;
375 }
376
377
378
379
380 public void setColumnNumber(short newColumnNumber) {
381 _columnNumber = newColumnNumber;
382 }
383
384
385
386
387 public short getColumnId() {
388 return ((_columnId != UNSET_COLUMN_ID) ? _columnId : _columnNumber);
389 }
390
391
392
393
394 public void setColumnId(short newColumnId) {
395 _columnId = newColumnId;
396 }
397
398
399
400
401 public ColumnImpl.SortOrder getTextSortOrder() {
402 return _sortOrder;
403 }
404
405
406
407
408 public void setTextSortOrder(ColumnImpl.SortOrder newTextSortOrder) {
409 _sortOrder = newTextSortOrder;
410 }
411
412
413
414
415 public boolean storeInNullMask() {
416 return (getType() == DataType.BOOLEAN);
417 }
418
419
420
421
422 public int getFixedDataSize() {
423 return _type.getFixedSize(_length);
424 }
425
426
427
428
429
430
431
432 public void validate(JetFormat format) {
433 DatabaseImpl.validateIdentifierName(
434 getName(), format.MAX_COLUMN_NAME_LENGTH, "column");
435
436 if(getType() == null) {
437 throw new IllegalArgumentException(withErrorContext("must have type"));
438 }
439 if(getType().isUnsupported()) {
440 throw new IllegalArgumentException(withErrorContext(
441 "Cannot create column with unsupported type " + getType()));
442 }
443 if(!format.isSupportedDataType(getType())) {
444 throw new IllegalArgumentException(withErrorContext(
445 "Database format " + format + " does not support type " + getType()));
446 }
447
448 if(!getType().isVariableLength()) {
449 if(getLength() < getType().getFixedSize()) {
450 throw new IllegalArgumentException(withErrorContext(
451 "Invalid fixed length size " + getLength()));
452 }
453 } else if(!getType().isLongValue()) {
454 if(!getType().isValidSize(getLength())) {
455 throw new IllegalArgumentException(withErrorContext(
456 "Var length must be from " + getType().getMinSize() + " to " +
457 getType().getMaxSize() + " inclusive, found " + getLength()));
458 }
459 }
460
461 if(getType().getHasScalePrecision()) {
462 if(!getType().isValidScale(getScale())) {
463 throw new IllegalArgumentException(withErrorContext(
464 "Scale must be from " + getType().getMinScale() + " to " +
465 getType().getMaxScale() + " inclusive, found " + getScale()));
466 }
467 if(!getType().isValidPrecision(getPrecision())) {
468 throw new IllegalArgumentException(withErrorContext(
469 "Precision must be from " + getType().getMinPrecision() + " to " +
470 getType().getMaxPrecision() + " inclusive, found " +
471 getPrecision()));
472 }
473 }
474
475 if(isAutoNumber()) {
476 if(!getType().mayBeAutoNumber()) {
477 throw new IllegalArgumentException(withErrorContext(
478 "Auto number column must be long integer or guid"));
479 }
480 }
481
482 if(isCompressedUnicode()) {
483 if(!getType().isTextual()) {
484 throw new IllegalArgumentException(withErrorContext(
485 "Only textual columns allow unicode compression (text/memo)"));
486 }
487 }
488
489 if(isHyperlink()) {
490 if(getType() != DataType.MEMO) {
491 throw new IllegalArgumentException(withErrorContext(
492 "Only memo columns can be hyperlinks"));
493 }
494 }
495
496 if(isCalculated()) {
497 if(!format.isSupportedCalculatedDataType(getType())) {
498 throw new IllegalArgumentException(withErrorContext(
499 "Database format " + format + " does not support calculated type " +
500 getType()));
501 }
502
503
504 if(getProperty(PropertyMap.EXPRESSION_PROP) == null) {
505 throw new IllegalArgumentException(withErrorContext(
506 "No expression provided for calculated type " + getType()));
507 }
508
509
510 if(getProperty(PropertyMap.RESULT_TYPE_PROP) == null) {
511 putProperty(PropertyMap.RESULT_TYPE_PROP, getType().getValue());
512 }
513 }
514 }
515
516
517
518
519 public ColumnBuilder toColumn() {
520
521 return this;
522 }
523
524
525
526
527
528 public Column addToTable(Table table) throws IOException {
529 return addToTableDefinition(table);
530 }
531
532
533
534
535
536 public Column addToTableDefinition(TableDefinition table) throws IOException {
537 return new TableUpdater((TableImpl)table).addColumn(this);
538 }
539
540 @Override
541 public String toString() {
542 return ToStringBuilder.builder(this)
543 .append("name", _name)
544 .append("type", _type)
545 .append("number", _columnNumber)
546 .append("id", getColumnId())
547 .appendIfNotNull("length", _length)
548 .appendIfNotNull("precision", _precision)
549 .appendIfNotNull("scale", _scale)
550 .append("autoNumber", _autoNumber)
551 .append("compressedUnicode", _compressedUnicode)
552 .append("calculated", _calculated)
553 .append("hyperlink", _hyperlink)
554 .appendIfNotNull("textSortOrder", _sortOrder)
555 .appendIfNotNull("props", _props)
556 .toString();
557 }
558
559 private String withErrorContext(String msg) {
560 return msg + "(Column=" + getName() + ")";
561 }
562 }