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
   *
   * http://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.calcite.test.catalog;
  
  import org.apache.calcite.linq4j.Ord;
  import org.apache.calcite.rel.type.RelDataType;
  import org.apache.calcite.rel.type.RelDataTypeFactory;
  import org.apache.calcite.rel.type.RelDataTypeField;
  import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
  import org.apache.calcite.rel.type.StructKind;
  import org.apache.calcite.runtime.PairList;
  import org.apache.calcite.util.Pair;
  
  import java.util.AbstractList;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  /** ColumnResolver implementation that resolves CompoundNameColumn by simulating
   * Phoenix behaviors. */
  final class CompoundNameColumnResolver implements MockCatalogReader.ColumnResolver {
    private final Map<String, Integer> nameMap = new HashMap<>();
    private final Map<String, Map<String, Integer>> groupMap = new HashMap<>();
    private final String defaultColumnGroup;
  
    CompoundNameColumnResolver(
        List<CompoundNameColumn> columns, String defaultColumnGroup) {
      this.defaultColumnGroup = defaultColumnGroup;
      for (Ord<CompoundNameColumn> column : Ord.zip(columns)) {
        nameMap.put(column.e.getName(), column.i);
        Map<String, Integer> subMap =
            groupMap.computeIfAbsent(column.e.first, k -> new HashMap<>());
        subMap.put(column.e.second, column.i);
      }
    }
  
    @Override public List<Pair<RelDataTypeField, List<String>>> resolveColumn(
        RelDataType rowType, RelDataTypeFactory typeFactory, List<String> names) {
      List<Pair<RelDataTypeField, List<String>>> ret = new ArrayList<>();
      if (names.size() >= 2) {
        Map<String, Integer> subMap = groupMap.get(names.get(0));
        if (subMap != null) {
          Integer index = subMap.get(names.get(1));
          if (index != null) {
            ret.add(
                new Pair<RelDataTypeField, List<String>>(
                    rowType.getFieldList().get(index),
                    names.subList(2, names.size())));
          }
        }
      }
  
      final String columnName = names.get(0);
      final List<String> remainder = names.subList(1, names.size());
      Integer index = nameMap.get(columnName);
      if (index != null) {
        ret.add(
            new Pair<RelDataTypeField, List<String>>(
                rowType.getFieldList().get(index), remainder));
        return ret;
      }
  
      final List<String> priorityGroups = Arrays.asList("", defaultColumnGroup);
      for (String group : priorityGroups) {
        Map<String, Integer> subMap = groupMap.get(group);
        if (subMap != null) {
          index = subMap.get(columnName);
          if (index != null) {
            ret.add(
                new Pair<RelDataTypeField, List<String>>(
                    rowType.getFieldList().get(index), remainder));
            return ret;
          }
        }
      }
      for (Map.Entry<String, Map<String, Integer>> entry : groupMap.entrySet()) {
        if (priorityGroups.contains(entry.getKey())) {
          continue;
        }
        index = entry.getValue().get(columnName);
        if (index != null) {
          ret.add(new Pair<>(rowType.getFieldList().get(index), remainder));
        }
      }
  
      if (ret.isEmpty() && names.size() == 1) {
        Map<String, Integer> subMap = groupMap.get(columnName);
        if (subMap != null) {
          PairList<String, Integer> entries = PairList.of(subMap);
          entries.sort(Comparator.comparingInt(Map.Entry::getValue));
          ret.add(
              new Pair<>(
                  new RelDataTypeFieldImpl(columnName, -1,
                      createStructType(rowType, typeFactory, entries)),
                  remainder));
        }
      }
  
      return ret;
    }
  
    private static RelDataType createStructType(
        final RelDataType rowType,
        RelDataTypeFactory typeFactory,
        final List<Map.Entry<String, Integer>> entries) {
      return typeFactory.createStructType(
          StructKind.PEEK_FIELDS,
          new AbstractList<RelDataType>() {
            @Override public RelDataType get(int index) {
              final int i = entries.get(index).getValue();
              return rowType.getFieldList().get(i).getType();
            }
            @Override public int size() {
              return entries.size();
            }
          },
          new AbstractList<String>() {
            @Override public String get(int index) {
              return entries.get(index).getKey();
            }
            @Override public int size() {
              return entries.size();
            }
          });
    }
  }
  