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.access;
  
  import org.apache.cayenne.CayenneRuntimeException;
  import org.apache.cayenne.ObjectId;
  import org.apache.cayenne.ResultIterator;
  import org.apache.cayenne.map.DataMap;
  import org.apache.cayenne.query.Query;
  import org.apache.cayenne.query.QueryMetadata;
  import org.apache.cayenne.query.QueryRouter;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  /**
   * DataDomain query action that relies on externally provided OperationObserver to process
   * the results.
   * 
   * @since 1.2
   */
  class DataDomainLegacyQueryAction implements QueryRouter, OperationObserver {
  
      static final boolean DONE = true;
  
      DataDomain domain;
      OperationObserver callback;
      Query query;
      QueryMetadata metadata;
  
      Map<QueryEngine, List<Query>> queriesByNode;
      Map<Query, Query> queriesByExecutedQueries;
  
      DataDomainLegacyQueryAction(DataDomain domain, Query query, OperationObserver callback) {
          this.domain = domain;
          this.query = query;
          this.metadata = query.getMetaData(domain.getEntityResolver());
          this.callback = callback;
      }
  
      /*
       * Gets response from the underlying DataNodes.
       */
      final void execute() {
  
          // reset
          queriesByNode = null;
          queriesByExecutedQueries = null;
  
          // categorize queries by node and by "executable" query...
          query.route(this, domain.getEntityResolver(), null);
  
          // run categorized queries
          if (queriesByNode != null) {
              for (final Map.Entry<QueryEngine, List<Query>> entry : queriesByNode
                      .entrySet()) {
                  QueryEngine nextNode = entry.getKey();
                  Collection<Query> nodeQueries = entry.getValue();
                  nextNode.performQueries(nodeQueries, this);
              }
          }
      }
  
      @Override
      public void route(QueryEngine engine, Query query, Query substitutedQuery) {
  
          List<Query> queries = null;
          if (queriesByNode == null) {
              queriesByNode = new HashMap<>();
          }
          else {
              queries = queriesByNode.get(engine);
          }
  
          if (queries == null) {
              queries = new ArrayList<>(5);
              queriesByNode.put(engine, queries);
          }
  
          queries.add(query);
  
          // handle case when routing resuled in an "executable" query different from the
          // original query.
          if (substitutedQuery != null && substitutedQuery != query) {
  
              if (queriesByExecutedQueries == null) {
                  queriesByExecutedQueries = new HashMap<>();
              }
  
              queriesByExecutedQueries.put(query, substitutedQuery);
          }
      }
  
      @Override
      public QueryEngine engineForDataMap(DataMap map) {
          if (map == null) {
              throw new NullPointerException("Null DataMap, can't determine DataNode.");
          }
  
          QueryEngine node = domain.lookupDataNode(map);
  
          if (node == null) {
              throw new CayenneRuntimeException("No DataNode exists for DataMap %s", map);
          }
  
          return node;
      }
      
      /**
       * @since 4.0
       */
      @Override
      public QueryEngine engineForName(String name) {
  
          QueryEngine node;
  
          if (name != null) {
              node = domain.getDataNode(name);
              if (node == null) {
                  throw new CayenneRuntimeException("No DataNode exists for name %s", name);
              }
          } else {
              node = domain.getDefaultNode();
              if (node == null) {
                  throw new CayenneRuntimeException("No default DataNode exists.");
              }
          }
  
          return node;
      }
  
      @Override
      public void nextCount(Query query, int resultCount) {
          callback.nextCount(queryForExecutedQuery(query), resultCount);
      }
  
      @Override
      public void nextBatchCount(Query query, int[] resultCount) {
          callback.nextBatchCount(queryForExecutedQuery(query), resultCount);
      }
  
      @Override
      public void nextRows(Query query, List<?> dataRows) {
          callback.nextRows(queryForExecutedQuery(query), dataRows);
      }
  
      @Override
      public void nextRows(Query q, ResultIterator it) {
          callback.nextRows(queryForExecutedQuery(q), it);
      }
  
      @Override
      public void nextGeneratedRows(Query query, ResultIterator<?> keys, List<ObjectId> idsToUpdate) {
          callback.nextGeneratedRows(queryForExecutedQuery(query), keys, idsToUpdate);
      }
  
      @Override
      public void nextQueryException(Query query, Exception ex) {
          callback.nextQueryException(queryForExecutedQuery(query), ex);
      }
  
      @Override
      public void nextGlobalException(Exception e) {
          callback.nextGlobalException(e);
      }
  
      @Override
      public boolean isIteratedResult() {
          return callback.isIteratedResult();
      }
  
      Query queryForExecutedQuery(Query executedQuery) {
          Query q = null;
  
          if (queriesByExecutedQueries != null) {
              q = queriesByExecutedQueries.get(executedQuery);
          }
  
          return q != null ? q : executedQuery;
      }
  }
  