View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.test.util;
20  
21  import java.io.BufferedReader;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.io.Reader;
25  import java.io.StringReader;
26  import java.net.URL;
27  import java.nio.charset.StandardCharsets;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Map;
34  
35  import org.eclipse.aether.artifact.Artifact;
36  import org.eclipse.aether.artifact.DefaultArtifact;
37  import org.eclipse.aether.graph.Dependency;
38  import org.eclipse.aether.graph.Exclusion;
39  import org.eclipse.aether.repository.RemoteRepository;
40  
41  /**
42   * @see IniArtifactDescriptorReader
43   */
44  class IniArtifactDataReader {
45  
46      private String prefix = "";
47  
48      /**
49       * Constructs a data reader with the prefix {@code ""}.
50       */
51      IniArtifactDataReader() {
52          this("");
53      }
54  
55      /**
56       * Constructs a data reader with the given prefix.
57       *
58       * @param prefix the prefix to use for loading resources from the classpath.
59       */
60      IniArtifactDataReader(String prefix) {
61          this.prefix = prefix;
62      }
63  
64      /**
65       * Load an artifact description from the classpath and parse it.
66       */
67      public ArtifactDescription parse(String resource) throws IOException {
68          URL res = this.getClass().getClassLoader().getResource(prefix + resource);
69  
70          if (res == null) {
71              throw new IOException("cannot find resource: " + resource);
72          }
73          return parse(res);
74      }
75  
76      /**
77       * Open the given URL and parse ist.
78       */
79      public ArtifactDescription parse(URL res) throws IOException {
80          return parse(new InputStreamReader(res.openStream(), StandardCharsets.UTF_8));
81      }
82  
83      /**
84       * Parse the given String.
85       */
86      public ArtifactDescription parseLiteral(String description) throws IOException {
87          StringReader reader = new StringReader(description);
88          return parse(reader);
89      }
90  
91      private enum State {
92          NONE,
93          RELOCATION,
94          DEPENDENCIES,
95          MANAGEDDEPENDENCIES,
96          REPOSITORIES
97      }
98  
99      private ArtifactDescription parse(Reader reader) throws IOException {
100         String line = null;
101 
102         State state = State.NONE;
103 
104         Map<State, List<String>> sections = new HashMap<>();
105 
106         BufferedReader in = null;
107         try {
108             in = new BufferedReader(reader);
109             while ((line = in.readLine()) != null) {
110 
111                 line = cutComment(line);
112                 if (isEmpty(line)) {
113                     continue;
114                 }
115 
116                 if (line.startsWith("[")) {
117                     try {
118                         String name = line.substring(1, line.length() - 1);
119                         name = name.replace("-", "").toUpperCase(Locale.ENGLISH);
120                         state = State.valueOf(name);
121                         sections.put(state, new ArrayList<>());
122                     } catch (IllegalArgumentException e) {
123                         throw new IOException("unknown section: " + line);
124                     }
125                 } else {
126                     List<String> lines = sections.get(state);
127                     if (lines == null) {
128                         throw new IOException("missing section: " + line);
129                     }
130                     lines.add(line.trim());
131                 }
132             }
133 
134             in.close();
135             in = null;
136         } finally {
137             try {
138                 if (in != null) {
139                     in.close();
140                 }
141             } catch (final IOException e) {
142                 // Suppressed due to an exception already thrown in the try block.
143             }
144         }
145 
146         Artifact relocation = relocation(sections.get(State.RELOCATION));
147         List<Dependency> dependencies = dependencies(sections.get(State.DEPENDENCIES), false);
148         List<Dependency> managedDependencies = dependencies(sections.get(State.MANAGEDDEPENDENCIES), true);
149         List<RemoteRepository> repositories = repositories(sections.get(State.REPOSITORIES));
150 
151         return new ArtifactDescription(relocation, dependencies, managedDependencies, repositories);
152     }
153 
154     private List<RemoteRepository> repositories(List<String> list) {
155         ArrayList<RemoteRepository> ret = new ArrayList<>();
156         if (list == null) {
157             return ret;
158         }
159         for (String coords : list) {
160             String[] split = coords.split(":", 3);
161             String id = split[0];
162             String type = split[1];
163             String url = split[2];
164 
165             ret.add(new RemoteRepository.Builder(id, type, url).build());
166         }
167         return ret;
168     }
169 
170     private List<Dependency> dependencies(List<String> list, boolean managed) {
171         List<Dependency> ret = new ArrayList<>();
172         if (list == null) {
173             return ret;
174         }
175 
176         Collection<Exclusion> exclusions = new ArrayList<>();
177 
178         Boolean optional = null;
179         Artifact artifact = null;
180         String scope = null;
181 
182         for (String coords : list) {
183             if (coords.startsWith("-")) {
184                 coords = coords.substring(1);
185                 String[] split = coords.split(":");
186                 exclusions.add(new Exclusion(split[0], split[1], "*", "*"));
187             } else {
188                 if (artifact != null) {
189                     // commit dependency
190                     Dependency dep = new Dependency(artifact, scope, optional, exclusions);
191                     ret.add(dep);
192 
193                     exclusions = new ArrayList<>();
194                 }
195 
196                 ArtifactDefinition def = new ArtifactDefinition(coords);
197 
198                 optional = managed ? def.getOptional() : Boolean.valueOf(Boolean.TRUE.equals(def.getOptional()));
199 
200                 scope = "".equals(def.getScope()) && !managed ? "compile" : def.getScope();
201 
202                 artifact = new DefaultArtifact(
203                         def.getGroupId(), def.getArtifactId(), "", def.getExtension(), def.getVersion());
204             }
205         }
206         if (artifact != null) {
207             // commit dependency
208             Dependency dep = new Dependency(artifact, scope, optional, exclusions);
209             ret.add(dep);
210         }
211 
212         return ret;
213     }
214 
215     private Artifact relocation(List<String> list) {
216         if (list == null || list.isEmpty()) {
217             return null;
218         }
219         String coords = list.get(0);
220         ArtifactDefinition def = new ArtifactDefinition(coords);
221         return new DefaultArtifact(def.getGroupId(), def.getArtifactId(), "", def.getExtension(), def.getVersion());
222     }
223 
224     private static boolean isEmpty(String line) {
225         return line == null || line.length() == 0;
226     }
227 
228     private static String cutComment(String line) {
229         int idx = line.indexOf('#');
230 
231         if (idx != -1) {
232             line = line.substring(0, idx);
233         }
234 
235         return line;
236     }
237 
238     static class Definition {
239         private String groupId;
240 
241         private String artifactId;
242 
243         private String extension;
244 
245         private String version;
246 
247         private String scope = "";
248 
249         private String definition;
250 
251         private String id = null;
252 
253         private String reference = null;
254 
255         private boolean optional = false;
256 
257         Definition(String def) {
258             this.definition = def.trim();
259 
260             if (definition.startsWith("(")) {
261                 int idx = definition.indexOf(')');
262                 this.id = definition.substring(1, idx);
263                 this.definition = definition.substring(idx + 1);
264             } else if (definition.startsWith("^")) {
265                 this.reference = definition.substring(1);
266                 return;
267             }
268 
269             String[] split = definition.split(":");
270             if (split.length < 4) {
271                 throw new IllegalArgumentException(
272                         "Need definition like 'gid:aid:ext:ver[:scope]', but was: " + definition);
273             }
274             groupId = split[0];
275             artifactId = split[1];
276             extension = split[2];
277             version = split[3];
278             if (split.length > 4) {
279                 scope = split[4];
280             }
281             if (split.length > 5 && "true".equalsIgnoreCase(split[5])) {
282                 optional = true;
283             }
284         }
285 
286         public String getGroupId() {
287             return groupId;
288         }
289 
290         public String getArtifactId() {
291             return artifactId;
292         }
293 
294         public String getType() {
295             return extension;
296         }
297 
298         public String getVersion() {
299             return version;
300         }
301 
302         public String getScope() {
303             return scope;
304         }
305 
306         @Override
307         public String toString() {
308             return definition;
309         }
310 
311         public String getId() {
312             return id;
313         }
314 
315         public String getReference() {
316             return reference;
317         }
318 
319         public boolean isReference() {
320             return reference != null;
321         }
322 
323         public boolean hasId() {
324             return id != null;
325         }
326 
327         public boolean isOptional() {
328             return optional;
329         }
330     }
331 }