src/net/sourceforge/transparent/CommandLineClearCase.java (237 lines of code) (raw):

package net.sourceforge.transparent; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.LineTokenizer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.vcsUtil.VcsUtil; import net.sourceforge.transparent.exceptions.ClearCaseException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; public class CommandLineClearCase implements ClearCase { @NonNls private final static String VERSIONED_SIG = "@@"; @NonNls private final static String HIJACKED_SIG = "[hijacked]"; @NonNls private final static String CHECKEDOUT_SIG = "Rule: CHECKEDOUT"; @NonNls private final static String CHECKEDOUT_REMOVED_SIG = "checkedout but removed"; @NonNls private final static String ACTIVITY_SIG = " activity:"; @NonNls private final static String NOT_VOB_ELEMENT = "Pathname is not within"; @NonNls private final static String UNABLE_TO_ACCESS = "Unable to access"; @NonNls private final static String NO_SUCH_FILE_OR_DIR = "No such file or directory"; @NonNls private final static String IDENTICAL_CONTENT_SIG = "version with data identical to"; private TransparentVcs host; public String getName() { return (net.sourceforge.transparent.CommandLineClearCase.class).getName(); } public void setHost( TransparentVcs host ) { this.host = host; } public void undoCheckOut( File file ) { cleartool( new String[] { "unco", "-rm", file.getAbsolutePath() } ); } public void checkIn( File file, String comment ) { if( host.getConfig().useIdenticalSwitch ) { if( StringUtil.isNotEmpty( comment ) ) cleartool( new String[] { "ci", "-c", quote( comment ), "-identical", file.getAbsolutePath() } ); else cleartool( new String[] { "ci", "-nc", "-identical", file.getAbsolutePath() } ); } else { try { if( StringUtil.isNotEmpty( comment ) ) cleartool( new String[] { "ci", "-c", quote( comment ), file.getAbsolutePath() } ); else cleartool( new String[] { "ci", "-nc", file.getAbsolutePath() } ); } catch( ClearCaseException e ) { if( e.getMessage().indexOf( IDENTICAL_CONTENT_SIG ) != -1 ) { undoCheckOut( file ); } else { throw e; } } } } public void checkOut(File file, boolean isReserved, String comment, boolean noData) { @NonNls String[] params; String canonName; try { canonName = file.getCanonicalPath(); } catch (IOException e) { canonName = file.getAbsolutePath(); } final List<String> commandLine = new ArrayList<>(); commandLine.add("co"); if (StringUtil.isNotEmpty(comment)) { commandLine.add("-c"); commandLine.add(quote(comment)); } else { commandLine.add("-nc"); } commandLine.add(isReserved ? "-reserved" : "-unreserved"); if (noData) { commandLine.add("-ndata"); } commandLine.add("-nq"); commandLine.add(canonName); params = commandLine.toArray(new String[commandLine.size()]); Runner runner = cleartool( params, true ); if( !runner.isSuccessfull() ) { boolean isCheckedOut = false; try { final Status status = getStatus(file); isCheckedOut = Status.CHECKED_OUT.equals(status); } catch (ClearCaseException e) { // ignore and show first exception } if (! isCheckedOut) { throw new ClearCaseException(runner.getOutput()); } } String activity = extractActivity( runner.getOutput() ); // In the case we did not manage to parse out the activity or we deal with // non-UCM views, do not disturb host. if( activity != null && !file.isDirectory() ) { CCaseViewsManager viewsManager = CCaseViewsManager.getInstance( host.getProject() ); viewsManager.addFile2Changelist( file.getPath(), activity ); // If the current activity for a view was changed in the CCase Explorer // and we did not synchronize that in IDEA, we can catch that automatically // by the monitoring the last activity for checked out file. viewsManager.checkChangedActivityForView( file.getPath(), activity ); } } public void delete( File file, String comment) { String canonName; try { canonName = file.getCanonicalPath(); } catch (IOException e) { canonName = file.getAbsolutePath(); } if( StringUtil.isNotEmpty( comment ) ) cleartool( new String[] { "rmname", "-force", "-c", quote(comment), canonName } ); else cleartool( new String[] { "rmname", "-force", canonName } ); } public void add( File file, String comment ) { if( file.isDirectory() ) doAddDir( file, comment ); else doAdd( "mkelem", file.getAbsolutePath(), comment ); } /** * From ClearCase documentation: * ---------------------------- * Converting View-Private Directories * You cannot create a directory element with the same name as an existing * view-private file or directory, and you cannot use mkdir to convert an * existing view-private directory structure into directory and file elements. * To accomplish this task, use clearfsimport. */ private static void doAddDir( File dir, String comment ) { String ext = Long.toString( new Date().getTime() ); // Error message if first rename fails. @NonNls String prefix = "Could not rename the content of " + dir.getPath(); @NonNls String error = prefix + " as part of adding it to ClearCase." + " Please add it manually"; File tmpDir = new File( dir.getParentFile(), dir.getName() + "." + ext ); try { FileUtil.rename( dir, tmpDir ); try { doAdd( "mkdir", dir.getAbsolutePath(), comment ); FileUtil.delete( dir ); } finally { // Error message if second rename (back) fails. error = prefix + " back as part of adding it to Clearcase:\n" + "Its old content is in the " + tmpDir.getName() + ". Please rename it back manually."; FileUtil.moveDirWithContent( tmpDir, dir ); } } catch( IOException e ) { throw new ClearCaseException( error ); } } private static void doAdd( @NonNls String subcmd, String path, String comment ) { if( StringUtil.isNotEmpty( comment ) ) cleartool( new String[] { subcmd, "-c", quote(comment), path } ); else cleartool( new String[] { subcmd, "-nc", path } ); } public void move(File file, File target, String comment) { cleartool( new String[] { "mv", "-c", quote(comment), file.getAbsolutePath(), target.getAbsolutePath() } ); } public boolean isElement(File file) { return getStatus(file) != Status.NOT_AN_ELEMENT; } public boolean isCheckedOut(File file) { return getStatus(file) == Status.CHECKED_OUT; } public Status getStatus( File file ) { String fileName = VcsUtil.getCanonicalPath( file ); Runner runner = cleartool( new String[] { "ls", "-directory", fileName }, true ); String output = runner.getOutput(); if (output == null) output = ""; return parseLine(output, runner.isSuccessfull()); } private Status parseLine(@NotNull String output, final boolean wasSuccessful) { // Check message "Pathname is not withing a VOB:..." first because it comes // along with Failure exit code for cleartool command, and we may return // with ClearCaseException without giving useful information. if( output.indexOf( NOT_VOB_ELEMENT ) != -1 ) return Status.NOT_AN_ELEMENT; // Check message "Unable to access...No such file or directory" first // because it comes along with Failure exit code for cleartool command, // and we may return with ClearCaseException without giving useful information. // NB: I found this message only appearing when working with dynamic views, // potential failure in synching VirtualFile via VFS? if( output.indexOf( UNABLE_TO_ACCESS ) != -1 && output.indexOf( NO_SUCH_FILE_OR_DIR ) != -1 ) return Status.NOT_AN_ELEMENT; if (! wasSuccessful) throw new ClearCaseException(output); if( output.indexOf( VERSIONED_SIG ) == -1 ) return Status.NOT_AN_ELEMENT; if( output.indexOf( HIJACKED_SIG ) != -1 ) return Status.HIJACKED; if( output.indexOf( CHECKEDOUT_SIG ) != -1 || output.indexOf( CHECKEDOUT_REMOVED_SIG ) != -1 ) return Status.CHECKED_OUT; else return Status.CHECKED_IN; } public CheckedOutStatus getCheckedOutStatus( File file ) { return TransparentVcs.getCheckedOutStatus( file ); } @Nullable public String getCheckoutComment( File file ) { return TransparentVcs.getCheckoutComment( file ); } public static String quote(String str) { final String result = "\"" + str.replaceAll("\"", "\\\\\\\"") + "\""; return result; } public void cleartool( @NonNls String subcmd ) { cleartool( new String[] { "cleartool", subcmd } ); } public static void cleartool( @NonNls String[] subcmd ) { cleartool(subcmd, false); } private static Runner cleartool(@NonNls String[] subcmd, boolean canFail) { @NonNls String[] cmd = Runner.getCommand( "cleartool", subcmd ); Runner runner = new Runner(); runner.run(cmd, canFail); return runner; } /**--------------------------------------------------------------------------- * Parse the output of the following content, extract the name of the * activity under which the file is checked out. *--------------------------------------------------------------------------- * !Checked out "Class2ForPack4.java" from version "\main\Dev2IrinaVOB\2". * ! Attached activities: * ! activity:First_Activity@\IrinaTestVOB "First Activity" */ @Nullable private static String extractActivity( String out ) { String activity = null; String[] lines = LineTokenizer.tokenize( out, false ); for( String line : lines ) { if( line.startsWith( ACTIVITY_SIG ) ) { activity = line; int quoteIndex = line.indexOf( '\"' ); if( quoteIndex != -1 ) { activity = line.substring( quoteIndex + 1, line.length() - 1 ); } break; } } return activity; } }