/**
   * Used by subclasses to resolve deferred values on demand. This is useful when a certain value
   * comes from a generated key of another master object.
   */
  protected Object getValue(Map<String, Object> valueMap, DbAttribute attribute) {

    Object value = valueMap.get(attribute.getName());

    // if a value is a Factory, resolve it here...
    // slight chance that a normal value will implement Factory interface???
    if (value instanceof Factory) {
      value = ((Factory) value).create();
      valueMap.put(attribute.getName(), value);

      // update replacement id
      if (attribute.isPrimaryKey()) {
        // sanity check
        if (value == null) {
          String name = attribute.getEntity() != null ? attribute.getEntity().getName() : "<null>";
          throw new CayenneRuntimeException(
              "Failed to generate PK: " + name + "." + attribute.getName());
        }

        ObjectId id = getObjectId();
        if (id != null) {
          // always override with fresh value as this is what's in the
          // DB
          id.getReplacementIdMap().put(attribute.getName(), value);
        }
      }
    }

    return value;
  }
  /**
   * Generates a unique and non-repeating primary key for specified dbEntity.
   *
   * <p>This implementation is naive since it does not lock the database rows when executing select
   * and subsequent update. Adapter-specific implementations are more robust.
   *
   * @since 3.0
   */
  public Object generatePk(DataNode node, DbAttribute pk) throws Exception {

    DbEntity entity = (DbEntity) pk.getEntity();

    switch (pk.getType()) {
      case Types.BINARY:
      case Types.VARBINARY:
        return IDUtil.pseudoUniqueSecureByteSequence(pk.getMaxLength());
    }

    DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator();
    long cacheSize;
    if (pkGenerator != null && pkGenerator.getKeyCacheSize() != null)
      cacheSize = pkGenerator.getKeyCacheSize().intValue();
    else cacheSize = pkCacheSize;

    long value;

    // if no caching, always generate fresh
    if (cacheSize <= 1) {
      value = longPkFromDatabase(node, entity);
    } else {
      synchronized (pkCache) {
        LongPkRange r = pkCache.get(entity.getName());

        if (r == null) {
          // created exhausted LongPkRange
          r = new LongPkRange(1l, 0l);
          pkCache.put(entity.getName(), r);
        }

        if (r.isExhausted()) {
          long val = longPkFromDatabase(node, entity);
          r.reset(val, val + cacheSize - 1);
        }

        value = r.getNextPrimaryKey();
      }
    }

    if (pk.getType() == Types.BIGINT) {
      return Long.valueOf(value);
    } else {
      // leaving it up to the user to ensure that PK does not exceed max int...
      return Integer.valueOf((int) value);
    }
  }