/*
 * Copyright DataStax, Inc.
 *
 * Licensed 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 com.datastax.oss.driver.internal.core.data;

import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.data.AccessibleByName;
import com.datastax.oss.driver.api.core.data.GettableById;
import com.datastax.oss.driver.api.core.data.GettableByName;
import com.datastax.oss.driver.internal.core.util.Strings;
import com.datastax.oss.driver.shaded.guava.common.collect.LinkedListMultimap;
import com.datastax.oss.driver.shaded.guava.common.collect.ListMultimap;
import java.util.List;
import java.util.Locale;
import net.jcip.annotations.Immutable;

/**
 * Indexes an ordered list of identifiers.
 *
 * @see GettableByName
 * @see GettableById
 */
@Immutable
public class IdentifierIndex {

  private final ListMultimap<CqlIdentifier, Integer> byId;
  private final ListMultimap<String, Integer> byCaseSensitiveName;
  private final ListMultimap<String, Integer> byCaseInsensitiveName;

  public IdentifierIndex(List<CqlIdentifier> ids) {
    this.byId = LinkedListMultimap.create(ids.size());
    this.byCaseSensitiveName = LinkedListMultimap.create(ids.size());
    this.byCaseInsensitiveName = LinkedListMultimap.create(ids.size());

    int i = 0;
    for (CqlIdentifier id : ids) {
      byId.put(id, i);
      byCaseSensitiveName.put(id.asInternal(), i);
      byCaseInsensitiveName.put(id.asInternal().toLowerCase(Locale.ROOT), i);
      i += 1;
    }
  }

  /**
   * Returns all occurrences of a given name, given the matching rules described in {@link
   * AccessibleByName}.
   */
  public List<Integer> allIndicesOf(String name) {
    return Strings.isDoubleQuoted(name)
        ? byCaseSensitiveName.get(Strings.unDoubleQuote(name))
        : byCaseInsensitiveName.get(name.toLowerCase(Locale.ROOT));
  }

  /**
   * Returns the first occurrence of a given name, given the matching rules described in {@link
   * AccessibleByName}, or -1 if it's not in the list.
   */
  public int firstIndexOf(String name) {
    List<Integer> indices = allIndicesOf(name);
    return indices.isEmpty() ? -1 : indices.get(0);
  }

  /** Returns all occurrences of a given identifier. */
  public List<Integer> allIndicesOf(CqlIdentifier id) {
    return byId.get(id);
  }

  /** Returns the first occurrence of a given identifier, or -1 if it's not in the list. */
  public int firstIndexOf(CqlIdentifier id) {
    List<Integer> indices = allIndicesOf(id);
    return indices.isEmpty() ? -1 : indices.get(0);
  }
}

@@ -932,16 +932,176 @@
 trings;%0A
+import com.datastax.oss.driver.shaded.guava.common.collect.ArrayListMultimap;%0Aimport com.datastax.oss.driver.shaded.guava.common.collect.ImmutableListMultimap;%0A
 import c
@@ -1163,32 +1163,32 @@
 edListMultimap;%0A
-
 import com.datas
@@ -1240,24 +1240,51 @@
 stMultimap;%0A
+import java.util.Iterator;%0A
 import java.
@@ -1756,68 +1756,153 @@
     
-this.byId = LinkedListMultimap.create(ids.size
+ImmutableListMultimap.Builder%3CCqlIdentifier, Integer%3E byId = ImmutableListMultimap.builder
 ()
-)
 ;%0A    
-this.
+ImmutableListMultimap.Builder%3CString, Integer%3E 
 byCa
@@ -1911,38 +1911,41 @@
 SensitiveName = 
-Linked
+Immutable
 ListMultimap.cre
@@ -1945,37 +1945,70 @@
 map.
-create(ids.size());%0A    this.
+builder();%0A    ImmutableListMultimap.Builder%3CString, Integer%3E 
 byCa
@@ -2027,22 +2027,25 @@
 eName = 
-Linked
+Immutable
 ListMult
@@ -2053,25 +2053,16 @@
 map.
-create(ids.size()
+builder(
 );%0A%0A
@@ -2282,16 +2282,171 @@
 1;%0A    %7D
+%0A%0A    this.byId = byId.build();%0A    this.byCaseSensitiveName = byCaseSensitiveName.build();%0A    this.byCaseInsensitiveName = byCaseInsensitiveName.build();
 %0A  %7D%0A%0A  
@@ -2997,36 +2997,40 @@
 ing name) %7B%0A    
-List
+Iterator
 %3CInteger%3E indice
@@ -3051,16 +3051,27 @@
 Of(name)
+.iterator()
 ;%0A    re
@@ -3075,39 +3075,39 @@
  return indices.
-isEmpty
+hasNext
 () ? -1 : indice
@@ -3104,29 +3104,29 @@
 1 : indices.
-ge
+nex
 t(
-0
 );%0A  %7D%0A%0A  /*
@@ -3405,20 +3405,24 @@
 ) %7B%0A    
-List
+Iterator
 %3CInteger
@@ -3445,24 +3445,35 @@
 ndicesOf(id)
+.iterator()
 ;%0A    return
@@ -3485,15 +3485,15 @@
 ces.
-isEmpty
+hasNext
 () ?
@@ -3510,13 +3510,13 @@
 ces.
-ge
+nex
 t(
-0
 );%0A 

Improve IdentifierIndex firstIndexOf performance (#1615)