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.ivy.core.module.id;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.apache.ivy.core.IvyPatternHelper;
  import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
  import org.apache.ivy.plugins.matcher.MapMatcher;
  import org.apache.ivy.plugins.matcher.PatternMatcher;
  
  /**
   * This class targets to speed up lookup for exact pattern matcher by keys, which are created with
   * (organization, module) information. When exact pattern matcher is added, the key is created from
   * matcher's attributes. When matcher is looked up against specific module, the key is recreated
   * from module's attributes.
   * <p>
   * The lookup doesn't target to speed up lookup for non exact pattern matcher. All non exact
   * matchers are placed in non-keyed collection.
   * </p>
   * <p>
   * At lookup for matchers against specific module, all non exact pattern matchers are iterated to
   * match with module attributes, and exact pattern matchers binding to the same key will also
   * iterated to match with module attributes.
   * </p>
   * <p>
   * If there are much more exact pattern matchers than non exact pattern matchers, the matcher lookup
   * speed can benefit from this class significantly. A quick example could be user declares lots of
   * dependencyOverrides which are typically exact pattern matchers.
   * </p>
   * <p>
   * If there are balanced exact and non exact pattern matchers, the matcher lookup speed doesn't hurt
   * by this class.
   * </p>
   */
  public class MatcherLookup {
  
      // private static final String FORMAT = "{org:%s, module:%s}";
  
      private static final String DEFAULT = "{org:" + "default" + ", module:" + "default" + "}";
  
      private Map<String, List<MapMatcher>> lookup = new HashMap<>();
  
      private List<MapMatcher> nonExactMatchers = new ArrayList<>();
  
      /**
       * Add matcher.
       *
       * If matcher is exact pattern matcher, it will be associated with a key and placed in keyed
       * collection.
       *
       * If matcher is not exact pattern matcher, it will be placed into non-keyed collection
       *
       * @param matcher MapMatcher
       */
      public void add(MapMatcher matcher) {
          if (!(matcher.getPatternMatcher() instanceof ExactPatternMatcher)) {
              nonExactMatchers.add(matcher);
              return;
          }
          String key = key(matcher.getAttributes());
          List<MapMatcher> exactMatchers = lookup.get(key);
          if (exactMatchers == null) {
              exactMatchers = new ArrayList<>();
              lookup.put(key, exactMatchers);
          }
          exactMatchers.add(matcher);
      }
  
      /**
       * @param attrs
       *            A map of attributes that matcher should match.
       *
       * @return a list of matchers that can apply to module withs specified attributes
       */
      public List<MapMatcher> get(Map<String, String> attrs) {
          List<MapMatcher> matchers = new ArrayList<>();
          // Step 1: find matchers from nonExactMatchers list
          if (!nonExactMatchers.isEmpty()) {
              for (MapMatcher matcher : nonExactMatchers) {
                  if (matcher.matches(attrs)) {
                      matchers.add(matcher);
                  }
              }
          }
          // Step 2: find matchers from exactMatchers list of key
          String key = key(attrs);
          List<MapMatcher> exactMatchers = lookup.get(key);
          if (exactMatchers != null) {
              for (MapMatcher matcher : exactMatchers) {
                  if (matcher.matches(attrs)) {
                      matchers.add(matcher);
                  }
              }
          }
          // Step 3: (iff key != DEFAULT) find matchers from exactMatchers of DEFAULT
          if (!DEFAULT.equals(key)) {
              List<MapMatcher> defaultExactMatchers = lookup.get(DEFAULT);
              if (defaultExactMatchers != null) {
                  for (MapMatcher matcher : defaultExactMatchers) {
                      if (matcher.matches(attrs)) {
                          matchers.add(matcher);
                      }
                  }
              }
          }
          return matchers;
      }
  
      /**
       * Create a key from specified attributes
       *
       * @param attrs
       *            A map of attributes
       * @return key object
       */
      private String key(Map<String, String> attrs) {
          String org = attrs.get(IvyPatternHelper.ORGANISATION_KEY);
          String module = attrs.get(IvyPatternHelper.MODULE_KEY);
          if (org == null || PatternMatcher.ANY_EXPRESSION.equals(org) || module == null
                  || PatternMatcher.ANY_EXPRESSION.equals(module)) {
              return DEFAULT;
          }
          return "{org:" + org + ", module:" + module + "}";
      }
  
  }
  