package org.intellij.vcs.mks.sicommands.cli;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.vcsUtil.VcsUtil;
import org.intellij.vcs.mks.MksCLIConfiguration;
import org.intellij.vcs.mks.model.MksMemberState;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Thibaut Fagart
*/
public abstract class AbstractViewSandboxCommand extends SiCLICommand {
public static final String COMMAND = "viewsandbox";
protected static final String DROPPED_TYPE = "dropped";
private static final String fieldsParam;
protected static final String wholeLinePatternString;
// protected static final String typePattern = "((?:sandbox)|(?:subsandbox)|(?:shared-subsandbox)|(?:shared-variant-subsandbox)" +
// "|(?:shared-build-subsandbox)|(?:member)|(?:archived)|(?:dropped)|(?:variant-subsandbox)" +
// "|(?:" + DEFERRED_ADD + ")|(?:" + DEFERRED_DROP + "))";
protected static final String typePattern = "([^\\s]+)";
static {
// --fields=workingrev,memberrev,workingcpid,deferred,pendingcpid,revsyncdelta,type,wfdelta,name
fieldsParam = "--fields=locker,workingrev,workingcpid,deferred,type,name,memberrev";
wholeLinePatternString = "^" + userPattern
+ " " + revisionPattern + " " + changePackageIdPattern
+ " " + deferredPattern + " " + typePattern
+ " " + namePattern
+ " " + revisionPattern + " " + sandboxPattern +
"$"; // locked sandbox is null when member is not locked
}
private static final int LOCKER_GROUP_IDX = 1;
private static final int WORKING_REV_GROUP_IDX = LOCKER_GROUP_IDX + 1;
private static final int WORKING_CPID_GROUP_IDX = WORKING_REV_GROUP_IDX + 1;
private static final int DEFERRED_GROUP_IDX = WORKING_CPID_GROUP_IDX + 1;
private static final int TYPE_GROUP_IDX = DEFERRED_GROUP_IDX + 1;
private static final int NAME_GROUP_IDX = TYPE_GROUP_IDX + 1;
private static final int MEMBER_REV_GROUP_IDX = NAME_GROUP_IDX + 1;
private static final int LOCKED_SANDBOX_GROUP_IDX = MEMBER_REV_GROUP_IDX + 1;
/**
* Map<FilePath.getPath(), MksMemberState>
* records the state as far as mks knows it for every file returned by the command
*/
protected final Map<String, MksMemberState> memberStates = new HashMap<String, MksMemberState>();
protected final String sandboxPath;
protected AbstractViewSandboxCommand(final List<VcsException> errors, final MksCLIConfiguration mksCLIConfiguration,
final String sandboxPath, final String... filters) {
super(errors, mksCLIConfiguration, COMMAND,
createParams(mksCLIConfiguration, fieldsParam, "--recurse", "--sandbox=" + sandboxPath, filters));
this.sandboxPath = sandboxPath;
}
private static String[] createParams(MksCLIConfiguration mksCLIConfiguration, final String fieldsParam, final String s, final String s1,
final String[] filters) {
final boolean isMks2007 = mksCLIConfiguration.isMks2007();
String[] params = new String[3 + (isMks2007 ? 1 : 0) + filters.length];
int i = 0;
params[i++] = fieldsParam + (isMks2007 ? ",lockrecord" : ",locksandbox");
if (isMks2007) {
params[i++] = "--lockrecordformat={sandbox}";
}
params[i++] = s;
params[i++] = s1;
System.arraycopy(filters, 0, params, (params.length - filters.length), filters.length);
return params;
}
@Override
public void execute() {
try {
executeCommand();
} catch (IOException e) {
//noinspection ThrowableInstanceNeverThrown
errors.add(new VcsException(e));
return;
}
// System.out.println("pattern [" + wholeLinePatternString + "]");
Pattern wholeLinePattern = Pattern.compile(wholeLinePatternString);
BufferedReader reader = new BufferedReader(new StringReader(commandOutput));
String line;
try {
while ((line = reader.readLine()) != null) {
Matcher matcher = wholeLinePattern.matcher(line);
if (matcher.matches()) {
String workingRev = matcher.group(WORKING_REV_GROUP_IDX);
String memberRev = matcher.group(MEMBER_REV_GROUP_IDX);
String workingCpid = matcher.group(WORKING_CPID_GROUP_IDX);
String deferred = matcher.group(DEFERRED_GROUP_IDX);
String type = matcher.group(TYPE_GROUP_IDX);
String locker = matcher.group(LOCKER_GROUP_IDX);
String name = matcher.group(NAME_GROUP_IDX);
String lockedSandbox = matcher.group(LOCKED_SANDBOX_GROUP_IDX);
try {
if (isRelevant(type)) {
MksMemberState memberState = createState(workingRev, memberRev, workingCpid, locker,
lockedSandbox, type, deferred);
setState(name, memberState);
} else {
LOGGER.debug("ignoring " + line);
// } else {
// LOGGER.warn("unexpected type " + type + " for " + line);
}
} catch (VcsException e) {
// should not happen, VcsExceptions on ChangePackageId
if (e.getCause() != null) {
LOGGER.error(e);
}
//noinspection ThrowableInstanceNeverThrown
errors.add(new VcsException(name + " " + e.getMessage()));
}
} else {
//noinspection ThrowableInstanceNeverThrown
errors.add(new VcsException(toString() + " : unexpected line [" + line + "]"));
}
}
} catch (IOException e) {
// shouldnt happen :IO exceptions on stringReader.read
//noinspection ThrowableInstanceNeverThrown
errors.add(new VcsException(e));
}
}
protected abstract MksMemberState createState(String workingRev, String memberRev, String workingCpid,
final String locker, final String lockedSandbox, final String type,
final String deferred) throws VcsException;
protected boolean isRelevant(final String type) {
return isMember(type);
}
private boolean isSandbox(final String type) {
return type.contains("sandbox");
}
/**
* Associates a member state with its file name.
* made protected to be overriden for unit tests as VcsUtil is not available at that time
*
* @param name the absolute file name
* @param memberState the state of the file
*/
protected void setState(@NotNull final String name, @NotNull final MksMemberState memberState) {
memberStates.put(VcsUtil.getFilePath(name).getPath(), memberState);
}
private boolean isMember(final String type) {
return !isSandbox(type) && !type.contains("subproject");
}
public Map<String, MksMemberState> getMemberStates() {
return Collections.unmodifiableMap(memberStates);
}
protected boolean isDropped(String type) {
return DROPPED_TYPE.equals(type) || DEFERRED_DROP.equals(type);
}
}