IDEA-129008 PushDialog: create PushSpec only for @NotNull PushTarget, and add annotations.

* nullable target could be only in MyRepoModel,in any other places PushTarget should be @NotNull;
* generics updated, need to improve
This commit is contained in:
Nadya Zabrodina
2014-08-29 02:52:16 +04:00
parent 725717b1dd
commit 70f8aa3a5e
7 changed files with 84 additions and 53 deletions
@@ -16,7 +16,6 @@
package com.intellij.dvcs.push;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* For a single repository, specifies what is pushed and where.
@@ -24,9 +23,9 @@ import org.jetbrains.annotations.Nullable;
public class PushSpec<S extends PushSource, T extends PushTarget> {
@NotNull private S mySource;
@Nullable private T myTarget;
@NotNull private T myTarget;
public PushSpec(@NotNull S source, @Nullable T target) {
public PushSpec(@NotNull S source, @NotNull T target) {
mySource = source;
myTarget = target;
}
@@ -36,7 +35,7 @@ public class PushSpec<S extends PushSource, T extends PushTarget> {
return mySource;
}
@Nullable
@NotNull
public T getTarget() {
return myTarget;
}
@@ -45,5 +44,4 @@ public class PushSpec<S extends PushSource, T extends PushTarget> {
public String toString() {
return mySource + "->" + myTarget;
}
}
@@ -41,7 +41,7 @@ public abstract class PushSupport<Repo extends Repository, Source extends PushSo
public abstract Pusher<Repo, Source, Target> getPusher();
@NotNull
public abstract OutgoingCommitsProvider getOutgoingCommitsProvider();
public abstract OutgoingCommitsProvider<Repo, Source, Target> getOutgoingCommitsProvider();
/**
* @return Default push destination
@@ -63,12 +63,11 @@ public abstract class PushSupport<Repo extends Repository, Source extends PushSo
public abstract Source getSource(@NotNull Repo repository);
/**
* Parse user input string, and create the valid target for push,
* or return <code><b>null</b></code> if the target name is not valid.
* Parse user input string, and create the VALID target for push
*
* @see #validateSpec(Repository, PushSpec)
*/
@Nullable
@NotNull
public abstract Target createTarget(@NotNull Repo repository, @NotNull String targetName);
/**
@@ -40,4 +40,8 @@ public class VcsError {
myErrorHandleListener.handleError(loader);
}
}
public static VcsError createEmptyTargetError(@NotNull String name) {
return new VcsError("Please, specify remote push path for repository " + name + ".");
}
}
@@ -96,13 +96,8 @@ public class PushController implements Disposable {
for (Map.Entry<RepositoryNode, MyRepoModel> entry : myView2Model.entrySet()) {
MyRepoModel model = entry.getValue();
if (model.isSelected()) {
//has one or more selected roots
if (model.hasError()) return new ValidationInfo(model.getError().getText());
validInfo = null;
PushTarget target = model.getSpec().getTarget();
//todo add validation for model -> hasErrors, too
if (target == null) {
return new ValidationInfo("Invalid remote for repository " + DvcsUtil.getShortRepositoryName(model.getRepository()));
}
}
}
return validInfo;
@@ -145,8 +140,7 @@ public class PushController implements Disposable {
private <R extends Repository, S extends PushSource, T extends PushTarget> int createNodesForVcs(
@NotNull PushSupport<R, S, T> pushSupport,
@NotNull CheckedTreeNode rootNode,
@NotNull List<? extends Repository> preselectedRepositories)
{
@NotNull List<? extends Repository> preselectedRepositories) {
RepositoryManager<R> repositoryManager = pushSupport.getRepositoryManager();
List<R> repositories = repositoryManager.getRepositories();
for (R repository : repositories) {
@@ -156,15 +150,19 @@ public class PushController implements Disposable {
}
private <R extends Repository, S extends PushSource, T extends PushTarget> void createRepoNode(@NotNull final PushSupport<R, S, T> support,
@NotNull final R repository,
@NotNull CheckedTreeNode rootNode,
boolean isSelected,
boolean isSingleRepositoryProject) {
@NotNull final R repository,
@NotNull CheckedTreeNode rootNode,
boolean isSelected,
boolean isSingleRepositoryProject) {
T target = support.getDefaultTarget(repository);
String repoName = DvcsUtil.getShortRepositoryName(repository);
final MyRepoModel<R, S, T> model = new MyRepoModel<R, S, T>(repository, support, isSingleRepositoryProject || isSelected,
new PushSpec<S, T>(support.getSource(repository), target),
support.getSource(repository), target,
DEFAULT_CHILDREN_PRESENTATION_NUMBER);
RepositoryWithBranchPanel repoPanel = new RepositoryWithBranchPanel(myProject, DvcsUtil.getShortRepositoryName(repository),
if (target == null) {
model.setError(VcsError.createEmptyTargetError(repoName));
}
RepositoryWithBranchPanel repoPanel = new RepositoryWithBranchPanel(myProject, repoName,
support.getSource(repository).getPresentation(),
target == null ? "" : target.getPresentation(),
support.getTargetNames(repository));
@@ -180,15 +178,16 @@ public class PushController implements Disposable {
if (validationError == null) {
T newTarget = support.createTarget(repository, newValue);
repoNode.setTargetPresentation(support.renderTarget(newTarget));
model.setSpec(new PushSpec<S, T>(model.getSpec().getSource(), newTarget));
model.setTarget(newTarget);
model.clearErrors();
loadCommits(model, repoNode, false);
}
else {
//todo change presentation for invalid/null target!!!! Should it be common for all invalid target or custom?
repoNode.setTargetPresentation(StringUtil.isEmptyOrSpaces(newValue)
? support.renderTarget(null)
: new SimpleColoredText(newValue, SimpleTextAttributes.ERROR_ATTRIBUTES));
model.setSpec(new PushSpec<S, T>(model.getSpec().getSource(), null));
model.setError(validationError); // todo may be should accept and store errors collection, now store one major target error
model.setTarget(null);
}
myDialog.updateButtons();
}
@@ -203,13 +202,15 @@ public class PushController implements Disposable {
rootNode.add(repoNode);
}
private void loadCommits(@NotNull final MyRepoModel model,
@NotNull final RepositoryNode node,
final boolean initial) {
private <R extends Repository, S extends PushSource, T extends PushTarget> void loadCommits(@NotNull final MyRepoModel<R, S, T> model,
@NotNull final RepositoryNode node,
final boolean initial) {
node.stopLoading();
final T target = model.getTarget();
if (target == null) return; //todo should be removed when commit loader executor will be modified
myPushLog.startLoading(node);
final ProgressIndicator indicator = node.startLoading();
final PushSupport support = model.getSupport();
final PushSupport<R, S, T> support = model.getSupport();
final AtomicReference<OutgoingResult> result = new AtomicReference<OutgoingResult>();
Task.Backgroundable task = new Task.Backgroundable(myProject, "Loading Commits", true) {
@@ -251,7 +252,8 @@ public class PushController implements Disposable {
@Override
public void run(@NotNull ProgressIndicator indicator) {
OutgoingResult outgoing = support.getOutgoingCommitsProvider().getOutgoingCommits(model.getRepository(), model.getSpec(), initial);
OutgoingResult outgoing = support.getOutgoingCommitsProvider()
.getOutgoingCommits(model.getRepository(), new PushSpec<S, T>(model.getSource(), model.getTarget()), initial);
result.compareAndSet(null, outgoing);
}
};
@@ -289,7 +291,11 @@ public class PushController implements Disposable {
Collection<MyRepoModel> repositoriesInformation = getSelectedRepoNode();
for (MyRepoModel repoModel : repositoriesInformation) {
if (pushSupport.equals(repoModel.getSupport())) {
pushSpecs.put((R)repoModel.getRepository(), repoModel.getSpec());
//todo improve generics: unchecked casts
T target = (T)repoModel.getTarget();
if (target != null) {
pushSpecs.put((R)repoModel.getRepository(), new PushSpec<S, T>((S)repoModel.getSource(), target));
}
}
}
return pushSpecs;
@@ -386,22 +392,23 @@ public class PushController implements Disposable {
private static class MyRepoModel<Repo extends Repository, S extends PushSource, T extends PushTarget> {
@NotNull final Repo myRepository;
@NotNull private PushSupport<Repo, S, T> mySupport;
@NotNull private final S mySource;
@Nullable private T myTarget;
@Nullable VcsError myTargetError;
@NotNull PushSpec<S, T> mySpec;
int myNumberOfShownCommits;
List<? extends VcsFullCommitDetails> myLoadedCommits;
boolean myIsSelected;
public MyRepoModel(@NotNull Repo repository,
@NotNull PushSupport<Repo, S, T> supportForRepo,
boolean isSelected,
@NotNull PushSpec<S, T> spec,
boolean isSelected, @NotNull S source, @Nullable T target,
int num) {
myRepository = repository;
mySupport = supportForRepo;
myIsSelected = isSelected;
mySpec = spec;
mySource = source;
myTarget = target;
myNumberOfShownCommits = num;
}
@@ -415,6 +422,20 @@ public class PushController implements Disposable {
return mySupport;
}
@NotNull
public S getSource() {
return mySource;
}
@Nullable
public T getTarget() {
return myTarget;
}
public void setTarget(@Nullable T target) {
myTarget = target;
}
public boolean isSelected() {
return myIsSelected;
}
@@ -423,13 +444,21 @@ public class PushController implements Disposable {
return myRepository.getVcs();
}
@NotNull
public PushSpec<S, T> getSpec() {
return mySpec;
@Nullable
public VcsError getError() {
return myTargetError;
}
public void setSpec(@NotNull PushSpec<S, T> spec) {
mySpec = spec;
public void setError(@Nullable VcsError error) {
myTargetError = error;
}
public void clearErrors() {
myTargetError = null;
}
public boolean hasError() {
return myTargetError != null;
}
public void setSelected(boolean isSelected) {
@@ -16,7 +16,6 @@
package org.zmlx.hg4idea.push;
import com.intellij.dvcs.push.*;
import com.intellij.dvcs.repo.Repository;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
@@ -27,6 +26,7 @@ import org.zmlx.hg4idea.command.HgOutgoingCommand;
import org.zmlx.hg4idea.execution.HgCommandResult;
import org.zmlx.hg4idea.log.HgBaseLogParser;
import org.zmlx.hg4idea.log.HgHistoryUtil;
import org.zmlx.hg4idea.repo.HgRepository;
import org.zmlx.hg4idea.util.HgChangesetUtil;
import org.zmlx.hg4idea.util.HgErrorUtil;
import org.zmlx.hg4idea.util.HgVersion;
@@ -35,7 +35,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HgOutgoingCommitsProvider extends OutgoingCommitsProvider {
public class HgOutgoingCommitsProvider extends OutgoingCommitsProvider<HgRepository, HgPushSource, HgTarget> {
private static final Logger LOG = Logger.getInstance(HgOutgoingCommitsProvider.class);
@@ -43,8 +43,8 @@ public class HgOutgoingCommitsProvider extends OutgoingCommitsProvider {
@NotNull
@Override
public OutgoingResult getOutgoingCommits(@NotNull final Repository repository,
@NotNull final PushSpec pushSpec,
public OutgoingResult getOutgoingCommits(@NotNull final HgRepository repository,
@NotNull final PushSpec<HgPushSource, HgTarget> pushSpec,
boolean initial) {
final Project project = repository.getProject();
HgVcs hgvcs = HgVcs.getInstance(project);
@@ -52,9 +52,9 @@ public class HgOutgoingCommitsProvider extends OutgoingCommitsProvider {
final HgVersion version = hgvcs.getVersion();
String[] templates = HgBaseLogParser.constructFullTemplateArgument(true, version);
HgOutgoingCommand hgOutgoingCommand = new HgOutgoingCommand(project);
HgTarget hgTarget = (HgTarget)pushSpec.getTarget();
HgTarget hgTarget = pushSpec.getTarget();
List<VcsError> errors = new ArrayList<VcsError>();
if (hgTarget == null || StringUtil.isEmptyOrSpaces(hgTarget.myTarget)) {
if (StringUtil.isEmptyOrSpaces(hgTarget.myTarget)) {
errors.add(new VcsError("Hg push path could not be empty."));
return new OutgoingResult(Collections.<VcsFullCommitDetails>emptyList(), errors);
}
@@ -15,6 +15,7 @@
*/
package org.zmlx.hg4idea.push;
import com.intellij.dvcs.DvcsUtil;
import com.intellij.dvcs.push.*;
import com.intellij.dvcs.repo.RepositoryManager;
import com.intellij.openapi.project.Project;
@@ -58,7 +59,7 @@ public class HgPushSupport extends PushSupport<HgRepository, HgPushSource, HgTar
@NotNull
@Override
public OutgoingCommitsProvider getOutgoingCommitsProvider() {
public OutgoingCommitsProvider<HgRepository, HgPushSource, HgTarget> getOutgoingCommitsProvider() {
return new HgOutgoingCommitsProvider();
}
@@ -88,6 +89,7 @@ public class HgPushSupport extends PushSupport<HgRepository, HgPushSource, HgTar
}
@Override
@NotNull
public HgTarget createTarget(@NotNull HgRepository repository, @NotNull String targetName) {
return new HgTarget(targetName);
}
@@ -106,7 +108,9 @@ public class HgPushSupport extends PushSupport<HgRepository, HgPushSource, HgTar
@Override
@Nullable
public VcsError validate(@NotNull HgRepository repository, @Nullable String targetToValidate) {
return StringUtil.isEmptyOrSpaces(targetToValidate) ? new VcsError("Please, specify remote push path for repository!") : null;
return StringUtil.isEmptyOrSpaces(targetToValidate)
? VcsError.createEmptyTargetError(DvcsUtil.getShortRepositoryName(repository))
: null;
}
@Override
@@ -53,9 +53,6 @@ public class HgPusher extends Pusher<HgRepository, HgPushSource, HgTarget> {
HgRepository repository = entry.getKey();
PushSpec<HgPushSource, HgTarget> hgSpec = entry.getValue();
HgTarget destination = hgSpec.getTarget();
if (destination == null) {
continue;
}
HgPushSource source = hgSpec.getSource();
Project project = repository.getProject();
final HgPushCommand pushCommand = new HgPushCommand(project, repository.getRoot(), destination.myTarget);