PageRenderTime 122ms CodeModel.GetById 18ms app.highlight 91ms RepoModel.GetById 1ms app.codeStats 1ms

/src/com/google/appengine/datanucleus/mapping/DatastoreTable.java

http://datanucleus-appengine.googlecode.com/
Java | 1430 lines | 1026 code | 156 blank | 248 comment | 351 complexity | 0653b973053f01eba9e16fc25da0dd34 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/**********************************************************************
   2Copyright (c) 2009 Google Inc.
   3
   4Licensed under the Apache License, Version 2.0 (the "License");
   5you may not use this file except in compliance with the License.
   6You may obtain a copy of the License at
   7
   8http://www.apache.org/licenses/LICENSE-2.0
   9
  10Unless required by applicable law or agreed to in writing, software
  11distributed under the License is distributed on an "AS IS" BASIS,
  12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13See the License for the specific language governing permissions and
  14limitations under the License.
  15**********************************************************************/
  16package com.google.appengine.datanucleus.mapping;
  17
  18import org.datanucleus.ClassLoaderResolver;
  19import org.datanucleus.exceptions.NucleusException;
  20import org.datanucleus.exceptions.NucleusUserException;
  21import org.datanucleus.exceptions.NucleusFatalUserException;
  22import org.datanucleus.identity.OID;
  23import org.datanucleus.metadata.AbstractClassMetaData;
  24import org.datanucleus.metadata.AbstractMemberMetaData;
  25import org.datanucleus.metadata.ClassMetaData;
  26import org.datanucleus.metadata.ColumnMetaData;
  27import org.datanucleus.metadata.ColumnMetaDataContainer;
  28import org.datanucleus.metadata.DiscriminatorMetaData;
  29import org.datanucleus.metadata.DiscriminatorStrategy;
  30import org.datanucleus.metadata.FieldPersistenceModifier;
  31import org.datanucleus.metadata.FieldRole;
  32import org.datanucleus.metadata.IdentityStrategy;
  33import org.datanucleus.metadata.IdentityType;
  34import org.datanucleus.metadata.InheritanceStrategy;
  35import org.datanucleus.metadata.MetaData;
  36import org.datanucleus.metadata.MetaDataManager;
  37import org.datanucleus.metadata.OrderMetaData;
  38import org.datanucleus.metadata.PropertyMetaData;
  39import org.datanucleus.metadata.RelationType;
  40import org.datanucleus.metadata.VersionMetaData;
  41import org.datanucleus.plugin.ConfigurationElement;
  42import org.datanucleus.store.types.SCOUtils;
  43import org.datanucleus.store.mapped.exceptions.NoSuchPersistentFieldException;
  44import org.datanucleus.store.mapped.exceptions.NoTableManagedException;
  45import org.datanucleus.store.mapped.DatastoreAdapter;
  46import org.datanucleus.store.mapped.DatastoreClass;
  47import org.datanucleus.store.mapped.DatastoreField;
  48import org.datanucleus.store.mapped.DatastoreIdentifier;
  49import org.datanucleus.store.mapped.IdentifierFactory;
  50import org.datanucleus.store.mapped.IdentifierType;
  51import org.datanucleus.store.mapped.MappedStoreManager;
  52import org.datanucleus.store.mapped.exceptions.DuplicateDatastoreFieldException;
  53import org.datanucleus.store.mapped.mapping.CorrespondentColumnsMapper;
  54import org.datanucleus.store.mapped.mapping.DatastoreMapping;
  55import org.datanucleus.store.mapped.mapping.DiscriminatorLongMapping;
  56import org.datanucleus.store.mapped.mapping.DiscriminatorStringMapping;
  57import org.datanucleus.store.mapped.mapping.IndexMapping;
  58import org.datanucleus.store.mapped.mapping.IntegerMapping;
  59import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
  60import org.datanucleus.store.mapped.mapping.LongMapping;
  61import org.datanucleus.store.mapped.mapping.MappingConsumer;
  62import org.datanucleus.store.mapped.mapping.OIDMapping;
  63import org.datanucleus.store.mapped.mapping.PersistableMapping;
  64import org.datanucleus.util.MultiMap;
  65import org.datanucleus.util.NucleusLogger;
  66import org.datanucleus.util.StringUtils;
  67
  68import com.google.appengine.datanucleus.DatastoreManager;
  69import com.google.appengine.datanucleus.MetaDataUtils;
  70import com.google.appengine.datanucleus.Utils;
  71
  72import java.text.MessageFormat;
  73import java.util.Collection;
  74import java.util.HashMap;
  75import java.util.HashSet;
  76import java.util.LinkedHashMap;
  77import java.util.LinkedList;
  78import java.util.List;
  79import java.util.Map;
  80import java.util.Set;
  81
  82/**
  83 * Describes a 'table' in the datastore.  We don't actually have tables, but
  84 * we need to represent this logically in order to take care of all the nice
  85 * mapping logic that datanucleus does for us.
  86 *
  87 * This code is largely copied from AbstractClassTable, ClassTable, TableImpl, and AbstractTable
  88 *
  89 * TODO : Drop the code adding "foreign key" when we drop support for old storage versions
  90 *
  91 * @author Max Ross <maxr@google.com>
  92 */
  93public class DatastoreTable implements DatastoreClass {
  94
  95  /** All callbacks for class tables waiting to be performed. */
  96  private static final MultiMap callbacks = new MultiMap();
  97
  98  private final DatastoreManager storeMgr;
  99  private final AbstractClassMetaData cmd;
 100  private final ClassLoaderResolver clr;
 101  private final DatastoreAdapter dba;
 102  private final DatastoreIdentifier identifier;
 103
 104  /**
 105   * Mappings for fields mapped to this table, keyed by the FieldMetaData.
 106   * Supports fast lookup but also preserves order.
 107   */
 108  private final Map<AbstractMemberMetaData, JavaTypeMapping> fieldMappingsMap =
 109      new LinkedHashMap<AbstractMemberMetaData, JavaTypeMapping>();
 110
 111  /**
 112   * Similar to {@link #fieldMappingsMap} except primary key fields are added as
 113   * well.  This is needed to support persistence capable classes that can be
 114   * persisted as top-level classes and as embedded classes.
 115   */
 116  private final Map<AbstractMemberMetaData, JavaTypeMapping> embeddedFieldMappingsMap =
 117      new LinkedHashMap<AbstractMemberMetaData, JavaTypeMapping>();
 118
 119  /**
 120   * All the properties in the table.  Even though the datastore is schemaless,
 121   * the mappings provided by the ORM effectively impose a schema.  This allows
 122   * us to know, up front, what properties we can expect.
 123   */
 124  private final List<DatastoreProperty> datastoreProperties = Utils.newArrayList();
 125
 126  /**
 127   * Index to the props, keyed by name.
 128   */
 129  protected Map<String, DatastoreProperty> datastorePropertiesByName = Utils.newHashMap();
 130
 131  /** Mapping for datastore identity (optional). */
 132  private JavaTypeMapping datastoreIDMapping;
 133
 134  /** Mappings for application identity (optional). */
 135  private JavaTypeMapping[] pkMappings;
 136
 137  /** Mapping for the id of the table. */
 138  private JavaTypeMapping idMapping;
 139
 140  /** MetaData for discriminator for objects stored in this kind. */
 141  protected DiscriminatorMetaData discriminatorMetaData;
 142
 143  /** Mapping for any discriminator column. */
 144  private JavaTypeMapping discriminatorMapping;
 145  
 146  /** Highest absolute field number managed by this table */
 147  private int highestFieldNumber = 0;
 148
 149  /** Mappings to the owner where this is an element. */
 150  private final Map<AbstractMemberMetaData, JavaTypeMapping> externalFkMappings = Utils.newHashMap();
 151
 152  /** Mappings of the position where this is an element in a List. */
 153  private final Map<AbstractMemberMetaData, JavaTypeMapping> externalOrderMappings = Utils.newHashMap();
 154
 155  private AbstractMemberMetaData parentMappingMemberMetaData;
 156
 157  /** MetaData for all classes being managed here. */
 158  private final Collection<AbstractClassMetaData> managedClassMetaData = new HashSet<AbstractClassMetaData>();
 159  
 160  public DatastoreTable(String kind, DatastoreManager storeMgr, AbstractClassMetaData cmd,
 161      ClassLoaderResolver clr, DatastoreAdapter dba) {
 162    this.storeMgr = storeMgr;
 163    this.cmd = cmd;
 164    this.clr = clr;
 165    this.dba = dba;
 166    this.identifier = new DatastoreKind(kind);
 167  }
 168
 169  public String getType() {
 170    return cmd.getFullClassName();
 171  }
 172
 173  public IdentityType getIdentityType() {
 174    return cmd.getIdentityType();
 175  }
 176
 177  public boolean isObjectIdDatastoreAttributed() {
 178    return true;
 179  }
 180
 181  public boolean isBaseDatastoreClass() {
 182    return true;
 183  }
 184
 185  public DatastoreClass getBaseDatastoreClass() {
 186    return this;
 187  }
 188
 189  public DatastoreClass getBaseDatastoreClassWithMember(AbstractMemberMetaData fmd) {
 190    if (fieldMappingsMap.get(fmd) != null) {
 191      return this;
 192    }
 193    return null;
 194  }
 195
 196  public DatastoreClass getSuperDatastoreClass() {
 197    return null;
 198  }
 199
 200  public Collection getSecondaryDatastoreClasses() {
 201    return null;
 202  }
 203
 204  public boolean managesClass(String className) {
 205    return cmd.getFullClassName().equals(className);
 206  }
 207
 208  public JavaTypeMapping getDatastoreObjectIdMapping() {
 209    return datastoreIDMapping;
 210  }
 211
 212  public JavaTypeMapping getMemberMapping(String fieldName) {
 213    AbstractMemberMetaData fmd = getFieldMetaData(fieldName);
 214    JavaTypeMapping m = getMemberMapping(fmd);
 215    if (m == null) {
 216        throw new NoSuchPersistentFieldException(cmd.getFullClassName(), fieldName);
 217    }
 218    return m;
 219  }
 220
 221  private AbstractMemberMetaData getFieldMetaData(String fieldName) {
 222    return cmd.getMetaDataForMember(fieldName);
 223  }
 224
 225  public JavaTypeMapping getMemberMapping(AbstractMemberMetaData mmd) {
 226    if (mmd == null) {
 227      return null;
 228    }
 229
 230    return fieldMappingsMap.get(mmd);
 231  }
 232
 233  public JavaTypeMapping getMemberMappingInDatastoreClass(AbstractMemberMetaData mmd) {
 234    return getMemberMapping(mmd);
 235  }
 236
 237  // Mostly copied from AbstractTable.addDatastoreField
 238  public DatastoreProperty addDatastoreField(String storedJavaType, DatastoreIdentifier name,
 239      JavaTypeMapping mapping, ColumnMetaData colmd) {
 240
 241    // Create the column
 242    DatastoreProperty prop =
 243        new DatastoreProperty(this, mapping.getJavaType().getName(), name, (ColumnMetaData) colmd);
 244
 245    if (hasColumnName(name)) {
 246      if (colmd == null || !(colmd instanceof ColumnMetaData)) {
 247        throw new NucleusException(
 248            "invalid column meta data property name on class " + getType() + " : " + colmd);
 249      }
 250
 251      if (((ColumnMetaData) colmd).getInsertable() || ((ColumnMetaData) colmd).getUpdateable()) {
 252        // duplicate property names are ok if the field is neither insertable nor updatable
 253        if (!isSuperclassColumn(prop, mapping, colmd)) {
 254          throw new NucleusException(
 255              "Duplicate property name on class " + getType() + " : " + name);
 256        }
 257      }
 258    }
 259    DatastoreIdentifier colName = prop.getIdentifier();
 260
 261    datastoreProperties.add(prop);
 262    datastorePropertiesByName.put(colName.getIdentifierName(), prop);
 263    return prop;
 264  }
 265
 266  protected boolean hasColumnName(DatastoreIdentifier colName) {
 267    return getDatastoreField(colName) != null;
 268  }
 269
 270
 271  public boolean hasDatastoreField(DatastoreIdentifier identifier) {
 272    return (hasColumnName(identifier));
 273  }
 274
 275  public boolean isSuperclassColumn(DatastoreField col, JavaTypeMapping mapping, MetaData colmd) {
 276    // Verify if a duplicate column is valid. A duplicate column name is (currently) valid when :-
 277    // 1. subclasses defining the duplicated column are using "super class table" strategy
 278    //
 279    // Find the MetaData for the existing column
 280    DatastoreIdentifier name = col.getIdentifier();
 281    DatastoreField existingCol = getDatastoreField(name);
 282    MetaData md = existingCol.getColumnMetaData().getParent();
 283    while (!(md instanceof AbstractClassMetaData)) {
 284      if (md == null) {
 285        // ColumnMetaData for existing column has no parent class somehow!
 286        throw new NucleusUserException(MessageFormat.format(
 287            "The property \"{0}\" exists in entity \"{1}\" and has invalid metadata. The existing property is \"{2}\"",
 288            name, this.identifier, colmd.toString()));
 289      }
 290      md = md.getParent();
 291    }
 292
 293    // Find the MetaData for the column to be added
 294    MetaData dupMd = colmd.getParent();
 295    while (!(dupMd instanceof AbstractClassMetaData)) {
 296      dupMd = dupMd.getParent();
 297      if (dupMd == null) {
 298        // ColumnMetaData for required column has no parent class somehow!
 299        throw new NucleusUserException(MessageFormat.format(
 300            "The column \"{0}\" exists in table \"{1}\" and cannot be validated because a duplicated column has been specified and the metadata is invalid. The column is \"{2}\"",
 301            name, this.identifier, colmd.toString()));
 302      }
 303    }
 304    if (((AbstractClassMetaData) md).getFullClassName().equals(
 305        ((AbstractClassMetaData) dupMd).getFullClassName())) {
 306      // compare the current column defining class and the duplicated column defining class. if the same class,
 307      // we raise an exception when within one class it is defined a column twice
 308      // in some cases it could still be possible to have these duplicated columns, but does not make too
 309      // much sense in most of the cases. (this whole block of duplicated column check, could be optional, like a pmf property)
 310      throw new DuplicateDatastoreFieldException(this.toString(), existingCol, col);
 311    }
 312
 313    // Make sure the field JavaTypeMappings are compatible
 314    if (mapping != null &&
 315        !mapping.getClass().isAssignableFrom(existingCol.getJavaTypeMapping().getClass()) &&
 316        !existingCol.getJavaTypeMapping().getClass().isAssignableFrom(mapping.getClass())) {
 317      // the mapping class must be the same (not really required, but to avoid user mistakes)
 318      throw new DuplicateDatastoreFieldException(this.toString(), existingCol, col);
 319    }
 320
 321    // Make sure the field java types are compatible
 322    Class<?> fieldStoredJavaTypeClass = null;
 323    Class<?> existingColStoredJavaTypeClass = null;
 324    try {
 325      ClassLoaderResolver clr = storeMgr.getNucleusContext().getClassLoaderResolver(null);
 326      fieldStoredJavaTypeClass = clr.classForName(col.getStoredJavaType());
 327      existingColStoredJavaTypeClass = clr.classForName(col.getStoredJavaType());
 328    }
 329    catch (RuntimeException cnfe) {
 330      // Do nothing
 331    }
 332    if (fieldStoredJavaTypeClass != null && existingColStoredJavaTypeClass != null &&
 333        !fieldStoredJavaTypeClass.isAssignableFrom(existingColStoredJavaTypeClass) &&
 334        !existingColStoredJavaTypeClass.isAssignableFrom(fieldStoredJavaTypeClass)) {
 335      // the stored java type must be the same (not really required, but to avoid user mistakes)
 336      throw new DuplicateDatastoreFieldException(this.toString(), existingCol, col);
 337    }
 338    return true;
 339  }
 340
 341  public DatastoreField getDatastoreField(String colName) {
 342    return datastorePropertiesByName.get(colName);
 343  }
 344  public DatastoreField getDatastoreField(DatastoreIdentifier identifier) {
 345    return getDatastoreField(identifier.getIdentifierName());
 346  }
 347
 348  public JavaTypeMapping getIdMapping() {
 349    return idMapping;
 350  }
 351
 352  public MappedStoreManager getStoreManager() {
 353    return storeMgr;
 354  }
 355
 356  public DatastoreIdentifier getIdentifier() {
 357    return identifier;
 358  }
 359
 360  public boolean isSuperDatastoreClass(DatastoreClass datastoreClass) {
 361    return false;
 362  }
 363
 364  public boolean managesMapping(JavaTypeMapping javaTypeMapping) {
 365    return true;
 366  }
 367
 368  public DatastoreField[] getDatastoreFields() {
 369    return datastoreProperties.toArray(new DatastoreField[datastoreProperties.size()]);
 370  }
 371
 372  public void buildMapping() {
 373    initializePK();
 374    initializeNonPK();
 375    initializeDiscriminatorMapping(cmd.getDiscriminatorMetaDataForTable());
 376    runCallBacks();
 377    this.managedClassMetaData.add(cmd);
 378  }
 379
 380  public void manageClass(AbstractClassMetaData cmd) {
 381    //	We demand DiscriminatorMetaData, because in cases where an object of the
 382    //	top class in the inheritance hierarchy will be persisted and the subclasses
 383    //	are not yet known to datanucleus, we already need to know about the discriminator
 384    //	property. At this point here the exception can be too late, if an object of
 385    //	the top class has been persisted already. But at least the developer is
 386    //	now informed, that he should add the DiscriminatorMetaData.
 387    if (getDiscriminatorMetaData() == null) {
 388      throw new NucleusUserException("Descriminator meta data for " +
 389                                     cmd.getFullClassName() +
 390                                     " is missing. Please specify at least the discriminator column.");
 391    }
 392
 393    // Go through the fields for this class and add columns for them
 394    for (AbstractMemberMetaData fmd : cmd.getManagedMembers()) {
 395      addFieldMapping(cmd, fmd);
 396    }
 397    this.managedClassMetaData.add(cmd);
 398  }
 399
 400  /**
 401   * returns the names of all classes managed by this table.
 402   * @return Names of the classes managed (stored) here
 403   */
 404  public String[] getManagedClasses() {
 405    List<String> list = Utils.transform(managedClassMetaData, new Utils.Function<AbstractClassMetaData, String>() {
 406          public String apply(AbstractClassMetaData cmd) {
 407            return cmd.getFullClassName();
 408          }
 409        });
 410    return list.toArray(new String[list.size()]);
 411  }
 412
 413  private void initializeNonPK() {
 414    // We only support inheritance strategies that resolve to all fields for
 415    // a class plus all its subclasses living in every "table," so we'll
 416    // iterate over all managed fields in the entire chain, topmost classes
 417    // first so that overrides work properly.
 418    List<AbstractClassMetaData> cmdl = buildClassMetaDataList();
 419    
 420    // get the class level overridden members
 421    // simply reversing the order we iterate through the hierarchy
 422    // of ClassMetaData disorders the field numbers 
 423    Map<String, AbstractMemberMetaData> overriddenFieldMap = new HashMap<String, AbstractMemberMetaData>();
 424    for (AbstractClassMetaData curCmd : cmdl) {
 425      for (AbstractMemberMetaData fmd : curCmd.getOverriddenMembers()) {
 426        overriddenFieldMap.put(fmd.getFullFieldName(), fmd);
 427      }
 428    }    
 429    
 430    for (AbstractClassMetaData curCmd : cmdl) {
 431      // Go through the fields for this class and add columns for them
 432      for (AbstractMemberMetaData fmd : curCmd.getManagedMembers()) {
 433        if (overriddenFieldMap.containsKey(fmd.getFullFieldName())) {
 434          fmd = overriddenFieldMap.get(fmd.getFullFieldName());
 435        }
 436        addFieldMapping(curCmd, fmd);
 437      }
 438    }
 439
 440    Class curClass = clr.classForName(cmd.getFullClassName()).getSuperclass();
 441    // see if any of our superclasses have a parentMappingField
 442    while (!Object.class.equals(curClass)) {
 443      DatastoreTable dt = null;
 444      try {
 445        dt = (DatastoreTable) storeMgr.getDatastoreClass(curClass.getName(), clr);
 446      } catch (NoTableManagedException ntme) {
 447        // this is ok, not all parent classes need to be managed
 448      }
 449      // sometimes the parent table is not yet initialized
 450      if (dt != null) {
 451        // inherit the parentMappingField
 452        if (parentMappingMemberMetaData == null) {
 453          if (dt.parentMappingMemberMetaData != null) {
 454            parentMappingMemberMetaData = dt.parentMappingMemberMetaData;
 455            break;
 456          }
 457        }
 458        // inherit any external fk mappings
 459        if (!dt.externalFkMappings.isEmpty()) {
 460          externalFkMappings.putAll(dt.externalFkMappings);
 461        }
 462      }
 463      curClass = curClass.getSuperclass();
 464    }
 465  }
 466
 467  private void addFieldMapping(AbstractClassMetaData curCmd, AbstractMemberMetaData fmd) {
 468    // Primary key fields are added by the initialisePK method
 469    if (fmd.isPrimaryKey()) {
 470      // We need to know about this mapping when accessing the class as an embedded field.
 471      embeddedFieldMappingsMap.put(fmd, pkMappings[0]);
 472    } else {
 473      if (managesField(fmd.getFullFieldName())) {
 474        if (!fmd.getClassName(true).equals(curCmd.getFullClassName())) {
 475          throw new UnsupportedOperationException("Overrides not currently supported.");
 476        }
 477      } else {
 478        MetaDataManager mmgr = storeMgr.getMetaDataManager();
 479
 480        // Manage the field if not already managed (may already exist if overriding a superclass field)
 481        JavaTypeMapping mapping = null;
 482        if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
 483          boolean isPrimary = true;
 484          if (fmd.getTable() != null && fmd.getJoinMetaData() == null) {
 485            // Field has a table specified and is not a 1-N with join table so is mapped to a secondary table
 486            isPrimary = false;
 487          }
 488
 489          if (isPrimary) {
 490            // Add the field to this table
 491            mapping = dba.getMappingManager(storeMgr).getMapping(this, fmd, clr, FieldRole.ROLE_FIELD);
 492            addFieldMapping(mapping);
 493            embeddedFieldMappingsMap.put(fmd, fieldMappingsMap.get(fmd));
 494          } else {
 495            throw new UnsupportedOperationException("No support for secondary tables.");
 496          }
 497        } else if (fmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
 498          throw new NucleusException("Invalid persistence-modifier for field ").setFatal();
 499        }
 500
 501        // Calculate if we need a FK adding due to a 1-N relationship TODO Remove this when no longer supporting old storageVersion
 502        // Note that we ignore any "join" setting since we don't use join tables
 503        boolean needsFKToContainerOwner = false;
 504        RelationType relationType = fmd.getRelationType(clr);
 505        if (relationType == RelationType.ONE_TO_MANY_BI) {
 506          /*AbstractMemberMetaData[] relatedMmds = fmd.getRelatedMemberMetaData(clr);
 507          if (fmd.getJoinMetaData() == null && relatedMmds[0].getJoinMetaData() == null) {*/
 508            needsFKToContainerOwner = true;
 509          /*}*/
 510        } else if (relationType == RelationType.ONE_TO_MANY_UNI) {
 511          /*if (fmd.getJoinMetaData() == null) {*/
 512            needsFKToContainerOwner = true;
 513          /*}*/
 514        } else if (relationType == RelationType.ONE_TO_ONE_BI) {
 515          if (fmd.getMappedBy() != null && MetaDataUtils.isOwnedRelation(fmd, storeMgr)) {
 516            // This element type has a many-to-one pointing back.
 517            // We assume that our pk is part of the pk of the element type.
 518            DatastoreTable dt = storeMgr.getDatastoreClass(fmd.getAbstractClassMetaData().getFullClassName(), clr);
 519            dt.runCallBacks();
 520            dt.markFieldAsParentKeyProvider(fmd.getName());
 521          }
 522        } else if (relationType == RelationType.MANY_TO_ONE_BI) {
 523        }
 524
 525        if (needsFKToContainerOwner) {
 526          // 1-N uni/bidirectional using FK, so update the element side with a FK
 527          if ((fmd.getCollection() != null && !SCOUtils.collectionHasSerialisedElements(fmd)) ||
 528              (fmd.getArray() != null && !SCOUtils.arrayIsStoredInSingleColumn(fmd, mmgr))) {
 529            // 1-N ForeignKey collection/array, so add FK to element table
 530            AbstractClassMetaData elementCmd;
 531            if (fmd.hasCollection()) {
 532              // Collection
 533              elementCmd = mmgr.getMetaDataForClass(fmd.getCollection().getElementType(), clr);
 534            } else {
 535              // Array
 536              elementCmd = mmgr.getMetaDataForClass(fmd.getType().getComponentType(), clr);
 537            }
 538            if (elementCmd == null) {
 539              // Elements that are reference types or non-PC will come through here
 540            } else {
 541              AbstractClassMetaData[] elementCmds;
 542              // TODO : Cater for interface elements, and get the metadata for the implementation classes here
 543              if (elementCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
 544                elementCmds = storeMgr.getClassesManagingTableForClass(elementCmd, clr);
 545              } else {
 546                elementCmds = new ClassMetaData[1];
 547                elementCmds[0] = elementCmd;
 548              }
 549
 550              // Run callbacks for each of the element classes.
 551              for (AbstractClassMetaData elementCmd1 : elementCmds) {
 552                callbacks.put(elementCmd1.getFullClassName(), new CallBack(fmd, cmd.getFullClassName()));
 553                DatastoreTable dt =
 554                    (DatastoreTable) storeMgr.getDatastoreClass(elementCmd1.getFullClassName(), clr);
 555                dt.runCallBacks();
 556                if (fmd.getMappedBy() != null && MetaDataUtils.isOwnedRelation(fmd, storeMgr)) {
 557                  // This element type has a many-to-one pointing back.
 558                  // We assume that our pk is part of the pk of the element type.
 559                  dt.markFieldAsParentKeyProvider(fmd.getMappedBy());
 560                }
 561              }
 562            }
 563          } else if (fmd.getMap() != null && !SCOUtils.mapHasSerialisedKeysAndValues(fmd)) {
 564            // 1-N ForeignKey map, so add FK to value table
 565            if (fmd.getKeyMetaData() != null && fmd.getKeyMetaData().getMappedBy() != null) {
 566              // Key is stored in the value table so add the FK to the value table
 567              AbstractClassMetaData valueCmd = mmgr.getMetaDataForClass(fmd.getMap().getValueType(), clr);
 568              if (valueCmd == null) {
 569                // Interface elements will come through here and java.lang.String and others as well
 570              }
 571            } else if (fmd.getValueMetaData() != null
 572                       && fmd.getValueMetaData().getMappedBy() != null) {
 573              // Value is stored in the key table so add the FK to the key table
 574              AbstractClassMetaData keyCmd = mmgr.getMetaDataForClass(fmd.getMap().getKeyType(), clr);
 575              if (keyCmd == null) {
 576                // Interface elements will come through here and java.lang.String and others as well
 577              } else {
 578              }
 579            }
 580          }
 581        }
 582      }
 583    }
 584  }
 585
 586  /**
 587   * Constructs a list containing the class meta data of {@link #cmd} and all
 588   * its superclasses.  We add to the beginning of the list as we iterate up so
 589   * the class at the top of the hierarchy is returned first. 
 590   */
 591  private LinkedList<AbstractClassMetaData> buildClassMetaDataList() {
 592    LinkedList<AbstractClassMetaData> stack = Utils.newLinkedList();
 593    AbstractClassMetaData curCmd = cmd;
 594    while (curCmd != null) {
 595      stack.addFirst(curCmd);
 596      curCmd = curCmd.getSuperAbstractClassMetaData();
 597    }
 598    return stack;
 599  }
 600
 601  private void markFieldAsParentKeyProvider(String mappedBy) {
 602
 603    AbstractMemberMetaData newParentMappingField = getFieldMetaData(mappedBy);
 604    if (parentMappingMemberMetaData == null) {
 605      parentMappingMemberMetaData = newParentMappingField;
 606    } else if (parentMappingMemberMetaData != newParentMappingField) { // intentional reference compare
 607      throw new NucleusException(
 608          "App Engine ORM does not support multiple parent key provider fields : class=" + cmd.getFullClassName() + 
 609          " requested to have member=" + mappedBy + 
 610          " but already have member=" + parentMappingMemberMetaData.getName());
 611    }
 612  }
 613
 614  protected void addFieldMapping(JavaTypeMapping fieldMapping) {
 615    AbstractMemberMetaData fmd = fieldMapping.getMemberMetaData();
 616    fieldMappingsMap.put(fmd, fieldMapping);
 617    // Update highest field number if this is higher
 618    int absoluteFieldNumber = fmd.getAbsoluteFieldNumber();
 619    if (absoluteFieldNumber > highestFieldNumber) {
 620      highestFieldNumber = absoluteFieldNumber;
 621    }
 622  }
 623
 624  public boolean managesField(String fieldName) {
 625    return fieldName != null && getMappingForFullFieldName(fieldName) != null;
 626
 627  }
 628
 629  /**
 630   * Accessor for the JavaTypeMapping that is handling the field of the specified name. Returns the
 631   * first one that matches.
 632   *
 633   * @param fieldName Name of the field
 634   * @return The java type mapping
 635   */
 636  public JavaTypeMapping getMappingForFullFieldName(String fieldName) {
 637    Set fields = fieldMappingsMap.keySet();
 638    for (Object field : fields) {
 639      AbstractMemberMetaData fmd = (AbstractMemberMetaData) field;
 640      if (fmd.getFullFieldName().equals(fieldName)) {
 641        return fieldMappingsMap.get(fmd);
 642      }
 643    }
 644    return null;
 645  }
 646
 647  public JavaTypeMapping getMappingForSimpleFieldName(String fieldName) {
 648    Set fields = fieldMappingsMap.keySet();
 649    for (Object field : fields) {
 650      AbstractMemberMetaData fmd = (AbstractMemberMetaData) field;
 651      if (fmd.getName().equals(fieldName)) {
 652        return fieldMappingsMap.get(fmd);
 653      }
 654    }
 655    return null;
 656  }
 657
 658  void addDatastoreId(ColumnMetaData columnMetaData, DatastoreClass refTable,
 659      AbstractClassMetaData cmd) {
 660    datastoreIDMapping = new OIDMapping();
 661    datastoreIDMapping.initialize(storeMgr, cmd.getFullClassName());
 662
 663    // Create a ColumnMetaData in the container if none is defined
 664    ColumnMetaData colmd;
 665    if (columnMetaData == null) {
 666      colmd = new ColumnMetaData();
 667    } else {
 668      colmd = columnMetaData;
 669    }
 670    if (colmd.getName() == null) {
 671      // Provide default column naming if none is defined
 672      if (refTable != null) {
 673        colmd.setName(storeMgr.getIdentifierFactory()
 674            .newDatastoreFieldIdentifier(refTable.getIdentifier().getIdentifierName(),
 675                this.storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(OID.class),
 676                FieldRole.ROLE_OWNER).getIdentifierName());
 677      } else {
 678        colmd.setName(
 679            storeMgr.getIdentifierFactory().newDatastoreFieldIdentifier(identifier.getIdentifierName(),
 680                this.storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(OID.class),
 681                FieldRole.ROLE_NONE).getIdentifierName());
 682      }
 683    }
 684
 685    // Add the datastore identity column as the PK
 686    DatastoreField idColumn = addDatastoreField(OID.class.getName(),
 687        storeMgr.getIdentifierFactory().newIdentifier(IdentifierType.COLUMN, colmd.getName()),
 688        datastoreIDMapping, colmd);
 689    idColumn.setAsPrimaryKey();
 690
 691    // Set the identity column type based on the IdentityStrategy
 692    String strategyName = cmd.getIdentityMetaData().getValueStrategy().toString();
 693    if (cmd.getIdentityMetaData().getValueStrategy().equals(IdentityStrategy.CUSTOM)) {
 694      strategyName = cmd.getIdentityMetaData().getValueStrategy().getCustomName();
 695    }
 696
 697    // Check the POID type being stored
 698    Class poidClass = Long.class;
 699    ConfigurationElement elem =
 700        storeMgr.getNucleusContext().getPluginManager().getConfigurationElementForExtension(
 701            "org.datanucleus.store_valuegenerator",
 702            new String[]{"name", "unique"}, new String[]{strategyName, "true"});
 703    if (elem == null) {
 704      // Not datastore-independent, so try for this datastore
 705      elem = storeMgr.getNucleusContext().getPluginManager().getConfigurationElementForExtension(
 706          "org.datanucleus.store_valuegenerator",
 707          new String[]{"name", "datastore"},
 708          new String[]{strategyName, storeMgr.getStoreManagerKey()});
 709    }
 710    if (elem != null) {
 711      // Set the generator name (for use by the PoidManager)
 712      String generatorClassName = elem.getAttribute("class-name");
 713      Class generatorClass =
 714          getStoreManager().getNucleusContext().getClassLoaderResolver(null)
 715              .classForName(generatorClassName);
 716      try {
 717        poidClass = (Class) generatorClass.getMethod("getStorageClass").invoke(null);
 718      }
 719      catch (Exception e) {
 720        // Unable to get the storage class from the PoidGenerator class
 721        NucleusLogger.VALUEGENERATION
 722            .warn("Error retrieving storage class for POID generator " + generatorClassName +
 723                " " + e.getMessage());
 724      }
 725    }
 726
 727    dba.getMappingManager(storeMgr)
 728        .createDatastoreMapping(datastoreIDMapping, idColumn, poidClass.getName());
 729
 730    // Handle any auto-increment requirement
 731    if (isObjectIdDatastoreAttributed()) {
 732      // Only the base class can be autoincremented
 733//      idColumn.setAutoIncrement(true);
 734    }
 735  }
 736
 737  private void initializePK() {
 738    AbstractMemberMetaData[] fieldsToAdd = new AbstractMemberMetaData[cmd.getNoOfPrimaryKeyMembers()];
 739
 740    // Initialise Primary Key mappings for application id with PK fields in this class
 741    int pkFieldNum = 0;
 742    int fieldCount = cmd.getNoOfManagedMembers();
 743    boolean hasPrimaryKeyInThisClass = false;
 744    if (cmd.getNoOfPrimaryKeyMembers() > 0) {
 745      pkMappings = new JavaTypeMapping[cmd.getNoOfPrimaryKeyMembers()];
 746      if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
 747        // COMPLETE-TABLE so use root class metadata and add PK members
 748        // TODO Does this allow for overridden PK field info ?
 749        AbstractClassMetaData baseCmd = cmd.getBaseAbstractClassMetaData();
 750        fieldCount = baseCmd.getNoOfManagedMembers();
 751        for (int relFieldNum = 0; relFieldNum < fieldCount; ++relFieldNum) {
 752          AbstractMemberMetaData mmd = baseCmd.getMetaDataForManagedMemberAtRelativePosition(relFieldNum);
 753          if (mmd.isPrimaryKey()) {
 754            if (mmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
 755              fieldsToAdd[pkFieldNum++] = mmd;
 756              hasPrimaryKeyInThisClass = true;
 757            } else if (mmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
 758              throw new NucleusException("Invalid persistence-modifier for field " + mmd.getName()).setFatal();
 759            }
 760          }
 761        }
 762      } else {
 763        for (int relFieldNum = 0; relFieldNum < fieldCount; ++relFieldNum) {
 764          AbstractMemberMetaData fmd = cmd.getMetaDataForManagedMemberAtRelativePosition(relFieldNum);
 765          if (fmd.isPrimaryKey()) {
 766            if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
 767              fieldsToAdd[pkFieldNum++] = fmd;
 768              hasPrimaryKeyInThisClass = true;
 769            } else if (fmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
 770              throw new NucleusException("Invalid persistence-modifier for field" + fmd.getName()).setFatal();
 771            }
 772          }
 773        }
 774      }
 775    }
 776
 777    // No Primary Key defined, so search for superclass or handle datastore id
 778    if (!hasPrimaryKeyInThisClass) {
 779      if (cmd.getIdentityType() == IdentityType.APPLICATION) {
 780        // application-identity
 781        DatastoreClass elementCT =
 782            storeMgr.getDatastoreClass(cmd.getPersistenceCapableSuperclass(), clr);
 783        if (elementCT != null) {
 784          // Superclass has a table so copy its PK mappings
 785          ColumnMetaDataContainer colContainer = null;
 786          if (cmd.getInheritanceMetaData() != null) {
 787            // Try via <inheritance><join>...</join></inheritance>
 788            colContainer = cmd.getInheritanceMetaData().getJoinMetaData();
 789          }
 790          if (colContainer == null) {
 791            // Try via <primary-key>...</primary-key>
 792            colContainer = cmd.getPrimaryKeyMetaData();
 793          }
 794
 795          addApplicationIdUsingClassTableId(colContainer, elementCT, clr, cmd);
 796        } else {
 797          // Superclass has no table so create new mappings and columns
 798          AbstractClassMetaData pkCmd =
 799              storeMgr.getClassWithPrimaryKeyForClass(cmd.getSuperAbstractClassMetaData(), clr);
 800          if (pkCmd != null) {
 801            pkMappings = new JavaTypeMapping[pkCmd.getNoOfPrimaryKeyMembers()];
 802            pkFieldNum = 0;
 803            fieldCount = pkCmd.getNoOfInheritedManagedMembers() + pkCmd.getNoOfManagedMembers();
 804            for (int absFieldNum = 0; absFieldNum < fieldCount; ++absFieldNum) {
 805              AbstractMemberMetaData fmd = pkCmd
 806                  .getMetaDataForManagedMemberAtAbsolutePosition(absFieldNum);
 807              if (fmd.isPrimaryKey()) {
 808                AbstractMemberMetaData overriddenFmd = cmd.getOverriddenMember(fmd.getName());
 809                if (overriddenFmd != null) {
 810                  // PK field is overridden so use the overriding definition
 811                  fmd = overriddenFmd;
 812                }
 813
 814                if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
 815                  fieldsToAdd[pkFieldNum++] = fmd;
 816                } else if (fmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
 817                  throw new NucleusException("Invalid persistence-modifier for field " + fmd.getName()).setFatal();
 818                }
 819              }
 820            }
 821          }
 822        }
 823      } else if (cmd.getIdentityType() == IdentityType.DATASTORE) {
 824        // datastore-identity
 825        ColumnMetaData colmd = null;
 826        if (cmd.getIdentityMetaData() != null
 827            && cmd.getIdentityMetaData().getColumnMetaData() != null) {
 828          // Try via <datastore-identity>...</datastore-identity>
 829          colmd = cmd.getIdentityMetaData().getColumnMetaData();
 830        }
 831        if (colmd == null) {
 832          // Try via <primary-key>...</primary-key>
 833          if (cmd.getPrimaryKeyMetaData() != null && cmd.getPrimaryKeyMetaData().getColumnMetaData() != null &&
 834              cmd.getPrimaryKeyMetaData().getColumnMetaData().length > 0) {
 835            colmd = cmd.getPrimaryKeyMetaData().getColumnMetaData()[0];
 836          }
 837        }
 838        addDatastoreId(colmd, null, cmd);
 839      } else if (cmd.getIdentityType() == IdentityType.NONDURABLE) {
 840        // Do nothing since no identity!
 841      }
 842    }
 843
 844    //add field mappings in the end, so we compute all columns after the post initialize
 845    for (int i = 0; i < fieldsToAdd.length; i++) {
 846      if (fieldsToAdd[i] != null) {
 847        try {
 848          DatastoreClass datastoreClass = getStoreManager()
 849              .getDatastoreClass(fieldsToAdd[i].getType().getName(), clr);
 850          if (datastoreClass.getIdMapping() == null) {
 851            throw new NucleusException(
 852                "Unsupported relationship with field " + fieldsToAdd[i].getFullFieldName())
 853                .setFatal();
 854          }
 855        }
 856        catch (NoTableManagedException ex) {
 857          //do nothing
 858        }
 859        JavaTypeMapping fieldMapping = dba.getMappingManager(storeMgr)
 860            .getMapping(this, fieldsToAdd[i], clr, FieldRole.ROLE_FIELD);
 861        addFieldMapping(fieldMapping);
 862        pkMappings[i] = fieldMapping;
 863      }
 864    }
 865    initializeIDMapping();
 866  }
 867
 868  private void initializeDiscriminatorMapping(DiscriminatorMetaData dismd) {
 869    if (dismd != null) {
 870      discriminatorMetaData = dismd;
 871      if (dismd.getStrategy() == DiscriminatorStrategy.CLASS_NAME) {
 872        discriminatorMapping = new DiscriminatorStringMapping(
 873            dba, this, dba.getMappingManager(storeMgr).getMapping(String.class));
 874      } else if (dismd.getStrategy() == DiscriminatorStrategy.VALUE_MAP) {
 875        ColumnMetaData disColmd = dismd.getColumnMetaData();
 876        if (disColmd != null && disColmd.getJdbcType() != null) {
 877          if (disColmd.getJdbcType().equalsIgnoreCase("INTEGER")
 878              || disColmd.getJdbcType().equalsIgnoreCase("BIGINT")
 879              || disColmd.getJdbcType().equalsIgnoreCase("NUMERIC")) {
 880            discriminatorMapping = new DiscriminatorLongMapping(dba, this, dba
 881                .getMappingManager(storeMgr).getMapping(Long.class));
 882          } else {
 883            discriminatorMapping = new DiscriminatorStringMapping(
 884                dba, this, dba.getMappingManager(storeMgr).getMapping(String.class));
 885          }
 886        } else {
 887          discriminatorMapping = new DiscriminatorStringMapping(
 888              dba, this, dba.getMappingManager(storeMgr).getMapping(String.class));
 889        }
 890      }
 891    }
 892  }
 893
 894  /**
 895   * Initialize the ID Mapping
 896   */
 897  private void initializeIDMapping() {
 898    if (idMapping != null) {
 899      return;
 900    }
 901
 902    final PersistableMapping mapping = new PersistableMapping();
 903    mapping.initialize(getStoreManager(), cmd.getFullClassName());
 904    if (getIdentityType() == IdentityType.DATASTORE) {
 905      mapping.addJavaTypeMapping(datastoreIDMapping);
 906    } else if (getIdentityType() == IdentityType.APPLICATION) {
 907      for (JavaTypeMapping pkMapping : pkMappings) {
 908        mapping.addJavaTypeMapping(pkMapping);
 909      }
 910    } else {
 911      // Nothing to do for nondurable since no identity
 912    }
 913
 914    idMapping = mapping;
 915  }
 916
 917  final void addApplicationIdUsingClassTableId(ColumnMetaDataContainer columnContainer,
 918      DatastoreClass refTable, ClassLoaderResolver clr, AbstractClassMetaData cmd) {
 919    ColumnMetaData[] userdefinedCols = null;
 920    int nextUserdefinedCol = 0;
 921    if (columnContainer != null) {
 922      userdefinedCols = columnContainer.getColumnMetaData();
 923    }
 924
 925    pkMappings = new JavaTypeMapping[cmd.getPKMemberPositions().length];
 926    for (int i = 0; i < cmd.getPKMemberPositions().length; i++) {
 927      AbstractMemberMetaData fmd = cmd
 928          .getMetaDataForManagedMemberAtAbsolutePosition(cmd.getPKMemberPositions()[i]);
 929      JavaTypeMapping mapping = refTable.getMemberMapping(fmd);
 930      if (mapping == null) {
 931        //probably due to invalid metadata defined by the user
 932        throw new NucleusUserException("Cannot find mapping for field " + fmd.getFullFieldName() +
 933            " in table " + refTable.toString() + " " +
 934            StringUtils.objectArrayToString(refTable.getDatastoreFields()));
 935      }
 936
 937      JavaTypeMapping masterMapping = storeMgr.getMappingManager()
 938          .getMapping(clr.classForName(mapping.getType()));
 939      masterMapping.setMemberMetaData(fmd); // Update field info in mapping
 940      pkMappings[i] = masterMapping;
 941
 942      // Loop through each id column in the reference table and add the same here
 943      // applying the required names from the columnContainer
 944      for (int j = 0; j < mapping.getNumberOfDatastoreMappings(); j++) {
 945        JavaTypeMapping m = masterMapping;
 946        DatastoreField refColumn = mapping.getDatastoreMapping(j).getDatastoreField();
 947        if (mapping instanceof PersistableMapping) {
 948          m = storeMgr.getMappingManager()
 949              .getMapping(clr.classForName(refColumn.getJavaTypeMapping().getType()));
 950          ((PersistableMapping) masterMapping).addJavaTypeMapping(m);
 951        }
 952
 953        ColumnMetaData userdefinedColumn = null;
 954        if (userdefinedCols != null) {
 955          for (ColumnMetaData userdefinedCol : userdefinedCols) {
 956            if (refColumn.getIdentifier().toString().equals(userdefinedCol.getTarget())) {
 957              userdefinedColumn = userdefinedCol;
 958              break;
 959            }
 960          }
 961          if (userdefinedColumn == null && nextUserdefinedCol < userdefinedCols.length) {
 962            userdefinedColumn = userdefinedCols[nextUserdefinedCol++];
 963          }
 964        }
 965
 966        // Add this application identity column
 967        DatastoreField idColumn;
 968        if (userdefinedColumn != null) {
 969          // User has provided a name for this column
 970          // Currently we only use the column namings from the users definition but we could easily
 971          // take more of their details.
 972          idColumn = addDatastoreField(refColumn.getStoredJavaType(),
 973              storeMgr.getIdentifierFactory().newIdentifier(IdentifierType.COLUMN,
 974                  userdefinedColumn.getName()),
 975              m, refColumn.getColumnMetaData());
 976        } else {
 977          // No name provided so take same as superclass
 978          idColumn = addDatastoreField(refColumn.getStoredJavaType(), refColumn.getIdentifier(),
 979              m, refColumn.getColumnMetaData());
 980        }
 981        if (mapping != null
 982            && mapping.getDatastoreMapping(j).getDatastoreField().getColumnMetaData() != null) {
 983          refColumn.copyConfigurationTo(idColumn);
 984        }
 985        idColumn.setAsPrimaryKey();
 986
 987        // Set the column type based on the field.getType()
 988        getStoreManager().getMappingManager()
 989            .createDatastoreMapping(m, idColumn, refColumn.getJavaTypeMapping().getType());
 990      }
 991
 992      // Update highest field number if this is higher
 993      int absoluteFieldNumber = fmd.getAbsoluteFieldNumber();
 994      if (absoluteFieldNumber > highestFieldNumber) {
 995        highestFieldNumber = absoluteFieldNumber;
 996      }
 997    }
 998  }
 999
