001package org.apache.maven.model.normalization;
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.util.ArrayList;
023import java.util.Collections;
024import java.util.LinkedHashMap;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.maven.model.Build;
029import org.apache.maven.model.Dependency;
030import org.apache.maven.model.Model;
031import org.apache.maven.model.Plugin;
032import org.apache.maven.model.building.ModelBuildingRequest;
033import org.apache.maven.model.building.ModelProblemCollector;
034import org.apache.maven.model.merge.MavenModelMerger;
035import org.codehaus.plexus.component.annotations.Component;
036import org.codehaus.plexus.util.StringUtils;
037
038/**
039 * Handles normalization of a model.
040 *
041 * @author Benjamin Bentmann
042 */
043@Component( role = ModelNormalizer.class )
044public class DefaultModelNormalizer
045    implements ModelNormalizer
046{
047
048    private DuplicateMerger merger = new DuplicateMerger();
049
050    @Override
051    public void mergeDuplicates( Model model, ModelBuildingRequest request, ModelProblemCollector problems )
052    {
053        Build build = model.getBuild();
054        if ( build != null )
055        {
056            List<Plugin> plugins = build.getPlugins();
057            Map<Object, Plugin> normalized = new LinkedHashMap<>( plugins.size() * 2 );
058
059            for ( Plugin plugin : plugins )
060            {
061                Object key = plugin.getKey();
062                Plugin first = normalized.get( key );
063                if ( first != null )
064                {
065                    merger.mergePlugin( plugin, first );
066                }
067                normalized.put( key, plugin );
068            }
069
070            if ( plugins.size() != normalized.size() )
071            {
072                build.setPlugins( new ArrayList<>( normalized.values() ) );
073            }
074        }
075
076        /*
077         * NOTE: This is primarily to keep backward-compat with Maven 2.x which did not validate that dependencies are
078         * unique within a single POM. Upon multiple declarations, 2.x just kept the last one but retained the order of
079         * the first occurrence. So when we're in lenient/compat mode, we have to deal with such broken POMs and mimic
080         * the way 2.x works. When we're in strict mode, the removal of duplicates just saves other merging steps from
081         * aftereffects and bogus error messages.
082         */
083        List<Dependency> dependencies = model.getDependencies();
084        Map<String, Dependency> normalized = new LinkedHashMap<>( dependencies.size() * 2 );
085
086        for ( Dependency dependency : dependencies )
087        {
088            normalized.put( dependency.getManagementKey(), dependency );
089        }
090
091        if ( dependencies.size() != normalized.size() )
092        {
093            model.setDependencies( new ArrayList<>( normalized.values() ) );
094        }
095    }
096
097    protected static class DuplicateMerger
098        extends MavenModelMerger
099    {
100
101        public void mergePlugin( Plugin target, Plugin source )
102        {
103            super.mergePlugin( target, source, false, Collections.emptyMap() );
104        }
105
106    }
107
108    @Override
109    public void injectDefaultValues( Model model, ModelBuildingRequest request, ModelProblemCollector problems )
110    {
111        injectDependencyDefaults( model.getDependencies() );
112
113        Build build = model.getBuild();
114        if ( build != null )
115        {
116            for ( Plugin plugin : build.getPlugins() )
117            {
118                injectDependencyDefaults( plugin.getDependencies() );
119            }
120        }
121    }
122
123    private void injectDependencyDefaults( List<Dependency> dependencies )
124    {
125        for ( Dependency dependency : dependencies )
126        {
127            if ( StringUtils.isEmpty( dependency.getScope() ) )
128            {
129                // we cannot set this directly in the MDO due to the interactions with dependency management
130                dependency.setScope( "compile" );
131            }
132        }
133    }
134
135}