001    package org.apache.archiva.repository.metadata;
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    
022    import java.util.ArrayList;
023    import java.util.List;
024    
025    import org.apache.commons.lang.StringUtils;
026    import org.apache.archiva.model.ArchivaModelCloner;
027    import org.apache.archiva.model.ArchivaRepositoryMetadata;
028    import org.apache.archiva.model.Plugin;
029    import org.apache.archiva.model.SnapshotVersion;
030    
031    /**
032     * RepositoryMetadataMerge 
033     *
034     *
035     */
036    public class RepositoryMetadataMerge
037    {
038        public static ArchivaRepositoryMetadata merge( final ArchivaRepositoryMetadata mainMetadata,
039                                                       final ArchivaRepositoryMetadata sourceMetadata )
040            throws RepositoryMetadataException
041        {
042            if ( mainMetadata == null )
043            {
044                throw new RepositoryMetadataException( "Cannot merge a null main project." );
045            }
046    
047            if ( sourceMetadata == null )
048            {
049                throw new RepositoryMetadataException( "Cannot copy to a null parent project." );
050            }
051    
052            ArchivaRepositoryMetadata merged = new ArchivaRepositoryMetadata();
053    
054            merged.setGroupId( merge( mainMetadata.getGroupId(), sourceMetadata.getGroupId() ) );
055            merged.setArtifactId(  merge(mainMetadata.getArtifactId(), sourceMetadata.getArtifactId()));
056            merged.setVersion( merge(mainMetadata.getVersion(), sourceMetadata.getVersion()) );
057            merged.setReleasedVersion( merge( mainMetadata.getReleasedVersion(), sourceMetadata.getReleasedVersion() ) );
058            merged.setSnapshotVersion( merge( mainMetadata.getSnapshotVersion(), sourceMetadata.getSnapshotVersion() ) );
059            merged.setAvailableVersions( mergeAvailableVersions( mainMetadata.getAvailableVersions(), sourceMetadata.getAvailableVersions() ) );
060            merged.setPlugins( mergePlugins( mainMetadata.getPlugins(), sourceMetadata.getPlugins() ) );
061            
062            //Don't set if merge was not possible
063            long lastUpdated = mergeTimestamp( mainMetadata.getLastUpdated(), sourceMetadata.getLastUpdated());
064            if (lastUpdated > -1)
065            {
066                merged.setLastUpdated(  Long.toString(lastUpdated) );
067            }
068            
069            return merged;
070        }
071    
072        private static boolean empty( String val )
073        {
074            if ( val == null )
075            {
076                return true;
077            }
078    
079            return ( val.trim().length() <= 0 );
080        }
081        
082        private static long mergeTimestamp(String mainTimestamp, String sourceTimestamp)
083        {
084            if (sourceTimestamp == null && mainTimestamp != null)
085            {
086                return convertTimestampToLong(mainTimestamp);
087            }
088            
089            if (mainTimestamp == null && sourceTimestamp != null)
090            {
091                return convertTimestampToLong(sourceTimestamp);
092            }
093            
094            if (sourceTimestamp == null && mainTimestamp == null)
095            {
096                return -1;
097            }
098            
099            return mergeTimestamp(convertTimestampToLong(mainTimestamp), convertTimestampToLong(sourceTimestamp));
100        }
101        
102        private static long mergeTimestamp(long mainTimestamp, long sourceTimestamp)
103        { 
104            return Math.max( mainTimestamp, sourceTimestamp );
105        }
106    
107        private static SnapshotVersion merge( SnapshotVersion mainSnapshotVersion, SnapshotVersion sourceSnapshotVersion )
108        {
109            if ( sourceSnapshotVersion == null )
110            {
111                return mainSnapshotVersion;
112            }
113    
114            if ( mainSnapshotVersion == null )
115            {
116                return ArchivaModelCloner.clone( sourceSnapshotVersion );
117            }
118    
119            SnapshotVersion merged = new SnapshotVersion();
120           
121            long mainSnapshotLastUpdated = convertTimestampToLong(mainSnapshotVersion.getTimestamp());
122            long sourceSnapshotLastUpdated = convertTimestampToLong(sourceSnapshotVersion.getTimestamp());
123                            
124            long lastUpdated = mergeTimestamp(mainSnapshotLastUpdated, sourceSnapshotLastUpdated);
125            
126            if (lastUpdated == mainSnapshotLastUpdated)
127            {
128                merged.setTimestamp(mainSnapshotVersion.getTimestamp());
129                merged.setBuildNumber(mainSnapshotVersion.getBuildNumber());
130            }
131            else
132            {
133                merged.setTimestamp(sourceSnapshotVersion.getTimestamp());
134                merged.setBuildNumber(sourceSnapshotVersion.getBuildNumber());
135            }
136    
137            return merged;
138        }
139        
140        private static long convertTimestampToLong(String timestamp)
141        {
142            if (timestamp == null)
143            {
144                return -1;
145            }
146            
147            return getLongFromTimestampSafely(StringUtils.replace(timestamp, ".", ""));
148        }
149        
150        private static long getLongFromTimestampSafely( String timestampString )
151        {
152            try
153            {
154                return Long.parseLong(timestampString);
155            }
156            catch (NumberFormatException e)
157            {
158                return -1;
159            }
160        }
161    
162        private static String merge( String main, String source )
163        {
164            if ( empty( main ) && !empty( source ) )
165            {
166                return source;
167            }
168    
169            return main;
170        }
171        
172        private static List<Plugin> mergePlugins(List<Plugin> mainPlugins, List<Plugin> sourcePlugins)
173        {
174            if ( sourcePlugins == null )
175            {
176                return mainPlugins;
177            }
178            
179            if ( mainPlugins == null )
180            {
181                return clonePlugins( sourcePlugins );
182            }
183            
184            List<Plugin> merged = clonePlugins( mainPlugins );
185            
186            for ( Plugin plugin : sourcePlugins )
187            {
188                if ( !merged.contains( plugin ) )
189                {
190                    merged.add( plugin );
191                }
192            }
193    
194            return merged;
195        }
196        
197        /**
198         * Clones a list of plugins.
199         * 
200         * This method exists because ArchivaModelCloner.clonePlugins() 
201         * only works with artifact references.
202         * 
203         * @param plugins
204         * @return list of cloned plugins
205         */
206        private static List<Plugin> clonePlugins(List<Plugin> plugins)
207        {
208            if (plugins == null)
209            {
210                return null;
211            }
212            
213            List<Plugin> result = new ArrayList<Plugin>();
214            
215            for (Plugin plugin : plugins)
216            {
217                Plugin clonedPlugin = new Plugin();
218                clonedPlugin.setArtifactId(plugin.getArtifactId());
219                clonedPlugin.setName(plugin.getName());
220                clonedPlugin.setPrefix(plugin.getPrefix());
221                result.add(plugin);
222            }
223            
224            return result;
225        }
226    
227        private static List<String> mergeAvailableVersions( List<String> mainAvailableVersions, List<String> sourceAvailableVersions )
228        {
229            if ( sourceAvailableVersions == null )
230            {
231                return mainAvailableVersions;
232            }
233    
234            if ( mainAvailableVersions == null )
235            {
236                return ArchivaModelCloner.cloneAvailableVersions( sourceAvailableVersions );
237            }
238    
239            List<String> merged = ArchivaModelCloner.cloneAvailableVersions( mainAvailableVersions );
240    
241            for ( String sourceVersion : sourceAvailableVersions )
242            {
243                if ( !merged.contains( sourceVersion ) )
244                {
245                    merged.add( sourceVersion );
246                }
247            }
248    
249            return merged;
250        }
251    }