1000  public void provideDatastoreIdMappings(MappingConsumer consumer) {
1001    consumer.preConsumeMapping(highestFieldNumber + 1);
1002
1003    if (getIdentityType() == IdentityType.DATASTORE) {
1004      consumer.consumeMapping(getDatastoreObjectIdMapping(), MappingConsumer.MAPPING_TYPE_DATASTORE_ID);
1005    }
1006  }
1007
1008  public void providePrimaryKeyMappings(MappingConsumer consumer) {
1009    consumer.preConsumeMapping(highestFieldNumber + 1);
1010
1011    if (pkMappings != null) {
1012      // Application identity
1013      int[] primaryKeyFieldNumbers = cmd.getPKMemberPositions();
1014      for (int i = 0; i < pkMappings.length; i++) {
1015        // Make the assumption that the pkMappings are in the same order as the absolute field numbers
1016        AbstractMemberMetaData fmd = cmd
1017            .getMetaDataForManagedMemberAtAbsolutePosition(primaryKeyFieldNumbers[i]);
1018        consumer.consumeMapping(pkMappings[i], fmd);
1019      }
1020    } else {
1021      // Datastore identity
1022      int[] primaryKeyFieldNumbers = cmd.getPKMemberPositions();
1023      int countPkFields = cmd.getNoOfPrimaryKeyMembers();
1024      for (int i = 0; i < countPkFields; i++) {
1025        AbstractMemberMetaData pkfmd = cmd
1026            .getMetaDataForManagedMemberAtAbsolutePosition(primaryKeyFieldNumbers[i]);
1027        consumer.consumeMapping(getMemberMapping(pkfmd), pkfmd);
1028      }
1029    }
1030  }
1031
1032  public void provideNonPrimaryKeyMappings(MappingConsumer consumer) {
1033    provideNonPrimaryKeyMappings(consumer, false);
1034  }
1035
1036  public void provideNonPrimaryKeyMappings(MappingConsumer consumer, boolean isEmbedded) {
1037    consumer.preConsumeMapping(highestFieldNumber + 1);
1038
1039    Set<Map.Entry<AbstractMemberMetaData, JavaTypeMapping>> entries =
1040        isEmbedded ? embeddedFieldMappingsMap.entrySet() : fieldMappingsMap.entrySet();
1041    for (Map.Entry<AbstractMemberMetaData, JavaTypeMapping> entry : entries) {
1042      if (entry.getValue() != null) {
1043        if (!entry.getKey().isPrimaryKey() || isEmbedded) {
1044          consumer.consumeMapping(entry.getValue(), entry.getKey());
1045        }
1046      }
1047    }
1048  }
1049
1050  public void provideMappingsForMembers(MappingConsumer consumer, AbstractMemberMetaData[] mmds,
1051      boolean includeSecondaryTables) {
1052    for (AbstractMemberMetaData aFieldMetaData : mmds) {
1053      JavaTypeMapping fieldMapping = fieldMappingsMap.get(aFieldMetaData);
1054      if (fieldMapping != null) {
1055        if (!aFieldMetaData.isPrimaryKey()) {
1056          consumer.consumeMapping(fieldMapping, aFieldMetaData);
1057        }
1058      }
1059    }
1060  }
1061
1062  public void provideVersionMappings(MappingConsumer consumer) {
1063  }
1064
1065  public void provideDiscriminatorMappings(MappingConsumer consumer) {
1066    consumer.preConsumeMapping(highestFieldNumber + 1);
1067    if (getDiscriminatorMapping(false) != null) {
1068      consumer.consumeMapping(getDiscriminatorMapping(false),
1069                              MappingConsumer.MAPPING_TYPE_DISCRIMINATOR);
1070    }
1071  }
1072
1073  public void provideUnmappedDatastoreFields(MappingConsumer consumer) {
1074  }
1075
1076  public void provideExternalMappings(MappingConsumer consumer, int mappingType) {
1077  }
1078
1079  public JavaTypeMapping getExternalMapping(AbstractMemberMetaData fmd, int mappingType) {
1080    if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK) {
1081      return getExternalFkMappings().get(fmd);
1082    } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK_DISCRIM) {
1083      return null; //getExternalFkDiscriminatorMappings().get(fmd);
1084    } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_INDEX) {
1085      return getExternalOrderMappings().get(fmd);
1086    } else {
1087      return null;
1088    }
1089  }
1090
1091  public AbstractMemberMetaData getMetaDataForExternalMapping(JavaTypeMapping mapping,
1092      int mappingType) {
1093    if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK) {
1094      Set entries = getExternalFkMappings().entrySet();
1095      for (Object entry1 : entries) {
1096        Map.Entry entry = (Map.Entry) entry1;
1097        if (entry.getValue() == mapping) {
1098          return (AbstractMemberMetaData) entry.getKey();
1099        }
1100      }
1101    }
1102    return null;
1103  }
1104
1105  public DiscriminatorMetaData getDiscriminatorMetaData() {
1106    return discriminatorMetaData;
1107  }
1108
1109  public JavaTypeMapping getDiscriminatorMapping(boolean allowSuperclasses) {
1110    return discriminatorMapping;
1111  }
1112
1113  public VersionMetaData getVersionMetaData() {
1114    return null;
1115  }
1116
1117  public JavaTypeMapping getVersionMapping(boolean allowSuperclasses) {
1118    return null;
1119  }
1120
1121  /**
1122   * Accessor for all of the external FK mappings (used by FK Lists, Collections, Arrays)
1123   * @return The mappings for the FKs of 1-Ns, keyed by the field/property metadata.
1124   */
1125  public Map<AbstractMemberMetaData, JavaTypeMapping> getExternalFkMappings() {
1126    return externalFkMappings;
1127  }
1128
1129  /**
1130   * Accessor for all of the order mappings (used by FK Lists, Collections, Arrays)
1131   * @return The mappings for the order columns of Lists, keyed by the field/property metadata.
1132   */
1133  public Map<AbstractMemberMetaData, JavaTypeMapping> getExternalOrderMappings() {
1134    return externalOrderMappings;
1135  }
1136
1137  /**
1138   * Execute the callbacks for the classes that this table maps to.
1139   */
1140  private void runCallBacks() {
1141    Collection c = (Collection) callbacks.remove(cmd.getFullClassName());
1142    if (c == null) {
1143      return;
1144    }
1145    for (Object aC : c) {
1146      CallBack callback = (CallBack) aC;
1147
1148   /* if (callback.fmd.getJoinMetaData() == null) {*/
1149        // 1-N relationship
1150        AbstractMemberMetaData ownerFmd = callback.fmd;
1151        if (ownerFmd.getMappedBy() != null) {
1152          // Bidirectional (element has a PC mapping to the owner)
1153          // Check that the "mapped-by" field in the other class actually exists
1154          AbstractMemberMetaData fmd = cmd.getMetaDataForMember(ownerFmd.getMappedBy());
1155          if (fmd == null) {
1156            throw new NucleusUserException(
1157                String.format(
1158                    "Unable to find the field \"{0}\" in the class \"{1}\" with a relationship to the field \"{2}\"",
1159                    ownerFmd.getMappedBy(),
1160                    cmd.getFullClassName(),
1161                    ownerFmd.getFullFieldName()));
1162          }
1163          // Add the order mapping as necessary
1164          addOrderMapping(ownerFmd, null);
1165        } else {
1166          // Unidirectional (element knows nothing about the owner)
1167          String ownerClassName = callback.ownerClassName;
1168          JavaTypeMapping fkMapping = new PersistableMapping();
1169          fkMapping.initialize(storeMgr, ownerClassName);
1170          JavaTypeMapping orderMapping = null;
1171
1172          // Get the owner id mapping of the "1" end
1173          JavaTypeMapping ownerIdMapping =
1174              storeMgr.getDatastoreClass(ownerClassName, clr).getIdMapping();
1175    

Large files files are truncated, but you can click here to view the full file