001package org.apache.maven.scm.provider.clearcase.repository;
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.log.ScmLogger;
023import org.apache.maven.scm.provider.ScmProviderRepository;
024import org.apache.maven.scm.providers.clearcase.settings.Settings;
025import org.apache.maven.scm.repository.ScmRepositoryException;
026
027import java.io.File;
028import java.net.InetAddress;
029import java.net.MalformedURLException;
030import java.net.URI;
031import java.net.URISyntaxException;
032import java.net.URL;
033import java.net.UnknownHostException;
034import java.util.StringTokenizer;
035
036/**
037 * Provider Repository for ClearCase (standard, LT, UCM)
038 * <p />
039 * Url format for ClearCase and ClearCaseLT : <br />
040 * [view_name]:[configspec] or [view_name]|[configspec]
041 * <p />
042 * Url format for ClearCaseUCM : <br />
043 * [view_name]|[configspec]|[vob_name]|[stream_name] or [view_name]:[configspec]:[vob_name]:[stream_name]
044 * <p />
045 * [configspec] can be used in two different ways:
046 * <ul>
047 * <li>Path to a config spec file that is
048 * used when creating the snapshot view, e.g.
049 * "\\myserver\clearcase\configspecs\my_module.txt", or:</li>
050 * <li>A load rule that is used to automatically create a config spec, e.g. "load /MY_VOB/my/project/dir"</li>
051 * </ul>
052 * Notice that checking out from a tag is currently only supported when the second option is used.
053 *
054 * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
055 * @version $Id: ClearCaseScmProviderRepository.java 483105 2006-12-06 15:07:54Z
056 *          evenisse $
057 */
058public class ClearCaseScmProviderRepository
059    extends ScmProviderRepository
060{
061    private ScmLogger logger;
062
063    private boolean viewNameGivenByUser = false;
064
065    private String viewName;
066
067    /**
068     * The user-specified config spec; may be null.
069     */
070    private File configSpec;
071
072    /**
073     * The directory to be loaded, when auto-generating the config spec.
074     */
075    private String loadDirectory;
076
077    /**
078     * Describe the stream linked to the view. Only used with ClearCaseUCM
079     */
080    private String streamName;
081
082    /**
083     * Describe the vob containing the stream. Only used with ClearCaseUCM
084     */
085    private String vobName;
086
087    /**
088     * Provider configuration settings
089     */
090    private Settings settings;
091
092    /**
093     * Describe the Element Name
094     */
095    private String elementName;
096
097    /**
098     * Define the flag used in the clearcase-settings.xml when using ClearCaseLT
099     */
100    public static final String CLEARCASE_LT = "LT";
101
102    /**
103     * Define the flag used in the clearcase-settings.xml when using ClearCaseUCM
104     */
105    public static final String CLEARCASE_UCM = "UCM";
106
107    /**
108     * Define the default value from the clearcase-settings.xml when using ClearCase
109     */
110    public static final String CLEARCASE_DEFAULT = null;
111
112    public ClearCaseScmProviderRepository( ScmLogger logger, String url, Settings settings )
113        throws ScmRepositoryException
114    {
115        this.logger = logger;
116        this.settings = settings;
117        try
118        {
119            parseUrl( url );
120        }
121        catch ( MalformedURLException e )
122        {
123            throw new ScmRepositoryException( "Illegal URL: " + url + "(" + e.getMessage() + ")" );
124        }
125        catch ( URISyntaxException e )
126        {
127            throw new ScmRepositoryException( "Illegal URL: " + url + "(" + e.getMessage() + ")" );
128        }
129        catch ( UnknownHostException e )
130        {
131            throw new ScmRepositoryException( "Illegal URL: " + url + "(" + e.getMessage() + ")" );
132        }
133    }
134
135    private void parseUrl( String url )
136        throws MalformedURLException, URISyntaxException, UnknownHostException
137    {
138        if ( url.indexOf( '|' ) != -1 )
139        {
140            StringTokenizer tokenizer = new StringTokenizer( url, "|" );
141            fillInProperties( tokenizer );
142        }
143        else
144        {
145            StringTokenizer tokenizer = new StringTokenizer( url, ":" );
146            fillInProperties( tokenizer );
147        }
148    }
149
150    private void fillInProperties( StringTokenizer tokenizer )
151        throws UnknownHostException, URISyntaxException, MalformedURLException
152    {
153        String configSpecString = null;
154
155        if ( CLEARCASE_UCM.equals( settings.getClearcaseType() ) )
156        {
157            configSpecString = fillUCMProperties( tokenizer );
158        }
159        else
160        {
161            configSpecString = fillDefaultProperties( tokenizer );
162        }
163
164        if ( !configSpecString.startsWith( "load " ) )
165        {
166            configSpec = createConfigSpecFile( configSpecString );
167            loadDirectory = null;
168        }
169        else
170        {
171            configSpec = null;
172            loadDirectory = configSpecString.substring( 5 );
173
174        }
175    }
176
177    private String fillDefaultProperties( StringTokenizer tokenizer )
178        throws UnknownHostException
179    {
180        int tokenNumber = tokenizer.countTokens();
181        String configSpecString;
182        if ( tokenNumber == 1 )
183        {
184            // No view name was given
185            viewName = getDefaultViewName();
186            configSpecString = tokenizer.nextToken();
187        }
188        else
189        {
190            configSpecString = checkViewName( tokenizer );
191            checkUnexpectedParameter( tokenizer, tokenNumber, 2 );
192        }
193        if ( logger.isDebugEnabled() )
194        {
195            logger.debug( "viewName = '" + viewName + "' ; configSpec = '" + configSpecString + "'" );
196        }
197        return configSpecString;
198    }
199
200    private String fillUCMProperties( StringTokenizer tokenizer )
201        throws UnknownHostException, MalformedURLException
202    {
203        int tokenNumber = tokenizer.countTokens();
204        if ( tokenNumber <= 2 )
205        {
206            throw new MalformedURLException( "ClearCaseUCM need more parameters. Expected url format : "
207                + "[view_name]|[configspec]|[vob_name]|[stream_name]" );
208        }
209
210        String configSpecString;
211        if ( tokenNumber == 3 )
212        {
213            // No view name was given
214            viewName = getDefaultViewName();
215            configSpecString = tokenizer.nextToken();
216            vobName = tokenizer.nextToken();
217            streamName = tokenizer.nextToken();
218        }
219        else if ( tokenNumber == 4 )
220        {
221            String[] tokens = new String[4];
222            tokens[0] = tokenizer.nextToken();
223            tokens[1] = tokenizer.nextToken();
224            tokens[2] = tokenizer.nextToken();
225            tokens[3] = tokenizer.nextToken();
226
227            if ( tokens[3].startsWith( "/main/" ) )
228            {
229                viewName = getDefaultViewName();
230                configSpecString = tokens[0];
231                vobName = tokens[1];
232                streamName = tokens[2];
233                elementName = tokens[3];
234            }
235            else
236            {
237                viewName = tokens[0];
238                viewNameGivenByUser = true;
239                configSpecString = tokens[1];
240                vobName = tokens[2];
241                streamName = tokens[3];
242            }
243        }
244        else
245        {
246            configSpecString = checkViewName( tokenizer );
247            vobName = tokenizer.nextToken();
248            streamName = tokenizer.nextToken();
249            elementName = tokenizer.nextToken();
250            checkUnexpectedParameter( tokenizer, tokenNumber, 5 );
251        }
252
253        if ( logger.isInfoEnabled() )
254        {
255            logger.info( "viewName = '" + viewName + "' ; configSpec = '" + configSpecString + "' ; vobName = '"
256                + vobName + "' ; streamName = '" + streamName + "' ; elementName = '" + elementName + "'" );
257        }
258
259        return configSpecString;
260    }
261
262    private String checkViewName( StringTokenizer tokenizer )
263        throws UnknownHostException
264    {
265        viewName = tokenizer.nextToken();
266        if ( viewName.length() > 0 )
267        {
268            viewNameGivenByUser = true;
269        }
270        else
271        {
272            viewName = getDefaultViewName();
273        }
274
275        return tokenizer.nextToken();
276    }
277
278    private void checkUnexpectedParameter( StringTokenizer tokenizer, int tokenNumber, int maxTokenNumber )
279    {
280        if ( tokenNumber > maxTokenNumber )
281        {
282            String unexpectedToken = tokenizer.nextToken();
283            if ( logger.isInfoEnabled() )
284            {
285                logger.info( "The SCM URL contains unused parameter : " + unexpectedToken );
286            }
287        }
288    }
289
290    private File createConfigSpecFile( String spec )
291        throws URISyntaxException, MalformedURLException
292    {
293        File result;
294        if ( spec.indexOf( ':' ) == -1 )
295        {
296            result = new File( spec );
297        }
298        else
299        {
300            result = new File( new URI( new URL( spec ).toString() ) );
301        }
302        return result;
303    }
304
305    /**
306     * Default: ${hostname}-{user.name}-maven
307     *
308     * @return the default view name
309     */
310    private String getDefaultViewName()
311        throws UnknownHostException
312    {
313        String username = System.getProperty( "user.name", "nouser" );
314        String hostname = getHostName();
315        return username + "-" + hostname + "-maven";
316    }
317
318    private String getHostName()
319        throws UnknownHostException
320    {
321        return InetAddress.getLocalHost().getHostName();
322    }
323
324    /**
325     * Returns the name of the view. If it is defined in the scm url, then it is returned as defined there.
326     * If it is the default name, then the uniqueId is added
327     *
328     * @param uniqueId
329     * @return the name of the view
330     */
331    public String getViewName( String uniqueId )
332    {
333        String result;
334        if ( viewNameGivenByUser )
335        {
336            result = viewName;
337        }
338        else
339        {
340            result = viewName + "-" + uniqueId;
341        }
342
343        return result;
344    }
345
346    /**
347     * Returns the user-supplied config spec or <code>null</code> in case it
348     * should be automatically generated
349     *
350     * @return File or <code>null</code>
351     * @see #isAutoConfigSpec()
352     */
353    public File getConfigSpec()
354    {
355        return configSpec;
356    }
357
358    /**
359     * Returns true when the config spec has not been supplied by the user, but
360     * instead should automatically be generated by the plugin
361     *
362     * @return true if auto config spec
363     */
364    public boolean isAutoConfigSpec()
365    {
366        return configSpec == null;
367    }
368
369    /**
370     * Returns the VOB directory to be loaded when auto-generating the config
371     * spec.
372     *
373     * @return <code>null</code> when isAutoConfigSpec() returns false;
374     *         otherwise the VOB directory
375     */
376    public String getLoadDirectory()
377    {
378        return loadDirectory;
379    }
380
381    public String getStreamName()
382    {
383        return streamName;
384    }
385
386    public String getVobName()
387    {
388        return vobName;
389    }
390
391    public String getElementName()
392    {
393        return elementName;
394    }
395
396    public boolean hasElements()
397    {
398        if ( elementName == null )
399        {
400            return false;
401        }
402
403        return true;
404    }
405}