2 Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
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.
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.
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
18 package com.mysql.clusterj.core.metadata;
20 import java.util.ArrayList;
21 import java.util.BitSet;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
28 import com.mysql.clusterj.ClusterJException;
29 import com.mysql.clusterj.ClusterJFatalInternalException;
30 import com.mysql.clusterj.ClusterJUserException;
31 import com.mysql.clusterj.core.CacheManager;
32 import com.mysql.clusterj.core.query.CandidateIndexImpl;
33 import com.mysql.clusterj.core.spi.DomainFieldHandler;
34 import com.mysql.clusterj.core.spi.DomainTypeHandler;
35 import com.mysql.clusterj.core.spi.ValueHandler;
36 import com.mysql.clusterj.core.store.Column;
37 import com.mysql.clusterj.core.store.Dictionary;
38 import com.mysql.clusterj.core.store.IndexOperation;
39 import com.mysql.clusterj.core.store.Operation;
40 import com.mysql.clusterj.core.store.PartitionKey;
41 import com.mysql.clusterj.core.store.ResultData;
42 import com.mysql.clusterj.core.store.Table;
43 import com.mysql.clusterj.core.util.I18NHelper;
44 import com.mysql.clusterj.core.util.Logger;
45 import com.mysql.clusterj.core.util.LoggerFactoryService;
47 /** Abstract class implementing DomainTypeHandler. This class implements common
48 * behavior to manage persistent representations of tables, including field
49 * handlers for persistent field values. Subclasses will implement behavior
50 * specific to the actual representations of persistence.
52 public abstract class AbstractDomainTypeHandlerImpl<T> implements DomainTypeHandler<T> {
54 /** My message translator */
55 protected static final I18NHelper local = I18NHelper.getInstance(AbstractDomainTypeHandlerImpl.class);
58 protected static final Logger logger = LoggerFactoryService.getFactory().getInstance(AbstractDomainTypeHandlerImpl.class);
60 /** The name of the class. */
61 protected String name;
63 /** The table for the class. */
64 protected String tableName;
66 /** The NDB table for the class. */
67 protected Table table;
69 /** The number of id fields for the class. */
70 protected int numberOfIdFields;
72 /** The field numbers of the id fields. */
73 protected int[] idFieldNumbers;
75 /** The id field(s) for the class, mapped to primary key columns */
76 protected DomainFieldHandler[] idFieldHandlers;
78 /** The PrimaryKey column names. */
79 protected String[] primaryKeyColumnNames;
81 /** The number of partition key columns */
82 protected int numberOfPartitionKeyColumns = 0;
84 /** The partition key fields */
85 protected DomainFieldHandler[] partitionKeyFieldHandlers;
87 /** The names of the partition key columns */
88 protected String[] partitionKeyColumnNames;
90 /** The number of fields. Dynamically created as fields are added. */
91 protected int numberOfFields = 0;
93 /** Persistent fields. */
94 protected List<DomainFieldHandler> persistentFieldHandlers = new ArrayList<DomainFieldHandler>();
97 protected List<DomainFieldHandler> nonPKFieldHandlers = new ArrayList<DomainFieldHandler>();
99 /** Primitive fields. */
100 protected List<DomainFieldHandler> primitiveFieldHandlers = new ArrayList<DomainFieldHandler>();
102 /** Map of field names to field numbers. */
103 protected Map<String, Integer> fieldNameToNumber = new HashMap<String, Integer>();
106 protected String[] fieldNames;
108 /** All index handlers defined for the mapped class. The position in this
109 * array is significant. Each DomainFieldHandlerImpl contains the index into
110 * this array and the index into the fields array within the
113 protected List<IndexHandlerImpl> indexHandlerImpls = new ArrayList<IndexHandlerImpl>();
115 /** Set of index names to check for duplicates. */
116 protected Set<String> indexNames = new HashSet<String>();
118 /** Register a primary key column field. This is used to associate
119 * primary key and partition key column names with field handlers.
120 * This method is called by the DomainFieldHandlerImpl constructor
121 * after the mapped column name is known. It is only called by fields
122 * that are mapped to primary key columns.
123 * @param fmd the field handler instance calling us
124 * @param columnName the name of the column
126 public void registerPrimaryKeyColumn(DomainFieldHandler fmd, String columnName) {
127 // find the primary key column that matches the primary key column
128 for (int i = 0; i < primaryKeyColumnNames.length; ++i) {
129 if (primaryKeyColumnNames[i].equals(columnName)) {
130 idFieldHandlers[i] = fmd;
131 idFieldNumbers[i] = fmd.getFieldNumber();
132 if (logger.isDetailEnabled()) logger.detail("registerPrimaryKeyColumn found primary key " + columnName);
135 // find the partition key column that matches the primary key column
136 for (int j = 0; j < partitionKeyColumnNames.length; ++j) {
137 if (partitionKeyColumnNames[j].equals(columnName)) {
138 partitionKeyFieldHandlers[j] = fmd;
139 if (logger.isDetailEnabled()) logger.detail("registerPrimaryKeyColumn found partition key " + columnName);
145 /** Create and register an index from a field and return a special int[][] that
146 * contains all indexHandlerImpls in which the
147 * field participates. The int[][] is used by the query optimizer to determine
148 * which if any index can be used. This method is called by the
149 * DomainFieldHandlerImpl constructor after the mapped column name is known.
150 * @see AbstractDomainFieldHandlerImpl#indices
151 * @param fmd the FieldHandler
152 * @param columnName the column name mapped to the field
153 * @return the array of array identifying the indexes into the IndexHandler
154 * list and columns in the IndexHandler corresponding to the field
156 public int[][] registerIndices(AbstractDomainFieldHandlerImpl fmd, String columnName) {
157 // Find all the indexes that this field belongs to, by iterating
158 // the list of indexes and comparing column names.
159 List<int[]> result =new ArrayList<int[]>();
160 for (int i = 0; i < indexHandlerImpls.size(); ++i) {
161 IndexHandlerImpl indexHandler = indexHandlerImpls.get(i);
162 String[] columns = indexHandler.getColumnNames();
163 for (int j = 0; j < columns.length; ++j) {
164 if (fmd.getColumnName().equals(columns[j])) {
165 if (logger.isDetailEnabled()) logger.detail("Found field " + fmd.getName()
166 + " column " + fmd.getColumnName() + " matching " + indexHandler.getIndexName());
167 indexHandler.setDomainFieldHandlerFor(j, fmd);
168 result.add(new int[]{i,j});
173 if (logger.isDebugEnabled()) logger.debug("Found " + result.size() + " indexes for " + columnName);
174 return result.toArray(new int[result.size()][]);
177 /** Return the list of index names corresponding to the array of indexes.
178 * This method is called by the DomainFieldHandlerImpl constructor after the
179 * registerIndices method is called.
180 * @param indexArray the result of registerIndices
181 * @return all index names for the corresponding indexes
183 public Set<String> getIndexNames(int[][] indexArray) {
184 Set<String> result = new HashSet<String>();
185 for (int[] index: indexArray) {
186 result.add(indexHandlerImpls.get(index[0]).getIndexName());
191 /** Extract the column names from store Columns.
193 * @param indexName the index name (for error messages)
194 * @param columns the store Column instances
195 * @return an array of column names
197 protected String[] getColumnNames(String indexName, Column[] columns) {
198 Set<String> columnNames = new HashSet<String>();
199 for (Column column : columns) {
200 String columnName = column.getName();
201 if (columnNames.contains(columnName)) {
202 // error: the column name is duplicated
203 throw new ClusterJUserException(
204 local.message("ERR_Duplicate_Column",
205 name, indexName, columnName));
207 columnNames.add(columnName);
209 return columnNames.toArray(new String[columnNames.size()]);
212 /** Create a list of candidate indexes to evaluate query terms and
213 * decide what type of operation to use. The result must correspond
214 * one to one with the indexHandlerImpls.
215 * @return a new array of CandidateIndexImpl
217 public CandidateIndexImpl[] createCandidateIndexes() {
218 CandidateIndexImpl[] result = new CandidateIndexImpl[indexHandlerImpls.size()];
220 for (IndexHandlerImpl indexHandler: indexHandlerImpls) {
221 result[i++] = indexHandler.toCandidateIndexImpl();
226 public String getTableName() {
230 public int getNumberOfFields() {
231 return numberOfFields;
234 public DomainFieldHandler[] getIdFieldHandlers() {
235 return idFieldHandlers;
238 public DomainFieldHandler getFieldHandler(String fieldName) {
239 for (DomainFieldHandler fmd: persistentFieldHandlers) {
240 if (fmd.getName().equals(fieldName)) {
244 throw new ClusterJUserException(
245 local.message("ERR_Not_A_Member", fieldName, name));
248 public int getFieldNumber(String fieldName) {
249 Integer fieldNumber = fieldNameToNumber.get(fieldName);
250 if (fieldNumber == null) {
251 throw new ClusterJFatalInternalException(
252 local.message("ERR_No_Field_Number", fieldName, name));
254 return fieldNumber.intValue();
257 public void operationSetNonPKValues(ValueHandler handler, Operation op) {
258 for (DomainFieldHandler fmd: nonPKFieldHandlers) {
259 if (handler.isModified(fmd.getFieldNumber())) {
260 fmd.operationSetValue(handler, op);
265 public void operationSetValues(ValueHandler handler, Operation op) {
266 for (DomainFieldHandler fmd: persistentFieldHandlers) {
267 if (logger.isDetailEnabled()) logger.detail("operationSetValues field: " + fmd.getName());
268 fmd.operationSetValue(handler, op);
272 public void operationSetModifiedNonPKValues(ValueHandler handler, Operation op) {
273 for (DomainFieldHandler fmd: nonPKFieldHandlers) {
274 fmd.operationSetModifiedValue(handler, op);
278 public void operationSetModifiedValues(ValueHandler handler, Operation op) {
279 for (DomainFieldHandler fmd: persistentFieldHandlers) {
280 fmd.operationSetModifiedValue(handler, op);
284 public void operationSetKeys(ValueHandler handler, Operation op) {
285 for (DomainFieldHandler fmd: idFieldHandlers) {
286 fmd.operationSetValue(handler, op);
290 public void operationGetValues(Operation op) {
291 for (DomainFieldHandler fmd: persistentFieldHandlers) {
292 fmd.operationGetValue(op);
296 public void operationGetValues(Operation op, BitSet fields) {
297 if (fields == null) {
298 operationGetValues(op);
301 for (DomainFieldHandler fmd: persistentFieldHandlers) {
302 if (fields.get(i++)) {
303 fmd.operationGetValue(op);
309 public void operationGetValuesExcept(IndexOperation op, String index) {
310 for (DomainFieldHandler fmd: persistentFieldHandlers) {
311 if (!fmd.includedInIndex(index)) {
312 if (logger.isDetailEnabled()) logger.detail("operationGetValuesExcept index: " + index);
313 fmd.operationGetValue(op);
318 public void objectSetValues(ResultData rs, ValueHandler handler) {
319 for (DomainFieldHandler fmd: persistentFieldHandlers) {
320 fmd.objectSetValue(rs, handler);
324 public void objectSetValuesExcept(ResultData rs, ValueHandler handler, String indexName) {
325 for (DomainFieldHandler fmd: persistentFieldHandlers) {
326 fmd.objectSetValueExceptIndex(rs, handler, indexName);
330 protected Table getTable(Dictionary dictionary) {
333 result = dictionary.getTable(tableName);
334 } catch (Exception ex) {
335 throw new ClusterJException(
336 local.message("ERR_Get_NdbTable", name, tableName), ex);
341 public int[] getKeyFieldNumbers() {
342 return idFieldNumbers;
345 public Table getStoreTable() {
349 /** Create a partition key for a find by primary key.
350 * @param handler the handler that contains the values of the primary key
352 public PartitionKey createPartitionKey(ValueHandler handler) {
353 // create the partition key based on the mapped table
354 PartitionKey result = table.createPartitionKey();
355 // add partition key part value for each partition key field
356 for (DomainFieldHandler fmd: partitionKeyFieldHandlers) {
357 if (logger.isDetailEnabled()) logger.detail(
358 "Field number " + fmd.getFieldNumber()
359 + " column name " + fmd.getName() + " field name " + fmd.getName());
360 fmd.partitionKeySetPart(result, handler);
365 public String getName() {
369 public Set<String> getColumnNames(BitSet fields) {
370 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
373 public Set<com.mysql.clusterj.core.store.Column> getStoreColumns(BitSet fields) {
374 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
377 public ValueHandler createKeyValueHandler(Object keys) {
378 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
381 public T getInstance(ValueHandler handler) {
382 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
385 public Class<?> getOidClass() {
386 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
389 public Class<T> getProxyClass() {
390 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
393 public ValueHandler getValueHandler(Object instance) {
394 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
397 public boolean isSupportedType() {
398 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
401 public T newInstance() {
402 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
405 public void objectMarkModified(ValueHandler handler, String fieldName) {
406 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
409 public void objectResetModified(ValueHandler handler) {
410 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
413 public void objectSetCacheManager(CacheManager cm, Object instance) {
414 throw new ClusterJFatalInternalException(local.message("ERR_Implementation_Should_Not_Occur"));
417 protected String removeUniqueSuffix(String indexName) {
418 int beginIndex = indexName.lastIndexOf("$unique");
419 if (beginIndex < 0) {
420 // there's no $unique suffix
423 String result = indexName.substring(0, beginIndex);
427 public String[] getFieldNames() {