View Javadoc
1   /*
2    * Prometheus: Application Framework
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.prometheus.database;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
21  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
22  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
23  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
24  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
25  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRate;
26  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
27  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
28  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
29  import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
30  import io.github.tonywasher.joceanus.prometheus.preference.PrometheusColumnType;
31  
32  import java.math.BigDecimal;
33  import java.sql.Date;
34  import java.sql.PreparedStatement;
35  import java.sql.ResultSet;
36  import java.sql.SQLException;
37  import java.sql.Types;
38  import java.time.LocalDate;
39  import java.util.Iterator;
40  import java.util.List;
41  import java.util.ListIterator;
42  
43  /**
44   * Column definition classes handling data-type specifics.
45   *
46   * @author Tony Washer
47   */
48  public abstract class PrometheusColumnDefinition
49          implements PrometheusColumnControl {
50      /**
51       * Open bracket.
52       */
53      private static final String STR_OPNBRK = "(";
54  
55      /**
56       * Close bracket.
57       */
58      private static final String STR_CLSBRK = ")";
59  
60      /**
61       * Comma.
62       */
63      private static final String STR_COMMA = ",";
64  
65      /**
66       * Decimal length.
67       */
68      private static final String STR_NUMLEN = "18";
69  
70      /**
71       * Standard Decimal length.
72       */
73      private static final String STR_STDDECLEN = "4";
74  
75      /**
76       * Decimal length.
77       */
78      private static final String STR_XTRADECLEN = "6";
79  
80      /**
81       * Table Definition.
82       */
83      private final PrometheusTableControl theTable;
84  
85      /**
86       * Column Identity.
87       */
88      private final MetisDataFieldId theIdentity;
89  
90      /**
91       * Is the column null-able.
92       */
93      private boolean isNullable;
94  
95      /**
96       * Is the value set.
97       */
98      private boolean isValueSet;
99  
100     /**
101      * The value of the column.
102      */
103     private Object theValue;
104 
105     /**
106      * The sort order of the column.
107      */
108     private PrometheusSortOrder theOrder;
109 
110     /**
111      * Constructor.
112      *
113      * @param pTable the table to which the column belongs
114      * @param pId    the column id
115      */
116     protected PrometheusColumnDefinition(final PrometheusTableControl pTable,
117                                          final MetisDataFieldId pId) {
118         /* Record the identity and table */
119         theIdentity = pId;
120         theTable = pTable;
121 
122         /* Add to the map */
123         theTable.getMap().put(theIdentity, this);
124     }
125 
126     /**
127      * Obtain the column name.
128      *
129      * @return the name
130      */
131     protected String getColumnName() {
132         return theIdentity.getId();
133     }
134 
135     /**
136      * Obtain the column id.
137      *
138      * @return the id
139      */
140     protected MetisDataFieldId getColumnId() {
141         return theIdentity;
142     }
143 
144     /**
145      * Obtain the sort order.
146      *
147      * @return the sort order
148      */
149     protected PrometheusSortOrder getSortOrder() {
150         return theOrder;
151     }
152 
153     /**
154      * Obtain the value.
155      *
156      * @return the value
157      */
158     protected Object getObject() {
159         return theValue;
160     }
161 
162     /**
163      * Obtain the table.
164      *
165      * @return the table
166      */
167     protected PrometheusTableControl getTable() {
168         return theTable;
169     }
170 
171     /**
172      * Obtain the value.
173      *
174      * @return the value
175      */
176     protected PrometheusJDBCDriver getDriver() {
177         return theTable.getDriver();
178     }
179 
180     /**
181      * Clear value.
182      */
183     protected void clearValue() {
184         theValue = null;
185         isValueSet = false;
186     }
187 
188     /**
189      * Set value.
190      *
191      * @param pValue the value
192      */
193     protected void setObject(final Object pValue) {
194         theValue = pValue;
195         isValueSet = true;
196     }
197 
198     /**
199      * Is the value set.
200      *
201      * @return true/false
202      */
203     protected boolean isValueSet() {
204         return isValueSet;
205     }
206 
207     /**
208      * Build the creation string for this column.
209      *
210      * @param pBuilder the String builder
211      */
212     protected void buildCreateString(final StringBuilder pBuilder) {
213         /* Add the name of the column */
214         theTable.addQuoteIfAllowed(pBuilder);
215         pBuilder.append(getColumnName());
216         theTable.addQuoteIfAllowed(pBuilder);
217         pBuilder.append(' ');
218 
219         /* Add the type of the column */
220         buildColumnType(pBuilder);
221 
222         /* Add null-able indication */
223         if (!isNullable) {
224             pBuilder.append(" not");
225         }
226         pBuilder.append(" null");
227 
228         /* build the key reference */
229         buildKeyReference(pBuilder);
230     }
231 
232     /**
233      * Set null-able column.
234      */
235     protected void setNullable() {
236         isNullable = true;
237     }
238 
239     /**
240      * Note that we have a sort on reference.
241      */
242     protected void setSortOnReference() {
243         theTable.setSortOnReference();
244     }
245 
246     /**
247      * Set sortOrder.
248      *
249      * @param pOrder the Sort direction
250      */
251     public void setSortOrder(final PrometheusSortOrder pOrder) {
252         theOrder = pOrder;
253         theTable.getSortList().add(this);
254     }
255 
256     /**
257      * Build the column type for this column.
258      *
259      * @param pBuilder the String builder
260      */
261     protected abstract void buildColumnType(StringBuilder pBuilder);
262 
263     /**
264      * Load the value for this column.
265      *
266      * @param pResults the results
267      * @param pIndex   the index of the result column
268      * @throws SQLException on error
269      */
270     protected abstract void loadValue(ResultSet pResults,
271                                       int pIndex) throws SQLException;
272 
273     /**
274      * Store the value for this column.
275      *
276      * @param pStatement the prepared statement
277      * @param pIndex     the index of the statement
278      * @throws SQLException on error
279      */
280     protected abstract void storeValue(PreparedStatement pStatement,
281                                        int pIndex) throws SQLException;
282 
283     /**
284      * Define the key reference.
285      *
286      * @param pBuilder the String builder
287      */
288     protected void buildKeyReference(final StringBuilder pBuilder) {
289     }
290 
291     /**
292      * Locate reference.
293      *
294      * @param pTables the list of defined tables
295      */
296     protected void locateReference(final List<PrometheusTableInstance<?>> pTables) {
297     }
298 
299     /**
300      * The integerColumn Class.
301      */
302     public static class PrometheusIntegerColumn
303             extends PrometheusColumnDefinition {
304         /**
305          * Constructor.
306          *
307          * @param pTable the table to which the column belongs
308          * @param pId    the column id
309          */
310         protected PrometheusIntegerColumn(final PrometheusTableControl pTable,
311                                           final MetisDataFieldId pId) {
312             /* Record the column type and name */
313             super(pTable, pId);
314         }
315 
316         @Override
317         protected void buildColumnType(final StringBuilder pBuilder) {
318             /* Add the column type */
319             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.INTEGER));
320         }
321 
322         /**
323          * Set the value.
324          *
325          * @param pValue the value
326          */
327         protected void setValue(final Integer pValue) {
328             super.setObject(pValue);
329         }
330 
331         /**
332          * Get the value.
333          *
334          * @return the value
335          */
336         protected Integer getValue() {
337             return (Integer) super.getObject();
338         }
339 
340         @Override
341         protected void loadValue(final ResultSet pResults,
342                                  final int pIndex) throws SQLException {
343             final int myValue = pResults.getInt(pIndex);
344             if (pResults.wasNull()) {
345                 setValue(null);
346             } else {
347                 setValue(myValue);
348             }
349         }
350 
351         @Override
352         protected void storeValue(final PreparedStatement pStatement,
353                                   final int pIndex) throws SQLException {
354             final Integer myValue = getValue();
355             if (myValue == null) {
356                 pStatement.setNull(pIndex, Types.INTEGER);
357             } else {
358                 pStatement.setInt(pIndex, myValue);
359             }
360         }
361     }
362 
363     /**
364      * The idColumn Class.
365      */
366     public static final class PrometheusIdColumn
367             extends PrometheusIntegerColumn {
368         /**
369          * Constructor.
370          *
371          * @param pTable the table to which the column belongs
372          */
373         PrometheusIdColumn(final PrometheusTableControl pTable) {
374             /* Record the column type */
375             super(pTable, MetisDataResource.DATA_ID);
376         }
377 
378         @Override
379         protected void buildKeyReference(final StringBuilder pBuilder) {
380             /* Add the column type */
381             pBuilder.append(" primary key");
382         }
383     }
384 
385     /**
386      * The referenceColumn Class.
387      */
388     public static final class PrometheusReferenceColumn
389             extends PrometheusIntegerColumn {
390         /**
391          * The name of the referenced table.
392          */
393         private final String theReference;
394 
395         /**
396          * The definition of the referenced table.
397          */
398         private PrometheusTableControl theDefinition;
399 
400         /**
401          * Constructor.
402          *
403          * @param pTable    the table to which the column belongs
404          * @param pId       the column id
405          * @param pRefTable the name of the referenced table
406          */
407         PrometheusReferenceColumn(final PrometheusTableControl pTable,
408                                   final MetisDataFieldId pId,
409                                   final String pRefTable) {
410             /* Record the column type */
411             super(pTable, pId);
412             theReference = pRefTable;
413         }
414 
415         @Override
416         public void setSortOrder(final PrometheusSortOrder pOrder) {
417             super.setSortOrder(pOrder);
418             setSortOnReference();
419         }
420 
421         @Override
422         protected void buildKeyReference(final StringBuilder pBuilder) {
423             /* Add the reference */
424             pBuilder.append(" REFERENCES ");
425             getTable().addQuoteIfAllowed(pBuilder);
426             pBuilder.append(theReference);
427             getTable().addQuoteIfAllowed(pBuilder);
428             pBuilder.append(STR_OPNBRK);
429             getTable().addQuoteIfAllowed(pBuilder);
430             pBuilder.append(MetisDataResource.DATA_ID.getValue());
431             getTable().addQuoteIfAllowed(pBuilder);
432             pBuilder.append(STR_CLSBRK);
433         }
434 
435         @Override
436         protected void locateReference(final List<PrometheusTableInstance<?>> pTables) {
437             /* Access the Iterator */
438             final ListIterator<PrometheusTableInstance<?>> myIterator;
439             myIterator = pTables.listIterator();
440 
441             /* Loop through the Tables */
442             while (myIterator.hasNext()) {
443                 /* Access Table */
444                 final PrometheusTableInstance<?> myTable = myIterator.next();
445 
446                 /* If this is the referenced table */
447                 if (theReference.compareTo(myTable.getTableName()) == 0) {
448                     /* Store the reference and break the loop */
449                     theDefinition = myTable.getDefinition();
450                     break;
451                 }
452             }
453         }
454 
455         /**
456          * build Join String.
457          *
458          * @param pBuilder the String Builder
459          * @param pChar    the character for this table
460          * @param pOffset  the join offset
461          */
462         void buildJoinString(final StringBuilder pBuilder,
463                              final char pChar,
464                              final Integer pOffset) {
465             Integer myOffset = pOffset;
466 
467             /* Calculate join character */
468             final char myChar = (char) ('a' + myOffset);
469 
470             /* Build Initial part of string */
471             pBuilder.append(" join ");
472             getTable().addQuoteIfAllowed(pBuilder);
473             pBuilder.append(theReference);
474             getTable().addQuoteIfAllowed(pBuilder);
475             pBuilder.append(" ");
476             pBuilder.append(myChar);
477 
478             /* Build the join */
479             pBuilder.append(" on ");
480             pBuilder.append(pChar);
481             pBuilder.append(".");
482             getTable().addQuoteIfAllowed(pBuilder);
483             pBuilder.append(getColumnName());
484             getTable().addQuoteIfAllowed(pBuilder);
485             pBuilder.append(" = ");
486             pBuilder.append(myChar);
487             pBuilder.append(".");
488             getTable().addQuoteIfAllowed(pBuilder);
489             pBuilder.append(MetisDataResource.DATA_ID.getValue());
490             getTable().addQuoteIfAllowed(pBuilder);
491 
492             /* Increment offset */
493             myOffset++;
494 
495             /* Add the join string for the underlying table */
496             pBuilder.append(theDefinition.getJoinString(myChar, myOffset));
497         }
498 
499         /**
500          * build Order String.
501          *
502          * @param pBuilder the String Builder
503          * @param pOffset  the join offset
504          */
505         void buildOrderString(final StringBuilder pBuilder,
506                               final Integer pOffset) {
507             final Iterator<PrometheusColumnControl> myIterator;
508             PrometheusColumnDefinition myDef;
509             boolean myFirst = true;
510             Integer myOffset = pOffset;
511 
512             /* Calculate join character */
513             final char myChar = (char) ('a' + myOffset);
514 
515             /* Create the iterator */
516             myIterator = theDefinition.getSortList().iterator();
517 
518             /* Loop through the columns */
519             while (myIterator.hasNext()) {
520                 /* Access next column */
521                 myDef = (PrometheusColumnDefinition) myIterator.next();
522 
523                 /* Handle subsequent columns */
524                 if (!myFirst) {
525                     pBuilder.append(", ");
526                 }
527 
528                 /* If this is a reference column */
529                 if (myDef instanceof PrometheusReferenceColumn myColumn) {
530                     /* Increment offset */
531                     myOffset++;
532 
533                     /* Determine new char */
534                     final char myNewChar = (char) ('a' + myOffset);
535 
536                     /* Add the order string for the underlying table. */
537                     /* Note that forced to implement in one line to avoid Sonar false positive. */
538                     pBuilder.append(myColumn.theDefinition.getOrderString(myNewChar, myOffset));
539 
540                     /* else standard column */
541                 } else {
542                     /* Build the column name */
543                     pBuilder.append(myChar);
544                     pBuilder.append(".");
545                     getTable().addQuoteIfAllowed(pBuilder);
546                     pBuilder.append(myDef.getColumnName());
547                     getTable().addQuoteIfAllowed(pBuilder);
548                     if (myDef.getSortOrder() == PrometheusSortOrder.DESCENDING) {
549                         pBuilder.append(" DESC");
550                     }
551                 }
552 
553                 /* Note we have a column */
554                 myFirst = false;
555             }
556         }
557     }
558 
559     /**
560      * The shortColumn Class.
561      */
562     public static final class PrometheusShortColumn
563             extends PrometheusColumnDefinition {
564         /**
565          * Constructor.
566          *
567          * @param pTable the table to which the column belongs
568          * @param pId    the column id
569          */
570         PrometheusShortColumn(final PrometheusTableControl pTable,
571                               final MetisDataFieldId pId) {
572             /* Record the column type */
573             super(pTable, pId);
574         }
575 
576         @Override
577         protected void buildColumnType(final StringBuilder pBuilder) {
578             /* Add the column type */
579             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.SHORT));
580         }
581 
582         /**
583          * Set the value.
584          *
585          * @param pValue the value
586          */
587         void setValue(final Short pValue) {
588             super.setObject(pValue);
589         }
590 
591         /**
592          * Get the value.
593          *
594          * @return the value
595          */
596         Short getValue() {
597             return (Short) super.getObject();
598         }
599 
600         @Override
601         protected void loadValue(final ResultSet pResults,
602                                  final int pIndex) throws SQLException {
603             final short myValue = pResults.getShort(pIndex);
604             if (pResults.wasNull()) {
605                 setValue(null);
606             } else {
607                 setValue(myValue);
608             }
609         }
610 
611         @Override
612         protected void storeValue(final PreparedStatement pStatement,
613                                   final int pIndex) throws SQLException {
614             final Short myValue = getValue();
615             if (myValue == null) {
616                 pStatement.setNull(pIndex, Types.SMALLINT);
617             } else {
618                 pStatement.setShort(pIndex, myValue);
619             }
620         }
621     }
622 
623     /**
624      * The longColumn Class.
625      */
626     public static final class PrometheusLongColumn
627             extends PrometheusColumnDefinition {
628         /**
629          * Constructor.
630          *
631          * @param pTable the table to which the column belongs
632          * @param pId    the column id
633          */
634         PrometheusLongColumn(final PrometheusTableControl pTable,
635                              final MetisDataFieldId pId) {
636             /* Record the column type */
637             super(pTable, pId);
638         }
639 
640         @Override
641         protected void buildColumnType(final StringBuilder pBuilder) {
642             /* Add the column type */
643             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.LONG));
644         }
645 
646         /**
647          * Set the value.
648          *
649          * @param pValue the value
650          */
651         void setValue(final Long pValue) {
652             super.setObject(pValue);
653         }
654 
655         /**
656          * Get the value.
657          *
658          * @return the value
659          */
660         Long getValue() {
661             return (Long) super.getObject();
662         }
663 
664         @Override
665         protected void loadValue(final ResultSet pResults,
666                                  final int pIndex) throws SQLException {
667             final long myValue = pResults.getLong(pIndex);
668             if (pResults.wasNull()) {
669                 setValue(null);
670             } else {
671                 setValue(myValue);
672             }
673         }
674 
675         @Override
676         protected void storeValue(final PreparedStatement pStatement,
677                                   final int pIndex) throws SQLException {
678             final Long myValue = getValue();
679             if (myValue == null) {
680                 pStatement.setNull(pIndex, Types.BIGINT);
681             } else {
682                 pStatement.setLong(pIndex, myValue);
683             }
684         }
685     }
686 
687     /**
688      * The dateColumn Class.
689      */
690     public static final class PrometheusDateColumn
691             extends PrometheusColumnDefinition {
692         /**
693          * Constructor.
694          *
695          * @param pTable the table to which the column belongs
696          * @param pId    the column id
697          */
698         PrometheusDateColumn(final PrometheusTableControl pTable,
699                              final MetisDataFieldId pId) {
700             /* Record the column type */
701             super(pTable, pId);
702         }
703 
704         @Override
705         protected void buildColumnType(final StringBuilder pBuilder) {
706             /* Add the column type */
707             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DATE));
708         }
709 
710         /**
711          * Set the value.
712          *
713          * @param pValue the value
714          */
715         void setValue(final OceanusDate pValue) {
716             super.setObject(pValue);
717         }
718 
719         /**
720          * Get the value.
721          *
722          * @return the value
723          */
724         OceanusDate getValue() {
725             return (OceanusDate) super.getObject();
726         }
727 
728         @Override
729         protected void loadValue(final ResultSet pResults,
730                                  final int pIndex) throws SQLException {
731             final Date myValue = pResults.getDate(pIndex);
732             setValue(myValue == null
733                     ? null
734                     : new OceanusDate(myValue.toLocalDate()));
735         }
736 
737         @Override
738         protected void storeValue(final PreparedStatement pStatement,
739                                   final int pIndex) throws SQLException {
740             final OceanusDate myValue = getValue();
741 
742             /* Build the date as a SQL date */
743             if (myValue == null) {
744                 pStatement.setNull(pIndex, Types.DATE);
745             } else {
746                 final LocalDate myDateValue = myValue.getDate();
747                 pStatement.setDate(pIndex, Date.valueOf(myDateValue));
748             }
749         }
750     }
751 
752     /**
753      * The booleanColumn Class.
754      */
755     public static final class PrometheusBooleanColumn
756             extends PrometheusColumnDefinition {
757         /**
758          * Constructor.
759          *
760          * @param pTable the table to which the column belongs
761          * @param pId    the column id
762          */
763         PrometheusBooleanColumn(final PrometheusTableControl pTable,
764                                 final MetisDataFieldId pId) {
765             /* Record the column type */
766             super(pTable, pId);
767         }
768 
769         @Override
770         protected void buildColumnType(final StringBuilder pBuilder) {
771             /* Add the column type */
772             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.BOOLEAN));
773         }
774 
775         /**
776          * Set the value.
777          *
778          * @param pValue the value
779          */
780         void setValue(final Boolean pValue) {
781             super.setObject(pValue);
782         }
783 
784         /**
785          * Get the value.
786          *
787          * @return the value
788          */
789         Boolean getValue() {
790             return (Boolean) super.getObject();
791         }
792 
793         @Override
794         protected void loadValue(final ResultSet pResults,
795                                  final int pIndex) throws SQLException {
796             final boolean myValue = pResults.getBoolean(pIndex);
797             if (pResults.wasNull()) {
798                 setValue(null);
799             } else {
800                 setValue(myValue);
801             }
802         }
803 
804         @Override
805         protected void storeValue(final PreparedStatement pStatement,
806                                   final int pIndex) throws SQLException {
807             final Boolean myValue = getValue();
808             if (myValue == null) {
809                 pStatement.setNull(pIndex, Types.BIT);
810             } else {
811                 pStatement.setBoolean(pIndex, myValue);
812             }
813         }
814     }
815 
816     /**
817      * The stringColumn Class.
818      */
819     public static class PrometheusStringColumn
820             extends PrometheusColumnDefinition {
821         /**
822          * The length of the column.
823          */
824         private final int theLength;
825 
826         /**
827          * Constructor.
828          *
829          * @param pTable  the table to which the column belongs
830          * @param pId     the column id
831          * @param pLength the length
832          */
833         protected PrometheusStringColumn(final PrometheusTableControl pTable,
834                                          final MetisDataFieldId pId,
835                                          final int pLength) {
836             /* Record the column type */
837             super(pTable, pId);
838             theLength = pLength;
839         }
840 
841         @Override
842         protected void buildColumnType(final StringBuilder pBuilder) {
843             /* Add the column type */
844             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.STRING));
845             pBuilder.append(STR_OPNBRK);
846             pBuilder.append(theLength);
847             pBuilder.append(STR_CLSBRK);
848         }
849 
850         /**
851          * Set the value.
852          *
853          * @param pValue the value
854          */
855         protected void setValue(final String pValue) {
856             super.setObject(pValue);
857         }
858 
859         /**
860          * Get the value.
861          *
862          * @return the value
863          */
864         protected String getValue() {
865             return (String) super.getObject();
866         }
867 
868         @Override
869         protected void loadValue(final ResultSet pResults,
870                                  final int pIndex) throws SQLException {
871             final String myValue = pResults.getString(pIndex);
872             setValue(myValue);
873         }
874 
875         @Override
876         protected void storeValue(final PreparedStatement pStatement,
877                                   final int pIndex) throws SQLException {
878             final String myValue = getValue();
879             if (myValue == null) {
880                 pStatement.setNull(pIndex, Types.VARCHAR);
881             } else {
882                 pStatement.setString(pIndex, myValue);
883             }
884         }
885     }
886 
887     /**
888      * The moneyColumn Class.
889      */
890     public static final class PrometheusMoneyColumn
891             extends PrometheusStringColumn {
892         /**
893          * Constructor.
894          *
895          * @param pTable the table to which the column belongs
896          * @param pId    the column id
897          */
898         PrometheusMoneyColumn(final PrometheusTableControl pTable,
899                               final MetisDataFieldId pId) {
900             /* Record the column type */
901             super(pTable, pId, 0);
902         }
903 
904         @Override
905         protected void buildColumnType(final StringBuilder pBuilder) {
906             /* Add the column type */
907             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.MONEY));
908         }
909 
910         /**
911          * Set the value.
912          *
913          * @param pValue the value
914          */
915         void setValue(final OceanusMoney pValue) {
916             String myString = null;
917             if (pValue != null) {
918                 myString = pValue.toString();
919             }
920             super.setObject(myString);
921         }
922 
923         @Override
924         protected void storeValue(final PreparedStatement pStatement,
925                                   final int pIndex) throws SQLException {
926             final String myValue = getValue();
927             if (myValue != null) {
928                 final BigDecimal myDecimal = new BigDecimal(myValue);
929                 pStatement.setBigDecimal(pIndex, myDecimal);
930             } else {
931                 pStatement.setNull(pIndex, Types.DECIMAL);
932             }
933         }
934 
935         /**
936          * Obtain the value.
937          *
938          * @param pFormatter the data formatter
939          * @return the money value
940          * @throws OceanusException on error
941          */
942         public OceanusMoney getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
943             try {
944                 return pFormatter.parseValue(getValue(), OceanusMoney.class);
945             } catch (IllegalArgumentException e) {
946                 throw new PrometheusDataException(getValue(), "Bad Money Value", e);
947             }
948         }
949     }
950 
951     /**
952      * The rateColumn Class.
953      */
954     public static final class PrometheusRateColumn
955             extends PrometheusStringColumn {
956         /**
957          * Constructor.
958          *
959          * @param pTable the table to which the column belongs
960          * @param pId    the column id
961          */
962         PrometheusRateColumn(final PrometheusTableControl pTable,
963                              final MetisDataFieldId pId) {
964             /* Record the column type */
965             super(pTable, pId, 0);
966         }
967 
968         @Override
969         protected void buildColumnType(final StringBuilder pBuilder) {
970             /* Add the column type */
971             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DECIMAL));
972             pBuilder.append(STR_OPNBRK);
973             pBuilder.append(STR_NUMLEN);
974             pBuilder.append(STR_COMMA);
975             pBuilder.append(STR_STDDECLEN);
976             pBuilder.append(STR_CLSBRK);
977         }
978 
979         /**
980          * Set the value.
981          *
982          * @param pValue the value
983          */
984         void setValue(final OceanusRate pValue) {
985             String myString = null;
986             if (pValue != null) {
987                 myString = pValue.toString();
988             }
989             super.setObject(myString);
990         }
991 
992         @Override
993         protected void storeValue(final PreparedStatement pStatement,
994                                   final int pIndex) throws SQLException {
995             final String myValue = getValue();
996             if (myValue != null) {
997                 final BigDecimal myDecimal = new BigDecimal(myValue);
998                 pStatement.setBigDecimal(pIndex, myDecimal);
999             } else {
1000                 pStatement.setNull(pIndex, Types.DECIMAL);
1001             }
1002         }
1003 
1004         /**
1005          * Obtain the value.
1006          *
1007          * @param pFormatter the data formatter
1008          * @return the money value
1009          * @throws OceanusException on error
1010          */
1011         public OceanusRate getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
1012             try {
1013                 return pFormatter.parseValue(getValue(), OceanusRate.class);
1014             } catch (IllegalArgumentException e) {
1015                 throw new PrometheusDataException(getValue(), "Bad Rate Value", e);
1016             }
1017         }
1018     }
1019 
1020     /**
1021      * The priceColumn Class.
1022      */
1023     public static final class PrometheusPriceColumn
1024             extends PrometheusStringColumn {
1025         /**
1026          * Constructor.
1027          *
1028          * @param pTable the table to which the column belongs
1029          * @param pId    the column id
1030          */
1031         PrometheusPriceColumn(final PrometheusTableControl pTable,
1032                               final MetisDataFieldId pId) {
1033             /* Record the column type */
1034             super(pTable, pId, 0);
1035         }
1036 
1037         @Override
1038         protected void buildColumnType(final StringBuilder pBuilder) {
1039             /* Add the column type */
1040             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DECIMAL));
1041             pBuilder.append(STR_OPNBRK);
1042             pBuilder.append(STR_NUMLEN);
1043             pBuilder.append(STR_COMMA);
1044             pBuilder.append(STR_STDDECLEN);
1045             pBuilder.append(STR_CLSBRK);
1046         }
1047 
1048         /**
1049          * Set the value.
1050          *
1051          * @param pValue the value
1052          */
1053         void setValue(final OceanusPrice pValue) {
1054             String myString = null;
1055             if (pValue != null) {
1056                 myString = pValue.toString();
1057             }
1058             super.setObject(myString);
1059         }
1060 
1061         @Override
1062         protected void storeValue(final PreparedStatement pStatement,
1063                                   final int pIndex) throws SQLException {
1064             final String myValue = getValue();
1065             if (myValue != null) {
1066                 final BigDecimal myDecimal = new BigDecimal(myValue);
1067                 pStatement.setBigDecimal(pIndex, myDecimal);
1068             } else {
1069                 pStatement.setNull(pIndex, Types.DECIMAL);
1070             }
1071         }
1072 
1073         /**
1074          * Obtain the value.
1075          *
1076          * @param pFormatter the data formatter
1077          * @return the money value
1078          * @throws OceanusException on error
1079          */
1080         public OceanusPrice getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
1081             try {
1082                 return pFormatter.parseValue(getValue(), OceanusPrice.class);
1083             } catch (IllegalArgumentException e) {
1084                 throw new PrometheusDataException(getValue(), "Bad Price Value", e);
1085             }
1086         }
1087     }
1088 
1089     /**
1090      * The unitsColumn Class.
1091      */
1092     public static final class PrometheusUnitsColumn
1093             extends PrometheusStringColumn {
1094         /**
1095          * Constructor.
1096          *
1097          * @param pTable the table to which the column belongs
1098          * @param pId    the column id
1099          */
1100         PrometheusUnitsColumn(final PrometheusTableControl pTable,
1101                               final MetisDataFieldId pId) {
1102             /* Record the column type */
1103             super(pTable, pId, 0);
1104         }
1105 
1106         @Override
1107         protected void buildColumnType(final StringBuilder pBuilder) {
1108             /* Add the column type */
1109             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DECIMAL));
1110             pBuilder.append(STR_OPNBRK);
1111             pBuilder.append(STR_NUMLEN);
1112             pBuilder.append(STR_COMMA);
1113             pBuilder.append(STR_STDDECLEN);
1114             pBuilder.append(STR_CLSBRK);
1115         }
1116 
1117         /**
1118          * Set the value.
1119          *
1120          * @param pValue the value
1121          */
1122         void setValue(final OceanusUnits pValue) {
1123             String myString = null;
1124             if (pValue != null) {
1125                 myString = pValue.toString();
1126             }
1127             super.setObject(myString);
1128         }
1129 
1130         @Override
1131         protected void storeValue(final PreparedStatement pStatement,
1132                                   final int pIndex) throws SQLException {
1133             final String myValue = getValue();
1134             if (myValue != null) {
1135                 final BigDecimal myDecimal = new BigDecimal(myValue);
1136                 pStatement.setBigDecimal(pIndex, myDecimal);
1137             } else {
1138                 pStatement.setNull(pIndex, Types.DECIMAL);
1139             }
1140         }
1141 
1142         /**
1143          * Obtain the value.
1144          *
1145          * @param pFormatter the data formatter
1146          * @return the money value
1147          * @throws OceanusException on error
1148          */
1149         public OceanusUnits getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
1150             try {
1151                 return pFormatter.parseValue(getValue(), OceanusUnits.class);
1152             } catch (IllegalArgumentException e) {
1153                 throw new PrometheusDataException(getValue(), "Bad Units Value", e);
1154             }
1155         }
1156     }
1157 
1158     /**
1159      * The ratioColumn Class.
1160      */
1161     public static final class PrometheusRatioColumn
1162             extends PrometheusStringColumn {
1163         /**
1164          * Constructor.
1165          *
1166          * @param pTable the table to which the column belongs
1167          * @param pId    the column id
1168          */
1169         PrometheusRatioColumn(final PrometheusTableControl pTable,
1170                               final MetisDataFieldId pId) {
1171             /* Record the column type */
1172             super(pTable, pId, 0);
1173         }
1174 
1175         @Override
1176         protected void buildColumnType(final StringBuilder pBuilder) {
1177             /* Add the column type */
1178             pBuilder.append(getDriver().getDatabaseType(PrometheusColumnType.DECIMAL));
1179             pBuilder.append(STR_OPNBRK);
1180             pBuilder.append(STR_NUMLEN);
1181             pBuilder.append(STR_COMMA);
1182             pBuilder.append(STR_XTRADECLEN);
1183             pBuilder.append(STR_CLSBRK);
1184         }
1185 
1186         /**
1187          * Set the value.
1188          *
1189          * @param pValue the value
1190          */
1191         void setValue(final OceanusRatio pValue) {
1192             String myString = null;
1193             if (pValue != null) {
1194                 myString = pValue.toString();
1195             }
1196             super.setObject(myString);
1197         }
1198 
1199         @Override
1200         protected void storeValue(final PreparedStatement pStatement,
1201                                   final int pIndex) throws SQLException {
1202             final String myValue = getValue();
1203             if (myValue != null) {
1204                 final BigDecimal myDecimal = new BigDecimal(myValue);
1205                 pStatement.setBigDecimal(pIndex, myDecimal);
1206             } else {
1207                 pStatement.setNull(pIndex, Types.DECIMAL);
1208             }
1209         }
1210 
1211         /**
1212          * Obtain the value.
1213          *
1214          * @param pFormatter the data formatter
1215          * @return the money value
1216          * @throws OceanusException on error
1217          */
1218         public OceanusRatio getValue(final OceanusDataFormatter pFormatter) throws OceanusException {
1219             try {
1220                 return pFormatter.parseValue(getValue(), OceanusRatio.class);
1221             } catch (IllegalArgumentException e) {
1222                 throw new PrometheusDataException(getValue(), "Bad Ratio Value", e);
1223             }
1224         }
1225     }
1226 
1227     /**
1228      * The binaryColumn Class.
1229      */
1230     public static final class PrometheusBinaryColumn
1231             extends PrometheusColumnDefinition {
1232         /**
1233          * The length of the column.
1234          */
1235         private final int theLength;
1236 
1237         /**
1238          * Constructor.
1239          *
1240          * @param pTable  the table to which the column belongs
1241          * @param pId     the column id
1242          * @param pLength the length of the column
1243          */
1244         PrometheusBinaryColumn(final PrometheusTableControl pTable,
1245                                final MetisDataFieldId pId,
1246                                final int pLength) {
1247             /* Record the column type */
1248             super(pTable, pId);
1249             theLength = pLength;
1250         }
1251 
1252         @Override
1253         protected void buildColumnType(final StringBuilder pBuilder) {
1254             /* Add the column type */
1255             final PrometheusJDBCDriver myDriver = getDriver();
1256             pBuilder.append(myDriver.getDatabaseType(PrometheusColumnType.BINARY));
1257             if (myDriver.defineBinaryLength()) {
1258                 pBuilder.append("(");
1259                 pBuilder.append(theLength);
1260                 pBuilder.append(')');
1261             }
1262         }
1263 
1264         /**
1265          * Set the value.
1266          *
1267          * @param pValue the value
1268          */
1269         void setValue(final byte[] pValue) {
1270             super.setObject(pValue);
1271         }
1272 
1273         /**
1274          * Get the value.
1275          *
1276          * @return the value
1277          */
1278         byte[] getValue() {
1279             return (byte[]) super.getObject();
1280         }
1281 
1282         @Override
1283         protected void loadValue(final ResultSet pResults,
1284                                  final int pIndex) throws SQLException {
1285             final byte[] myValue = pResults.getBytes(pIndex);
1286             setValue(myValue);
1287         }
1288 
1289         @Override
1290         protected void storeValue(final PreparedStatement pStatement,
1291                                   final int pIndex) throws SQLException {
1292             pStatement.setBytes(pIndex, getValue());
1293         }
1294     }
1295 
1296     /**
1297      * SortOrder.
1298      */
1299     public enum PrometheusSortOrder {
1300         /**
1301          * Ascending.
1302          */
1303         ASCENDING,
1304 
1305         /**
1306          * Descending.
1307          */
1308         DESCENDING;
1309     }
1310 }