diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 40c5c958a766..7c70eb85064c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -476,14 +476,20 @@ public class JavaBuilder extends ModuleLevelBuilder { return cached; } - public static List getCompilationOptions(CompileContext context, ModuleChunk chunk, @Nullable ProcessorConfigProfile profile) { + private static List getCompilationOptions(CompileContext context, ModuleChunk chunk, @Nullable ProcessorConfigProfile profile) { List cached = JAVAC_OPTIONS.get(context); if (cached == null) { loadCommonJavacOptions(context); cached = JAVAC_OPTIONS.get(context); + assert cached != null : context; } - final List options = new ArrayList(cached); + List options = new ArrayList(cached); + addCompilationOptions(options, context, chunk, profile); + return options; + } + + public static void addCompilationOptions(List options, CompileContext context, ModuleChunk chunk, @Nullable ProcessorConfigProfile profile) { if (!isEncodingSet(options)) { final CompilerEncodingConfiguration config = context.getProjectDescriptor().getEncodingConfiguration(); final String encoding = config.getPreferredModuleChunkEncoding(chunk); @@ -574,8 +580,6 @@ public class JavaBuilder extends ModuleLevelBuilder { else { options.add("-proc:none"); } - - return options; } private static String getLanguageLevel(JpsModule module) { diff --git a/platform/dvcs/testFramework/com/intellij/dvcs/test/MockVcsHelper.java b/platform/dvcs/testFramework/com/intellij/dvcs/test/MockVcsHelper.java index 7cf6b346d536..da3acfb17603 100644 --- a/platform/dvcs/testFramework/com/intellij/dvcs/test/MockVcsHelper.java +++ b/platform/dvcs/testFramework/com/intellij/dvcs/test/MockVcsHelper.java @@ -20,7 +20,7 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.CommitExecutor; +import com.intellij.openapi.vcs.changes.CommitResultHandler; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsHistoryProvider; @@ -185,10 +185,22 @@ public class MockVcsHelper extends AbstractVcsHelper { } @Override - public boolean commitChanges(@NotNull List changes, @NotNull LocalChangeList initialChangeList, - @NotNull String commitMessage, @Nullable CommitExecutor executor) { + public boolean commitChanges(@NotNull Collection changes, @NotNull LocalChangeList initialChangeList, + @NotNull String commitMessage, @Nullable CommitResultHandler customResultHandler) { if (myCommitHandler != null) { - return myCommitHandler.commit(commitMessage); + boolean success = myCommitHandler.commit(commitMessage); + if (customResultHandler != null) { + if (success) { + customResultHandler.onSuccess(commitMessage); + } + else { + customResultHandler.onFailure(); + } + } + return success; + } + if (customResultHandler != null) { + customResultHandler.onFailure(); } return false; } diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/MockChangeList.java b/platform/testFramework/testSrc/com/intellij/testFramework/vcs/MockChangeList.java index f1242a36fa58..c2fb7749ea35 100644 --- a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/MockChangeList.java +++ b/platform/testFramework/testSrc/com/intellij/testFramework/vcs/MockChangeList.java @@ -18,6 +18,7 @@ package com.intellij.testFramework.vcs; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.LocalChangeList; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -79,6 +80,12 @@ public class MockChangeList extends LocalChangeList { throw new UnsupportedOperationException(); } + @Nullable + @Override + public Object getData() { + throw new UnsupportedOperationException(); + } + @Override public LocalChangeList copy() { throw new UnsupportedOperationException(); diff --git a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/MockChangeListManager.java b/platform/testFramework/testSrc/com/intellij/testFramework/vcs/MockChangeListManager.java index 8514ec50d46a..470b3ccbc155 100644 --- a/platform/testFramework/testSrc/com/intellij/testFramework/vcs/MockChangeListManager.java +++ b/platform/testFramework/testSrc/com/intellij/testFramework/vcs/MockChangeListManager.java @@ -365,4 +365,8 @@ public class MockChangeListManager extends ChangeListManagerEx { public void freezeImmediately(@Nullable String reason) { } + @Override + public LocalChangeList addChangeList(@NotNull String name, @Nullable String comment, @Nullable Object data) { + return addChangeList(name, comment); + } } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcsHelper.java b/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcsHelper.java index fbdc14b28527..b90eee515c20 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcsHelper.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcsHelper.java @@ -21,7 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.CommitExecutor; +import com.intellij.openapi.vcs.changes.CommitResultHandler; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsHistoryProvider; @@ -167,11 +167,11 @@ public abstract class AbstractVcsHelper { /** - * Shows commit dialog, fills it with the given changes and given commit message, initially selects the given changelist. - * @return true if user commits the changes, and commit succeeds; false if user presses Cancel or commit fails with errors. + *

Shows commit dialog, fills it with the given changes and given commit message, initially selects the given changelist.

+ *

Note that the method is asynchronous: it returns right after user presses "Commit" or "Cancel" and after all pre-commit handlers + * have been called. It doesn't wait for commit itself to succeed or fail - for this use the {@code customResultHandler}.

+ * @return true if user decides to commit the changes, false if user presses Cancel. */ - public abstract boolean commitChanges(@NotNull List changes, @NotNull LocalChangeList initialChangeList, - @NotNull String commitMessage, @Nullable CommitExecutor executor); - - + public abstract boolean commitChanges(@NotNull Collection changes, @NotNull LocalChangeList initialChangeList, + @NotNull String commitMessage, @Nullable CommitResultHandler customResultHandler); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitResultHandler.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitResultHandler.java new file mode 100644 index 000000000000..aaf7575de301 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CommitResultHandler.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.changes; + +import com.intellij.openapi.vcs.AbstractVcsHelper; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * CommitResultHandler may be passed to {@link AbstractVcsHelper#commitChanges(Collection, LocalChangeList, String, CommitResultHandler)}. + * It is called after commit is performed: successful or failed. + * + * @author Kirill Likhodedov + */ +public interface CommitResultHandler { + + void onSuccess(@NotNull String commitMessage); + + void onFailure(); + +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/LocalChangeList.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/LocalChangeList.java index fbb3cfbf6302..002c0163321c 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/LocalChangeList.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/LocalChangeList.java @@ -63,6 +63,12 @@ public abstract class LocalChangeList implements Cloneable, ChangeList { public abstract void setReadOnly(boolean isReadOnly); + /** + * Get additional data associated with this changelist. + */ + @Nullable + public abstract Object getData(); + public abstract LocalChangeList copy(); public boolean hasDefaultName() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerEx.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerEx.java index b95b94cf323e..bab36f26697b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerEx.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerEx.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.vcs.changes; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -31,4 +32,6 @@ public abstract class ChangeListManagerEx extends ChangeListManager { public abstract void freezeImmediately(@Nullable String reason); + + public abstract LocalChangeList addChangeList(@NotNull String name, @Nullable final String comment, @Nullable Object data); } \ No newline at end of file diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java index eb615cd57a33..d34ae8db6eb0 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerImpl.java @@ -230,7 +230,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec public void run() { synchronized (myDataLock) { if (myWorker.isEmpty()) { - final LocalChangeList list = myWorker.addChangeList(VcsBundle.message("changes.default.changlist.name"), null); + final LocalChangeList list = myWorker.addChangeList(VcsBundle.message("changes.default.changlist.name"), null, null); setDefaultChangeList(list); if (myIgnoredIdeaLevel.isEmpty()) { @@ -829,11 +829,16 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec } public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String comment) { + return addChangeList(name, comment, null); + } + + @Override + public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String comment, @Nullable final Object data) { return ApplicationManager.getApplication().runReadAction(new Computable() { @Override public LocalChangeList compute() { synchronized (myDataLock) { - final LocalChangeList changeList = myModifier.addChangeList(name, comment); + final LocalChangeList changeList = myModifier.addChangeList(name, comment, data); myChangesViewManager.scheduleRefresh(); return changeList; } @@ -841,6 +846,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec }); } + public void removeChangeList(final String name) { ApplicationManager.getApplication().runReadAction(new Runnable() { @Override @@ -1158,7 +1164,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec private boolean doCommit(final LocalChangeList changeList, final List changes, final boolean synchronously) { return new CommitHelper(myProject, changeList, changes, changeList.getName(), - changeList.getComment(), new ArrayList(), false, synchronously, NullableFunction.NULL).doCommit(); + changeList.getComment(), new ArrayList(), false, synchronously, NullableFunction.NULL, null).doCommit(); } public void commitChangesSynchronously(LocalChangeList changeList, List changes) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerSerialization.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerSerialization.java index 2a4ee9a8d071..71e271d07a38 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerSerialization.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListManagerSerialization.java @@ -70,7 +70,8 @@ class ChangeListManagerSerialization { final String changeListName = listNode.getAttributeValue(ATT_NAME); LocalChangeList list = myWorker.getCopyByName(changeListName); if (list == null) { - list = myWorker.addChangeList(listNode.getAttributeValue(ATT_ID), changeListName, listNode.getAttributeValue(ATT_COMMENT), false); + list = myWorker.addChangeList(listNode.getAttributeValue(ATT_ID), changeListName, listNode.getAttributeValue(ATT_COMMENT), false, + null); } //noinspection unchecked final List changeNodes = (List)listNode.getChildren(NODE_CHANGE); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java index 350a4e7ef77e..2082443edc24 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListWorker.java @@ -198,14 +198,16 @@ public class ChangeListWorker implements ChangeListsWriteOperations { return list != null; } - public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String description) { - return addChangeList(null, name, description, false); + public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String comment, @Nullable Object data) { + return addChangeList(null, name, comment, false, data); } - LocalChangeList addChangeList(String id, @NotNull final String name, @Nullable final String description, final boolean inUpdate) { + LocalChangeList addChangeList(String id, @NotNull final String name, @Nullable final String description, final boolean inUpdate, + @Nullable Object data) { final boolean contains = myMap.containsKey(name); LOG.assertTrue(! contains, "Attempt to create duplicate changelist " + name); final LocalChangeListImpl newList = (LocalChangeListImpl) LocalChangeList.createEmptyChangeList(myProject, name); + newList.setData(data); if (description != null) { newList.setCommentImpl(description); @@ -751,7 +753,7 @@ public class ChangeListWorker implements ChangeListsWriteOperations { } public LocalChangeList addChangeList(final String name, final String comment) { - return myWorker.addChangeList(null, name, comment, true); + return myWorker.addChangeList(null, name, comment, true, null); } public LocalChangeList findOrCreateList(final String name, final String comment) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListsWriteOperations.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListsWriteOperations.java index d7caa4f60cb0..2256393ce7d5 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListsWriteOperations.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangeListsWriteOperations.java @@ -23,7 +23,7 @@ public interface ChangeListsWriteOperations { @Nullable String setDefault(String name); boolean setReadOnly(String name, boolean value); - LocalChangeList addChangeList(@NotNull String name, @Nullable String description); + LocalChangeList addChangeList(@NotNull String name, @Nullable String comment, @Nullable Object data); boolean removeChangeList(@NotNull String name); @Nullable MultiMap moveChangesTo(String name, Change[] changes); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LocalChangeListImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LocalChangeListImpl.java index ab74f53e7856..9be47384272b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LocalChangeListImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/LocalChangeListImpl.java @@ -29,6 +29,7 @@ public class LocalChangeListImpl extends LocalChangeList { private String myId; @NotNull private String myName; private String myComment = ""; + @Nullable private Object myData; private boolean myIsDefault = false; private boolean myIsReadOnly = false; @@ -116,6 +117,16 @@ public class LocalChangeListImpl extends LocalChangeList { myIsReadOnly = isReadOnly; } + void setData(@Nullable Object data) { + myData = data; + } + + @Nullable + @Override + public Object getData() { + return myData; + } + void addChange(Change change) { if (ChangeListManagerImpl.DEBUG) { ChangeListManagerImpl.log("LocalChangeListImpl.addChange: this = " + this + ", change = " + change); @@ -260,6 +271,7 @@ public class LocalChangeListImpl extends LocalChangeList { copy.myComment = myComment; copy.myIsDefault = myIsDefault; copy.myIsReadOnly = myIsReadOnly; + copy.myData = myData; if (myChanges != null) { copy.myChanges = new HashSet(myChanges); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/Modifier.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/Modifier.java index ded8c2013286..de2cb4003360 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/Modifier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/Modifier.java @@ -39,8 +39,8 @@ public class Modifier implements ChangeListsWriteOperations { myCommandQueue = new LinkedList(); } - public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String comment) { - final AddList command = new AddList(name, comment); + public LocalChangeList addChangeList(@NotNull final String name, @Nullable final String comment, @Nullable Object data) { + final AddList command = new AddList(name, comment, data); impl(command); return command.getNewListCopy(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/local/AddList.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/local/AddList.java index fc4ee4e530df..5be08d54c56a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/local/AddList.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/local/AddList.java @@ -25,17 +25,19 @@ import org.jetbrains.annotations.Nullable; public class AddList implements ChangeListCommand { @NotNull private final String myName; @Nullable private final String myComment; + @Nullable private final Object myData; private LocalChangeList myNewListCopy; - public AddList(@NotNull final String name, @Nullable final String comment) { + public AddList(@NotNull final String name, @Nullable final String comment, @Nullable Object data) { myName = name; myComment = comment; + myData = data; } public void apply(final ChangeListWorker worker) { if (! worker.findListByName(myName)) { - myNewListCopy = worker.addChangeList(myName, myComment); + myNewListCopy = worker.addChangeList(myName, myComment, myData); } else { worker.editComment(myName, myComment); myNewListCopy = worker.getCopyByName(myName); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/AlienLocalChangeList.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/AlienLocalChangeList.java index cefeb54a9da1..18130b616ca8 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/AlienLocalChangeList.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/AlienLocalChangeList.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vcs.changes.ui; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.LocalChangeList; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; @@ -67,6 +68,12 @@ public class AlienLocalChangeList extends LocalChangeList { throw new UnsupportedOperationException(); } + @Nullable + @Override + public Object getData() { + throw new UnsupportedOperationException(); + } + public LocalChangeList copy() { throw new UnsupportedOperationException(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 065b05204d95..561ec3170e12 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -111,6 +111,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj private final ZipperUpdater myZipperUpdater; private final Runnable myRefreshDetails; private CommitAction myCommitAction; + @Nullable private CommitResultHandler myResultHandler; private static class MyUpdateButtonsRunnable implements Runnable { private CommitChangeListDialog myDialog; @@ -139,7 +140,8 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj private final MyUpdateButtonsRunnable myUpdateButtonsRunnable = new MyUpdateButtonsRunnable(this); private static boolean commit(final Project project, final List changes, final LocalChangeList initialSelection, - final List executors, final boolean showVcsCommit, final String comment) { + final List executors, final boolean showVcsCommit, final String comment, + @Nullable CommitResultHandler customResultHandler) { final AbstractVcs[] allActiveVcss = ProjectLevelVcsManager.getInstance(project).getAllActiveVcss(); final List factoryList = CheckinHandlersManager.getInstance().getMatchingVcsFactories(Arrays.asList(allActiveVcss)); @@ -155,7 +157,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj final ArrayList changeLists = new ArrayList(manager.getChangeListsCopy()); CommitChangeListDialog dialog = new CommitChangeListDialog(project, changes, initialSelection, executors, showVcsCommit, defaultList, changeLists, null, false, - comment); + comment, customResultHandler); if (!ApplicationManager.getApplication().isUnitTestMode()) { dialog.show(); } @@ -179,10 +181,10 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj public static boolean commitChanges(final Project project, final Collection changes, final LocalChangeList initialSelection, @Nullable final CommitExecutor executor, final String comment) { if (executor == null) { - return commitChanges(project, changes, initialSelection, collectExecutors(project, changes), true, comment); + return commitChanges(project, changes, initialSelection, collectExecutors(project, changes), true, comment, null); } else { - return commitChanges(project, changes, initialSelection, Collections.singletonList(executor), false, comment); + return commitChanges(project, changes, initialSelection, Collections.singletonList(executor), false, comment, null); } } @@ -197,22 +199,29 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj return result; } + /** + * Shows the commit dialog, and performs the selected action: commit, commit & push, create patch, etc. + * @param customResultHandler If this is not null, after commit is completed, custom result handler is called instead of + * showing the default notification in case of commit or failure. + * @return true if user agreed to commit, false if he pressed "Cancel". + */ public static boolean commitChanges(final Project project, final Collection changes, final LocalChangeList initialSelection, - final List executors, final boolean showVcsCommit, final String comment) { + final List executors, final boolean showVcsCommit, final String comment, + @Nullable CommitResultHandler customResultHandler) { if (changes.isEmpty() && !ApplicationManager.getApplication().isUnitTestMode()) { Messages.showInfoMessage(project, VcsBundle.message("commit.dialog.no.changes.detected.text") , VcsBundle.message("commit.dialog.no.changes.detected.title")); return false; } - return commit(project, new ArrayList(changes), initialSelection, executors, showVcsCommit, comment); + return commit(project, new ArrayList(changes), initialSelection, executors, showVcsCommit, comment, customResultHandler); } public static void commitAlienChanges(final Project project, final List changes, final AbstractVcs vcs, final String changelistName, final String comment) { final LocalChangeList lcl = new AlienLocalChangeList(changes, changelistName); new CommitChangeListDialog(project, changes, null, null, true, AlienLocalChangeList.DEFAULT_ALIEN, Collections.singletonList(lcl), vcs, - true, comment).show(); + true, comment, null).show(); } private CommitChangeListDialog(final Project project, @@ -221,13 +230,14 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj final List executors, final boolean showVcsCommit, final LocalChangeList defaultChangeList, final List changeLists, final AbstractVcs singleVcs, final boolean isAlien, - final String comment) { + final String comment, @Nullable CommitResultHandler customResultHandler) { super(project, true); myCommitContext = new CommitContext(); myProject = project; myExecutors = executors; myShowVcsCommit = showVcsCommit; myVcs = singleVcs; + myResultHandler = customResultHandler; myListComments = new HashMap(); myAdditionalData = new PseudoMap(); myDiffDetails = new ShortDiffDetails(myProject, new Getter() { @@ -570,7 +580,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj runBeforeCommitHandlers(new Runnable() { public void run() { CommitChangeListDialog.super.doOKAction(); - doCommit(); + doCommit(myResultHandler); } }, null); @@ -925,7 +935,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj super.doCancelAction(); } - private void doCommit() { + private void doCommit(@Nullable CommitResultHandler customResultHandler) { final CommitHelper helper = new CommitHelper( myProject, myBrowser.getSelectedChangeList(), @@ -933,7 +943,7 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj myActionName, getCommitMessage(), myHandlers, - myAllOfDefaultChangeListChangesIncluded, false, myAdditionalData); + myAllOfDefaultChangeListChangesIncluded, false, myAdditionalData, customResultHandler); if (myIsAlien) { helper.doAlienCommit(myVcs); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java index e84293f2bbee..7d591bde1e52 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitHelper.java @@ -44,6 +44,7 @@ import com.intellij.util.NullableFunction; import com.intellij.util.WaitForProgressToShow; import com.intellij.util.ui.ConfirmationDialog; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; @@ -66,6 +67,7 @@ public class CommitHelper { private final boolean myAllOfDefaultChangeListChangesIncluded; private final boolean myForceSyncCommit; private final NullableFunction myAdditionalData; + @Nullable private final CommitResultHandler myCustomResultHandler; private final List myCommittingDocuments = new ArrayList(); private final VcsConfiguration myConfiguration; private final VcsDirtyScopeManager myDirtyScopeManager; @@ -78,7 +80,8 @@ public class CommitHelper { final String commitMessage, final List handlers, final boolean allOfDefaultChangeListChangesIncluded, - final boolean synchronously, final NullableFunction additionalDataHolder) { + final boolean synchronously, final NullableFunction additionalDataHolder, + @Nullable CommitResultHandler customResultHandler) { myProject = project; myChangeList = changeList; myIncludedChanges = includedChanges; @@ -88,6 +91,7 @@ public class CommitHelper { myAllOfDefaultChangeListChangesIncluded = allOfDefaultChangeListChangesIncluded; myForceSyncCommit = synchronously; myAdditionalData = additionalDataHolder; + myCustomResultHandler = customResultHandler; myConfiguration = VcsConfiguration.getInstance(myProject); myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); myFeedback = new HashSet(); @@ -133,8 +137,11 @@ public class CommitHelper { @Override public NotificationInfo notifyFinished() { - String text = reportSuccess(processor); - return new NotificationInfo("VCS Commit", "VCS Commit Finished", text, true); + if (myCustomResultHandler == null) { + String text = reportSuccess(processor); + return new NotificationInfo("VCS Commit", "VCS Commit Finished", text, true); + } + return null; } }; ProgressManager.getInstance().run(task); @@ -202,14 +209,18 @@ public class CommitHelper { processor.doBeforeRefresh(); AbstractVcsHelper.getInstance(myProject).showErrors(processor.getVcsExceptions(), myActionName); - } catch (Exception e) { + } + catch (RuntimeException e) { LOG.error(e); - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new RuntimeException(e); - } - } finally { + processor.myVcsExceptions.add(new VcsException(e)); + throw e; + } + catch (Throwable e) { + LOG.error(e); + processor.myVcsExceptions.add(new VcsException(e)); + throw new RuntimeException(e); + } + finally { commitCompleted(processor.getVcsExceptions(), processor); processor.customRefresh(); WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { @@ -511,6 +522,9 @@ public class CommitHelper { } processor.afterSuccessfulCheckIn(); + if (myCustomResultHandler != null) { + myCustomResultHandler.onSuccess(myCommitMessage); + } } else { for (CheckinHandler handler : myHandlers) { @@ -523,28 +537,38 @@ public class CommitHelper { if (indicator != null) { indicator.setText(VcsBundle.message("commit.dialog.completed.successfully")); } - } else { - WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { - public void run() { - final String message; - if (errorsSize > 0 && warningsSize > 0) { - message = VcsBundle.message("message.text.commit.failed.with.errors.and.warnings"); - } - else if (errorsSize > 0) { - message = VcsBundle.message("message.text.commit.failed.with.errors"); - } - else { - message = VcsBundle.message("message.text.commit.finished.with.warnings"); - } - //new VcsBalloonProblemNotifier(myProject, message, MessageType.ERROR).run(); - Messages.showErrorDialog(message, VcsBundle.message("message.title.commit")); - - if (errorsSize > 0) { - processor.afterFailedCheckIn(); - } - } - }, null, myProject); } + else { + if (myCustomResultHandler == null) { + showErrorDialogAndMoveToAnotherList(processor, errorsSize, warningsSize); + } + else { + myCustomResultHandler.onFailure(); + } + } + } + + private void showErrorDialogAndMoveToAnotherList(final GeneralCommitProcessor processor, final int errorsSize, final int warningsSize) { + WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { + public void run() { + final String message; + if (errorsSize > 0 && warningsSize > 0) { + message = VcsBundle.message("message.text.commit.failed.with.errors.and.warnings"); + } + else if (errorsSize > 0) { + message = VcsBundle.message("message.text.commit.failed.with.errors"); + } + else { + message = VcsBundle.message("message.text.commit.finished.with.warnings"); + } + //new VcsBalloonProblemNotifier(myProject, message, MessageType.ERROR).run(); + Messages.showErrorDialog(message, VcsBundle.message("message.title.commit")); + + if (errorsSize > 0) { + processor.afterFailedCheckIn(); + } + } + }, null, myProject); } public static void moveToFailedList(final ChangeList changeList, diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java index 1cbbae8458f8..2ee68e92031c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java @@ -47,10 +47,7 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.actions.AnnotateToggleAction; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; -import com.intellij.openapi.vcs.changes.BackgroundFromStartOption; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.CommitExecutor; -import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.committed.*; import com.intellij.openapi.vcs.changes.ui.*; import com.intellij.openapi.vcs.history.*; @@ -225,9 +222,11 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper { } @Override - public boolean commitChanges(@NotNull List changes, @NotNull LocalChangeList initialChangeList, - @NotNull String commitMessage, @Nullable CommitExecutor executor) { - return CommitChangeListDialog.commitChanges(myProject, changes, initialChangeList, executor, commitMessage); + public boolean commitChanges(@NotNull Collection changes, @NotNull LocalChangeList initialChangeList, + @NotNull String commitMessage, @Nullable CommitResultHandler customResultHandler) { + return CommitChangeListDialog.commitChanges(myProject, changes, initialChangeList, + CommitChangeListDialog.collectExecutors(myProject, changes), true, commitMessage, + customResultHandler); } private static void addDirectMessages(VcsErrorViewPanel vcsErrorViewPanel, List abstractVcsExceptions) { diff --git a/plugins/git4idea/src/git4idea/changes/GitChangeList.java b/plugins/git4idea/src/git4idea/changes/GitChangeList.java deleted file mode 100644 index 61452047d3ee..000000000000 --- a/plugins/git4idea/src/git4idea/changes/GitChangeList.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package git4idea.changes; - -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.LocalChangeList; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -/** - * Git change list - */ -public class GitChangeList extends LocalChangeList { - private String name; - private String comment; - private final Collection changes; - - public GitChangeList(@NotNull String name, String comment, Collection changes) { - super(); - setName(name); - setComment(comment); - this.changes = changes; - } - - @NotNull - public String getName() { - return name; - } - - public void setName(@NotNull String name) { - this.name = name; - } - - public String getComment() { - return comment; - } - - public void setComment(String comment) { - this.comment = comment; - } - - public boolean isDefault() { - return true; - } - - public boolean isReadOnly() { - return true; - } - - public void setReadOnly(boolean isReadOnly) { - } - - public Collection getChanges() { - return changes; - } - - public LocalChangeList copy() { - return new GitChangeList(name, comment, changes); - } -} diff --git a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java index 6bfeb56dd7d4..3516dd0e4a82 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java +++ b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java @@ -25,10 +25,8 @@ import com.intellij.openapi.vcs.CheckinProjectPanel; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.ObjectsConvertor; import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ChangeList; -import com.intellij.openapi.vcs.changes.ContentRevision; -import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.checkin.CheckinChangeListSpecificComponent; import com.intellij.openapi.vcs.checkin.CheckinEnvironment; import com.intellij.openapi.vcs.ui.RefreshableOnComponent; import com.intellij.openapi.vfs.VirtualFile; @@ -49,6 +47,7 @@ import git4idea.commands.GitSimpleHandler; import git4idea.config.GitConfigUtil; import git4idea.config.GitVcsSettings; import git4idea.history.NewGitUsersComponent; +import git4idea.history.browser.GitCommit; import git4idea.i18n.GitBundle; import git4idea.push.GitPusher; import git4idea.repo.GitRepositoryFiles; @@ -61,6 +60,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.io.*; +import java.text.SimpleDateFormat; import java.util.*; import java.util.List; @@ -73,13 +73,14 @@ public class GitCheckinEnvironment implements CheckinEnvironment { @NonNls private static final String GIT_COMMIT_MSG_FILE_EXT = ".txt"; // the file extension for commit message file private final Project myProject; + public static final SimpleDateFormat COMMIT_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private final VcsDirtyScopeManager myDirtyScopeManager; private final GitVcsSettings mySettings; private String myNextCommitAuthor = null; // The author for the next commit private boolean myNextCommitAmend; // If true, the next commit is amended private Boolean myNextCommitIsPushed = null; // The push option of the next commit - + private Date myNextCommitAuthorDate; public GitCheckinEnvironment(@NotNull Project project, @NotNull final VcsDirtyScopeManager dirtyScopeManager, final GitVcsSettings settings) { myProject = project; @@ -178,7 +179,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment { Set files = new HashSet(); files.addAll(added); files.addAll(removed); - commit(myProject, root, files, messageFile, myNextCommitAuthor, myNextCommitAmend); + commit(myProject, root, files, messageFile, myNextCommitAuthor, myNextCommitAmend, myNextCommitAuthorDate); } catch (VcsException ex) { if (!isMergeCommit(ex)) { @@ -440,12 +441,15 @@ public class GitCheckinEnvironment implements CheckinEnvironment { /** * Prepare delete files handler. * - * @param project the project - * @param root a vcs root - * @param files a files to commit - * @param message a message file to use - * @param nextCommitAuthor a author for the next commit - * @param nextCommitAmend true, if the commit should be amended + * + * + * @param project the project + * @param root a vcs root + * @param files a files to commit + * @param message a message file to use + * @param nextCommitAuthor a author for the next commit + * @param nextCommitAmend true, if the commit should be amended + * @param nextCommitAuthorDate Author date timestamp to override the date of the commit or null if this overriding is not needed. * @return a simple handler that does the task * @throws VcsException in case of git problem */ @@ -454,7 +458,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment { Collection files, File message, final String nextCommitAuthor, - boolean nextCommitAmend) + boolean nextCommitAmend, Date nextCommitAuthorDate) throws VcsException { boolean amend = nextCommitAmend; for (List paths : VcsFileUtil.chunkPaths(root, files)) { @@ -470,6 +474,9 @@ public class GitCheckinEnvironment implements CheckinEnvironment { if (nextCommitAuthor != null) { handler.addParameters("--author=" + nextCommitAuthor); } + if (nextCommitAuthorDate != null) { + handler.addParameters("--date", COMMIT_DATE_FORMAT.format(nextCommitAuthorDate)); + } handler.endOptions(); handler.addParameters(paths); handler.run(); @@ -559,12 +566,13 @@ public class GitCheckinEnvironment implements CheckinEnvironment { myNextCommitAmend = false; myNextCommitAuthor = null; myNextCommitIsPushed = null; + myNextCommitAuthorDate = null; } /** * Checkin options for git */ - private class GitCheckinOptions implements RefreshableOnComponent { + private class GitCheckinOptions implements CheckinChangeListSpecificComponent { /** * A container panel */ @@ -577,6 +585,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment { * The amend checkbox */ private final JCheckBox myAmend; + private Date myAuthorDate; /** * A constructor @@ -661,7 +670,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment { * {@inheritDoc} */ public void saveState() { - String author = (String)myAuthor.getSelectedItem(); + String author = (String)myAuthor.getEditor().getItem(); myNextCommitAuthor = author.length() == 0 ? null : author; if (author.length() == 0) { myNextCommitAuthor = null; @@ -671,6 +680,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment { mySettings.saveCommitAuthor(author); } myNextCommitAmend = myAmend.isSelected(); + myNextCommitAuthorDate = myAuthorDate; } /** @@ -679,6 +689,17 @@ public class GitCheckinEnvironment implements CheckinEnvironment { public void restoreState() { refresh(); } + + @Override + public void onChangeListSelected(LocalChangeList list) { + Object data = list.getData(); + if (data instanceof GitCommit) { + GitCommit commit = (GitCommit)data; + String author = String.format("%s <%s>", commit.getAuthor(), commit.getAuthorEmail()); + myAuthor.getEditor().setItem(author); + myAuthorDate = new Date(commit.getAuthorTime()); + } + } } public void setNextCommitIsPushed(Boolean nextCommitIsPushed) { diff --git a/plugins/git4idea/src/git4idea/history/browser/GitCherryPicker.java b/plugins/git4idea/src/git4idea/history/browser/GitCherryPicker.java index c13413180501..e29c20aae7b2 100644 --- a/plugins/git4idea/src/git4idea/history/browser/GitCherryPicker.java +++ b/plugins/git4idea/src/git4idea/history/browser/GitCherryPicker.java @@ -18,24 +18,17 @@ package git4idea.history.browser; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.*; -import com.intellij.openapi.vcs.changes.ui.CommitHelper; import com.intellij.openapi.vcs.checkin.CheckinEnvironment; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.merge.MergeDialogCustomizer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; -import com.intellij.util.WaitForProgressToShow; import git4idea.GitPlatformFacade; -import git4idea.checkin.GitCheckinEnvironment; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; import git4idea.commands.GitSimpleEventDetector; @@ -43,15 +36,13 @@ import git4idea.commands.GitUntrackedFilesOverwrittenByOperationDetector; import git4idea.merge.GitConflictResolver; import git4idea.repo.GitRepository; import git4idea.util.UntrackedFilesNotifier; -import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import javax.swing.*; import javax.swing.event.HyperlinkEvent; import java.io.File; import java.io.IOException; import java.util.*; +import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -163,7 +154,7 @@ public class GitCherryPicker { @NotNull GitCommitWrapper commit, @NotNull List successfulCommits) { CherryPickData data = updateChangeListManager(commit.getCommit()); - boolean committed = showCommitDialog(repository, commit, data.myChangeList, data.myCommitMessage); + boolean committed = showCommitDialogAndWaitForCommit(repository, commit, data.myChangeList, data.myCommitMessage); if (committed) { removeChangeList(data); successfulCommits.add(commit); @@ -206,12 +197,12 @@ public class GitCherryPicker { refreshChangedFiles(paths); final String commitMessage = createCommitMessage(commit, paths); LocalChangeList previouslyDefaultChangeList = myChangeListManager.getDefaultChangeList(); - LocalChangeList changeList = createChangeListAfterUpdate(commit.getChanges(), paths, commitMessage); + LocalChangeList changeList = createChangeListAfterUpdate(commit, paths, commitMessage); return new CherryPickData(changeList, commitMessage, previouslyDefaultChangeList); } @NotNull - private LocalChangeList createChangeListAfterUpdate(@NotNull final List changes, @NotNull final Collection paths, + private LocalChangeList createChangeListAfterUpdate(@NotNull final GitCommit commit, @NotNull final Collection paths, @NotNull final String commitMessage) { final AtomicReference changeList = new AtomicReference(); myPlatformFacade.invokeAndWait(new Runnable() { @@ -219,7 +210,7 @@ public class GitCherryPicker { public void run() { myChangeListManager.invokeAfterUpdate(new Runnable() { public void run() { - changeList.set(createChangeList(changes, commitMessage)); + changeList.set(createChangeList(commit, commitMessage)); } }, InvokeAfterUpdateMode.SYNCHRONOUS_NOT_CANCELLABLE, "Cherry-pick", new Consumer() { @@ -243,29 +234,53 @@ public class GitCherryPicker { return message; } - private boolean showCommitDialog(@NotNull final GitRepository repository, @NotNull final GitCommitWrapper commit, - @NotNull final LocalChangeList changeList, @NotNull final String commitMessage) { + private boolean showCommitDialogAndWaitForCommit(@NotNull final GitRepository repository, @NotNull final GitCommitWrapper commit, + @NotNull final LocalChangeList changeList, @NotNull final String commitMessage) { final AtomicBoolean commitSucceeded = new AtomicBoolean(); + final Semaphore sem = new Semaphore(0); myPlatformFacade.invokeAndWait(new Runnable() { @Override public void run() { - cancelCherryPick(repository); - List changes = commit.getCommit().getChanges(); - CherryPickCommitExecutor executor = new CherryPickCommitExecutor(myProject, myPlatformFacade, changes, commitMessage); - boolean commitNotCancelled = myPlatformFacade.getVcsHelper(myProject).commitChanges(changes, changeList, commitMessage, executor); - boolean success = commitNotCancelled && !executor.hasCommitFailed(); - if (success) { - commit.setActualSubject(getSubjectFromCommitMessage(executor.getActualCommitMessage())); + try { + cancelCherryPick(repository); + List changes = commit.getCommit().getChanges(); + boolean commitNotCancelled = myPlatformFacade.getVcsHelper(myProject).commitChanges(changes, changeList, commitMessage, + new CommitResultHandler() { + @Override + public void onSuccess(@NotNull String commitMessage) { + commit.setActualSubject(commitMessage); + commitSucceeded.set(true); + sem.release(); + } + + @Override + public void onFailure() { + commitSucceeded.set(false); + sem.release(); + } + }); + + if (!commitNotCancelled) { + commitSucceeded.set(false); + sem.release(); + } + } catch (Throwable t) { + LOG.error(t); + commitSucceeded.set(false); + sem.release(); } - commitSucceeded.set(success); } }, ModalityState.NON_MODAL); - return commitSucceeded.get(); - } - private static String getSubjectFromCommitMessage(String commitMessage) { - int newLine = commitMessage.indexOf("\n"); - return newLine < 0 ? commitMessage : commitMessage.substring(0, newLine); + // need additional waiting, because commitChanges is asynchronous + try { + sem.acquire(); + } + catch (InterruptedException e) { + LOG.error(e); + return false; + } + return commitSucceeded.get(); } /** @@ -334,7 +349,7 @@ public class GitCherryPicker { @NotNull private static String commitDetails(@NotNull GitCommitWrapper commit) { - return commit.getCommit().getShortHash().toString() + " \"" + commit.getSubject() + "\""; + return commit.getCommit().getShortHash().toString() + " \"" + commit.getOriginalSubject() + "\""; } private void refreshChangedFiles(@NotNull Collection filePaths) { @@ -347,9 +362,10 @@ public class GitCherryPicker { } @NotNull - private LocalChangeList createChangeList(@NotNull List changes, @NotNull String commitMessage) { + private LocalChangeList createChangeList(@NotNull GitCommit commit, @NotNull String commitMessage) { + List changes = commit.getChanges(); if (!changes.isEmpty()) { - final LocalChangeList changeList = myChangeListManager.addChangeList(commitMessage, commitMessage); + final LocalChangeList changeList = ((ChangeListManagerEx)myChangeListManager).addChangeList(commitMessage, commitMessage, commit); myChangeListManager.moveChangesTo(changeList, changes.toArray(new Change[changes.size()])); myChangeListManager.setDefaultChangeList(changeList); return changeList; @@ -451,142 +467,6 @@ public class GitCherryPicker { } } - /* - Commit procedure is overridden by the executor with its own CommitSession. - The reason of that is the asynchronous nature of the CommitHelper: it returns, we continue cherry-picking and occasionally pick - the next commit in the queue, and only then Git is called for commit. Thus it commits two cherry-picks at once, which is wrong. - - Here we call GitCheckinEnvironment manually - */ - private static class CherryPickCommitExecutor implements CommitExecutor { - - @NotNull private final Project myProject; - @NotNull private final GitPlatformFacade myPlatformFacade; - @NotNull private final List myChanges; - @NotNull private final String myOriginalCommitMessage; - private boolean myCommitFailed; - - @Nullable private CherryPickCommitExecutor.CherryPickCommitSession myCommitSession; - - CherryPickCommitExecutor(@NotNull Project project, @NotNull GitPlatformFacade platformFacade, - @NotNull List changes, @NotNull String originalCommitMessage) { - myProject = project; - myPlatformFacade = platformFacade; - myChanges = changes; - myOriginalCommitMessage = originalCommitMessage; - } - - @Nls - @Override - public String getActionText() { - return "Commit"; - } - - @NotNull - @Override - public CommitSession createCommitSession() { - myCommitSession = new CherryPickCommitSession(); - return myCommitSession; - } - - public boolean hasCommitFailed() { - return myCommitFailed; - } - - @NotNull - public String getActualCommitMessage() { - return myCommitSession == null ? myOriginalCommitMessage : myCommitSession.getActualCommitMessage(); - } - - private class CherryPickCommitSession implements CommitSession { - private String myActualCommitMessage; - - @Override - public JComponent getAdditionalConfigurationUI() { - return null; - } - - @Override - public JComponent getAdditionalConfigurationUI(Collection changes, String commitMessage) { - return null; - } - - @Override - public boolean canExecute(Collection changes, String commitMessage) { - return true; - } - - @Override - public void execute(Collection changes, String commitMessage) { - final Collection committingDocs = markCommittingDocs(); - try { - GitCheckinEnvironment ce = ServiceManager.getService(myProject, GitCheckinEnvironment.class); - try { - ce.reset(); - List exceptions = ce.commit(myChanges, commitMessage); - VcsDirtyScopeManager.getInstance(myProject).filePathsDirty(ChangesUtil.getPaths(myChanges), null); - if (exceptions != null && !exceptions.isEmpty()) { - VcsException exception = exceptions.get(0); - handleError(exception); - } - myActualCommitMessage = commitMessage; - } - catch (Throwable e) { - LOG.error(e); - handleError(e); - } - } - finally { - unmarkCommittingDocs(committingDocs); - } - } - - private void handleError(Throwable exception) { - myCommitFailed = true; - final String errorMessage = exception.getMessage(); - WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { - public void run() { - Messages.showErrorDialog(myProject, errorMessage, "Commit Failed"); - } - }, null, myProject); - } - - @Override - public void executionCanceled() { - } - - @Override - public String getHelpId() { - return null; - } - - private void unmarkCommittingDocs(final Collection committingDocs) { - myPlatformFacade.runReadAction(new Runnable() { - @Override - public void run() { - CommitHelper.unmarkCommittingDocuments(committingDocs); - } - }); - } - - @NotNull - private Collection markCommittingDocs() { - final Collection committingDocs = new ArrayList(); - myPlatformFacade.runReadAction(new Runnable() { - @Override - public void run() { - committingDocs.addAll(CommitHelper.markCommittingDocuments(myProject, myChanges)); - } - }); - return committingDocs; - } - - public String getActualCommitMessage() { - return myActualCommitMessage; - } - } - } - /** * This class is needed to hold both the original GitCommit, and the commit message which could be changed by the user. * Only the subject of the commit message is needed. @@ -613,6 +493,10 @@ public class GitCherryPicker { public GitCommit getCommit() { return myOriginalCommit; } + + public String getOriginalSubject() { + return myOriginalCommit.getSubject(); + } } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleContentRootManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleContentRootManager.java index 1d40834f1a14..d062f821f762 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleContentRootManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleContentRootManager.java @@ -1,18 +1,18 @@ package org.jetbrains.plugins.gradle.manage; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.model.gradle.GradleContentRoot; import org.jetbrains.plugins.gradle.model.gradle.SourceType; import org.jetbrains.plugins.gradle.model.intellij.ModuleAwareContentRoot; import org.jetbrains.plugins.gradle.util.GradleUtil; +import java.util.Collection; import java.util.Collections; /** @@ -28,76 +28,59 @@ public class GradleContentRootManager { } @SuppressWarnings("MethodMayBeStatic") - public void importContentRoots(@NotNull final Iterable contentRoots, @NotNull final Module module) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - doImportContentRoots(contentRoots, module); - } - }); - } - - private static void doImportContentRoots(@NotNull final Iterable contentRoots, @NotNull final Module module) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void importContentRoots(@NotNull final Collection contentRoots, @NotNull final Module module) { + if (contentRoots.isEmpty()) { + return; + } + GradleUtil.executeProjectChangeAction(module.getProject(), contentRoots, new Runnable() { @Override public void run() { final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); final ModifiableRootModel model = moduleRootManager.getModifiableModel(); - GradleUtil.executeProjectChangeAction(module.getProject(), contentRoots, new Runnable() { - @Override - public void run() { - try { - for (GradleContentRoot contentRoot : contentRoots) { - ContentEntry contentEntry = model.addContentEntry(toVfsUrl(contentRoot.getRootPath())); - for (String path : contentRoot.getPaths(SourceType.SOURCE)) { - contentEntry.addSourceFolder(toVfsUrl(path), false); - } - for (String path : contentRoot.getPaths(SourceType.TEST)) { - contentEntry.addSourceFolder(toVfsUrl(path), true); - } - for (String path : contentRoot.getPaths(SourceType.EXCLUDED)) { - contentEntry.addExcludeFolder(toVfsUrl(path)); - } - } + try { + for (GradleContentRoot contentRoot : contentRoots) { + ContentEntry contentEntry = model.addContentEntry(toVfsUrl(contentRoot.getRootPath())); + for (String path : contentRoot.getPaths(SourceType.SOURCE)) { + contentEntry.addSourceFolder(toVfsUrl(path), false); + } + for (String path : contentRoot.getPaths(SourceType.TEST)) { + contentEntry.addSourceFolder(toVfsUrl(path), true); + } + for (String path : contentRoot.getPaths(SourceType.EXCLUDED)) { + contentEntry.addExcludeFolder(toVfsUrl(path)); } - finally { - model.commit(); - } } - }); + } + finally { + model.commit(); + } } }); } - + private static String toVfsUrl(@NotNull String path) { return LocalFileSystem.PROTOCOL_PREFIX + path; } @SuppressWarnings("MethodMayBeStatic") - public void removeContentRoots(@NotNull final Iterable contentRoots) { - UIUtil.invokeLaterIfNeeded(new Runnable() { + public void removeContentRoots(@NotNull final Collection contentRoots) { + if (contentRoots.isEmpty()) { + return; + } + Project project = contentRoots.iterator().next().getModule().getProject(); + GradleUtil.executeProjectChangeAction(project, contentRoots, new Runnable() { @Override public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - for (final ModuleAwareContentRoot contentRoot : contentRoots) { - GradleUtil.executeProjectChangeAction(contentRoot.getModule().getProject(), contentRoot, new Runnable() { - @Override - public void run() { - final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(contentRoot.getModule()); - ModifiableRootModel model = moduleRootManager.getModifiableModel(); - try { - model.removeContentEntry(contentRoot); - } - finally { - model.commit(); - } - } - }); - } + for (ModuleAwareContentRoot contentRoot : contentRoots) { + final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(contentRoot.getModule()); + ModifiableRootModel model = moduleRootManager.getModifiableModel(); + try { + model.removeContentEntry(contentRoot); } - }); + finally { + model.commit(); + } + } } }); } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleDependencyManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleDependencyManager.java index 4fa493f1c634..f8ec9ac9121b 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleDependencyManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleDependencyManager.java @@ -1,20 +1,16 @@ package org.jetbrains.plugins.gradle.manage; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.config.PlatformFacade; import org.jetbrains.plugins.gradle.model.gradle.*; import org.jetbrains.plugins.gradle.sync.GradleProjectStructureHelper; import org.jetbrains.plugins.gradle.util.GradleUtil; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * Thread-safe. @@ -23,17 +19,19 @@ import java.util.List; * @since 2/7/12 3:23 PM */ public class GradleDependencyManager { - - @NotNull private final PlatformFacade myPlatformFacade; - public GradleDependencyManager(@NotNull PlatformFacade platformFacade) { + @NotNull private final PlatformFacade myPlatformFacade; + @NotNull private final GradleLibraryManager myLibraryManager; + + public GradleDependencyManager(@NotNull PlatformFacade platformFacade, @NotNull GradleLibraryManager manager) { myPlatformFacade = platformFacade; + myLibraryManager = manager; } public void importDependency(@NotNull GradleDependency dependency, @NotNull Module module) { importDependencies(Collections.singleton(dependency), module); } - + public void importDependencies(@NotNull Iterable dependencies, @NotNull Module module) { final List moduleDependencies = new ArrayList(); final List libraryDependencies = new ArrayList(); @@ -56,19 +54,12 @@ public class GradleDependencyManager { } @SuppressWarnings("MethodMayBeStatic") - public void importModuleDependencies(@NotNull final Iterable dependencies, @NotNull final Module module) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - doImportModuleDependencies(dependencies, module); - } - }); - } - - private static void doImportModuleDependencies(@NotNull final Iterable dependencies, - @NotNull final Module module) - { - ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void importModuleDependencies(@NotNull final Collection dependencies, @NotNull final Module module) { + if (dependencies.isEmpty()) { + return; + } + + GradleUtil.executeProjectChangeAction(module.getProject(), dependencies, new Runnable() { @Override public void run() { ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); @@ -77,12 +68,20 @@ public class GradleDependencyManager { final GradleProjectStructureHelper projectStructureHelper = module.getProject().getComponent(GradleProjectStructureHelper.class); for (GradleModuleDependency dependency : dependencies) { final String moduleName = dependency.getName(); - final Module intellijModuleDependency = projectStructureHelper.findIntellijModule(moduleName); - if (intellijModuleDependency == null) { + final Module intellijModule = projectStructureHelper.findIntellijModule(moduleName); + if (intellijModule == null) { assert false; continue; } - final ModuleOrderEntry orderEntry = moduleRootModel.addModuleOrderEntry(intellijModuleDependency); + else if (intellijModule.equals(module)) { + // Gradle api returns recursive module dependencies (a module depends on itself) for 'gradle' project. + continue; + } + + ModuleOrderEntry orderEntry = projectStructureHelper.findIntellijModuleDependency(dependency, moduleRootModel); + if (orderEntry == null) { + orderEntry = moduleRootModel.addModuleOrderEntry(intellijModule); + } orderEntry.setScope(dependency.getScope()); orderEntry.setExported(dependency.isExported()); } @@ -93,97 +92,79 @@ public class GradleDependencyManager { } }); } - + public void importLibraryDependencies(@NotNull final Iterable dependencies, @NotNull final Module module) { - final List infos = new ArrayList(); - final LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(module.getProject()); - for (GradleLibraryDependency dependency : dependencies) { - final Library library = libraryTable.getLibraryByName(dependency.getName()); - if (library != null) { - infos.add(new LibraryDependencyInfo(library, dependency.getScope(), dependency.isExported())); - } - } - doImportLibraryDependencies(infos, module); - } - - private static void doImportLibraryDependencies(@NotNull final Iterable infos, @NotNull final Module module) { - UIUtil.invokeLaterIfNeeded(new Runnable() { + GradleUtil.executeProjectChangeAction(module.getProject(), dependencies, new Runnable() { @Override public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - GradleUtil.executeProjectChangeAction(module.getProject(), infos, new Runnable() { - @Override - public void run() { - ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); - final ModifiableRootModel moduleRootModel = moduleRootManager.getModifiableModel(); - try { - for (LibraryDependencyInfo info : infos) { - LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(info.library); - orderEntry.setExported(info.exported); - orderEntry.setScope(info.scope); - } - } - finally { - moduleRootModel.commit(); - } - } - }); + LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(module.getProject()); + Set librariesToImport = new HashSet(); + for (GradleLibraryDependency dependency : dependencies) { + final Library library = libraryTable.getLibraryByName(dependency.getName()); + if (library == null) { + librariesToImport.add(dependency.getTarget()); } - }); + } + if (!librariesToImport.isEmpty()) { + myLibraryManager.importLibraries(librariesToImport, module.getProject()); + } + + for (GradleLibraryDependency dependency : dependencies) { + GradleProjectStructureHelper helper = module.getProject().getComponent(GradleProjectStructureHelper.class); + ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); + final ModifiableRootModel moduleRootModel = moduleRootManager.getModifiableModel(); + try { + libraryTable = myPlatformFacade.getProjectLibraryTable(module.getProject()); + final Library library = libraryTable.getLibraryByName(dependency.getName()); + if (library == null) { + assert false; + continue; + } + LibraryOrderEntry orderEntry = helper.findIntellijLibraryDependency(dependency.getName(), moduleRootModel); + if (orderEntry == null) { + // We need to get the most up-to-date Library object due to our project model restrictions. + orderEntry = moduleRootModel.addLibraryEntry(library); + } + orderEntry.setExported(dependency.isExported()); + orderEntry.setScope(dependency.getScope()); + } + finally { + moduleRootModel.commit(); + } + } } }); } @SuppressWarnings("MethodMayBeStatic") - public void removeDependencies(@NotNull final Iterable dependencies) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - for (final ExportableOrderEntry dependency : dependencies) { - final Module module = dependency.getOwnerModule(); - GradleUtil.executeProjectChangeAction(module.getProject(), dependency, new Runnable() { - @Override - public void run() { - ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); - final ModifiableRootModel moduleRootModel = moduleRootManager.getModifiableModel(); - try { - // The thing is that intellij created order entry objects every time new modifiable model is created, - // that's why we can't use target dependency object as is but need to get a reference to the current - // entry object from the model instead. - for (OrderEntry entry : moduleRootModel.getOrderEntries()) { - if (entry.getPresentableName().equals(dependency.getPresentableName())) { - moduleRootModel.removeOrderEntry(entry); - break; - } - } - } - finally { - moduleRootModel.commit(); - } - } - }); + public void removeDependencies(@NotNull final Collection dependencies) { + if (dependencies.isEmpty()) { + return; + } + + for (final ExportableOrderEntry dependency : dependencies) { + final Module module = dependency.getOwnerModule(); + GradleUtil.executeProjectChangeAction(module.getProject(), dependency, new Runnable() { + @Override + public void run() { + ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); + final ModifiableRootModel moduleRootModel = moduleRootManager.getModifiableModel(); + try { + // The thing is that intellij created order entry objects every time new modifiable model is created, + // that's why we can't use target dependency object as is but need to get a reference to the current + // entry object from the model instead. + for (OrderEntry entry : moduleRootModel.getOrderEntries()) { + if (entry.getPresentableName().equals(dependency.getPresentableName())) { + moduleRootModel.removeOrderEntry(entry); + break; + } } } - }); - } - }); - } - - private static class LibraryDependencyInfo { - - @NotNull public final Library library; - @NotNull public final DependencyScope scope; - public final boolean exported; - - LibraryDependencyInfo(@NotNull Library library, @NotNull DependencyScope scope, boolean exported) { - this.library = library; - this.scope = scope; - this.exported = exported; + finally { + moduleRootModel.commit(); + } + } + }); } } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleLibraryManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleLibraryManager.java index b749ebebb165..c0b38fae255e 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleLibraryManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleLibraryManager.java @@ -1,23 +1,21 @@ package org.jetbrains.plugins.gradle.manage; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.JavadocOrderRootType; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.config.PlatformFacade; import org.jetbrains.plugins.gradle.model.gradle.GradleLibrary; import org.jetbrains.plugins.gradle.model.gradle.LibraryPathType; import org.jetbrains.plugins.gradle.util.GradleLog; +import org.jetbrains.plugins.gradle.util.GradleUtil; import java.io.File; import java.util.*; @@ -34,8 +32,13 @@ public class GradleLibraryManager { myPlatformFacade = platformFacade; } - @NotNull - public Library importLibrary(@NotNull final GradleLibrary library, @NotNull final Project project) { + public void importLibraries(@NotNull Collection libraries, @NotNull Project project) { + for (GradleLibrary library : libraries) { + importLibrary(library, project); + } + } + + public void importLibrary(@NotNull final GradleLibrary library, @NotNull final Project project) { Map> libraryFiles = new HashMap>(); for (LibraryPathType pathType : LibraryPathType.values()) { final Set paths = library.getPaths(pathType); @@ -50,47 +53,22 @@ public class GradleLibraryManager { } })); } - return importLibrary(library.getName(), libraryFiles, project); + importLibrary(library.getName(), libraryFiles, project); } - @NotNull - public Library importLibrary(@NotNull final String libraryName, - @NotNull final Map> libraryFiles, - @NotNull final Project project) + public void importLibrary(@NotNull final String libraryName, + @NotNull final Map> libraryFiles, + @NotNull final Project project) { - final Ref result = new Ref(); - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - final GradleProjectEntityChangeListener publisher = project.getMessageBus().syncPublisher(GradleProjectEntityChangeListener.TOPIC); - publisher.onChangeStart(libraryName); - try { - result.set(doImportLibrary(libraryName, libraryFiles, project)); - } - finally { - publisher.onChangeEnd(libraryName); - } - } - }); - return result.get(); - } - - @NotNull - private Library doImportLibrary(@NotNull final String libraryName, - @NotNull final Map> libraryFiles, - @NotNull Project project) - { - // Is assumed to be called from the EDT. - final LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(project); - final Ref result = new Ref(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { + GradleUtil.executeProjectChangeAction(project, libraryName, new Runnable() { @Override public void run() { + // Is assumed to be called from the EDT. + final LibraryTable libraryTable = myPlatformFacade.getProjectLibraryTable(project); final LibraryTable.ModifiableModel projectLibraryModel = libraryTable.getModifiableModel(); final Library intellijLibrary; try { intellijLibrary = projectLibraryModel.createLibrary(libraryName); - result.set(intellijLibrary); } finally { projectLibraryModel.commit(); @@ -104,7 +82,6 @@ public class GradleLibraryManager { } } }); - return result.get(); } private static void registerPaths(@NotNull final Map> libraryFiles, @@ -115,9 +92,9 @@ public class GradleLibraryManager { for (File file : entry.getValue()) { VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file); if (virtualFile == null) { - GradleLog.LOG.warn( - String.format("Can't find %s of the library '%s' at path '%s'", entry.getKey(), libraryName, file.getAbsolutePath()) - ); + //GradleLog.LOG.warn( + // String.format("Can't find %s of the library '%s' at path '%s'", entry.getKey(), libraryName, file.getAbsolutePath()) + //); continue; } if (virtualFile.isDirectory()) { @@ -137,7 +114,7 @@ public class GradleLibraryManager { } } - public void removeLibraries(@NotNull List libraries) { + public void removeLibraries(@NotNull List libraries) { // TODO den implement //LibraryTable table = library.getTable(); //table.removeLibrary(library); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleModuleManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleModuleManager.java index 0ffc42f734cb..916ee0057597 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleModuleManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleModuleManager.java @@ -16,6 +16,7 @@ import org.jetbrains.plugins.gradle.util.GradleLog; import org.jetbrains.plugins.gradle.util.GradleUtil; import java.io.File; +import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -50,7 +51,13 @@ public class GradleModuleManager { importModules(Collections.singleton(module), project, false); } - public void importModules(@NotNull final Iterable modules, @NotNull final Project project, final boolean recursive) { + public void importModules(@NotNull final Collection modules, + @NotNull final Project project, + final boolean recursive) + { + if (modules.isEmpty()) { + return; + } if (!project.isInitialized()) { myAlarm.addRequest(new ImportModulesTask(project, modules, recursive), PROJECT_INITIALISATION_DELAY_MS); return; @@ -65,7 +72,7 @@ public class GradleModuleManager { @Override public void run() { final ModuleManager moduleManager = ModuleManager.getInstance(project); - final GradleProjectEntityChangeListener publisher + final GradleProjectEntityChangeListener publisher = project.getMessageBus().syncPublisher(GradleProjectEntityChangeListener.TOPIC); for (GradleModule module : modules) { publisher.onChangeStart(module); @@ -121,7 +128,7 @@ public class GradleModuleManager { }); } - private static void removeExistingModulesConfigs(@NotNull Iterable modules) { + private static void removeExistingModulesConfigs(@NotNull Collection modules) { for (GradleModule module : modules) { // Remove existing '*.iml' file if necessary. final String moduleFilePath = module.getModuleFilePath(); @@ -136,43 +143,37 @@ public class GradleModuleManager { } @SuppressWarnings("MethodMayBeStatic") - public void removeModules(@NotNull final Iterable modules) { - UIUtil.invokeLaterIfNeeded(new Runnable() { + public void removeModules(@NotNull final Collection modules) { + if (modules.isEmpty()) { + return; + } + Project project = modules.iterator().next().getProject(); + GradleUtil.executeProjectChangeAction(project, modules, new Runnable() { @Override public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - for (final Module module : modules) { - GradleUtil.executeProjectChangeAction(module.getProject(), module, new Runnable() { - @Override - public void run() { - ModuleManager moduleManager = ModuleManager.getInstance(module.getProject()); - String path = module.getModuleFilePath(); - moduleManager.disposeModule(module); - File file = new File(path); - if (file.isFile()) { - boolean success = file.delete(); - if (!success) { - GradleLog.LOG.warn("Can't remove module file at '" + path + "'"); - } - } - } - }); - } + for (Module module : modules) { + ModuleManager moduleManager = ModuleManager.getInstance(module.getProject()); + String path = module.getModuleFilePath(); + moduleManager.disposeModule(module); + File file = new File(path); + if (file.isFile()) { + boolean success = file.delete(); + if (!success) { + GradleLog.LOG.warn("Can't remove module file at '" + path + "'"); + } } - }); + } } }); } private class ImportModulesTask implements Runnable { - private final Project myProject; - private final Iterable myModules; - private final boolean myRecursive; + private final Project myProject; + private final Collection myModules; + private final boolean myRecursive; - ImportModulesTask(@NotNull Project project, @NotNull Iterable modules, boolean recursive) { + ImportModulesTask(@NotNull Project project, @NotNull Collection modules, boolean recursive) { myProject = project; myModules = modules; myRecursive = recursive; @@ -192,5 +193,4 @@ public class GradleModuleManager { importModules(myModules, myProject, myRecursive); } } - } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleModulesImporter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleModulesImporter.java deleted file mode 100644 index f55af6110203..000000000000 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleModulesImporter.java +++ /dev/null @@ -1,564 +0,0 @@ -package org.jetbrains.plugins.gradle.manage; - -import com.intellij.openapi.application.*; -import com.intellij.openapi.module.ModifiableModuleModel; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.module.StdModuleTypes; -import com.intellij.openapi.roots.impl.ModifiableModelCommitter; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.progress.Task; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.*; -import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; -import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.roots.libraries.LibraryTable; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.JarFileSystem; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.Alarm; -import com.intellij.util.containers.hash.HashMap; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.gradle.model.gradle.*; -import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangesModel; -import org.jetbrains.plugins.gradle.task.GradleResolveProjectTask; -import org.jetbrains.plugins.gradle.util.GradleBundle; -import org.jetbrains.plugins.gradle.util.GradleLog; - -import java.io.File; -import java.util.*; -import java.util.concurrent.TimeUnit; - -/** - * Encapsulates functionality of creating IntelliJ IDEA modules on the basis of {@link GradleModule gradle modules}. - *

- * Not thread-safe. - * - * @author Denis Zhdanov - * @since 8/26/11 10:01 AM - */ -// TODO den remove -public class GradleModulesImporter { - - private static final Map LIBRARY_ROOT_MAPPINGS - = new EnumMap(LibraryPathType.class); - static { - LIBRARY_ROOT_MAPPINGS.put(LibraryPathType.BINARY, OrderRootType.CLASSES); - LIBRARY_ROOT_MAPPINGS.put(LibraryPathType.SOURCE, OrderRootType.SOURCES); - LIBRARY_ROOT_MAPPINGS.put(LibraryPathType.DOC, JavadocOrderRootType.getInstance()); - assert LibraryPathType.values().length == LIBRARY_ROOT_MAPPINGS.size(); - } - - /** - * We can't modify project modules (add/remove) until it's initialised, so, we delay that activity. Current constant - * holds number of milliseconds to wait between 'after project initialisation' processing attempts. - */ - private static final int PROJECT_INITIALISATION_DELAY_MS = (int)TimeUnit.SECONDS.toMillis(1); - - private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); - - /** - * Entry point for the whole 'import modules' procedure. - * - * @param modules module info containers received from the gradle api - * @param project project that should host the modules - * @param model modules model - * @param gradleProjectPath file system path to the gradle project file being imported - * @return mappings between the given gradle modules and newly created intellij modules - */ - @NotNull - public Map importModules(@NotNull final Iterable modules, @Nullable final Project project, - @Nullable final ModifiableModuleModel model, @NotNull String gradleProjectPath) - { - if (project == null) { - return Collections.emptyMap(); - } - removeExistingModulesSettings(modules); - if (!project.isInitialized()) { - myAlarm.addRequest(new ImportModulesTask(project, modules, gradleProjectPath), PROJECT_INITIALISATION_DELAY_MS); - return Collections.emptyMap(); - } - if (model == null) { - return Collections.emptyMap(); - } - return importModules(modules, model, project, gradleProjectPath); - } - - private static void removeExistingModulesSettings(@NotNull Iterable modules) { - for (GradleModule module : modules) { - // Remove existing '*.iml' file if necessary. - final String moduleFilePath = module.getModuleFilePath(); - File file = new File(moduleFilePath); - if (file.isFile()) { - boolean success = file.delete(); - if (!success) { - GradleLog.LOG.warn("Can't remove existing module file at '" + moduleFilePath + "'"); - } - } - } - } - - public Map importModules(@NotNull final Iterable modules, - @NotNull final ModifiableModuleModel model, - @NotNull final Project intellijProject, - @NotNull final String gradleProjectPath) - { - final Map result = new HashMap(); - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - Application application = ApplicationManager.getApplication(); - AccessToken writeLock = application.acquireWriteActionLock(getClass()); - try { - final List rootModels = new ArrayList(); - final GradleProjectEntityChangeListener publisher = - intellijProject.getMessageBus().syncPublisher(GradleProjectEntityChangeListener.TOPIC); - for (GradleModule module : modules) { - publisher.onChangeStart(module); - } - try { - Map moduleMappings = doImportModules(modules, model, rootModels); - result.putAll(moduleMappings); - myAlarm.cancelAllRequests(); - myAlarm.addRequest( - new SetupExternalLibrariesTask(moduleMappings, gradleProjectPath, intellijProject), - PROJECT_INITIALISATION_DELAY_MS - ); - } - finally { - ModifiableRootModel[] modelsAsArray = rootModels.toArray(new ModifiableRootModel[rootModels.size()]); - ModifiableModelCommitter.multiCommit(modelsAsArray, model); - for (GradleModule module : modules) { - publisher.onChangeEnd(module); - } - } - } - finally { - writeLock.finish(); - } - } - }); - return result; - } - - /** - * Actual implementation of {@link #importModules(Iterable, Project, ModifiableModuleModel, String)}. - * Insists on all arguments to be ready to use. - * - * @param modules modules to import - * @param model modules model - * @param rootModels holder for the module root modules. Is expected to be populated during the current method processing - * @return mappings between the given gradle modules and corresponding intellij modules - */ - @NotNull - @SuppressWarnings("MethodMayBeStatic") - private Map doImportModules(@NotNull Iterable modules, - @NotNull ModifiableModuleModel model, - @NotNull List rootModels) - { - Map result = new HashMap(); - for (GradleModule moduleToImport : modules) { - Module createdModule = createModule(moduleToImport, model); - result.put(moduleToImport, createdModule); - } - for (GradleModule moduleToImport : modules) { - ModifiableRootModel rootModel = configureModule(moduleToImport, result); - rootModels.add(rootModel); - } - return result; - } - - /** - * We need to create module objects for all modules at first and then configure them. That is necessary for setting up - * module dependencies. - * - * @param module gradle module to import - * @param model module model - * @return newly created IJ module - */ - @NotNull - private static Module createModule(@NotNull GradleModule module, @NotNull ModifiableModuleModel model) { - Application application = ApplicationManager.getApplication(); - application.assertWriteAccessAllowed(); - final String moduleFilePath = module.getModuleFilePath(); - return model.newModule(moduleFilePath, StdModuleTypes.JAVA.getId()); - } - - /** - * Applies module settings received from the gradle api (encapsulate at the given {@link GradleModule} object) to the - * target intellij module (retrieved from the given module mappings). - * - * @param module target gradle module which corresponding intellij module should be configured - * @param modules gradle module to intellij modules mappings. Is assumed to have a value for the given gradle modules used as a key - * @return module roots model used during configuration - */ - @NotNull - private static ModifiableRootModel configureModule(@NotNull GradleModule module, @NotNull Map modules) { - Application application = ApplicationManager.getApplication(); - application.assertWriteAccessAllowed(); - - ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(modules.get(module)); - ModifiableRootModel rootModel = moduleRootManager.getModifiableModel(); - configureModule(module, rootModel, modules); - return rootModel; - } - - /** - * Contains actual logic of {@link #configureModule(GradleModule, Map)}. - * - * @param module target module settings holder - * @param model intellij module setting manager - * @param modules modules mappings - */ - private static void configureModule(@NotNull GradleModule module, @NotNull final ModifiableRootModel model, - @NotNull final Map modules) - { - // Ensure that dependencies are clear. - final Object key = new Object(); - final Object dummy = new Object(); - RootPolicy policy = new RootPolicy() { - @Override - public Object visitModuleSourceOrderEntry(ModuleSourceOrderEntry moduleSourceOrderEntry, Object value) { - return key; - } - }; - for (OrderEntry orderEntry : model.getOrderEntries()) { - // Don't remove 'module source' order entry (configured automatically on module creation). - if (key != orderEntry.accept(policy, dummy)) { - model.removeOrderEntry(orderEntry); - } - } - - // Configure SDK. - model.inheritSdk(); - - // Compile output. - CompilerModuleExtension compilerExtension = model.getModuleExtension(CompilerModuleExtension.class); - compilerExtension.inheritCompilerOutputPath(module.isInheritProjectCompileOutputPath()); - if (!module.isInheritProjectCompileOutputPath()) { - String compileOutputPath = module.getCompileOutputPath(SourceType.SOURCE); - String testCompileOutputPath = module.getCompileOutputPath(SourceType.TEST); - if (StringUtil.isEmpty(compileOutputPath) || StringUtil.isEmpty(testCompileOutputPath)) { - GradleLog.LOG.warn(String.format( - "Module '%s' doesn't inherit project compile output path but has incomplete local setup. Falling back to the project " - + "compile output path. Local compile output path: '%s', local test compile output path: '%s'", - module.getName(), compileOutputPath, testCompileOutputPath - )); - compilerExtension.inheritCompilerOutputPath(true); - } - else { - compilerExtension.setCompilerOutputPath(compileOutputPath); - compilerExtension.setCompilerOutputPathForTests(testCompileOutputPath); - } - } - - // Content roots. - for (GradleContentRoot contentRoot : module.getContentRoots()) { - ContentEntry contentEntry = model.addContentEntry(toVfsUrl(contentRoot.getRootPath())); - for (String path : contentRoot.getPaths(SourceType.SOURCE)) { - contentEntry.addSourceFolder(toVfsUrl(path), false); - } - for (String path : contentRoot.getPaths(SourceType.TEST)) { - contentEntry.addSourceFolder(toVfsUrl(path), true); - } - for (String path : contentRoot.getPaths(SourceType.EXCLUDED)) { - contentEntry.addExcludeFolder(toVfsUrl(path)); - } - } - - // Module dependencies. - for (GradleDependency dependency : module.getDependencies()) { - dependency.invite(new GradleEntityVisitorAdapter() { - @Override - public void visit(@NotNull GradleModuleDependency dependency) { - ModuleOrderEntry orderEntry = model.addModuleOrderEntry(modules.get(dependency.getTarget())); - orderEntry.setExported(dependency.isExported()); - orderEntry.setScope(dependency.getScope()); - } - }); - } - } - - /** - * Resolves (downloads if necessary) external libraries necessary for the gradle project located at the given path and configures - * them for the corresponding intellij project. - *

- * Note: is assumed to be executed under write action. - * - * @param moduleMappings gradle-intellij module mappings - * @param intellijProject intellij project for the target gradle project - * @param gradleProjectPath file system path to the target gradle project - */ - private static void setupLibraries(@NotNull final Map moduleMappings, - @NotNull final Project intellijProject, - @NotNull final String gradleProjectPath) - { - final Ref gradleProjectRef = new Ref(); - final Ref libraryToPreserve = new Ref(); - - final Runnable setupExternalDependenciesTask = new Runnable() { - @Override - public void run() { - final GradleProject gradleProject = gradleProjectRef.get(); - if (gradleProject == null) { - return; - } - - Application application = ApplicationManager.getApplication(); - AccessToken writeLock = application.acquireWriteActionLock(getClass()); - try { - doSetupLibraries(moduleMappings, gradleProject, intellijProject, libraryToPreserve.get()); - } - finally { - writeLock.finish(); - } - - if (intellijProject.isDisposed()) { - return; - } - - // Force refresh the infrastructure in order to apply newly introduce intellij project structure changes - final GradleProjectStructureChangesModel changesModel = intellijProject.getComponent(GradleProjectStructureChangesModel.class); - if (changesModel != null) { - final GradleProject project = changesModel.getGradleProject(); - if (project != null) { - changesModel.update(project); - } - } - } - }; - - final Runnable resolveDependenciesTask = new Runnable() { - @Override - public void run() { - ProgressManager.getInstance().run( - new Task.Backgroundable(intellijProject, GradleBundle.message("gradle.library.resolve.progress.text"), false) { - @Override - public void run(@NotNull final ProgressIndicator indicator) { - GradleResolveProjectTask task = new GradleResolveProjectTask(intellijProject, gradleProjectPath, true); - task.execute(indicator); - GradleProject projectWithResolvedLibraries = task.getGradleProject(); - gradleProjectRef.set(projectWithResolvedLibraries); - ApplicationManager.getApplication().invokeLater(setupExternalDependenciesTask, ModalityState.NON_MODAL); - } - }); - } - }; - - UIUtil.invokeLaterIfNeeded(resolveDependenciesTask); - } - - private static void doSetupLibraries(@NotNull Map moduleMappings, - @NotNull GradleProject gradleProject, - @NotNull Project intellijProject, - @Nullable Library libraryToPreserve) { - if (intellijProject.isDisposed()) { - return; - } - Application application = ApplicationManager.getApplication(); - application.assertWriteAccessAllowed(); - - LibraryTable projectLibraryTable = ProjectLibraryTable.getInstance(intellijProject); - if (projectLibraryTable == null) { - GradleLog.LOG.warn( - "Can't resolve external dependencies of the target gradle project (" + intellijProject + "). Reason: project " - + "library table is undefined" - ); - return; - } - LibraryTable.ModifiableModel model = projectLibraryTable.getModifiableModel(); - // Clean existing libraries (if any). - try { - for (Library library : model.getLibraries()) { - if (libraryToPreserve != library) { - model.removeLibrary(library); - } - } - } - finally { - model.commit(); - } - - model = projectLibraryTable.getModifiableModel(); - List modelsToCommit = new ArrayList(); - Map libraryMappings = registerProjectLibraries(gradleProject, model); - final GradleProjectEntityChangeListener publisher - = intellijProject.getMessageBus().syncPublisher(GradleProjectEntityChangeListener.TOPIC); - try { - if (libraryMappings == null) { - return; - } - for (GradleLibrary library : libraryMappings.keySet()) { - publisher.onChangeStart(library); - } - modelsToCommit.addAll(configureModulesLibraryDependencies(moduleMappings, libraryMappings, gradleProject)); - } - finally { - model.commit(); - ProjectRootManager projectRootManager = ProjectRootManager.getInstance(intellijProject); - ModifiableRootModel[] modelsAsArray = modelsToCommit.toArray(new ModifiableRootModel[modelsToCommit.size()]); - if (modelsAsArray.length > 0) { - ModifiableModelCommitter.multiCommit(modelsAsArray, ModuleManager.getInstance(modelsAsArray[0].getProject()).getModifiableModel()); - } - if (libraryMappings != null) { - for (GradleLibrary library : libraryMappings.keySet()) { - publisher.onChangeEnd(library); - } - } - } - } - - /** - * Registers {@link GradleProject#getLibraries() libraries} of the given gradle project at the intellij project. - * - * @param gradleProject target gradle project being imported - * @param librariesModel model that manages project libraries - * @return mapping between libraries of the given gradle and intellij projects - */ - @Nullable - private static Map registerProjectLibraries(@NotNull GradleProject gradleProject, - @NotNull LibraryTable.ModifiableModel librariesModel) - { - Map libraryMappings = new HashMap(); - for (GradleLibrary gradleLibrary : gradleProject.getLibraries()) { - Library intellijLibrary = librariesModel.createLibrary(gradleLibrary.getName()); - libraryMappings.put(gradleLibrary, intellijLibrary); - Library.ModifiableModel model = intellijLibrary.getModifiableModel(); - try { - registerPath(gradleLibrary, model); - } - finally { - model.commit(); - } - } - return libraryMappings; - } - - private static Collection configureModulesLibraryDependencies( - @NotNull Map moduleMappings, - @NotNull final Map libraryMappings, - @NotNull GradleProject gradleProject) - { - List result = new ArrayList(); - for (GradleModule gradleModule : gradleProject.getModules()) { - Module intellijModule = moduleMappings.get(gradleModule); - if (intellijModule == null) { - GradleLog.LOG.warn(String.format( - "Can't find intellij module for the gradle module '%s'. Registered mappings: %s", gradleModule, moduleMappings - )); - continue; - } - ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(intellijModule); - final ModifiableRootModel moduleRootModel = moduleRootManager.getModifiableModel(); - result.add(moduleRootModel); - GradleEntityVisitor visitor = new GradleEntityVisitorAdapter() { - @Override - public void visit(@NotNull GradleLibraryDependency dependency) { - GradleLibrary gradleLibrary = dependency.getTarget(); - Library intellijLibrary = libraryMappings.get(gradleLibrary); - if (intellijLibrary == null) { - GradleLog.LOG.warn(String.format( - "Can't find registered intellij library for gradle library '%s'. Registered mappings: %s", gradleLibrary, libraryMappings - )); - return; - } - LibraryOrderEntry orderEntry = moduleRootModel.addLibraryEntry(intellijLibrary); - orderEntry.setExported(dependency.isExported()); - orderEntry.setScope(dependency.getScope()); - } - }; - for (GradleDependency dependency : gradleModule.getDependencies()) { - dependency.invite(visitor); - } - } - return result; - } - - private static void registerPath(@NotNull GradleLibrary gradleLibrary, @NotNull Library.ModifiableModel model) { - for (LibraryPathType pathType : LibraryPathType.values()) { - for (String path : gradleLibrary.getPaths(pathType)) { - VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(new File(path)); - if (virtualFile == null) { - GradleLog.LOG.warn(String.format("Can't find %s of the library '%s' at path '%s'", pathType, gradleLibrary.getName(), path)); - continue; - } - if (virtualFile.isDirectory()) { - model.addRoot(virtualFile, LIBRARY_ROOT_MAPPINGS.get(pathType)); - } - else { - VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(virtualFile); - if (jarRoot == null) { - GradleLog.LOG.warn(String.format( - "Can't parse contents of the jar file at path '%s' for the library '%s''", path, gradleLibrary.getName() - )); - continue; - } - model.addRoot(jarRoot, LIBRARY_ROOT_MAPPINGS.get(pathType)); - } - } - } - } - - private static String toVfsUrl(@NotNull String path) { - return LocalFileSystem.PROTOCOL_PREFIX + path; - } - - private class ImportModulesTask implements Runnable { - - private final Project myProject; - private final Iterable myModules; - private final String myGradleProjectPath; - - ImportModulesTask(@NotNull Project project, @NotNull Iterable modules, @NotNull String gradleProjectPath) { - myProject = project; - myModules = modules; - myGradleProjectPath = gradleProjectPath; - } - - @Override - public void run() { - myAlarm.cancelAllRequests(); - if (!myProject.isInitialized()) { - myAlarm.addRequest( - new ImportModulesTask(myProject, myModules, myGradleProjectPath), - PROJECT_INITIALISATION_DELAY_MS - ); - return; - } - - final ModifiableModuleModel model = new ReadAction() { - protected void run(Result result) throws Throwable { - result.setResult(ModuleManager.getInstance(myProject).getModifiableModel()); - } - }.execute().getResultObject(); - - importModules(myModules, model, myProject, myGradleProjectPath); - } - } - - private static class SetupExternalLibrariesTask implements Runnable { - - private final Map myModules; - private final String myGradleProjectPath; - private final Project myIntellijProject; - - SetupExternalLibrariesTask(@NotNull Map modules, - @NotNull String gradleProjectPath, - @NotNull Project intellijProject) - { - myModules = modules; - myGradleProjectPath = gradleProjectPath; - myIntellijProject = intellijProject; - } - - @Override - public void run() { - setupLibraries(myModules, myIntellijProject, myGradleProjectPath); - } - } -} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleProjectImportBuilder.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleProjectImportBuilder.java index 1dc7cbe3fed5..33edefceeefa 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleProjectImportBuilder.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/GradleProjectImportBuilder.java @@ -4,11 +4,18 @@ import com.intellij.ide.util.projectWizard.WizardContext; import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.projectRoots.JavaSdk; import com.intellij.openapi.projectRoots.SdkTypeId; import com.intellij.openapi.roots.LanguageLevelProjectExtension; +import com.intellij.openapi.roots.ex.ProjectRootManagerEx; +import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.roots.ui.configuration.ModulesProvider; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Ref; @@ -16,14 +23,17 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.packaging.artifacts.ModifiableArtifactModel; import com.intellij.pom.java.LanguageLevel; import com.intellij.projectImport.ProjectImportBuilder; +import com.intellij.util.ui.UIUtil; import icons.GradleIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.config.GradleConfigurable; import org.jetbrains.plugins.gradle.config.GradleSettings; -import org.jetbrains.plugins.gradle.model.gradle.GradleEntity; +import org.jetbrains.plugins.gradle.model.gradle.GradleLibrary; import org.jetbrains.plugins.gradle.model.gradle.GradleModule; import org.jetbrains.plugins.gradle.model.gradle.GradleProject; +import org.jetbrains.plugins.gradle.sync.GradleProjectStructureHelper; +import org.jetbrains.plugins.gradle.task.GradleResolveProjectTask; import org.jetbrains.plugins.gradle.util.GradleBundle; import org.jetbrains.plugins.gradle.util.GradleConstants; import org.jetbrains.plugins.gradle.util.GradleLog; @@ -31,7 +41,10 @@ import org.jetbrains.plugins.gradle.util.GradleUtil; import javax.swing.*; import java.io.File; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; /** * GoF builder for gradle-backed projects. @@ -42,12 +55,22 @@ import java.util.*; @SuppressWarnings("MethodMayBeStatic") public class GradleProjectImportBuilder extends ProjectImportBuilder { - /** @see #setModuleMappings(Map) */ - private final Map myModuleMappings = new HashMap(); - - private GradleProject myGradleProject; + @NotNull private final GradleModuleManager myModuleManager; + @NotNull private final GradleLibraryManager myLibraryManager; + @NotNull private final GradleDependencyManager myDependencyManager; + + private GradleProject myGradleProject; private GradleConfigurable myConfigurable; + public GradleProjectImportBuilder(@NotNull GradleModuleManager moduleManager, + @NotNull GradleLibraryManager libraryManager, + @NotNull GradleDependencyManager manager) + { + myModuleManager = moduleManager; + myLibraryManager = libraryManager; + myDependencyManager = manager; + } + @NotNull @Override public String getName() { @@ -93,12 +116,13 @@ public class GradleProjectImportBuilder extends ProjectImportBuilder commit(final Project project, ModifiableModuleModel model, ModulesProvider modulesProvider, - ModifiableArtifactModel artifactModel) { + ModifiableArtifactModel artifactModel) + { System.setProperty(GradleConstants.NEWLY_IMPORTED_PROJECT, Boolean.TRUE.toString()); final GradleProject gradleProject = getGradleProject(); if (gradleProject != null) { @@ -114,15 +138,107 @@ public class GradleProjectImportBuilder extends ProjectImportBuilder mappings = - importer.importModules(myModuleMappings.values(), project, model, projectFile.getAbsolutePath()); - return new ArrayList(mappings.values()); + /** + * The whole import sequence looks like below: + *

+ *

+   * 
    + *
  1. Get project view from the gradle tooling api without resolving dependencies (downloading libraries);
  2. + *
  3. Allow to adjust project settings before importing;
  4. + *
  5. Create IJ project and modules;
  6. + *
  7. Ask gradle tooling api to resolve library dependencies (download the if necessary);
  8. + *
  9. Configure libraries used by the gradle project at intellij;
  10. + *
  11. Configure library dependencies;
  12. + *
+ *
+ *

+ * + * @param projectWithResolvedLibraries gradle project with resolved libraries (libraries have already been downloaded and + * are available at file system under gradle service directory) + * @param project current intellij project which should be configured by libraries and module library + * dependencies information available at the given gradle project + */ + private void setupLibraries(final GradleProject projectWithResolvedLibraries, final Project project) { + final Set libraries = projectWithResolvedLibraries.getLibraries(); + GradleUtil.executeProjectChangeAction(project, libraries, new Runnable() { + @Override + public void run() { + ProjectRootManagerEx.getInstanceEx(project).mergeRootsChangesDuring(new Runnable() { + @Override + public void run() { + // Clean existing libraries (if any). + LibraryTable projectLibraryTable = ProjectLibraryTable.getInstance(project); + if (projectLibraryTable == null) { + GradleLog.LOG.warn( + "Can't resolve external dependencies of the target gradle project (" + project + "). Reason: project " + + "library table is undefined" + ); + return; + } + LibraryTable.ModifiableModel model = projectLibraryTable.getModifiableModel(); + try { + for (Library library : model.getLibraries()) { + model.removeLibrary(library); + } + } + finally { + model.commit(); + } + + // Register libraries. + myLibraryManager.importLibraries(projectWithResolvedLibraries.getLibraries(), project); + GradleProjectStructureHelper helper = project.getComponent(GradleProjectStructureHelper.class); + for (GradleModule module : projectWithResolvedLibraries.getModules()) { + Module intellijModule = helper.findIntellijModule(module); + assert intellijModule != null; + myDependencyManager.importDependencies(module.getDependencies(), intellijModule); + } + } + }); + } + }); } @Nullable @@ -206,32 +322,6 @@ public class GradleProjectImportBuilder extends ProjectImportBuilder - *

-   * 
    - *
  1. Get project view from the gradle tooling api without resolving dependencies (downloading libraries);
  2. - *
  3. Allow to adjust project settings before importing;
  4. - *
  5. Create IJ project and modules;
  6. - *
  7. Ask gradle tooling api to resolve library dependencies (download the if necessary);
  8. - *
  9. Configure modules dependencies;
  10. - *
- *
- *

- * {@link GradleEntity} guarantees correct {@link #equals(Object)}/{@link #hashCode()} implementation, so, we expect - * to get {@link GradleModule modules} that are the same in terms of {@link #equals(Object)} on subsequent calls. However, - * end-user is allowed to change their settings before the importing (e.g. module name), so, we need to map modules with - * resolved libraries to the modules from project 'view'. That's why end-user adjusts settings of the cloned modules. - * Given collection holds mappings between them. - * - * @param mappings origin-adjusted modules mappings - */ - public void setModuleMappings(@NotNull Map mappings) { - myModuleMappings.clear(); - myModuleMappings.putAll(mappings); - } - /** * Allows to get {@link Project} instance to use. Basically, there are two alternatives - * {@link WizardContext#getProject() project from the current wizard context} and diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/wizard/adjust/GradleAdjustImportSettingsStep.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/wizard/adjust/GradleAdjustImportSettingsStep.java index 44c3da214b88..39dc8f4b7400 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/wizard/adjust/GradleAdjustImportSettingsStep.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/manage/wizard/adjust/GradleAdjustImportSettingsStep.java @@ -188,15 +188,11 @@ public class GradleAdjustImportSettingsStep extends AbstractImportFromGradleWiza List modules = new ArrayList(project.getModules()); Collections.sort(modules, Named.COMPARATOR); List moduleNodes = new ArrayList(); - Map moduleMappings = new HashMap(); - GradleEntityCloneContext cloneContext = new GradleEntityCloneContext(); for (GradleModule module : modules) { - GradleModule moduleCopy = module.clone(cloneContext); - moduleMappings.put(module, moduleCopy); GradleProjectStructureNode moduleNode = buildNode(module, entity2nodes, counter++); moduleNodes.add(moduleNode); - for (GradleContentRoot contentRoot : moduleCopy.getContentRoots()) { + for (GradleContentRoot contentRoot : module.getContentRoots()) { moduleNode.add(buildNode(contentRoot, entity2nodes, counter++)); } Collection dependencies = module.getDependencies(); @@ -263,7 +259,6 @@ public class GradleAdjustImportSettingsStep extends AbstractImportFromGradleWiza } myTree.expandPath(new TreePath(root.getPath())); - builder.setModuleMappings(moduleMappings); } private GradleProjectStructureNode buildNode( diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleApiFacadeImpl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleApiFacadeImpl.java index 74ed43dd9c74..e753764fbc9a 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleApiFacadeImpl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/remote/impl/GradleApiFacadeImpl.java @@ -113,7 +113,7 @@ public class GradleApiFacadeImpl extends RemoteServer implements GradleApiFacade try { I stub = (I)UnicastRemoteObject.exportObject(proxy, 0); I stored = (I)myRemotes.putIfAbsent(implClass, stub); - return stored == null ? result : stored; + return stored == null ? stub : stored; } catch (RemoteException e) { Object raceResult = myRemotes.get(implClass); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureHelper.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureHelper.java index df0e73be81c5..8a0b05240b37 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureHelper.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureHelper.java @@ -3,10 +3,7 @@ package org.jetbrains.plugins.gradle.sync; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.LibraryOrderEntry; -import com.intellij.openapi.roots.ModuleOrderEntry; -import com.intellij.openapi.roots.OrderEntry; -import com.intellij.openapi.roots.RootPolicy; +import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.util.Ref; @@ -177,6 +174,21 @@ public class GradleProjectStructureHelper extends AbstractProjectComponent { return null; } + @Nullable + public LibraryOrderEntry findIntellijLibraryDependency(@NotNull final String libraryName, + @NotNull ModifiableRootModel model) + { + for (OrderEntry entry : model.getOrderEntries()) { + if (entry instanceof LibraryOrderEntry) { + LibraryOrderEntry candidate = (LibraryOrderEntry)entry; + if (libraryName.equals(candidate.getLibraryName())) { + return candidate; + } + } + } + return null; + } + @Nullable public GradleLibrary findGradleLibrary(@NotNull final String libraryName) { final GradleProject project = myModel.getGradleProject(); @@ -268,6 +280,18 @@ public class GradleProjectStructureHelper extends AbstractProjectComponent { } } return null; + } + @Nullable + public ModuleOrderEntry findIntellijModuleDependency(@NotNull GradleModuleDependency dependency, @NotNull ModifiableRootModel model) { + for (OrderEntry entry : model.getOrderEntries()) { + if (entry instanceof ModuleOrderEntry) { + ModuleOrderEntry candidate = (ModuleOrderEntry)entry; + if (dependency.getName().equals(candidate.getModuleName())) { + return candidate; + } + } + } + return null; } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/task/AbstractGradleTask.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/AbstractGradleTask.java index d39d0e20d981..6be6f4448fc2 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/task/AbstractGradleTask.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/task/AbstractGradleTask.java @@ -74,7 +74,9 @@ public abstract class AbstractGradleTask implements GradleTask { catch (Throwable e) { setState(GradleTaskState.FAILED); myError.set(e); - LOG.warn(e); + if (myIntellijProject == null || !myIntellijProject.isDisposed()) { + LOG.warn(e); + } } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleUtil.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleUtil.java index 69b0ef1b7fcb..11e91135ec4f 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleUtil.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleUtil.java @@ -503,23 +503,34 @@ public class GradleUtil { return INSTALLATION_MANAGER.getValue().getGradleHome(project) != null; } + public static void executeProjectChangeAction(@NotNull Project project, @NotNull Object entityToChange, @NotNull Runnable task) { executeProjectChangeAction(project, Collections.singleton(entityToChange), task); } - public static void executeProjectChangeAction(@NotNull Project project, @NotNull Iterable entitiesToChange, @NotNull Runnable task) { - final GradleProjectEntityChangeListener publisher = project.getMessageBus().syncPublisher(GradleProjectEntityChangeListener.TOPIC); - for (Object e : entitiesToChange) { - publisher.onChangeStart(e); - } - try { - task.run(); - } - finally { - for (Object e : entitiesToChange) { - publisher.onChangeEnd(e); + public static void executeProjectChangeAction(@NotNull final Project project, @NotNull final Iterable entitiesToChange, @NotNull final Runnable task) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + final GradleProjectEntityChangeListener publisher = project.getMessageBus().syncPublisher(GradleProjectEntityChangeListener.TOPIC); + for (Object e : entitiesToChange) { + publisher.onChangeStart(e); + } + try { + task.run(); + } + finally { + for (Object e : entitiesToChange) { + publisher.onChangeEnd(e); + } + } + } + }); } - } + }); } private interface TaskUnderProgress { diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java index 4c75d27c207c..c8ffe9e6b71d 100644 --- a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java +++ b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgMockVcsHelper.java @@ -20,7 +20,7 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.annotate.AnnotationProvider; import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.CommitExecutor; +import com.intellij.openapi.vcs.changes.CommitResultHandler; import com.intellij.openapi.vcs.changes.LocalChangeList; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsHistoryProvider; @@ -171,8 +171,8 @@ public class HgMockVcsHelper extends AbstractVcsHelper { } @Override - public boolean commitChanges(@NotNull List changes, @NotNull LocalChangeList initialChangeList, - @NotNull String commitMessage, @Nullable CommitExecutor executor) { + public boolean commitChanges(@NotNull Collection changes, @NotNull LocalChangeList initialChangeList, + @NotNull String commitMessage, @Nullable CommitResultHandler customResultHandler) { throw new UnsupportedOperationException(); }