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