001package org.apache.maven.plugin.descriptor;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.IOException;
025import java.io.InputStream;
026import java.io.Reader;
027import java.net.MalformedURLException;
028import java.net.URL;
029import java.util.Collections;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
033import java.util.Set;
034
035import org.apache.maven.artifact.Artifact;
036import org.apache.maven.artifact.ArtifactUtils;
037import org.apache.maven.model.Plugin;
038import org.apache.maven.plugin.lifecycle.Lifecycle;
039import org.apache.maven.plugin.lifecycle.LifecycleConfiguration;
040import org.apache.maven.plugin.lifecycle.io.xpp3.LifecycleMappingsXpp3Reader;
041import org.codehaus.plexus.classworlds.realm.ClassRealm;
042import org.codehaus.plexus.component.repository.ComponentSetDescriptor;
043import org.codehaus.plexus.util.IOUtil;
044import org.codehaus.plexus.util.ReaderFactory;
045import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
046
047/**
048 * @author Jason van Zyl
049 */
050public class PluginDescriptor
051    extends ComponentSetDescriptor
052    implements Cloneable
053{
054
055    private static final String LIFECYCLE_DESCRIPTOR = "META-INF/maven/lifecycle.xml";
056
057    private String groupId;
058
059    private String artifactId;
060
061    private String version;
062
063    private String goalPrefix;
064
065    private String source;
066
067    private boolean inheritedByDefault = true;
068
069    private List<Artifact> artifacts;
070
071    private ClassRealm classRealm;
072
073    // calculated on-demand.
074    private Map<String, Artifact> artifactMap;
075
076    private Set<Artifact> introducedDependencyArtifacts;
077
078    private String name;
079
080    private String description;
081
082    private String requiredMavenVersion;
083
084    private Plugin plugin;
085
086    private Artifact pluginArtifact;
087
088    private Map<String, Lifecycle> lifecycleMappings;
089
090    // ----------------------------------------------------------------------
091    //
092    // ----------------------------------------------------------------------
093
094    @SuppressWarnings( { "unchecked", "rawtypes" } )
095    public List<MojoDescriptor> getMojos()
096    {
097        return (List) getComponents();
098    }
099
100    public void addMojo( MojoDescriptor mojoDescriptor )
101        throws DuplicateMojoDescriptorException
102    {
103        MojoDescriptor existing = null;
104        // this relies heavily on the equals() and hashCode() for ComponentDescriptor,
105        // which uses role:roleHint for identity...and roleHint == goalPrefix:goal.
106        // role does not vary for Mojos.
107        List<MojoDescriptor> mojos = getMojos();
108
109        if ( mojos != null && mojos.contains( mojoDescriptor ) )
110        {
111            int indexOf = mojos.indexOf( mojoDescriptor );
112
113            existing = mojos.get( indexOf );
114        }
115
116        if ( existing != null )
117        {
118            throw new DuplicateMojoDescriptorException( getGoalPrefix(), mojoDescriptor.getGoal(), existing
119                .getImplementation(), mojoDescriptor.getImplementation() );
120        }
121        else
122        {
123            addComponentDescriptor( mojoDescriptor );
124        }
125    }
126
127    public String getGroupId()
128    {
129        return groupId;
130    }
131
132    public void setGroupId( String groupId )
133    {
134        this.groupId = groupId;
135    }
136
137    public String getArtifactId()
138    {
139        return artifactId;
140    }
141
142    public void setArtifactId( String artifactId )
143    {
144        this.artifactId = artifactId;
145    }
146
147    // ----------------------------------------------------------------------
148    // Dependencies
149    // ----------------------------------------------------------------------
150
151    public static String constructPluginKey( String groupId, String artifactId, String version )
152    {
153        return groupId + ":" + artifactId + ":" + version;
154    }
155
156    public String getPluginLookupKey()
157    {
158        return groupId + ":" + artifactId;
159    }
160
161    public String getId()
162    {
163        return constructPluginKey( groupId, artifactId, version );
164    }
165
166    public static String getDefaultPluginArtifactId( String id )
167    {
168        return "maven-" + id + "-plugin";
169    }
170
171    public static String getDefaultPluginGroupId()
172    {
173        return "org.apache.maven.plugins";
174    }
175
176    /**
177     * Parse maven-...-plugin.
178     *
179     * @todo move to plugin-tools-api as a default only
180     */
181    public static String getGoalPrefixFromArtifactId( String artifactId )
182    {
183        if ( "maven-plugin-plugin".equals( artifactId ) )
184        {
185            return "plugin";
186        }
187        else
188        {
189            return artifactId.replaceAll( "-?maven-?", "" ).replaceAll( "-?plugin-?", "" );
190        }
191    }
192
193    public String getGoalPrefix()
194    {
195        return goalPrefix;
196    }
197
198    public void setGoalPrefix( String goalPrefix )
199    {
200        this.goalPrefix = goalPrefix;
201    }
202
203    public void setVersion( String version )
204    {
205        this.version = version;
206    }
207
208    public String getVersion()
209    {
210        return version;
211    }
212
213    public void setSource( String source )
214    {
215        this.source = source;
216    }
217
218    public String getSource()
219    {
220        return source;
221    }
222
223    public boolean isInheritedByDefault()
224    {
225        return inheritedByDefault;
226    }
227
228    public void setInheritedByDefault( boolean inheritedByDefault )
229    {
230        this.inheritedByDefault = inheritedByDefault;
231    }
232
233    /**
234     * Gets the artifacts that make up the plugin's class realm, excluding artifacts shadowed by the Maven core realm
235     * like {@code maven-project}.
236     *
237     * @return The plugin artifacts, never {@code null}.
238     */
239    public List<Artifact> getArtifacts()
240    {
241        return artifacts;
242    }
243
244    public void setArtifacts( List<Artifact> artifacts )
245    {
246        this.artifacts = artifacts;
247
248        // clear the calculated artifactMap
249        artifactMap = null;
250    }
251
252    /**
253     * The map of artifacts accessible by the versionlessKey, i.e. groupId:artifactId
254     * 
255     * @return a Map of artifacts, never {@code null}
256     * @see #getArtifacts()
257     */
258    public Map<String, Artifact> getArtifactMap()
259    {
260        if ( artifactMap == null )
261        {
262            artifactMap = ArtifactUtils.artifactMapByVersionlessId( getArtifacts() );
263        }
264
265        return artifactMap;
266    }
267
268    public boolean equals( Object object )
269    {
270        if ( this == object )
271        {
272            return true;
273        }
274
275        return object instanceof PluginDescriptor &&  getId().equals( ( (PluginDescriptor) object ).getId() );
276    }
277
278    public int hashCode()
279    {
280        return 10 + getId().hashCode();
281    }
282
283    public MojoDescriptor getMojo( String goal )
284    {
285        if ( getMojos() == null )
286        {
287            return null; // no mojo in this POM
288        }
289
290        // TODO: could we use a map? Maybe if the parent did that for components too, as this is too vulnerable to
291        // changes above not being propagated to the map
292        for ( MojoDescriptor desc : getMojos() )
293        {
294            if ( goal.equals( desc.getGoal() ) )
295            {
296                return desc;
297            }
298        }
299        return null;
300    }
301
302    public void setClassRealm( ClassRealm classRealm )
303    {
304        this.classRealm = classRealm;
305    }
306
307    public ClassRealm getClassRealm()
308    {
309        return classRealm;
310    }
311
312    public void setIntroducedDependencyArtifacts( Set<Artifact> introducedDependencyArtifacts )
313    {
314        this.introducedDependencyArtifacts = introducedDependencyArtifacts;
315    }
316
317    public Set<Artifact> getIntroducedDependencyArtifacts()
318    {
319        return ( introducedDependencyArtifacts != null ) ? introducedDependencyArtifacts
320                        : Collections.<Artifact> emptySet();
321    }
322
323    public void setName( String name )
324    {
325        this.name = name;
326    }
327
328    public String getName()
329    {
330        return name;
331    }
332
333    public void setDescription( String description )
334    {
335        this.description = description;
336    }
337
338    public String getDescription()
339    {
340        return description;
341    }
342
343    public void setRequiredMavenVersion( String requiredMavenVersion )
344    {
345        this.requiredMavenVersion = requiredMavenVersion;
346    }
347
348    public String getRequiredMavenVersion()
349    {
350        return requiredMavenVersion;
351    }
352
353    public void setPlugin( Plugin plugin )
354    {
355        this.plugin = plugin;
356    }
357
358    public Plugin getPlugin()
359    {
360        return plugin;
361    }
362
363    public Artifact getPluginArtifact()
364    {
365        return pluginArtifact;
366    }
367
368    public void setPluginArtifact( Artifact pluginArtifact )
369    {
370        this.pluginArtifact = pluginArtifact;
371    }
372
373    public Lifecycle getLifecycleMapping( String lifecycleId )
374        throws IOException, XmlPullParserException
375    {
376        if ( lifecycleMappings == null )
377        {
378            LifecycleConfiguration lifecycleConfiguration;
379
380            Reader reader = null;
381            try
382            {
383                reader = ReaderFactory.newXmlReader( getDescriptorStream( LIFECYCLE_DESCRIPTOR ) );
384
385                lifecycleConfiguration = new LifecycleMappingsXpp3Reader().read( reader );
386            }
387            finally
388            {
389                IOUtil.close( reader );
390            }
391
392            lifecycleMappings = new HashMap<String, Lifecycle>();
393
394            for ( Lifecycle lifecycle : lifecycleConfiguration.getLifecycles() )
395            {
396                lifecycleMappings.put( lifecycle.getId(), lifecycle );
397            }
398        }
399
400        return lifecycleMappings.get( lifecycleId );
401    }
402
403    private InputStream getDescriptorStream( String descriptor )
404        throws IOException
405    {
406        File pluginFile = ( pluginArtifact != null ) ? pluginArtifact.getFile() : null;
407        if ( pluginFile == null )
408        {
409            throw new IllegalStateException( "plugin main artifact has not been resolved for " + getId() );
410        }
411
412        if ( pluginFile.isFile() )
413        {
414            try
415            {
416                return new URL( "jar:" + pluginFile.toURI() + "!/" + descriptor ).openStream();
417            }
418            catch ( MalformedURLException e )
419            {
420                throw new IllegalStateException( e );
421            }
422        }
423        else
424        {
425            return new FileInputStream( new File( pluginFile, descriptor ) );
426        }
427    }
428
429    /**
430     * Creates a shallow copy of this plugin descriptor.
431     */
432    @Override
433    public PluginDescriptor clone()
434    {
435        try
436        {
437            return (PluginDescriptor) super.clone();
438        }
439        catch ( CloneNotSupportedException e )
440        {
441            throw new UnsupportedOperationException( e );
442        }
443    }
444
445}