content: /*****************************************************************
   *   Licensed to the Apache Software Foundation (ASF) under one
   *  or more contributor license agreements.  See the NOTICE file
   *  distributed with this work for additional information
   *  regarding copyright ownership.  The ASF licenses this file
   *  to you under the Apache License, Version 2.0 (the
   *  "License"); you may not use this file except in compliance
   *  with the License.  You may obtain a copy of the License at
   *
   *    https://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing,
   *  software distributed under the License is distributed on an
   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   *  KIND, either express or implied.  See the License for the
   *  specific language governing permissions and limitations
   *  under the License.
   ****************************************************************/
  
  package org.apache.cayenne;
  
  import java.util.HashMap;
  import java.util.Map;
  import java.util.concurrent.atomic.AtomicLong;
  
  import org.apache.cayenne.map.DbRelationship;
  import org.apache.cayenne.util.ToStringBuilder;
  import org.apache.cayenne.util.Util;
  
  /**
   * DataRow a map that holds values retrieved from the database for a given query row.
   * DataRows are used to cache raw database data and as a reference point for tracking
   * PersistentObject changes.
   * 
   * @since 1.1
   */
  public class DataRow extends HashMap<String, Object> {
  
      // make sure the starting value is different from Persistent default version value
      private static final AtomicLong currentVersion = new AtomicLong(Persistent.DEFAULT_VERSION + 1);
  
      protected long version = currentVersion.getAndIncrement();
      protected long replacesVersion = Persistent.DEFAULT_VERSION;
  
      /**
       * @since 3.0
       */
      protected String entityName;
  
      public DataRow(Map<String, ?> map) {
          super(map);
      }
  
      public DataRow(int initialCapacity) {
          super(initialCapacity);
      }
  
      public long getVersion() {
          return version;
      }
  
      public long getReplacesVersion() {
          return replacesVersion;
      }
  
      /**
       * Sets the version of DataRow replaced by this one in the store.
       */
      public void setReplacesVersion(long replacesVersion) {
          this.replacesVersion = replacesVersion;
      }
  
      /**
       * Builds a new DataRow, merging changes from <code>diff</code> parameter with data
       * contained in this DataRow.
       */
      public DataRow applyDiff(DataRow diff) {
          DataRow merged = new DataRow(this);
  
          for (Map.Entry<String, Object> entry : diff.entrySet()) {
              merged.put(entry.getKey(), entry.getValue());
          }
  
          return merged;
      }
  
      /**
       * Creates a DataRow that contains only the keys that have values that differ between
       * this object and <code>row</code> parameter. Diff values are taken from the
       * <code>row</code> parameter. It is assumed that key sets are compatible in both rows
       * (e.g. they represent snapshots for the same entity). Returns null if no differences
       * are found.
       */
      public DataRow createDiff(DataRow row) {
  
          // build a diff...
          DataRow diff = null;
  
          for (Map.Entry<String, Object> entry : entrySet()) {
  
              String key = entry.getKey();
              Object currentValue = entry.getValue();
              Object rowValue = row.get(key);
  
              if (!Util.nullSafeEquals(currentValue, rowValue)) {
                  if (diff == null) {
                      diff = new DataRow(this.size());
                  }
                  diff.put(key, rowValue);
              }
          }
  
          return diff;
      }
  
      /**
       * Returns an ObjectId of an object on the other side of the to-one relationship, for
       * this DataRow representing a source of relationship. Returns null if snapshot FK
       * columns indicate a null to-one relationship.
       */
      public ObjectId createTargetObjectId(String entityName, DbRelationship relationship) {
  
          if (relationship.isToMany()) {
              throw new CayenneRuntimeException("Only 'to one' can have a target ObjectId.");
          }
  
          Map<String, Object> target = relationship.targetPkSnapshotWithSrcSnapshot(this);
          return (target != null) ? ObjectId.of(entityName, target) : null;
      }
  
      @Override
      public String toString() {
          return new ToStringBuilder(this).append("values", super.toString()).append(
                  " version",
                  version).append(" replaces", replacesVersion).toString();
      }
  
      /**
       * @since 3.0
       */
      public String getEntityName() {
          return entityName;
      }
  
      /**
       * @since 3.0
       */
      public void setEntityName(String entityName) {
          this.entityName = entityName;
      }
  }
  