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