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}