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.plugin.ide;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.artifact.factory.ArtifactFactory;
29  import org.apache.maven.artifact.repository.ArtifactRepository;
30  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
31  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
32  import org.apache.maven.artifact.resolver.ArtifactResolver;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.model.PluginExecution;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugin.eclipse.Messages;
38  import org.apache.maven.plugin.logging.Log;
39  import org.apache.maven.project.MavenProject;
40  import org.codehaus.plexus.util.FileUtils;
41  import org.codehaus.plexus.util.PropertyUtils;
42  import org.codehaus.plexus.util.StringUtils;
43  import org.codehaus.plexus.util.xml.Xpp3Dom;
44  
45  /**
46   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
47   * @author <a href="mailto:fgiust@users.sourceforge.net">Fabrizio Giustina</a>
48   * @version $Id: IdeUtils.java 833043 2009-11-05 12:59:43Z nicolas $
49   */
50  public class IdeUtils
51  {
52      public static final String JAVA_1_1 = "1.1";
53  
54      public static final String JAVA_1_2 = "1.2";
55  
56      public static final String JAVA_1_3 = "1.3";
57  
58      public static final String JAVA_1_4 = "1.4";
59  
60      public static final String JAVA_5_0 = "5.0";
61  
62      public static final String JAVA_6_0 = "6.0";
63  
64      public static final String PROJECT_NAME_DEFAULT_TEMPLATE = "[artifactId]";
65  
66      public static final String PROJECT_NAME_WITH_VERSION_TEMPLATE = "[artifactId]-[version]";
67  
68      public static final String PROJECT_NAME_WITH_GROUP_TEMPLATE = "[groupId].[artifactId]";
69  
70      public static final String PROJECT_NAME_WITH_GROUP_AND_VERSION_TEMPLATE = "[groupId].[artifactId]-[version]";
71  
72      /**
73       * compiler plugin id.
74       */
75      private static final String ARTIFACT_MAVEN_COMPILER_PLUGIN = "maven-compiler-plugin"; //$NON-NLS-1$
76  
77      /**
78       * 'source' property for maven-compiler-plugin.
79       */
80      private static final String PROPERTY_SOURCE = "source"; //$NON-NLS-1$
81  
82      /**
83       * 'encoding' property for maven-compiler-plugin.
84       */
85      private static final String PROPERTY_ENCODING = "encoding"; //$NON-NLS-1$
86  
87      /**
88       * 'target' property for maven-compiler-plugin.
89       */
90      private static final String PROPERTY_TARGET = "target"; //$NON-NLS-1$
91  
92      /**
93       * The suffix used to mark a file as not available.
94       */
95      public static final String NOT_AVAILABLE_MARKER_FILE_SUFFIX = "-not-available";
96  
97      /**
98       * Delete a file, handling log messages and exceptions
99       *
100      * @param f File to be deleted
101      * @throws MojoExecutionException only if a file exists and can't be deleted
102      */
103     public static void delete( File f, Log log )
104         throws MojoExecutionException
105     {
106         if ( f.isDirectory() )
107         {
108             log.info( Messages.getString( "EclipseCleanMojo.deletingDirectory", f.getName() ) ); //$NON-NLS-1$
109         }
110         else
111         {
112             log.info( Messages.getString( "EclipseCleanMojo.deletingFile", f.getName() ) ); //$NON-NLS-1$
113         }
114 
115         if ( f.exists() )
116         {
117             if ( !f.delete() )
118             {
119                 try
120                 {
121                     FileUtils.forceDelete( f );
122                 }
123                 catch ( IOException e )
124                 {
125                     throw new MojoExecutionException( Messages.getString( "EclipseCleanMojo.failedtodelete", //$NON-NLS-1$
126                                                                           new Object[] { f.getName(),
127                                                                               f.getAbsolutePath() } ) );
128                 }
129             }
130         }
131         else
132         {
133             log.debug( Messages.getString( "EclipseCleanMojo.nofilefound", f.getName() ) ); //$NON-NLS-1$
134         }
135     }
136 
137     public static String getCanonicalPath( File file )
138         throws MojoExecutionException
139     {
140         try
141         {
142             return file.getCanonicalPath();
143         }
144         catch ( IOException e )
145         {
146             throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantcanonicalize", file //$NON-NLS-1$
147             .getAbsolutePath() ), e );
148         }
149     }
150 
151     /**
152      * Returns a compiler plugin settings, considering also settings altered in plugin executions .
153      *
154      * @param project maven project
155      * @return option value (may be null)
156      */
157     public static String getCompilerPluginSetting( MavenProject project, String optionName )
158     {
159         String value = findCompilerPluginSettingInPlugins( project.getModel().getBuild().getPlugins(), optionName );
160         if ( value == null && project.getModel().getBuild().getPluginManagement() != null )
161         {
162             value =
163                 findCompilerPluginSettingInPlugins( project.getModel().getBuild().getPluginManagement().getPlugins(),
164                                                     optionName );
165         }
166         return value;
167     }
168 
169     /**
170      * Returns the source version configured for the compiler plugin. Returns the minimum version required to compile
171      * both standard and test sources, if settings are different.
172      *
173      * @param project maven project
174      * @return java source version
175      */
176     public static String getCompilerSourceVersion( MavenProject project )
177     {
178         return IdeUtils.getCompilerPluginSetting( project, PROPERTY_SOURCE );
179     }
180 
181     /**
182      * Returns the source encoding configured for the compiler plugin. Returns the minimum version required to compile
183      * both standard and test sources, if settings are different.
184      *
185      * @param project maven project
186      * @return java source version
187      */
188     public static String getCompilerSourceEncoding( MavenProject project )
189     {
190         String value = IdeUtils.getCompilerPluginSetting( project, PROPERTY_ENCODING );
191 		if ( value == null )
192         {
193             project.getProperties().getProperty( "project.build.sourceEncoding" );
194         }
195 		return value;
196     }
197 
198     /**
199      * Returns the target version configured for the compiler plugin. Returns the minimum version required to compile
200      * both standard and test sources, if settings are different.
201      *
202      * @param project maven project
203      * @return java target version
204      */
205     public static String getCompilerTargetVersion( MavenProject project )
206     {
207         return IdeUtils.getCompilerPluginSetting( project, PROPERTY_TARGET );
208     }
209 
210     // /**
211     // * Extracts the version of the first matching dependency in the given list.
212     // *
213     // * @param artifactIds artifact names to compare against for extracting version
214     // * @param dependencies Collection of dependencies for our project
215     // * @param len expected length of the version sub-string
216     // * @return
217     // */
218     // public static String getDependencyVersion( String[] artifactIds, List dependencies, int len )
219     // {
220     // for ( int j = 0; j < artifactIds.length; j++ )
221     // {
222     // String id = artifactIds[j];
223     // for ( Iterator itr = dependencies.iterator(); itr.hasNext(); )
224     // {
225     // Dependency dependency = (Dependency) itr.next();
226     // if ( id.equals( dependency.getArtifactId() ) )
227     // {
228     // return StringUtils.substring( dependency.getVersion(), 0, len );
229     // }
230     // }
231     // }
232     // return null;
233     // }
234 
235     /**
236      * Extracts the version of the first matching artifact in the given list.
237      *
238      * @param artifactIds artifact names to compare against for extracting version
239      * @param artifacts Set of artifacts for our project
240      * @param len expected length of the version sub-string
241      * @return
242      */
243     public static String getArtifactVersion( String[] artifactIds, List dependencies, int len )
244     {
245         for ( int j = 0; j < artifactIds.length; j++ )
246         {
247             String id = artifactIds[j];
248             Iterator depIter = dependencies.iterator();
249             while ( depIter.hasNext() )
250             {
251                 Dependency dep = (Dependency) depIter.next();
252                 if ( id.equals( dep.getArtifactId() ) )
253                 {
254                     return StringUtils.substring( dep.getVersion(), 0, len );
255                 }
256 
257             }
258         }
259         return null;
260     }
261 
262     /**
263      * Search for a configuration setting of an other plugin for a configuration setting.
264      *
265      * @todo there should be a better way to do this
266      * @param project the current maven project to get the configuration from.
267      * @param pluginId the group id and artifact id of the plugin to search for
268      * @param optionName the option to get from the configuration
269      * @param defaultValue the default value if the configuration was not found
270      * @return the value of the option configured in the plugin configuration
271      */
272     public static String getPluginSetting( MavenProject project, String pluginId, String optionName, String defaultValue )
273     {
274         Xpp3Dom dom = getPluginConfigurationDom( project, pluginId );
275         if ( dom != null && dom.getChild( optionName ) != null )
276         {
277             return dom.getChild( optionName ).getValue();
278         }
279         return defaultValue;
280     }
281 
282     /**
283      * Search for the configuration Xpp3 dom of an other plugin.
284      *
285      * @todo there should be a better way to do this
286      * @param project the current maven project to get the configuration from.
287      * @param pluginId the group id and artifact id of the plugin to search for
288      * @return the value of the option configured in the plugin configuration
289      */
290     public static Xpp3Dom getPluginConfigurationDom( MavenProject project, String pluginId )
291     {
292 
293         Plugin plugin = (org.apache.maven.model.Plugin) project.getBuild().getPluginsAsMap().get( pluginId );
294         if ( plugin != null )
295         {
296             // TODO: This may cause ClassCastExceptions eventually, if the dom impls differ.
297             return (Xpp3Dom) plugin.getConfiguration();
298         }
299         return null;
300     }
301 
302     /**
303      * Search for the configuration Xpp3 dom of an other plugin.
304      *
305      * @todo there should be a better way to do this
306      * @param project the current maven project to get the configuration from.
307      * @param artifactId the artifact id of the plugin to search for
308      * @return the value of the option configured in the plugin configuration
309      */
310     public static Xpp3Dom[] getPluginConfigurationDom( MavenProject project, String artifactId,
311                                                        String[] subConfiguration )
312     {
313         ArrayList configurationDomList = new ArrayList();
314         Xpp3Dom configuration = getPluginConfigurationDom( project, artifactId );
315         if ( configuration != null )
316         {
317             configurationDomList.add( configuration );
318             for ( int index = 0; !configurationDomList.isEmpty() && subConfiguration != null
319                 && index < subConfiguration.length; index++ )
320             {
321                 ArrayList newConfigurationDomList = new ArrayList();
322                 for ( Iterator childElement = configurationDomList.iterator(); childElement.hasNext(); )
323                 {
324                     Xpp3Dom child = (Xpp3Dom) childElement.next();
325                     Xpp3Dom[] deeperChild = child.getChildren( subConfiguration[index] );
326                     for ( int deeperIndex = 0; deeperIndex < deeperChild.length; deeperIndex++ )
327                     {
328                         if ( deeperChild[deeperIndex] != null )
329                         {
330                             newConfigurationDomList.add( deeperChild[deeperIndex] );
331                         }
332                     }
333                 }
334                 configurationDomList = newConfigurationDomList;
335             }
336         }
337         return (Xpp3Dom[]) configurationDomList.toArray( new Xpp3Dom[configurationDomList.size()] );
338     }
339 
340     /**
341      * Calculate the project name template from the specified value <code>projectNameTemplate</code>,
342      * <code>addVersionToProjectName</code> and <code>addGroupIdToProjectName</code>
343      * <p>
344      * Note: if projectNameTemplate is not null then that value will be used regardless of the values for
345      * addVersionToProjectName or addGroupIdToProjectName and a warning will be issued.
346      *
347      * @param projectNameTemplate the current projectNameTemplate, if available
348      * @param addVersionToProjectName whether to include Version in the project name
349      * @param addGroupIdToProjectName whether to include GroupId in the project name.
350      * @return the project name template.
351      */
352     public static String calculateProjectNameTemplate( String projectNameTemplate, boolean addVersionToProjectName,
353                                                        boolean addGroupIdToProjectName, Log log )
354     {
355         if ( projectNameTemplate != null )
356         {
357             if ( addVersionToProjectName || addGroupIdToProjectName )
358             {
359                 log.warn( "projectNameTemplate definition overrides "
360                     + "addVersionToProjectName or addGroupIdToProjectName" );
361             }
362             return projectNameTemplate;
363         }
364         else if ( addVersionToProjectName && addGroupIdToProjectName )
365         {
366             return IdeUtils.PROJECT_NAME_WITH_GROUP_AND_VERSION_TEMPLATE;
367         }
368         else if ( addVersionToProjectName )
369         {
370             return IdeUtils.PROJECT_NAME_WITH_VERSION_TEMPLATE;
371         }
372         else if ( addGroupIdToProjectName )
373         {
374             return IdeUtils.PROJECT_NAME_WITH_GROUP_TEMPLATE;
375         }
376         return IdeUtils.PROJECT_NAME_DEFAULT_TEMPLATE;
377     }
378 
379     /**
380      * Use {@link IdeDependency#getEclipseProjectName()} instead.
381      */
382     protected static String getProjectName( String template, IdeDependency dep )
383     {
384         return getProjectName( template, dep.getGroupId(), dep.getArtifactId(), dep.getVersion() );
385     }
386 
387     /**
388      * Use the project name template to create an eclipse project.
389      *
390      * @param template Template for the project name
391      * @param artifact the artifact to create the project name for
392      * @return the created ide project name
393      */
394     public static String getProjectName( String template, Artifact artifact )
395     {
396         return getProjectName( template, artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion() );
397     }
398 
399     public static String getProjectName( String template, MavenProject project )
400     {
401         return getProjectName( template, project.getGroupId(), project.getArtifactId(), project.getVersion() );
402     }
403 
404     private static String getProjectName( IdeDependency dep, boolean addVersionToProjectName )
405     {
406         return getProjectName( addVersionToProjectName ? PROJECT_NAME_WITH_VERSION_TEMPLATE
407                         : PROJECT_NAME_DEFAULT_TEMPLATE, dep );
408     }
409 
410     public static String getProjectName( MavenProject project, boolean addVersionToProjectName )
411     {
412         return getProjectName( addVersionToProjectName ? PROJECT_NAME_WITH_VERSION_TEMPLATE
413                         : PROJECT_NAME_DEFAULT_TEMPLATE, project );
414     }
415 
416     /**
417      * @param artifact the artifact
418      * @return the not-available marker file for the specified artifact
419      */
420     public static File getNotAvailableMarkerFile( ArtifactRepository localRepository, Artifact artifact )
421     {
422         return new File( localRepository.getBasedir(), localRepository.pathOf( artifact )
423             + NOT_AVAILABLE_MARKER_FILE_SUFFIX );
424     }
425 
426     /**
427      * Wrapper around {@link ArtifactResolver#resolve(Artifact, List, ArtifactRepository)}
428      *
429      * @param artifactResolver see {@link ArtifactResolver#resolve(Artifact, List, ArtifactRepository)}
430      * @param artifact see {@link ArtifactResolver#resolve(Artifact, List, ArtifactRepository)}
431      * @param remoteRepos see {@link ArtifactResolver#resolve(Artifact, List, ArtifactRepository)}
432      * @param localRepository see {@link ArtifactResolver#resolve(Artifact, List, ArtifactRepository)}
433      * @param log Logger
434      * @return the artifact, resolved if possible.
435      */
436     public static Artifact resolveArtifact( ArtifactResolver artifactResolver, Artifact artifact, List remoteRepos,
437                                             ArtifactRepository localRepository, Log log )
438 
439     {
440         try
441         {
442             artifactResolver.resolve( artifact, remoteRepos, localRepository );
443         }
444         catch ( ArtifactNotFoundException e )
445         {
446             // ignore, the jar has not been found
447         }
448         catch ( ArtifactResolutionException e )
449         {
450             String message =
451                 Messages.getString( "IdeUtils.errorresolving", new Object[] { artifact.getClassifier(),
452                     artifact.getId(), e.getMessage() } );
453 
454             log.warn( message );
455         }
456 
457         return artifact;
458     }
459 
460     /**
461      * Wrap {@link ArtifactFactory#createArtifactWithClassifier} so that the type and classifier are set correctly for
462      * "sources" and "javadoc".
463      *
464      * @param groupId see {@link ArtifactFactory#createArtifactWithClassifier}
465      * @param artifactId see {@link ArtifactFactory#createArtifactWithClassifier}
466      * @param version see {@link ArtifactFactory#createArtifactWithClassifier}
467      * @param depClassifier see {@link ArtifactFactory#createArtifactWithClassifier}
468      * @param inClassifier either "sources" of "javadoc"
469      * @param artifactFactory see {@link ArtifactFactory#createArtifactWithClassifier}
470      * @return see {@link ArtifactFactory#createArtifactWithClassifier}
471      * @see ArtifactFactory#createArtifactWithClassifier
472      */
473     public static Artifact createArtifactWithClassifier( String groupId, String artifactId, String version,
474                                                          String depClassifier, String inClassifier,
475                                                          ArtifactFactory artifactFactory )
476     {
477         String type = null;
478 
479         // the "sources" classifier maps to the "java-source" type
480         if ( "sources".equals( inClassifier ) )
481         {
482             type = "java-source";
483         }
484         else
485         {
486             type = inClassifier;
487         }
488 
489         String finalClassifier = null;
490         if ( depClassifier == null )
491         {
492             finalClassifier = inClassifier;
493         }
494         else if ( "sources".equals( inClassifier ) && "tests".equals( depClassifier ) )
495         {
496             // MECLIPSE-151 - if the dependency is a test, get the correct classifier for it. (ignore for javadocs)
497             finalClassifier = "test-sources";
498         }
499         else
500         {
501             finalClassifier = depClassifier + "-" + inClassifier;
502         }
503 
504         return artifactFactory.createArtifactWithClassifier( groupId, artifactId, version, type, finalClassifier );
505     }
506 
507     public static String resolveJavaVersion( MavenProject project )
508     {
509         String version = IdeUtils.getCompilerTargetVersion( project );
510         if ( version == null )
511         {
512             version = IdeUtils.getCompilerSourceVersion( project );
513         }
514 
515         if ( "1.5".equals( version ) ) //$NON-NLS-1$
516         {
517             version = IdeUtils.JAVA_5_0;// see MECLIPSE-47 eclipse only accept 5.0 as a valid version
518         }
519         else if ( "1.6".equals( version ) ) //$NON-NLS-1$
520         {
521             version = IdeUtils.JAVA_6_0;
522         }
523         else if ( version != null && version.length() == 1 )
524         {
525             version = version + ".0";// 5->5.0 6->6.0 7->7.0 //$NON-NLS-1$
526         }
527 
528         return version == null ? IdeUtils.JAVA_1_4 : version;
529     }
530 
531     public static String toRelativeAndFixSeparator( File basedir, File fileToAdd, boolean replaceSlashesWithDashes )
532         throws MojoExecutionException
533     {
534         if ( !fileToAdd.isAbsolute() )
535         {
536             fileToAdd = new File( basedir, fileToAdd.getPath() );
537         }
538 
539         String basedirPath = getCanonicalPath( basedir );
540         String absolutePath = getCanonicalPath( fileToAdd );
541 
542         String relative = null;
543 
544         if ( absolutePath.equals( basedirPath ) )
545         {
546             relative = "."; //$NON-NLS-1$
547         }
548         else if ( absolutePath.startsWith( basedirPath ) )
549         {
550             // MECLIPSE-261
551             // The canonical form of a windows root dir ends in a slash, whereas the canonical form of any other file
552             // does not.
553             // The absolutePath is assumed to be: basedirPath + Separator + fileToAdd
554             // In the case of a windows root directory the Separator is missing since it is contained within
555             // basedirPath.
556             int length = basedirPath.length() + 1;
557             if ( basedirPath.endsWith( "\\" ) )
558             {
559                 length--;
560             }
561             relative = absolutePath.substring( length );
562         }
563         else
564         {
565             relative = absolutePath;
566         }
567 
568         relative = fixSeparator( relative );
569 
570         if ( replaceSlashesWithDashes )
571         {
572             relative = StringUtils.replace( relative, '/', '-' );
573             relative = StringUtils.replace( relative, ':', '-' ); // remove ":" for absolute paths in windows
574         }
575 
576         return relative;
577     }
578 
579     /**
580      * Convert the provided filename from a Windows separator \\ to a unix/java separator /
581      *
582      * @param filename file name to fix separator
583      * @return filename with all \\ replaced with /
584      */
585     public static String fixSeparator( String filename )
586     {
587         return StringUtils.replace( filename, '\\', '/' );
588     }
589 
590     /**
591      * NOTE: This is to account for the unfortunate fact that "file:" URIs differ between Windows and Unix. On a Windows
592      * box, the path "C:\dir" is mapped to "file:/C:/dir". On a Unix box, the path "/home/dir" is mapped to
593      * "file:/home/dir". So, in the first case the slash after "file:" is not part of the corresponding filesystem path
594      * while in the later case it is. This discrepancy makes verifying the javadoc attachments in ".classpath" a little
595      * tricky.
596      *
597      * @param input string input that may contain a windows URI
598      * @return all windows URI convert "file:C:/dir" to "file:/C:/dir"
599      */
600     public static String fixWindowsDriveURI( String input )
601     {
602         return input.replaceAll( "file:([a-zA-Z]):", "file:/$1:" );
603     }
604 
605     /**
606      * Returns a compiler plugin settings from a list of plugins .
607      *
608      * @param project maven project
609      * @return option value (may be null)
610      */
611     private static String findCompilerPluginSettingInPlugins( List plugins, String optionName )
612     {
613         String value = null;
614 
615         for ( Iterator it = plugins.iterator(); it.hasNext(); )
616         {
617             Plugin plugin = (Plugin) it.next();
618 
619             if ( plugin.getArtifactId().equals( ARTIFACT_MAVEN_COMPILER_PLUGIN ) )
620             {
621                 // TODO: This may cause ClassCastExceptions eventually, if the dom impls differ.
622                 Xpp3Dom o = (Xpp3Dom) plugin.getConfiguration();
623 
624                 // this is the default setting
625                 if ( o != null && o.getChild( optionName ) != null )
626                 {
627                     value = o.getChild( optionName ).getValue();
628                 }
629 
630                 List executions = plugin.getExecutions();
631 
632                 // a different source/target version can be configured for test sources compilation
633                 for ( Iterator iter = executions.iterator(); iter.hasNext(); )
634                 {
635                     PluginExecution execution = (PluginExecution) iter.next();
636 
637                     // TODO: This may cause ClassCastExceptions eventually, if the dom impls differ.
638                     o = (Xpp3Dom) execution.getConfiguration();
639 
640                     if ( o != null && o.getChild( optionName ) != null )
641                     {
642                         value = o.getChild( optionName ).getValue();
643                     }
644                 }
645             }
646         }
647         return value;
648     }
649 
650     private static String getProjectName( String template, String groupId, String artifactId, String version )
651     {
652         String s = template;
653         s = s.replaceAll( "\\[groupId\\]", groupId );
654         s = s.replaceAll( "\\[artifactId\\]", artifactId );
655         s = s.replaceAll( "\\[version\\]", version );
656         return s;
657     }
658 
659     private IdeUtils()
660     {
661         // don't instantiate
662     }
663 }