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