001 package org.apache.maven.scm.provider.accurev;
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
022 import java.io.File;
023 import java.util.Date;
024 import java.util.List;
025 import java.util.Map;
026 import java.util.regex.Matcher;
027 import java.util.regex.Pattern;
028
029 import org.apache.maven.scm.ScmVersion;
030 import org.apache.maven.scm.log.ScmLogger;
031 import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
032 import org.apache.maven.scm.provider.accurev.util.WorkspaceUtils;
033 import org.codehaus.plexus.util.StringUtils;
034
035 public class AccuRevScmProviderRepository
036 extends ScmProviderRepositoryWithHost
037 {
038 public static final String DEFAULT_TAG_FORMAT = "%s";
039
040 private AccuRev accurev;
041
042 private String streamName;
043
044 private String projectPath;
045
046 private String tagFormat = DEFAULT_TAG_FORMAT;
047
048 private ScmLogger logger;
049
050 public AccuRevScmProviderRepository()
051 {
052 super();
053 // True is a more sensible default (ie for tck tests)
054 // TODO raise jira so tck tests properly handle setPersist
055 setPersistCheckout( true );
056
057 setShouldUseExportForNonPersistentCheckout( true );
058 }
059
060 public String getTagFormat()
061 {
062 return tagFormat;
063 }
064
065 public void setTagFormat( String tagFormat )
066 {
067 if ( tagFormat == null || !tagFormat.contains( "%s" ) )
068 {
069 throw new IllegalArgumentException( "tagFormat must contain '%s' to be replaced" );
070 }
071 this.tagFormat = tagFormat;
072 }
073
074 public String getStreamName()
075 {
076 return streamName;
077 }
078
079 public void setStreamName( String streamName )
080 {
081 this.streamName = streamName;
082 }
083
084 public String getProjectPath()
085 {
086 return projectPath;
087 }
088
089 public void setProjectPath( String projectPath )
090 {
091 this.projectPath = projectPath;
092 setCheckoutRelativePath( projectPath );
093 }
094
095 public AccuRev getAccuRev()
096 {
097 return this.accurev;
098 }
099
100 public void setAccuRev( AccuRev accurev )
101 {
102 this.accurev = accurev;
103 }
104
105 /**
106 * @param info
107 * @return true if info indicates a root of the workspace.
108 */
109 public boolean isWorkSpaceRoot( AccuRevInfo info )
110 {
111 return ( ( getProjectPath() != null && WorkspaceUtils.isSameFile(info.getBasedir(), new File( info.getTop(), getProjectPath() ) ) ) || isWorkSpaceTop( info ) );
112 }
113
114 public boolean isWorkSpaceTop( AccuRevInfo info )
115 {
116 return info.isWorkSpaceTop();
117
118 }
119
120
121 String tagToStream( String tagName )
122 {
123 return String.format( getTagFormat(), tagName );
124 }
125
126 String streamToTag( String streamName )
127 {
128 tagFormat = getTagFormat();
129 // TODO - strictly we should quote either side of the %s
130 String tagPatternString = tagToStream( "(.*)" );
131 Pattern tagPattern = Pattern.compile( tagPatternString );
132
133 Matcher tagMatcher = tagPattern.matcher( streamName );
134 if ( tagMatcher.matches() )
135 {
136 return tagMatcher.group( 1 );
137 }
138 else
139 {
140 return streamName;
141 }
142
143 }
144
145 public void setLogger( ScmLogger logger )
146 {
147 this.logger = logger;
148 }
149
150 // TODO raise JIRA to pull up these methods to ScmProviderRepository
151
152 private String checkoutRelativePath;
153
154 private boolean shouldUseExportForNonPersistentCheckout = true;
155
156 /**
157 * The relative path of the directory of the checked out project in comparison to the checkout directory, or an
158 * empty String in case the checkout directory equals the project directory.
159 * <p/>
160 * With most SCMs, this is just an empty String, meaning that the checkout directory equals the project directory.
161 * But there are cases (e.g. ClearCase) where within the checkout directory, the directory structure of the SCM
162 * system is repeated. E.g. if you check out the project "my/project" to "/some/dir", the project sources are
163 * actually checked out to "some/dir/my/project". In this example, relativePathProjectDirectory would contain
164 * "my/project".
165 */
166 public String getCheckoutRelativePath()
167 {
168 if ( this.checkoutRelativePath == null )
169 {
170 return "";
171 }
172 return this.checkoutRelativePath;
173 }
174
175 public void setCheckoutRelativePath( String checkoutRelativePath )
176 {
177 this.checkoutRelativePath = checkoutRelativePath;
178 }
179
180 /**
181 * Relative project path for export
182 *
183 * @return default same as {@link #getCheckoutRelativePath()}
184 */
185 public String getExportRelativePath()
186 {
187 return getCheckoutRelativePath();
188 }
189
190 /**
191 * When checkout is not expected to be refreshed or committed, should export be used instead? Perforce, Clearcase
192 * and AccuRev store their meta-data about file status within the server rather than files in the source tree. This
193 * makes checkouts within checkouts (eg release:perform) difficult. Typically there is a way to do a lightweight
194 * export instead which can be implemented as the "export" command. This is a hint to downstream applications that
195 * "export" is available and should be used in preference to "checkout" in cases where "update" and "commit" are not
196 * intended to be used. (ie release:perform)
197 *
198 * @return false by default
199 */
200 public boolean shouldUseExportForNonPersistentCheckout()
201 {
202 return this.shouldUseExportForNonPersistentCheckout;
203 }
204
205 public void setShouldUseExportForNonPersistentCheckout( boolean shouldUseExportForNonPersistentCheckout )
206 {
207 this.shouldUseExportForNonPersistentCheckout = shouldUseExportForNonPersistentCheckout;
208 }
209
210 public String getDepotRelativeProjectPath()
211 {
212 return "/./" + ( projectPath == null ? "" : projectPath );
213 }
214
215 public AccuRevVersion getAccuRevVersion( ScmVersion scmVersion )
216 {
217
218 String tran = null;
219 String basisStream = null;
220
221 if ( scmVersion == null )
222 {
223 basisStream = getStreamName();
224 }
225 else
226 {
227 String name = StringUtils.clean( scmVersion.getName() );
228
229 String[] versionComponents = name.split( "[/\\\\]", 2 );
230 basisStream = versionComponents[0];
231 if ( basisStream.length() == 0 )
232 {
233 // Use the default stream from the URL
234 basisStream = getStreamName();
235 }
236 else
237 {
238 // name is a tag name - convert to a stream.
239 basisStream = tagToStream( basisStream );
240 }
241
242 if ( versionComponents.length == 2 && versionComponents[1].length() > 0 )
243 {
244 tran = versionComponents[1];
245 }
246 }
247
248 return new AccuRevVersion( basisStream, tran );
249 }
250
251 public String getSnapshotName( String tagName )
252 {
253 return tagToStream( tagName );
254 }
255
256 public String getRevision( String streamName, Date date )
257 {
258 return getRevision( streamName, AccuRev.ACCUREV_TIME_SPEC.format( date == null ? new Date() : date ) );
259 }
260
261 public String getRevision( String stream, long fromTranId )
262 {
263 return getRevision( stream, Long.toString( fromTranId ) );
264 }
265
266 public String getRevision( String streamName, String transaction )
267 {
268 return streamToTag( streamName ) + "/" + transaction;
269 }
270
271 public String getWorkSpaceRevision( String workspace )
272 throws AccuRevException
273 {
274 return getRevision( workspace, Long.toString( getCurrentTransactionId( workspace ) ) );
275 }
276
277 public Transaction getDepotTransaction( String stream, String tranSpec )
278 throws AccuRevException
279 {
280
281 if ( tranSpec == null )
282 {
283 tranSpec = "now";
284 }
285
286 List<Transaction> transactions = getAccuRev().history( stream, tranSpec, null, 1, true, true );
287
288 if ( transactions == null || transactions.isEmpty() )
289 {
290 logger.warn( "Unable to find transaction for tranSpec=" + tranSpec );
291 return null;
292 }
293 else
294 {
295 return transactions.get( 0 );
296 }
297
298 }
299
300 public String getDepotTransactionId( String stream, String tranSpec )
301 throws AccuRevException
302 {
303 Transaction t = getDepotTransaction( stream, tranSpec );
304
305 return t == null ? tranSpec : Long.toString( t.getTranId() );
306 }
307
308 private long getCurrentTransactionId( String workSpaceName )
309 throws AccuRevException
310 {
311 // AccuRev does not have a way to get at this workspace info by name.
312 // So we have to do it the hard way...
313
314 AccuRev accuRev = getAccuRev();
315
316 Map<String, WorkSpace> workSpaces = accuRev.showWorkSpaces();
317
318 WorkSpace workspace = workSpaces.get( workSpaceName );
319
320 if ( workspace == null )
321 {
322 // Must be a reftree
323 workSpaces = accuRev.showRefTrees();
324 workspace = workSpaces.get( workSpaceName );
325 }
326
327 if ( workspace == null )
328 {
329 throw new AccuRevException( "Can't find workspace " + workSpaceName );
330 }
331 return workspace.getTransactionId();
332 }
333
334 public String toString()
335 {
336 StringBuilder buff = new StringBuilder( "AccuRevScmProviderRepository" );
337 buff.append( " user=" );
338 buff.append( getUser() );
339 buff.append( " pass=" );
340 buff.append( getPassword() == null ? "null" : StringUtils.repeat( "*", getPassword().length() ) );
341 buff.append( " host=" );
342 buff.append( getHost() );
343 buff.append( " port=" );
344 buff.append( getPort() );
345 buff.append( " stream=" );
346 buff.append( getStreamName() );
347 buff.append( " projectPath=" );
348 buff.append( getProjectPath() );
349
350 return buff.toString();
351 }
352
353 public static String formatTimeSpec( Date when )
354 {
355
356 if ( when == null )
357 {
358 return "now";
359 }
360
361 return AccuRev.ACCUREV_TIME_SPEC.format( when );
362
363 }
364
365 }