001package org.apache.maven.scm.manager;
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 org.apache.maven.scm.ScmBranch;
023import org.apache.maven.scm.ScmBranchParameters;
024import org.apache.maven.scm.ScmException;
025import org.apache.maven.scm.ScmFileSet;
026import org.apache.maven.scm.ScmTagParameters;
027import org.apache.maven.scm.ScmVersion;
028import org.apache.maven.scm.command.add.AddScmResult;
029import org.apache.maven.scm.command.blame.BlameScmRequest;
030import org.apache.maven.scm.command.blame.BlameScmResult;
031import org.apache.maven.scm.command.branch.BranchScmResult;
032import org.apache.maven.scm.command.changelog.ChangeLogScmRequest;
033import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
034import org.apache.maven.scm.command.checkin.CheckInScmResult;
035import org.apache.maven.scm.command.checkout.CheckOutScmResult;
036import org.apache.maven.scm.command.diff.DiffScmResult;
037import org.apache.maven.scm.command.edit.EditScmResult;
038import org.apache.maven.scm.command.export.ExportScmResult;
039import org.apache.maven.scm.command.list.ListScmResult;
040import org.apache.maven.scm.command.mkdir.MkdirScmResult;
041import org.apache.maven.scm.command.remove.RemoveScmResult;
042import org.apache.maven.scm.command.status.StatusScmResult;
043import org.apache.maven.scm.command.tag.TagScmResult;
044import org.apache.maven.scm.command.unedit.UnEditScmResult;
045import org.apache.maven.scm.command.update.UpdateScmResult;
046import org.apache.maven.scm.log.ScmLogger;
047import org.apache.maven.scm.provider.ScmProvider;
048import org.apache.maven.scm.provider.ScmProviderRepository;
049import org.apache.maven.scm.provider.ScmUrlUtils;
050import org.apache.maven.scm.repository.ScmRepository;
051import org.apache.maven.scm.repository.ScmRepositoryException;
052import org.apache.maven.scm.repository.UnknownRepositoryStructure;
053
054import java.io.File;
055import java.util.ArrayList;
056import java.util.Date;
057import java.util.HashMap;
058import java.util.List;
059import java.util.Map;
060import java.util.Map.Entry;
061
062/**
063 * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
064 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
065 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
066 *
067 */
068public abstract class AbstractScmManager
069    implements ScmManager
070{
071    private Map<String, ScmProvider> scmProviders = new HashMap<String, ScmProvider>();
072
073    private ScmLogger logger;
074
075    private Map<String, String> userProviderTypes = new HashMap<String, String>();
076
077    protected void setScmProviders( Map<String, ScmProvider> providers )
078    {
079        this.scmProviders = providers;
080    }
081
082    /**
083     * @deprecated use {@link #setScmProvider(String, ScmProvider)} instead
084
085     * @param providerType the type of SCM, eg. <code>svn</code>, <code>git</code>
086     * @param provider     the provider that will be used for that SCM type
087     */
088    protected void addScmProvider( String providerType, ScmProvider provider )
089    {
090        setScmProvider( providerType, provider );
091    }
092
093    /**
094     * Set a provider to be used for a type of SCM.
095     * If there was already a designed provider for that type it will be replaced.
096     *
097     * @param providerType the type of SCM, eg. <code>svn</code>, <code>cvs</code>
098     * @param provider     the provider that will be used for that SCM type
099     */
100    public void setScmProvider( String providerType, ScmProvider provider )
101    {
102        scmProviders.put( providerType, provider );
103    }
104
105    protected abstract ScmLogger getScmLogger();
106
107    // ----------------------------------------------------------------------
108    // ScmManager Implementation
109    // ----------------------------------------------------------------------
110
111    /**
112     * {@inheritDoc}
113     */
114    public ScmProvider getProviderByUrl( String scmUrl )
115        throws ScmRepositoryException, NoSuchScmProviderException
116    {
117        if ( scmUrl == null )
118        {
119            throw new NullPointerException( "The scm url cannot be null." );
120        }
121
122        String providerType = ScmUrlUtils.getProvider( scmUrl );
123
124        return getProviderByType( providerType );
125    }
126
127    /**
128     * {@inheritDoc}
129     */
130    public void setScmProviderImplementation( String providerType, String providerImplementation )
131    {
132        userProviderTypes.put( providerType, providerImplementation );
133    }
134
135    /**
136     * {@inheritDoc}
137     */
138    public ScmProvider getProviderByType( String providerType )
139        throws NoSuchScmProviderException
140    {
141        if ( logger == null )
142        {
143            logger = getScmLogger();
144
145            for ( Entry<String, ScmProvider> entry : scmProviders.entrySet() )
146            {
147                ScmProvider p = entry.getValue();
148
149                p.addListener( logger );
150            }
151        }
152
153        String usedProviderType = System.getProperty( "maven.scm.provider." + providerType + ".implementation" );
154
155        if ( usedProviderType == null )
156        {
157            if ( userProviderTypes.containsKey( providerType ) )
158            {
159                usedProviderType = userProviderTypes.get( providerType );
160            }
161            else
162            {
163                usedProviderType = providerType;
164            }
165        }
166
167        ScmProvider scmProvider = scmProviders.get( usedProviderType );
168
169        if ( scmProvider == null )
170        {
171            throw new NoSuchScmProviderException( usedProviderType );
172        }
173
174        return scmProvider;
175    }
176
177    /**
178     * {@inheritDoc}
179     */
180    public ScmProvider getProviderByRepository( ScmRepository repository )
181        throws NoSuchScmProviderException
182    {
183        return getProviderByType( repository.getProvider() );
184    }
185
186    // ----------------------------------------------------------------------
187    // Repository
188    // ----------------------------------------------------------------------
189
190    /**
191     * {@inheritDoc}
192     */
193    public ScmRepository makeScmRepository( String scmUrl )
194        throws ScmRepositoryException, NoSuchScmProviderException
195    {
196        if ( scmUrl == null )
197        {
198            throw new NullPointerException( "The scm url cannot be null." );
199        }
200
201        char delimiter = ScmUrlUtils.getDelimiter( scmUrl ).charAt( 0 );
202
203        String providerType = ScmUrlUtils.getProvider( scmUrl );
204
205        ScmProvider provider = getProviderByType( providerType );
206
207        String scmSpecificUrl = cleanScmUrl( scmUrl.substring( providerType.length() + 5 ) );
208
209        ScmProviderRepository providerRepository = provider.makeProviderScmRepository( scmSpecificUrl, delimiter );
210
211        return new ScmRepository( providerType, providerRepository );
212    }
213
214    /**
215     * Clean the SCM url by removing all ../ in path
216     *
217     * @param scmUrl the SCM url
218     * @return the cleaned SCM url
219     */
220    protected String cleanScmUrl( String scmUrl )
221    {
222        if ( scmUrl == null )
223        {
224            throw new NullPointerException( "The scm url cannot be null." );
225        }
226
227        String pathSeparator = "";
228
229        int indexOfDoubleDot = -1;
230
231        // Clean Unix path
232        if ( scmUrl.indexOf( "../" ) > 1 )
233        {
234            pathSeparator = "/";
235
236            indexOfDoubleDot = scmUrl.indexOf( "../" );
237        }
238
239        // Clean windows path
240        if ( scmUrl.indexOf( "..\\" ) > 1 )
241        {
242            pathSeparator = "\\";
243
244            indexOfDoubleDot = scmUrl.indexOf( "..\\" );
245        }
246
247        if ( indexOfDoubleDot > 1 )
248        {
249            int startOfTextToRemove = scmUrl.substring( 0, indexOfDoubleDot - 1 ).lastIndexOf( pathSeparator );
250
251            String beginUrl = "";
252            if ( startOfTextToRemove >= 0 )
253            {
254                beginUrl = scmUrl.substring( 0, startOfTextToRemove );
255            }
256
257            String endUrl = scmUrl.substring( indexOfDoubleDot + 3 );
258
259            scmUrl = beginUrl + pathSeparator + endUrl;
260
261            // Check if we have other double dot
262            if ( scmUrl.indexOf( "../" ) > 1 || scmUrl.indexOf( "..\\" ) > 1 )
263            {
264                scmUrl = cleanScmUrl( scmUrl );
265            }
266        }
267
268        return scmUrl;
269    }
270
271    /**
272     * {@inheritDoc}
273     */
274    public ScmRepository makeProviderScmRepository( String providerType, File path )
275        throws ScmRepositoryException, UnknownRepositoryStructure, NoSuchScmProviderException
276    {
277        if ( providerType == null )
278        {
279            throw new NullPointerException( "The provider type cannot be null." );
280        }
281
282        ScmProvider provider = getProviderByType( providerType );
283
284        ScmProviderRepository providerRepository = provider.makeProviderScmRepository( path );
285
286        return new ScmRepository( providerType, providerRepository );
287    }
288
289    /**
290     * {@inheritDoc}
291     */
292    public List<String> validateScmRepository( String scmUrl )
293    {
294        List<String> messages = new ArrayList<String>();
295
296        messages.addAll( ScmUrlUtils.validate( scmUrl ) );
297
298        String providerType = ScmUrlUtils.getProvider( scmUrl );
299
300        ScmProvider provider;
301
302        try
303        {
304            provider = getProviderByType( providerType );
305        }
306        catch ( NoSuchScmProviderException e )
307        {
308            messages.add( "No such provider installed '" + providerType + "'." );
309
310            return messages;
311        }
312
313        String scmSpecificUrl = cleanScmUrl( scmUrl.substring( providerType.length() + 5 ) );
314
315        List<String> providerMessages =
316            provider.validateScmUrl( scmSpecificUrl, ScmUrlUtils.getDelimiter( scmUrl ).charAt( 0 ) );
317
318        if ( providerMessages == null )
319        {
320            throw new RuntimeException( "The SCM provider cannot return null from validateScmUrl()." );
321        }
322
323        messages.addAll( providerMessages );
324
325        return messages;
326    }
327
328    /**
329     * {@inheritDoc}
330     */
331    public AddScmResult add( ScmRepository repository, ScmFileSet fileSet )
332        throws ScmException
333    {
334        return this.getProviderByRepository( repository ).add( repository, fileSet );
335    }
336
337    /**
338     * {@inheritDoc}
339     */
340    public AddScmResult add( ScmRepository repository, ScmFileSet fileSet, String message )
341        throws ScmException
342    {
343        return this.getProviderByRepository( repository ).add( repository, fileSet, message );
344    }
345
346    /**
347     * {@inheritDoc}
348     */
349    public BranchScmResult branch( ScmRepository repository, ScmFileSet fileSet, String branchName )
350        throws ScmException
351    {
352        ScmBranchParameters scmBranchParameters = new ScmBranchParameters( "" );
353        return this.getProviderByRepository( repository ).branch( repository, fileSet, branchName,
354                                                                  scmBranchParameters );
355    }
356
357    /**
358     * {@inheritDoc}
359     */
360    public BranchScmResult branch( ScmRepository repository, ScmFileSet fileSet, String branchName, String message )
361        throws ScmException
362    {
363        ScmBranchParameters scmBranchParameters = new ScmBranchParameters( message );
364        return this.getProviderByRepository( repository ).branch( repository, fileSet, branchName,
365                                                                  scmBranchParameters );
366    }
367
368    /**
369     * {@inheritDoc}
370     */
371    public ChangeLogScmResult changeLog( ScmRepository repository, ScmFileSet fileSet, Date startDate, Date endDate,
372                                         int numDays, ScmBranch branch )
373        throws ScmException
374    {
375        return this.getProviderByRepository( repository ).changeLog( repository, fileSet, startDate, endDate, numDays,
376                                                                     branch );
377    }
378
379    /**
380     * {@inheritDoc}
381     */
382    public ChangeLogScmResult changeLog( ScmRepository repository, ScmFileSet fileSet, Date startDate, Date endDate,
383                                         int numDays, ScmBranch branch, String datePattern )
384        throws ScmException
385    {
386        return this.getProviderByRepository( repository ).changeLog( repository, fileSet, startDate, endDate, numDays,
387                                                                     branch, datePattern );
388    }
389
390    /**
391     * {@inheritDoc}
392     */
393    public ChangeLogScmResult changeLog( ChangeLogScmRequest scmRequest )
394        throws ScmException
395    {
396        return this.getProviderByRepository( scmRequest.getScmRepository() ).changeLog( scmRequest );
397    }
398
399    /**
400     * {@inheritDoc}
401     */
402    public ChangeLogScmResult changeLog( ScmRepository repository, ScmFileSet fileSet, ScmVersion startVersion,
403                                         ScmVersion endVersion )
404        throws ScmException
405    {
406        return this.getProviderByRepository( repository ).changeLog( repository, fileSet, startVersion, endVersion );
407    }
408
409    /**
410     * {@inheritDoc}
411     */
412    public ChangeLogScmResult changeLog( ScmRepository repository, ScmFileSet fileSet, ScmVersion startRevision,
413                                         ScmVersion endRevision, String datePattern )
414        throws ScmException
415    {
416        return this.getProviderByRepository( repository ).changeLog( repository, fileSet, startRevision, endRevision,
417                                                                     datePattern );
418    }
419
420    /**
421     * {@inheritDoc}
422     */
423    public CheckInScmResult checkIn( ScmRepository repository, ScmFileSet fileSet, String message )
424        throws ScmException
425    {
426        return this.getProviderByRepository( repository ).checkIn( repository, fileSet, message );
427    }
428
429    /**
430     * {@inheritDoc}
431     */
432    public CheckInScmResult checkIn( ScmRepository repository, ScmFileSet fileSet, ScmVersion revision, String message )
433        throws ScmException
434    {
435        return this.getProviderByRepository( repository ).checkIn( repository, fileSet, revision, message );
436    }
437
438    /**
439     * {@inheritDoc}
440     */
441    public CheckOutScmResult checkOut( ScmRepository repository, ScmFileSet fileSet )
442        throws ScmException
443    {
444        return this.getProviderByRepository( repository ).checkOut( repository, fileSet );
445    }
446
447    /**
448     * {@inheritDoc}
449     */
450    public CheckOutScmResult checkOut( ScmRepository repository, ScmFileSet fileSet, ScmVersion version )
451        throws ScmException
452    {
453        return this.getProviderByRepository( repository ).checkOut( repository, fileSet, version );
454    }
455
456    /**
457     * {@inheritDoc}
458     */
459    public CheckOutScmResult checkOut( ScmRepository repository, ScmFileSet fileSet, boolean recursive )
460        throws ScmException
461    {
462        return this.getProviderByRepository( repository ).checkOut( repository, fileSet, recursive );
463    }
464
465    /**
466     * {@inheritDoc}
467     */
468    public CheckOutScmResult checkOut( ScmRepository repository, ScmFileSet fileSet, ScmVersion version,
469                                       boolean recursive )
470        throws ScmException
471    {
472        return this.getProviderByRepository( repository ).checkOut( repository, fileSet, version, recursive );
473    }
474
475    /**
476     * {@inheritDoc}
477     */
478    public DiffScmResult diff( ScmRepository repository, ScmFileSet fileSet, ScmVersion startVersion,
479                               ScmVersion endVersion )
480        throws ScmException
481    {
482        return this.getProviderByRepository( repository ).diff( repository, fileSet, startVersion, endVersion );
483    }
484
485    /**
486     * {@inheritDoc}
487     */
488    public EditScmResult edit( ScmRepository repository, ScmFileSet fileSet )
489        throws ScmException
490    {
491        return this.getProviderByRepository( repository ).edit( repository, fileSet );
492    }
493
494    /**
495     * {@inheritDoc}
496     */
497    public ExportScmResult export( ScmRepository repository, ScmFileSet fileSet )
498        throws ScmException
499    {
500        return this.getProviderByRepository( repository ).export( repository, fileSet );
501    }
502
503    /**
504     * {@inheritDoc}
505     */
506    public ExportScmResult export( ScmRepository repository, ScmFileSet fileSet, ScmVersion version )
507        throws ScmException
508    {
509        return this.getProviderByRepository( repository ).export( repository, fileSet, version );
510    }
511
512    /**
513     * {@inheritDoc}
514     */
515    public ExportScmResult export( ScmRepository repository, ScmFileSet fileSet, String outputDirectory )
516        throws ScmException
517    {
518        return this.getProviderByRepository( repository ).export( repository, fileSet, (ScmVersion) null,
519                                                                  outputDirectory );
520    }
521
522    /**
523     * {@inheritDoc}
524     */
525    public ExportScmResult export( ScmRepository repository, ScmFileSet fileSet, ScmVersion version,
526                                   String outputDirectory )
527        throws ScmException
528    {
529        return this.getProviderByRepository( repository ).export( repository, fileSet, version, outputDirectory );
530    }
531
532    /**
533     * {@inheritDoc}
534     */
535    public ListScmResult list( ScmRepository repository, ScmFileSet fileSet, boolean recursive, ScmVersion version )
536        throws ScmException
537    {
538        return this.getProviderByRepository( repository ).list( repository, fileSet, recursive, version );
539    }
540
541    /**
542     * {@inheritDoc}
543     */
544    public MkdirScmResult mkdir( ScmRepository repository, ScmFileSet fileSet, String message, boolean createInLocal )
545        throws ScmException
546    {
547        return this.getProviderByRepository( repository ).mkdir( repository, fileSet, message, createInLocal );
548    }
549
550    /**
551     * {@inheritDoc}
552     */
553    public RemoveScmResult remove( ScmRepository repository, ScmFileSet fileSet, String message )
554        throws ScmException
555    {
556        return this.getProviderByRepository( repository ).remove( repository, fileSet, message );
557    }
558
559    /**
560     * {@inheritDoc}
561     */
562    public StatusScmResult status( ScmRepository repository, ScmFileSet fileSet )
563        throws ScmException
564    {
565        return this.getProviderByRepository( repository ).status( repository, fileSet );
566    }
567
568    /**
569     * {@inheritDoc}
570     */
571    public TagScmResult tag( ScmRepository repository, ScmFileSet fileSet, String tagName )
572        throws ScmException
573    {
574        return this.tag( repository, fileSet, tagName, "" );
575    }
576
577    /**
578     * {@inheritDoc}
579     */
580    public TagScmResult tag( ScmRepository repository, ScmFileSet fileSet, String tagName, String message )
581        throws ScmException
582    {
583        ScmTagParameters scmTagParameters = new ScmTagParameters( message );
584        return this.getProviderByRepository( repository ).tag( repository, fileSet, tagName, scmTagParameters );
585    }
586
587    /**
588     * {@inheritDoc}
589     */
590    public UnEditScmResult unedit( ScmRepository repository, ScmFileSet fileSet )
591        throws ScmException
592    {
593        return this.getProviderByRepository( repository ).unedit( repository, fileSet );
594    }
595
596    /**
597     * {@inheritDoc}
598     */
599    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet )
600        throws ScmException
601    {
602        return this.getProviderByRepository( repository ).update( repository, fileSet );
603    }
604
605    /**
606     * {@inheritDoc}
607     */
608    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version )
609        throws ScmException
610    {
611        return this.getProviderByRepository( repository ).update( repository, fileSet, version );
612    }
613
614    /**
615     * {@inheritDoc}
616     */
617    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, boolean runChangelog )
618        throws ScmException
619    {
620        return this.getProviderByRepository( repository ).update( repository, fileSet, runChangelog );
621    }
622
623    /**
624     * {@inheritDoc}
625     */
626    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version,
627                                   boolean runChangelog )
628        throws ScmException
629    {
630        return this.getProviderByRepository( repository ).update( repository, fileSet, version, runChangelog );
631    }
632
633    /**
634     * {@inheritDoc}
635     */
636    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, String datePattern )
637        throws ScmException
638    {
639        return this.getProviderByRepository( repository ).update( repository, fileSet, (ScmVersion) null, datePattern );
640    }
641
642    /**
643     * {@inheritDoc}
644     */
645    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version,
646                                   String datePattern )
647        throws ScmException
648    {
649        return this.getProviderByRepository( repository ).update( repository, fileSet, version, datePattern );
650    }
651
652    /**
653     * {@inheritDoc}
654     */
655    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, Date lastUpdate )
656        throws ScmException
657    {
658        return this.getProviderByRepository( repository ).update( repository, fileSet, (ScmVersion) null, lastUpdate );
659    }
660
661    /**
662     * {@inheritDoc}
663     */
664    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version, Date lastUpdate )
665        throws ScmException
666    {
667        return this.getProviderByRepository( repository ).update( repository, fileSet, version, lastUpdate );
668    }
669
670    /**
671     * {@inheritDoc}
672     */
673    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, Date lastUpdate, String datePattern )
674        throws ScmException
675    {
676        return this.getProviderByRepository( repository ).update( repository, fileSet, (ScmVersion) null, lastUpdate,
677                                                                  datePattern );
678    }
679
680    /**
681     * {@inheritDoc}
682     */
683    public UpdateScmResult update( ScmRepository repository, ScmFileSet fileSet, ScmVersion version, Date lastUpdate,
684                                   String datePattern )
685        throws ScmException
686    {
687        return this.getProviderByRepository( repository ).update( repository, fileSet, version, lastUpdate,
688                                                                  datePattern );
689    }
690
691    /**
692     * {@inheritDoc}
693     */
694    public BlameScmResult blame( ScmRepository repository, ScmFileSet fileSet, String filename )
695        throws ScmException
696    {
697        return this.getProviderByRepository( repository ).blame( repository, fileSet, filename );
698    }
699
700    public BlameScmResult blame( BlameScmRequest blameScmRequest )
701        throws ScmException
702    {
703        return this.getProviderByRepository( blameScmRequest.getScmRepository() ).blame( blameScmRequest );
704    }
705}