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