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.util.repository; 020 021import java.io.File; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.LinkedHashSet; 027import java.util.List; 028import java.util.Objects; 029import java.util.concurrent.atomic.AtomicReference; 030 031import org.eclipse.aether.artifact.Artifact; 032import org.eclipse.aether.repository.WorkspaceReader; 033import org.eclipse.aether.repository.WorkspaceRepository; 034 035import static java.util.Objects.requireNonNull; 036 037/** 038 * A workspace reader that delegates to a chain of other readers, effectively aggregating their contents. 039 */ 040public final class ChainedWorkspaceReader implements WorkspaceReader { 041 private final List<WorkspaceReader> readers; 042 private final AtomicReference<WorkspaceRepository> repository; 043 044 /** 045 * Creates a new workspace reader by chaining the specified readers. 046 * 047 * @param readers the readers to chain, may be {@code null} 048 * @see #newInstance(WorkspaceReader, WorkspaceReader) 049 */ 050 public ChainedWorkspaceReader(WorkspaceReader... readers) { 051 ArrayList<WorkspaceReader> list = new ArrayList<>(); 052 if (readers != null) { 053 Arrays.stream(readers).filter(Objects::nonNull).forEach(list::add); 054 } 055 StringBuilder buffer = new StringBuilder(); 056 for (WorkspaceReader reader : list) { 057 if (buffer.length() > 0) { 058 buffer.append('+'); 059 } 060 buffer.append(reader.getRepository().getContentType()); 061 } 062 this.readers = Collections.unmodifiableList(list); 063 this.repository = new AtomicReference<>(new WorkspaceRepository(buffer.toString(), new Key(list))); 064 } 065 066 /** 067 * Creates a new workspace reader by chaining the specified readers. In contrast to the constructor, this factory 068 * method will avoid creating an actual chained reader if one of the specified readers is actually {@code null}. 069 * 070 * @param reader1 the first workspace reader, may be {@code null} 071 * @param reader2 the second workspace reader, may be {@code null} 072 * @return the chained reader or {@code null} if no workspace reader was supplied 073 */ 074 public static WorkspaceReader newInstance(WorkspaceReader reader1, WorkspaceReader reader2) { 075 if (reader1 == null) { 076 return reader2; 077 } else if (reader2 == null) { 078 return reader1; 079 } 080 return new ChainedWorkspaceReader(reader1, reader2); 081 } 082 083 @Override 084 public File findArtifact(Artifact artifact) { 085 requireNonNull(artifact, "artifact cannot be null"); 086 File file = null; 087 088 for (WorkspaceReader reader : readers) { 089 file = reader.findArtifact(artifact); 090 if (file != null) { 091 break; 092 } 093 } 094 095 return file; 096 } 097 098 @Override 099 public List<String> findVersions(Artifact artifact) { 100 requireNonNull(artifact, "artifact cannot be null"); 101 Collection<String> versions = new LinkedHashSet<>(); 102 103 for (WorkspaceReader reader : readers) { 104 versions.addAll(reader.findVersions(artifact)); 105 } 106 107 return Collections.unmodifiableList(new ArrayList<>(versions)); 108 } 109 110 @Override 111 public WorkspaceRepository getRepository() { 112 Key key = new Key(readers); 113 return repository.updateAndGet(r -> { 114 if (!key.equals(r.getKey())) { 115 return new WorkspaceRepository(r.getContentType(), key); 116 } else { 117 return r; 118 } 119 }); 120 } 121 122 private static class Key { 123 private final List<Object> keys; 124 125 Key(List<WorkspaceReader> readers) { 126 ArrayList<Object> keys = new ArrayList<>(); 127 for (WorkspaceReader reader : readers) { 128 keys.add(reader.getRepository().getKey()); 129 } 130 this.keys = keys; 131 } 132 133 @Override 134 public boolean equals(Object obj) { 135 if (this == obj) { 136 return true; 137 } 138 if (obj == null || !getClass().equals(obj.getClass())) { 139 return false; 140 } 141 return keys.equals(((Key) obj).keys); 142 } 143 144 @Override 145 public int hashCode() { 146 return keys.hashCode(); 147 } 148 } 149}