View Javadoc
1   package org.apache.maven.archetype.ui.generation;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.lang3.math.NumberUtils;
23  import org.apache.maven.archetype.catalog.Archetype;
24  import org.apache.maven.archetype.ui.ArchetypeDefinition;
25  import org.apache.maven.artifact.versioning.ArtifactVersion;
26  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
27  import org.codehaus.plexus.component.annotations.Component;
28  import org.codehaus.plexus.component.annotations.Requirement;
29  import org.codehaus.plexus.components.interactivity.Prompter;
30  import org.codehaus.plexus.components.interactivity.PrompterException;
31  import org.codehaus.plexus.logging.AbstractLogEnabled;
32  
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.HashMap;
36  import java.util.HashSet;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Set;
40  import java.util.SortedMap;
41  import java.util.TreeMap;
42  
43  @Component( role = ArchetypeSelectionQueryer.class, hint = "default" )
44  public class DefaultArchetypeSelectionQueryer
45      extends AbstractLogEnabled
46      implements ArchetypeSelectionQueryer
47  {
48      @Requirement( hint = "archetype" )
49      private Prompter prompter;
50  
51      @Override
52      public boolean confirmSelection( ArchetypeDefinition archetypeDefinition )
53          throws PrompterException
54      {
55          String query =
56              "Confirm archetype selection: \n" + archetypeDefinition.getGroupId() + "/" + archetypeDefinition.getName()
57                  + "\n";
58  
59          String answer = prompter.prompt( query, Arrays.asList( new String[]{ "Y", "N" } ), "Y" );
60  
61          return "Y".equalsIgnoreCase( answer );
62      }
63  
64      @Override
65      public Archetype selectArchetype( Map<String, List<Archetype>> catalogs )
66          throws PrompterException
67      {
68          return selectArchetype( catalogs, null );
69      }
70  
71      @Override
72      public Archetype selectArchetype( Map<String, List<Archetype>> catalogs, ArchetypeDefinition defaultDefinition )
73          throws PrompterException
74      {
75          Archetype selection = null;
76          Map<String, List<Archetype>> filteredCatalogs = catalogs;
77  
78          do
79          {
80              StringBuilder query = new StringBuilder( "Choose archetype:\n" );
81  
82              Set<String> archetypeKeys = new HashSet<>();
83              List<String> answers = new ArrayList<>();
84              Map<String, Archetype> archetypeAnswerMap = new HashMap<>();
85  
86              int counter = 0;
87              int defaultSelection = 0;
88  
89              for ( Map.Entry<String, List<Archetype>> entry : filteredCatalogs.entrySet() )
90              {
91                  String catalog = entry.getKey();
92  
93                  for ( Archetype archetype : entry.getValue() )
94                  {
95                      String archetypeKey = archetype.getGroupId() + ":" + archetype.getArtifactId();
96  
97                      if ( !archetypeKeys.add( archetypeKey ) )
98                      {
99                          continue;
100                     }
101 
102                     counter++;
103 
104                     String description = archetype.getDescription();
105                     if ( description == null )
106                     {
107                         description = "-";
108                     }
109 
110                     String answer = String.valueOf( counter );
111 
112                     query.append( answer + ": " + catalog + " -> " + archetype.getGroupId() + ":"
113                         + archetype.getArtifactId() + " (" + description + ")\n" );
114 
115                     answers.add( answer );
116 
117                     archetypeAnswerMap.put( answer, archetype );
118 
119                     // the version is not tested. This is intentional.
120                     if ( defaultDefinition != null && archetype.getGroupId().equals( defaultDefinition.getGroupId() )
121                         && archetype.getArtifactId().equals( defaultDefinition.getArtifactId() ) )
122                     {
123                         defaultSelection = counter;
124                     }
125                 }
126             }
127 
128             if ( counter == 0 )
129             {
130                 query.append( "   Your filter doesn't match any archetype (hint: enter to return to initial list)\n" );
131             }
132 
133             query.append( "Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): " );
134 
135             String answer;
136             if ( defaultSelection == 0 )
137             {
138                 answer = prompter.prompt( query.toString() );
139             }
140             else
141             {
142                 answer = prompter.prompt( query.toString(), Integer.toString( defaultSelection ) );
143             }
144 
145             if ( NumberUtils.isNumber( answer ) )
146             {
147                 selection = archetypeAnswerMap.get( answer );
148             }
149             else
150             {
151                 // not a number so apply filter and ask again
152                 filteredCatalogs = ArchetypeSelectorUtils.getFilteredArchetypesByCatalog( catalogs, answer );
153             }
154         }
155         while ( selection == null );
156 
157         return selectVersion( catalogs, selection.getGroupId(), selection.getArtifactId() );
158     }
159 
160     private Archetype selectVersion( Map<String, List<Archetype>> catalogs, String groupId, String artifactId )
161         throws PrompterException
162     {
163         SortedMap<ArtifactVersion, Archetype> archetypeVersionsMap = new TreeMap<>();
164 
165         for ( Map.Entry<String, List<Archetype>> entry : catalogs.entrySet() )
166         {
167             for ( Archetype archetype : entry.getValue() )
168             {
169                 if ( !groupId.equals( archetype.getGroupId() ) || !artifactId.equals( archetype.getArtifactId() ) )
170                 {
171                     continue;
172                 }
173 
174                 ArtifactVersion version = new DefaultArtifactVersion( archetype.getVersion() );
175 
176                 // don't override the first catalog containing a defined version of the artifact
177                 if ( !archetypeVersionsMap.containsKey( version ) )
178                 {
179                     archetypeVersionsMap.put( version, archetype );
180                 }
181             }
182         }
183 
184         if ( archetypeVersionsMap.size() == 1 )
185         {
186             return archetypeVersionsMap.values().iterator().next();
187         }
188 
189         // let the user choose between available versions
190         StringBuilder query = new StringBuilder( "Choose " + groupId + ":" + artifactId + " version: \n" );
191 
192         List<String> answers = new ArrayList<>();
193         Map<String, Archetype> answerMap = new HashMap<>();
194 
195         int counter = 1;
196         String mapKey = null;
197 
198         for ( Map.Entry<ArtifactVersion, Archetype> entry : archetypeVersionsMap.entrySet() )
199         {
200             ArtifactVersion version = entry.getKey();
201             Archetype archetype = entry.getValue();
202 
203             mapKey = String.valueOf( counter );
204 
205             query.append( mapKey + ": " + version + "\n" );
206 
207             answers.add( mapKey );
208 
209             answerMap.put( mapKey, archetype );
210 
211             counter++;
212         }
213 
214         query.append( "Choose a number: " );
215 
216         Archetype archetype = null;
217 
218         do
219         {
220             String answer = prompter.prompt( query.toString(), answers, mapKey );
221 
222             archetype = answerMap.get( answer );
223         }
224         while ( archetype == null );
225 
226         return archetype;
227     }
228 
229     public void setPrompter( Prompter prompter )
230     {
231         this.prompter = prompter;
232     }
233 }