]> review.fuel-infra Code Review - packages/trusty/mysql-wsrep-5.6.git/blob
caf8d07257e43558fd8fdbcf696506300059ab3a
[packages/trusty/mysql-wsrep-5.6.git] /
1 /*
2    Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
16 */
17
18 package com.mysql.clusterj.core.metadata;
19
20 import com.mysql.clusterj.core.spi.ValueHandler;
21 import com.mysql.clusterj.ClusterJDatastoreException;
22 import com.mysql.clusterj.ClusterJUserException;
23 import com.mysql.clusterj.ColumnType;
24
25 import com.mysql.clusterj.annotation.Column;
26 import com.mysql.clusterj.annotation.Lob;
27 import com.mysql.clusterj.annotation.NotPersistent;
28 import com.mysql.clusterj.annotation.NullValue;
29 import com.mysql.clusterj.annotation.Persistent;
30
31 import com.mysql.clusterj.core.store.Operation;
32 import com.mysql.clusterj.core.store.Table;
33
34 import java.lang.annotation.Annotation;
35
36 import java.lang.reflect.Method;
37
38 import java.math.BigDecimal;
39 import java.math.BigInteger;
40
41 /** An instance of this class handles a field (property)
42  * of a persistence-capable class (interface).
43  * Currently only properties (paired get and set methods) of interfaces
44  * are supported.
45  * Instances of the class bind at construction time to implementations of
46  * type-specific handlers for Ndb operations.
47  * 
48  */
49 public class DomainFieldHandlerImpl extends AbstractDomainFieldHandlerImpl {
50
51     /** The NullValue setting of the column from the Persistent annotation. */
52     NullValue nullValue = NullValue.NONE;
53
54     /** The get method. */
55     Method getMethod;
56
57     /** The set method. */
58     protected Method setMethod;
59
60     /** The null value handler. */
61     protected NullObjectOperationHandler nullValueDelegate;
62
63     /** The Index annotation on the get method. */
64     protected com.mysql.clusterj.annotation.Index indexAnnotation = null;
65
66     /** The Persistent annotation on the get method. */
67     protected Persistent persistentAnnotation = null;
68
69     /** The Column annotation on the get method. */
70     protected Column columnAnnotation = null;
71
72     /** The AllowsNull annotation */
73     protected String columnAllowsNull;
74
75     /** Lob annotation is not null if annotated with @Lob. */
76     protected Lob lobAnnotation;
77
78     /** The NotPersistent annotation indicates that this field is not
79      * persistent, but can be used as a property that holds data not
80      * stored in the datastore.
81      */
82     protected NotPersistent notPersistentAnnotation;
83
84     public int compareTo(Object other) {
85         return compareTo((DomainFieldHandlerImpl)other);
86     }
87
88     /** Create a domain field handler for annotated interfaces.
89      * 
90      * @param domainTypeHandler the domain type handler
91      * @param table the table
92      * @param fieldNumber the field number (in schema definition order)
93      * @param name the field name
94      * @param type the java type
95      * @param getMethod the get method for the field
96      * @param setMethod the set method for the field
97      */
98     public DomainFieldHandlerImpl(DomainTypeHandlerImpl<?> domainTypeHandler, Table table,
99             int fieldNumber, String name, Class<?> type,
100             Method getMethod, Method setMethod) {
101         if (logger.isDebugEnabled()) logger.debug("new DomainFieldHandlerImpl: fieldNumber: " + fieldNumber + "; name:" + name + "; getMethod: " + getMethod + "; setMethod: " + setMethod);
102         this.domainTypeHandler = domainTypeHandler;
103         this.fieldNumber = fieldNumber;
104         this.name = name;
105         this.type = type;
106         this.setMethod = setMethod;
107         this.getMethod = getMethod;
108         
109         Annotation[] annotations = setMethod.getAnnotations();
110         if (annotations != null && annotations.length != 0) {
111             for (Annotation a: annotations) {
112                 error(local.message("ERR_Annotate_Set_Method",
113                         name, a.annotationType().getName()));
114             }
115         }
116         notPersistentAnnotation = getMethod.getAnnotation(NotPersistent.class);
117         if (isPersistent()) {
118             // process column annotation first and check the class annotation
119             // for primary key
120             // Initialize default column name; may be overridden with annotation
121             this.columnName = name.toLowerCase();
122             this.columnNames = new String[]{name};
123             columnAnnotation = getMethod.getAnnotation(Column.class);
124             if (columnAnnotation != null) {
125                 if (columnAnnotation.name() != null) {
126                     columnName = columnAnnotation.name();
127                     this.columnNames = new String[]{columnName};
128                 }
129                 if (logger.isDebugEnabled())
130                     logger.debug("Column name annotation for " + name + " is "
131                             + columnName);
132                 columnAllowsNull = columnAnnotation.allowsNull();
133                 if (logger.isDebugEnabled())
134                     logger.debug("Column allowsNull annotation for " + name
135                             + " is " + columnAllowsNull);
136                 columnDefaultValue = columnAnnotation.defaultValue();
137                 // if user has not specified column defaultValue, set it to null,
138                 // which makes it easier for later processing
139                 if (columnDefaultValue.equals("")) {
140                     columnDefaultValue = null;
141                 }
142                 if (logger.isDebugEnabled())
143                     logger.debug("Column defaultValue annotation for " + name
144                             + " is " + columnDefaultValue);
145             }
146             storeColumn = table.getColumn(columnName);
147             if (storeColumn == null) {
148                 throw new ClusterJUserException(local.message("ERR_No_Column",
149                         name, table.getName(), columnName));
150             }
151             initializeColumnMetadata(storeColumn);
152             if (logger.isDebugEnabled())
153                 logger.debug("Column type for " + name + " is "
154                         + storeColumnType.toString() + "; charset name is "
155                         + charsetName);
156             domainTypeHandler.registerPrimaryKeyColumn(this, columnName);
157             lobAnnotation = getMethod.getAnnotation(Lob.class);
158         }
159         if (primaryKey) {
160             if (type.equals(int.class)) {
161                 objectOperationHandlerDelegate = objectOperationHandlerKeyInt;
162             } else if (type.equals(long.class)) {
163                 objectOperationHandlerDelegate = objectOperationHandlerKeyLong;
164             } else if (type.equals(String.class)) {
165                 objectOperationHandlerDelegate = objectOperationHandlerKeyString;
166             } else if (type.equals(byte[].class)) {
167                 objectOperationHandlerDelegate = objectOperationHandlerKeyBytes;
168             } else {
169                 objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
170                 error(
171                         local.message("ERR_Primary_Field_Type", domainTypeHandler.getName(), name, printableName(type)));
172             }
173         } else if (lobAnnotation != null) {
174             // large object support for byte[]
175             if (type.equals(byte[].class)) {
176                 objectOperationHandlerDelegate = objectOperationHandlerBytesLob;
177             } else if (type.equals(String.class)) {
178                 objectOperationHandlerDelegate = objectOperationHandlerStringLob;
179             } else {
180                 objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
181                 error(
182                     local.message("ERR_Unsupported_Field_Type", printableName(type), name));
183             }
184         } else if (!isPersistent()) {
185             // NotPersistent field
186             if (type.equals(byte.class)) {
187                 objectOperationHandlerDelegate = objectOperationHandlerNotPersistentByte;
188             } else if (type.equals(double.class)) {
189                 objectOperationHandlerDelegate = objectOperationHandlerNotPersistentDouble;
190             } else if (type.equals(float.class)) {
191                 objectOperationHandlerDelegate = objectOperationHandlerNotPersistentFloat;
192             } else if (type.equals(int.class)) {
193                 objectOperationHandlerDelegate = objectOperationHandlerNotPersistentInt;
194             } else if (type.equals(long.class)) {
195                 objectOperationHandlerDelegate = objectOperationHandlerNotPersistentLong;
196             } else if (type.equals(short.class)) {
197                 objectOperationHandlerDelegate = objectOperationHandlerNotPersistentShort;
198             } else {
199                 objectOperationHandlerDelegate = objectOperationHandlerNotPersistentObject;
200             }
201         } else {
202             // not a pk field; use xxxValue to set values
203             if (type.equals(byte[].class)) {
204                 objectOperationHandlerDelegate = objectOperationHandlerBytes;
205             } else if (type.equals(java.util.Date.class)) {
206                 objectOperationHandlerDelegate = objectOperationHandlerJavaUtilDate;
207             } else if (type.equals(BigDecimal.class)) {
208                 objectOperationHandlerDelegate = objectOperationHandlerDecimal;
209             } else if (type.equals(BigInteger.class)) {
210                 objectOperationHandlerDelegate = objectOperationHandlerBigInteger;
211             } else if (type.equals(double.class)) {
212                 objectOperationHandlerDelegate = objectOperationHandlerDouble;
213             } else if (type.equals(float.class)) {
214                 objectOperationHandlerDelegate = objectOperationHandlerFloat;
215             } else if (type.equals(int.class)) {
216                 objectOperationHandlerDelegate = objectOperationHandlerInt;
217             } else if (type.equals(Integer.class)) {
218                 objectOperationHandlerDelegate = objectOperationHandlerObjectInteger;
219             } else if (type.equals(Long.class)) {
220                 objectOperationHandlerDelegate = objectOperationHandlerObjectLong;
221             } else if (type.equals(Short.class)) {
222                 if (ColumnType.Year.equals(storeColumnType)) {
223                     objectOperationHandlerDelegate = objectOperationHandlerObjectShortYear;
224                 } else {
225                     objectOperationHandlerDelegate = objectOperationHandlerObjectShort;
226                 }
227             } else if (type.equals(Float.class)) {
228                 objectOperationHandlerDelegate = objectOperationHandlerObjectFloat;
229             } else if (type.equals(Double.class)) {
230                 objectOperationHandlerDelegate = objectOperationHandlerObjectDouble;
231             } else if (type.equals(long.class)) {
232                 objectOperationHandlerDelegate = objectOperationHandlerLong;
233             } else if (type.equals(short.class)) {
234                 if (ColumnType.Year.equals(storeColumnType)) {
235                     objectOperationHandlerDelegate = objectOperationHandlerShortYear;
236                 } else {
237                     objectOperationHandlerDelegate = objectOperationHandlerShort;
238                 }
239             } else if (type.equals(String.class)) {
240                 objectOperationHandlerDelegate = objectOperationHandlerString;
241             } else if (type.equals(Byte.class)) {
242                 objectOperationHandlerDelegate = objectOperationHandlerObjectByte;
243             } else if (type.equals(byte.class)) {
244                 objectOperationHandlerDelegate = objectOperationHandlerByte;
245             } else if (type.equals(boolean.class)) {
246                 objectOperationHandlerDelegate = objectOperationHandlerBoolean;
247             } else if (type.equals(Boolean.class)) {
248                 objectOperationHandlerDelegate = objectOperationHandlerObjectBoolean;
249             } else if (type.equals(java.sql.Date.class)) {
250                 objectOperationHandlerDelegate = objectOperationHandlerJavaSqlDate;
251             } else if (type.equals(java.sql.Time.class)) {
252                 objectOperationHandlerDelegate = objectOperationHandlerJavaSqlTime;
253             } else if (type.equals(java.sql.Timestamp.class)) {
254                 objectOperationHandlerDelegate = objectOperationHandlerJavaSqlTimestamp;
255             } else {
256                 objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
257                 error(
258                     local.message("ERR_Unsupported_Field_Type", type.getName()));
259             }
260         }
261         // Handle indexes. One index can be annotated on this field.
262         // Other indexes including the column mapped to this field
263         // are annotated on the class.
264         // TODO: indexes are ignored since they are handled by reading the column metadata
265         indexAnnotation = getMethod.getAnnotation(
266                 com.mysql.clusterj.annotation.Index.class);
267         String indexName = null;
268         if (indexAnnotation != null) {
269             indexName = indexAnnotation.name();
270             if (indexAnnotation.columns().length != 0) {
271                 throw new ClusterJUserException(
272                         local.message("ERR_Index_Annotation_Columns", domainTypeHandler.getName(), name));
273             }
274         }
275         registerIndices(domainTypeHandler);
276
277         persistentAnnotation = getMethod.getAnnotation(Persistent.class);
278         if (persistentAnnotation != null) {
279             nullValue = persistentAnnotation.nullValue();
280             logger.debug("Persistent nullValue annotation for " + name + " is " + nullValue);
281         }
282         // convert the string default value to type-specific value
283         defaultValue = objectOperationHandlerDelegate.getDefaultValueFor(this, columnDefaultValue);
284         logger.debug("Default null value for " + name + " is " + defaultValue);
285
286         // set up the null value handler based on the annotation
287         switch (nullValue) {
288             case DEFAULT:
289                 // value is null and user has specified a default value
290                 nullValueDelegate = nullValueDEFAULT;
291                 break;
292             case EXCEPTION:
293                 // value is null and user wants a ClusterJ exception
294                 nullValueDelegate = nullValueEXCEPTION;
295                 break;
296             case NONE:
297                 // value is null and no special handling
298             nullValueDelegate = nullValueNONE;
299                 break;
300         }
301         reportErrors();
302     }
303
304     /** Create a domain field handler for dynamic objects.
305      * 
306      * @param domainTypeHandler the domain type handler
307      * @param table the table
308      * @param i the field number
309      * @param storeColumn the store column definition
310      */
311     public DomainFieldHandlerImpl(
312             DomainTypeHandlerImpl<?> domainTypeHandler, Table table, int i,
313             com.mysql.clusterj.core.store.Column storeColumn) {
314         if (logger.isDebugEnabled()) logger.debug("new dynamic DomainFieldHandlerImpl: " +
315                 "fieldNumber: " + fieldNumber + "; name:" + name);
316         this.domainTypeHandler = domainTypeHandler;
317         this.fieldNumber = i;
318         this.storeColumn = storeColumn;
319         initializeColumnMetadata(storeColumn);
320         this.name = this.columnName;
321         this.columnNames = new String[]{columnName};
322         if (primaryKey) {
323             domainTypeHandler.registerPrimaryKeyColumn(this, columnName);
324             switch (this.storeColumnType) {
325                 case Int:
326                 case Unsigned:
327                     this.objectOperationHandlerDelegate = objectOperationHandlerKeyInt;
328                     this.type = Integer.class;
329                     break;
330                 case Char:
331                 case Varchar:
332                     this.objectOperationHandlerDelegate = objectOperationHandlerKeyString;
333                     this.type = String.class;
334                     break;
335                 case Bigint:
336                 case Bigunsigned:
337                     this.objectOperationHandlerDelegate = objectOperationHandlerKeyLong;
338                     this.type = Long.class;
339                     break;
340                 case Binary:
341                 case Varbinary:
342                 case Longvarbinary:
343                     this.objectOperationHandlerDelegate = objectOperationHandlerKeyBytes;
344                     this.type = byte[].class;
345                     break;
346                 default:
347                     error(local.message("ERR_Primary_Column_Type", domainTypeHandler.getName(), name, this.storeColumnType));
348                 }
349         } else {
350             switch (this.storeColumnType) {
351                 case Bigint:
352                 case Bigunsigned:
353                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectLong;
354                     this.type = Long.class;
355                     break;
356                 case Binary:
357                     this.objectOperationHandlerDelegate = objectOperationHandlerBytes;
358                     this.type = byte[].class;
359                     break;
360                 case Bit:
361                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectLong;
362                     this.type = Long.class;
363                     break;
364                 case Blob:
365                     this.objectOperationHandlerDelegate = objectOperationHandlerBytesLob;
366                     this.type = byte[].class;
367                     break;
368                 case Char:
369                     this.objectOperationHandlerDelegate = objectOperationHandlerString;
370                     this.type = String.class;
371                     break;
372                 case Date:
373                     this.objectOperationHandlerDelegate = objectOperationHandlerJavaSqlDate;
374                     this.type = java.sql.Date.class;
375                     break;
376                 case Datetime:
377                     this.objectOperationHandlerDelegate = objectOperationHandlerJavaSqlTimestamp;
378                     this.type = java.sql.Timestamp.class;
379                     break;
380                 case Decimal:
381                 case Decimalunsigned:
382                     this.objectOperationHandlerDelegate = objectOperationHandlerDecimal;
383                     this.type = BigDecimal.class;
384                     break;
385                 case Double:
386                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectDouble;
387                     this.type = Double.class;
388                     break;
389                 case Float:
390                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectFloat;
391                     this.type = Float.class;
392                     break;
393                 case Int:
394                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectInteger;
395                     this.type = Integer.class;
396                     break;
397                 case Longvarbinary:
398                     this.objectOperationHandlerDelegate = objectOperationHandlerBytes;
399                     this.type = byte[].class;
400                     break;
401                 case Longvarchar:
402                     this.objectOperationHandlerDelegate = objectOperationHandlerString;
403                     this.type = String.class;
404                     break;
405                 case Mediumint:
406                 case Mediumunsigned:
407                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectInteger;
408                     this.type = Integer.class;
409                     break;
410                 case Olddecimal:
411                     error(local.message("ERR_Unsupported_Field_Type", "Olddecimal", name));
412                     objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
413                     break;
414                 case Olddecimalunsigned:
415                     error(local.message("ERR_Unsupported_Field_Type", "Olddecimalunsigned", name));
416                     objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
417                     break;
418                 case Smallint:
419                 case Smallunsigned:
420                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectShort;
421                     this.type = Short.class;
422                     break;
423                 case Text:
424                     this.objectOperationHandlerDelegate = objectOperationHandlerStringLob;
425                     this.type = String.class;
426                     break;
427                 case Time:
428                     this.objectOperationHandlerDelegate = objectOperationHandlerJavaSqlTime;
429                     this.type = java.sql.Time.class;
430                     break;
431                 case Timestamp:
432                     this.objectOperationHandlerDelegate = objectOperationHandlerJavaSqlTimestamp;
433                     this.type = java.sql.Timestamp.class;
434                     break;
435                 case Tinyint:
436                 case Tinyunsigned:
437                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectByte;
438                     this.type = Byte.class;
439                     break;
440                 case Undefined:
441                     error(local.message("ERR_Unsupported_Field_Type", "Undefined"));
442                     objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
443                     break;
444                 case Unsigned:
445                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectInteger;
446                     this.type = Integer.class;
447                     break;
448                 case Varbinary:
449                     this.objectOperationHandlerDelegate = objectOperationHandlerBytes;
450                     this.type = byte[].class;
451                     break;
452                 case Varchar:
453                     this.objectOperationHandlerDelegate = objectOperationHandlerString;
454                     this.type = String.class;
455                     break;
456                 case Year:
457                     this.objectOperationHandlerDelegate = objectOperationHandlerObjectShortYear;
458                     this.type = Short.class;
459                     break;
460                 default:
461                     error(local.message("ERR_Unsupported_Field_Type", this.storeColumnType));
462                     objectOperationHandlerDelegate = objectOperationHandlerUnsupportedType;
463             }
464         }
465         nullValueDelegate = nullValueNONE;
466         registerIndices(domainTypeHandler);
467         reportErrors();
468     }
469
470     public boolean isPersistent() {
471         return notPersistentAnnotation == null;
472     }
473
474     protected void registerIndices(DomainTypeHandlerImpl<?> domainTypeHandler) {
475         this.indices = domainTypeHandler.registerIndices(this, columnName);
476         this.indexNames = domainTypeHandler.getIndexNames(indices);
477         if (logger.isDebugEnabled()) logger.debug("Index names for " + name + " are " + indexNames);
478         if (logger.isDebugEnabled()) logger.debug("Indices for " + name + " are " + printIndices());
479     }
480
481     @Override
482     public void operationSetValue(ValueHandler handler, Operation op) {
483         // handle NullValue here
484         boolean isNull = handler.isNull(fieldNumber);
485         if (logger.isDetailEnabled()) logger.detail("Column: " + columnName + " field: " + name + " isNull: " + isNull + " type: " + type + " delegate " + objectOperationHandlerDelegate.handler());
486         try {
487             if (isNull) {
488                 // value is null; let delegate see what to do
489                 if (nullValueDelegate.operationSetValue(this, op)) {
490                     return;
491                 }
492             }
493             objectOperationHandlerDelegate.operationSetValue(this, handler, op);
494         } catch (ClusterJDatastoreException ex) {
495             throw new ClusterJDatastoreException(local.message("ERR_Value_Delegate", name, columnName, objectOperationHandlerDelegate.handler(), "setValue"), ex);
496         }
497     }
498
499     protected interface NullObjectOperationHandler {
500     /** Handle null values on operationSetValue. This method is called if the
501      * value to be set in the handler is null. The execution depends on
502      * the null value handling defined for the field.
503      *
504      * @param fmd the FieldHandler for the field
505      * @param op the NDB Operation
506      * @return true if the operationSetValue has been handled
507      * @throws com.mysql.cluster.ndbj.NdbApiException
508      */
509         boolean operationSetValue(DomainFieldHandlerImpl fmd, Operation op);
510     }
511
512     static NullObjectOperationHandler nullValueDEFAULT = new NullObjectOperationHandler() {
513         public boolean operationSetValue(DomainFieldHandlerImpl fmd, Operation op) {
514             // set the default value and then return
515             fmd.operationSetValue(fmd, fmd.defaultValue, op);
516             return true;
517         };
518     };
519
520     static NullObjectOperationHandler nullValueEXCEPTION = new NullObjectOperationHandler() {
521         public boolean operationSetValue(DomainFieldHandlerImpl fmd, Operation op) {
522             // always throw an exception
523             throw new ClusterJUserException(
524                     local.message("ERR_Null_Value_Exception",
525                     fmd.domainTypeHandler.getName(), fmd.name));
526         };
527     };
528
529     static NullObjectOperationHandler nullValueNONE = new NullObjectOperationHandler() {
530         public boolean operationSetValue(DomainFieldHandlerImpl fmd, Operation op) {
531             // don't do anything here but do the standard processing
532             return false;
533         };
534     };
535
536 }