1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.melati.poem;
47
48 import java.io.PrintStream;
49 import java.sql.PreparedStatement;
50 import java.sql.ResultSet;
51 import java.sql.SQLException;
52 import java.util.Enumeration;
53
54 import org.melati.poem.dbms.Dbms;
55 import org.melati.poem.util.EmptyEnumeration;
56 import org.melati.poem.util.StringUtils;
57
58
59
60
61
62
63
64 public abstract class Column implements FieldAttributes {
65 private Table table = null;
66 private String name;
67 private String quotedName;
68 private SQLPoemType type;
69 private DefinitionSource definitionSource;
70 private ColumnInfo info = null;
71
72
73
74
75
76
77
78
79 public Column(
80 Table table,
81 String name,
82 SQLPoemType type,
83 DefinitionSource definitionSource) {
84 this.table = table;
85 this.name = name;
86 this.quotedName = table.getDatabase().quotedName(name);
87 this.type = type;
88 this.definitionSource = definitionSource;
89 }
90
91
92
93
94
95
96
97
98
99
100 Dbms dbms() {
101 return getDatabase().getDbms();
102 }
103
104 void unifyType(SQLPoemType storeType, DefinitionSource source) {
105 PoemType unified = dbms().canRepresent(storeType, type);
106 if (unified == null || !(unified instanceof SQLPoemType))
107 throw new TypeDefinitionMismatchException(this, storeType, source);
108
109 type = (SQLPoemType) unified;
110 }
111
112 void assertMatches(ResultSet colDesc)
113 throws SQLException, TypeDefinitionMismatchException {
114 PoemType dbType = getDatabase().defaultPoemTypeOfColumnMetaData(colDesc);
115
116 if (dbms().canRepresent(dbType, type) == null)
117 throw new TypeDefinitionMismatchException(
118 this,
119 dbType,
120 DefinitionSource.sqlMetaData);
121 }
122
123 void setColumnInfo(ColumnInfo columnInfo) {
124 try {
125 unifyType(columnInfo.getType(), DefinitionSource.infoTables);
126 columnInfo.setColumn(this);
127 if (columnInfo.getDisplaylevel() == DisplayLevel.primary)
128 table.setDisplayColumn(this);
129 if (columnInfo.getSearchability() == Searchability.primary)
130 table.setSearchColumn(this);
131 info = columnInfo;
132 table.notifyColumnInfo(info);
133 } catch (Exception e) {
134 throw new UnexpectedExceptionPoemException(
135 e,
136 "Setting column info for " + name + " to " + columnInfo);
137 }
138 }
139
140 protected DisplayLevel defaultDisplayLevel() {
141 return DisplayLevel.summary;
142 }
143
144 protected Searchability defaultSearchability() {
145 return Searchability.yes;
146 }
147
148 protected Integer defaultDisplayOrderPriority() {
149 return null;
150 }
151
152 protected boolean defaultSortDescending() {
153 return false;
154 }
155
156 protected String defaultDisplayName() {
157 return StringUtils.capitalised(getName());
158 }
159
160 protected int defaultDisplayOrder() {
161 return 100;
162 }
163
164 protected String defaultDescription() {
165 return null;
166 }
167
168 protected boolean defaultUserEditable() {
169 return true;
170 }
171
172 protected boolean defaultUserCreateable() {
173 return true;
174 }
175
176 protected boolean defaultIndexed() {
177 return isTroidColumn();
178 }
179
180 protected boolean defaultUnique() {
181 return isTroidColumn();
182 }
183
184
185
186
187 protected StandardIntegrityFix defaultIntegrityFix() {
188 return StandardIntegrityFix.prevent;
189 }
190
191 protected int defaultWidth() {
192 return 20;
193 }
194
195 protected int defaultHeight() {
196 return 1;
197 }
198
199 protected int defaultPrecision() {
200 return 22;
201 }
202
203 protected int defaultScale() {
204 return 2;
205 }
206
207 protected String defaultRenderinfo() {
208 return null;
209 }
210
211 void createColumnInfo() throws PoemException {
212 if (info == null) {
213 info = (ColumnInfo)getDatabase().
214 getColumnInfoTable().create(new Initialiser() {
215 public void init(Persistent g) throws AccessPoemException {
216 ColumnInfo i = (ColumnInfo)g;
217 i.setName(getName());
218 i.setDisplayname(defaultDisplayName());
219 i.setDisplayorder(defaultDisplayOrder());
220 i.setDescription(defaultDescription());
221 i.setDisplaylevel(defaultDisplayLevel());
222 i.setSearchability(defaultSearchability());
223 i.setSortdescending(defaultSortDescending());
224 i.setDisplayorderpriority(defaultDisplayOrderPriority());
225 i.setTableinfoTroid(table.tableInfoID());
226 i.setUsereditable(defaultUserEditable());
227 i.setUsercreateable(defaultUserCreateable());
228 i.setIndexed(defaultIndexed());
229 i.setUnique(defaultUnique());
230 i.setWidth(defaultWidth());
231 i.setHeight(defaultHeight());
232 i.setRenderinfo(defaultRenderinfo());
233 i.setIntegrityfix(defaultIntegrityFix());
234 i.setPrecision(defaultPrecision());
235 i.setScale(defaultScale());
236 getType().saveColumnInfo(i);
237 }
238 });
239
240
241
242 if (defaultDisplayLevel() == DisplayLevel.primary)
243 table.setDisplayColumn(this);
244 if (defaultSearchability() == Searchability.primary)
245 table.setSearchColumn(this);
246 }
247 }
248
249 void unifyWithIndex(String indexName, ResultSet index) throws SQLException,
250 IndexUniquenessPoemException {
251 boolean indexUnique = !index.getBoolean("NON_UNIQUE");
252 if (indexUnique != getUnique())
253 throw new IndexUniquenessPoemException(
254 this,
255 indexName,
256 getUnique());
257 }
258
259
260
261
262
263
264
265
266
267
268 public final Database getDatabase() {
269 return getTable().getDatabase();
270 }
271
272
273
274
275 public final Table getTable() {
276 return table;
277 }
278
279 final void setTable(Table table) {
280 this.table = table;
281 }
282
283
284
285
286
287 public final String getName() {
288 return name;
289 }
290
291
292
293
294 public final String quotedName() {
295 return quotedName;
296 }
297
298
299
300
301 public final String fullQuotedName() {
302 return table.quotedName() + "." + quotedName;
303 }
304
305
306
307
308
309
310
311 public final String getDisplayName() {
312 return info.getDisplayname();
313 }
314
315
316
317
318
319 public final String getDescription() {
320 return info.getDescription();
321 }
322
323
324
325
326
327
328
329 final Integer columnInfoID() {
330 return info == null ? null : info.troid();
331 }
332
333
334
335
336 public final ColumnInfo getColumnInfo() {
337 return info;
338 }
339
340
341
342
343 public DisplayLevel getDisplayLevel() {
344 return info == null ? defaultDisplayLevel() : info.getDisplaylevel();
345 }
346
347
348
349
350 public void setDisplayLevel(DisplayLevel level) {
351 if (info != null)
352 info.setDisplaylevel(level);
353 }
354
355
356
357
358 public Searchability getSearchability() {
359 return info == null ? defaultSearchability() : info.getSearchability();
360 }
361
362
363
364
365 public void setSearchability(Searchability searchability) {
366 if (info != null)
367 info.setSearchability(searchability);
368 }
369
370
371
372
373
374 public final boolean getUserEditable() {
375 return !isTroidColumn()
376 && (info == null || info.getUsereditable().booleanValue());
377 }
378
379
380
381
382
383 public final boolean getUserCreateable() {
384 return !isTroidColumn()
385 && (info == null || info.getUsercreateable().booleanValue());
386 }
387
388
389
390
391 public final SQLPoemType getSQLType() {
392 return type;
393 }
394
395
396
397
398
399 public final PoemType getType() {
400 return type;
401 }
402
403
404
405
406 public final boolean isTroidColumn() {
407 return getType() instanceof TroidPoemType;
408 }
409
410
411
412
413
414
415 public final boolean isDeletedColumn() {
416 return getType() instanceof DeletedPoemType;
417 }
418
419
420
421
422
423 public final boolean getIndexed() {
424 return getUnique() || info.getIndexed().booleanValue();
425 }
426
427
428
429
430 public final boolean getUnique() {
431 return isTroidColumn() || info.getUnique().booleanValue();
432 }
433
434
435
436
437
438 public IntegrityFix getIntegrityFix() {
439 IntegrityFix it = info.getIntegrityfix();
440 return it == null ? defaultIntegrityFix() : it;
441 }
442
443
444
445
446 public void setIntegrityFix(StandardIntegrityFix fix) {
447 info.setIntegrityfix(fix);
448 }
449
450
451
452
453
454 public final String getRenderInfo() {
455 return info.getRenderinfo();
456 }
457
458
459
460
461
462 public final int getWidth() {
463 return info.getWidth().intValue();
464 }
465
466
467
468
469
470 public final int getHeight() {
471 return info.getHeight().intValue();
472 }
473
474
475
476
477 public final Integer getDisplayOrderPriority() {
478 return info == null ? null : info.getDisplayorderpriority();
479 }
480
481
482
483
484
485 public final boolean getSortDescending() {
486 return info.getSortdescending() == null
487 ? false
488 : info.getSortdescending().booleanValue();
489 }
490
491
492
493
494
495
496
497
498
499
500
501 public String toString() {
502 return table.getName()
503 + "."
504 + name
505 + ": "
506 + getType().toString()
507 + " (from "
508 + definitionSource
509 + ")";
510 }
511
512
513
514
515 public void dump() {
516 dump(System.out);
517 }
518
519
520
521
522
523
524 public void dump(PrintStream ps) {
525 ps.println(toString());
526 }
527
528
529
530
531
532
533 public String eqClause(Object raw) {
534 return fullQuotedName()
535 + (raw == null ? " IS NULL" : " = " + type.quotedRaw(raw));
536 }
537
538 private PreparedStatementFactory selectionWhereEq = null;
539
540 private PreparedStatementFactory statementWhereEq() {
541 if (selectionWhereEq == null)
542 selectionWhereEq =
543 new PreparedStatementFactory(
544 getDatabase(),
545 getTable().selectionSQL(
546 getTable().quotedName(),
547 fullQuotedName()
548 + " = "
549 + dbms().preparedStatementPlaceholder(getType()),
550 null,
551 false,
552 true));
553
554 return selectionWhereEq;
555 }
556
557 ResultSet resultSetWhereEq(Object raw) {
558 SessionToken token = PoemThread.sessionToken();
559 PreparedStatement ps =
560 statementWhereEq().preparedStatement(token.transaction);
561 type.setRaw(ps, 1, raw);
562 return statementWhereEq().resultSet(token, ps);
563 }
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587 public Enumeration selectionWhereEq(Object raw) {
588 return new ResultSetEnumeration(resultSetWhereEq(raw)) {
589 public Object mapped(ResultSet rs) throws SQLException {
590 return getTable().getObject(rs.getInt(1));
591 }
592 };
593 }
594
595
596
597
598
599
600
601 public Persistent firstWhereEq(Object raw) {
602 Enumeration them = selectionWhereEq(raw);
603 return them.hasMoreElements() ? (Persistent)them.nextElement() : null;
604 }
605
606
607
608
609
610
611
612 public CachedSelection cachedSelectionWhereEq(Object raw) {
613 return new CachedSelection(getTable(), eqClause(raw), null);
614 }
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632 public abstract Object getRaw(Persistent g) throws AccessPoemException;
633
634
635
636
637
638
639
640
641 public abstract Object getRaw_unsafe(Persistent g);
642
643
644
645
646
647
648
649
650
651
652
653
654
655 public abstract void setRaw(Persistent g, Object raw)
656 throws AccessPoemException, ValidationPoemException;
657
658
659
660
661
662
663
664
665 public abstract void setRaw_unsafe(Persistent g, Object raw);
666
667
668
669
670
671
672
673
674
675
676
677
678
679 public abstract Object getCooked(Persistent g)
680 throws AccessPoemException, PoemException;
681
682
683
684
685
686
687
688
689
690
691
692
693
694 public abstract void setCooked(Persistent g, Object cooked)
695 throws AccessPoemException, ValidationPoemException;
696
697
698
699
700 public static class LoadException extends UnexpectedExceptionPoemException {
701 private static final long serialVersionUID = 1L;
702
703 private Column column;
704
705
706
707
708
709
710 public LoadException(Column column, Exception problem) {
711 super(problem);
712 this.column = column;
713 }
714
715
716 public String getMessage() {
717 return "An unexpected problem arose loading "
718 + column
719 + " from the "
720 + "database:\n"
721 + subException;
722 }
723
724
725
726
727
728 protected Column getColumn() {
729 return column;
730 }
731 }
732
733
734
735
736
737
738
739
740
741
742 void load_unsafe(ResultSet rs, int rsCol, Persistent g)
743 throws LoadException {
744 try {
745 setRaw_unsafe(g, type.getRaw(rs, rsCol));
746 } catch (Exception e) {
747 throw new LoadException(this, e);
748 }
749 }
750
751
752
753
754
755
756
757
758
759 void save_unsafe(Persistent g, PreparedStatement ps, int psCol) {
760 try {
761 type.setRaw(ps, psCol, getRaw_unsafe(g));
762 } catch (Exception e) {
763 throw new FieldContentsPoemException(this, e);
764 }
765 }
766
767
768
769
770
771
772
773
774
775
776
777
778 public abstract Field asField(Persistent g);
779
780
781
782
783
784 public Field asEmptyField() {
785 return new Field((Object) null, this);
786 }
787
788
789
790
791
792 public static class SettingException extends NormalPoemException {
793 private static final long serialVersionUID = 1L;
794
795 public Persistent persistent;
796
797 public Column column;
798
799 public String columnDesc;
800
801
802
803
804
805
806
807 public SettingException(
808 Persistent persistent,
809 Column column,
810 Exception trouble) {
811 super(trouble);
812 this.persistent = persistent;
813 this.column = column;
814 columnDesc =
815 "field `"
816 + column.getDisplayName()
817 + "' in object `"
818 + persistent.displayString()
819 + "' of type `"
820 + column.getTable().getDisplayName()
821 + "'";
822 }
823
824
825 public String getMessage() {
826 return "Unable to set " + columnDesc + "\n" + subException;
827 }
828 }
829
830
831
832
833
834
835
836
837
838 public void setRawString(Persistent g, String rawString) {
839 Object raw;
840 try {
841 raw = getType().rawOfString(rawString);
842 } catch (Exception e) {
843 throw new SettingException(g, this, e);
844 }
845 setRaw(g, raw);
846 }
847
848
849
850
851
852
853
854
855 public Enumeration referencesTo(Persistent object) {
856 return getType() instanceof ReferencePoemType
857 && ((ReferencePoemType) getType()).targetTable() == object.getTable()
858 ? selectionWhereEq(object.troid())
859 : EmptyEnumeration.it;
860 }
861
862
863
864
865
866
867
868
869
870
871
872 public Persistent ensure(Persistent orCreate) {
873 Persistent there = firstWhereEq(getRaw_unsafe(orCreate));
874 if (there == null) {
875 getTable().create(orCreate);
876 return orCreate;
877 } else
878 return there;
879 }
880
881
882
883
884
885
886
887
888
889
890
891
892 public int firstFree(String whereClause) {
893 if (! (getType() instanceof IntegerPoemType))
894 throw new AppBugPoemException("firstFree called on a non Integer column");
895 getTable().readLock();
896 String querySelection =
897 quotedName
898 + " + 1 "
899 + "FROM "
900 + getTable().quotedName()
901 + " AS t1 "
902 + "WHERE "
903 + (whereClause == null ? "" : "(t1." + whereClause + ") AND ")
904 + "NOT EXISTS ("
905 + "SELECT * FROM "
906 + getTable().quotedName()
907 + " AS t2 "
908 + "WHERE "
909 + (whereClause == null ? "" : "(t2." + whereClause + ") AND ")
910 + "t2."
911 + quotedName
912 + " = t1."
913 + quotedName
914 + " + 1) ";
915
916 String query = getDatabase().getDbms().selectLimit(querySelection, 1);
917 ResultSet results = getDatabase().sqlQuery(query);
918 try {
919 if (results.next())
920 return results.getInt(1);
921 else
922 return 0;
923 } catch (SQLException e) {
924 throw new SQLSeriousPoemException(e);
925 }
926 }
927 }