blob: 4526cffefae7381fd155730c7c3757a3da6a919e [file] [log] [blame]
/*
* 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 + "}";
}
}