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.apache.maven.archetype.ui.generation;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.SortedMap;
32  import java.util.TreeMap;
33  
34  import org.apache.commons.lang3.math.NumberUtils;
35  import org.apache.maven.archetype.catalog.Archetype;
36  import org.apache.maven.archetype.ui.ArchetypeDefinition;
37  import org.apache.maven.artifact.versioning.ArtifactVersion;
38  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
39  import org.codehaus.plexus.components.interactivity.Prompter;
40  import org.codehaus.plexus.components.interactivity.PrompterException;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  @Named("default")
45  @Singleton
46  public class DefaultArchetypeSelectionQueryer implements ArchetypeSelectionQueryer {
47      private static final Logger LOGGER = LoggerFactory.getLogger(DefaultArchetypeSelectionQueryer.class);
48  
49      @Inject
50      @Named("archetype")
51      private Prompter prompter;
52  
53      @Override
54      public Archetype selectArchetype(Map<String, List<Archetype>> catalogs) throws PrompterException {
55          return selectArchetype(catalogs, null);
56      }
57  
58      @Override
59      public Archetype selectArchetype(Map<String, List<Archetype>> catalogs, ArchetypeDefinition defaultDefinition)
60              throws PrompterException {
61          Archetype selection = null;
62          Map<String, List<Archetype>> filteredCatalogs = catalogs;
63  
64          do {
65              StringBuilder query = new StringBuilder("Choose archetype:\n");
66  
67              Set<String> archetypeKeys = new HashSet<>();
68              List<String> answers = new ArrayList<>();
69              Map<String, Archetype> archetypeAnswerMap = new HashMap<>();
70  
71              int counter = 0;
72              int defaultSelection = 0;
73  
74              for (Map.Entry<String, List<Archetype>> entry : filteredCatalogs.entrySet()) {
75                  String catalog = entry.getKey();
76  
77                  for (Archetype archetype : entry.getValue()) {
78                      String archetypeKey = archetype.getGroupId() + ":" + archetype.getArtifactId();
79  
80                      if (!archetypeKeys.add(archetypeKey)) {
81                          continue;
82                      }
83  
84                      counter++;
85  
86                      String description = archetype.getDescription();
87                      if (description == null) {
88                          description = "-";
89                      }
90  
91                      String answer = String.valueOf(counter);
92  
93                      query.append(answer + ": " + catalog + " -> " + archetype.getGroupId() + ":"
94                              + archetype.getArtifactId() + " (" + description + ")\n");
95  
96                      answers.add(answer);
97  
98                      archetypeAnswerMap.put(answer, archetype);
99  
100                     // the version is not tested. This is intentional.
101                     if (defaultDefinition != null
102                             && archetype.getGroupId().equals(defaultDefinition.getGroupId())
103                             && archetype.getArtifactId().equals(defaultDefinition.getArtifactId())) {
104                         defaultSelection = counter;
105                     }
106                 }
107             }
108 
109             if (counter == 0) {
110                 query.append("   Your filter doesn't match any archetype (hint: enter to return to initial list)\n");
111             }
112 
113             query.append("Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): ");
114 
115             String answer;
116             if (defaultSelection == 0) {
117                 answer = prompter.prompt(query.toString());
118             } else {
119                 answer = prompter.prompt(query.toString(), Integer.toString(defaultSelection));
120             }
121 
122             if (NumberUtils.isCreatable(answer)) {
123                 selection = archetypeAnswerMap.get(answer);
124             } else {
125                 // not a number so apply filter and ask again
126                 filteredCatalogs = ArchetypeSelectorUtils.getFilteredArchetypesByCatalog(catalogs, answer);
127             }
128         } while (selection == null);
129 
130         return selectVersion(catalogs, selection.getGroupId(), selection.getArtifactId());
131     }
132 
133     private Archetype selectVersion(Map<String, List<Archetype>> catalogs, String groupId, String artifactId)
134             throws PrompterException {
135         SortedMap<ArtifactVersion, Archetype> archetypeVersionsMap = new TreeMap<>();
136 
137         for (Map.Entry<String, List<Archetype>> entry : catalogs.entrySet()) {
138             for (Archetype archetype : entry.getValue()) {
139                 if (!groupId.equals(archetype.getGroupId()) || !artifactId.equals(archetype.getArtifactId())) {
140                     continue;
141                 }
142 
143                 ArtifactVersion version = new DefaultArtifactVersion(archetype.getVersion());
144 
145                 // don't override the first catalog containing a defined version of the artifact
146                 if (!archetypeVersionsMap.containsKey(version)) {
147                     archetypeVersionsMap.put(version, archetype);
148                 }
149             }
150         }
151 
152         if (archetypeVersionsMap.size() == 1) {
153             return archetypeVersionsMap.values().iterator().next();
154         }
155 
156         // let the user choose between available versions
157         StringBuilder query = new StringBuilder("Choose " + groupId + ":" + artifactId + " version: \n");
158 
159         List<String> answers = new ArrayList<>();
160         Map<String, Archetype> answerMap = new HashMap<>();
161 
162         int counter = 1;
163         String mapKey = null;
164 
165         for (Map.Entry<ArtifactVersion, Archetype> entry : archetypeVersionsMap.entrySet()) {
166             ArtifactVersion version = entry.getKey();
167             Archetype archetype = entry.getValue();
168 
169             mapKey = String.valueOf(counter);
170 
171             query.append(mapKey + ": " + version + "\n");
172 
173             answers.add(mapKey);
174 
175             answerMap.put(mapKey, archetype);
176 
177             counter++;
178         }
179 
180         query.append("Choose a number: ");
181 
182         Archetype archetype = null;
183 
184         do {
185             String answer = prompter.prompt(query.toString(), answers, mapKey);
186 
187             archetype = answerMap.get(answer);
188         } while (archetype == null);
189 
190         return archetype;
191     }
192 
193     public void setPrompter(Prompter prompter) {
194         this.prompter = prompter;
195     }
196 }