001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.impl.scope; 020 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Comparator; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.Map; 027import java.util.Set; 028import java.util.concurrent.atomic.AtomicInteger; 029import java.util.stream.Collectors; 030import java.util.stream.Stream; 031 032import static java.util.Objects.requireNonNull; 033 034/** 035 * Generic matrix generator for {@link ProjectPath} and {@link BuildPath} combinations (all of them). 036 * 037 * @since 2.0.0 038 */ 039public final class BuildScopeMatrixSource implements BuildScopeSource { 040 private final Set<ProjectPath> projectPaths; 041 private final Set<BuildPath> buildPaths; 042 private final Map<String, BuildScope> buildScopes; 043 044 public BuildScopeMatrixSource( 045 Collection<ProjectPath> projectPaths, Collection<BuildPath> buildPaths, BuildScope... extras) { 046 requireNonNull(projectPaths, "projectPath"); 047 requireNonNull(buildPaths, "buildPaths"); 048 if (projectPaths.isEmpty() || buildPaths.isEmpty()) { 049 throw new IllegalArgumentException("empty matrix"); 050 } 051 HashMap<String, BuildScope> buildScopes = new HashMap<>(); 052 AtomicInteger counter = new AtomicInteger(0); 053 buildPaths.stream().sorted(Comparator.comparing(BuildPath::order)).forEach(buildPath -> { 054 Stream<ProjectPath> projectPathStream; 055 if (buildPath.isReverse()) { 056 projectPathStream = projectPaths.stream().sorted(Comparator.comparing(ProjectPath::reverseOrder)); 057 } else { 058 projectPathStream = projectPaths.stream().sorted(Comparator.comparing(ProjectPath::order)); 059 } 060 projectPathStream.forEach(projectPath -> { 061 String id = createId(projectPath, buildPath); 062 buildScopes.put( 063 id, 064 new BuildScopeImpl( 065 id, 066 Collections.singleton(projectPath), 067 Collections.singleton(buildPath), 068 counter.incrementAndGet())); 069 }); 070 }); 071 for (BuildScope extra : extras) { 072 buildScopes.put(extra.getId(), extra); 073 } 074 this.buildScopes = Collections.unmodifiableMap(buildScopes); 075 076 // now collect all paths 077 HashSet<ProjectPath> pp = new HashSet<>(projectPaths); 078 HashSet<BuildPath> bp = new HashSet<>(buildPaths); 079 buildScopes.values().forEach(s -> { 080 pp.addAll(s.getProjectPaths()); 081 bp.addAll(s.getBuildPaths()); 082 }); 083 this.projectPaths = Collections.unmodifiableSet(pp); 084 this.buildPaths = Collections.unmodifiableSet(bp); 085 } 086 087 private String createId(ProjectPath projectPath, BuildPath buildPath) { 088 return projectPath.getId() + "-" + buildPath.getId(); 089 } 090 091 @Override 092 public Collection<BuildScope> query(Collection<BuildScopeQuery> queries) { 093 HashSet<BuildScope> result = new HashSet<>(); 094 for (BuildScopeQuery query : queries) { 095 switch (query.getMode()) { 096 case ALL: 097 result.addAll(all()); 098 break; // we added all, whatever is after this, is unimportant 099 case BY_PROJECT_PATH: 100 result.addAll(byProjectPath(query.getProjectPath())); 101 continue; 102 case BY_BUILD_PATH: 103 result.addAll(byBuildPath(query.getBuildPath())); 104 continue; 105 case SELECT: 106 result.addAll(select(query.getProjectPath(), query.getBuildPath())); 107 continue; 108 case SINGLETON: 109 result.addAll(singleton(query.getProjectPath(), query.getBuildPath())); 110 continue; 111 default: 112 throw new IllegalArgumentException("Unsupported query"); 113 } 114 } 115 return result; 116 } 117 118 @Override 119 public Collection<ProjectPath> allProjectPaths() { 120 return projectPaths; 121 } 122 123 @Override 124 public Collection<BuildPath> allBuildPaths() { 125 return buildPaths; 126 } 127 128 private Collection<BuildScope> all() { 129 return buildScopes.values(); 130 } 131 132 private Collection<BuildScope> byProjectPath(ProjectPath projectPath) { 133 return all().stream() 134 .filter(s -> s.getProjectPaths().contains(projectPath)) 135 .collect(Collectors.toSet()); 136 } 137 138 private Collection<BuildScope> byBuildPath(BuildPath buildPath) { 139 return all().stream().filter(s -> s.getBuildPaths().contains(buildPath)).collect(Collectors.toSet()); 140 } 141 142 private Collection<BuildScope> singleton(ProjectPath projectPath, BuildPath buildPath) { 143 BuildScope result = buildScopes.get(createId(projectPath, buildPath)); 144 if (result == null) { 145 throw new IllegalArgumentException("no such build scope"); 146 } 147 return Collections.singleton(result); 148 } 149 150 private Collection<BuildScope> select(ProjectPath projectPath, BuildPath buildPath) { 151 HashSet<BuildScope> result = new HashSet<>(); 152 buildScopes.values().stream() 153 .filter(s -> s.getProjectPaths().contains(projectPath) 154 && s.getBuildPaths().contains(buildPath)) 155 .forEach(result::add); 156 return result; 157 } 158 159 private static final class BuildScopeImpl implements BuildScope { 160 private final String id; 161 private final Set<ProjectPath> projectPaths; 162 private final Set<BuildPath> buildPaths; 163 private final int order; 164 165 private BuildScopeImpl(String id, Set<ProjectPath> projectPaths, Set<BuildPath> buildPaths, int order) { 166 this.id = id; 167 this.projectPaths = projectPaths; 168 this.buildPaths = buildPaths; 169 this.order = order; 170 } 171 172 @Override 173 public String getId() { 174 return id; 175 } 176 177 @Override 178 public Set<ProjectPath> getProjectPaths() { 179 return projectPaths; 180 } 181 182 @Override 183 public Set<BuildPath> getBuildPaths() { 184 return buildPaths; 185 } 186 187 @Override 188 public int order() { 189 return order; 190 } 191 } 192}