From fc3b0ba81e30499d183e5eea36aca8198a4979be Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 13:40:28 +0400 Subject: [PATCH 01/85] svn: Refactored DiffClient.compare() implementations - make returned result always present changes needs to be done to target1 to get target2 state (assuming target1 is always url, target2 is either url or file) --- .../idea/svn/diff/CmdDiffClient.java | 51 ++++++++++--------- .../jetbrains/idea/svn/diff/DiffClient.java | 4 ++ .../svn/diff/DirectoryWithBranchComparer.java | 4 +- .../idea/svn/diff/SvnKitDiffClient.java | 30 +++++------ 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java index 497134727ee4..e561a2898d7d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java @@ -54,11 +54,11 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { @Override public List compare(@NotNull SvnTarget target1, @NotNull SvnTarget target2) throws VcsException { - assertUrl(target2); - if (target1.isFile()) { + assertUrl(target1); + if (target2.isFile()) { // Such combination (file and url) with "--summarize" option is supported only in svn 1.8. // For svn 1.7 "--summarize" is only supported when both targets are repository urls. - assertDirectory(target1); + assertDirectory(target2); WorkingCopyFormat format = WorkingCopyFormat.from(myFactory.createVersionClient().getVersion()); if (format.less(WorkingCopyFormat.ONE_DOT_EIGHT)) { @@ -114,16 +114,30 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { } } - private ContentRevision createRemoteRevision(@NotNull FilePath remotePath, @NotNull FilePath localPath, @NotNull FileStatus status) { + @NotNull + private ContentRevision createRevision(@NotNull FilePath path, + @NotNull FilePath localPath, + @NotNull FileStatus status) { + ContentRevision result; + + if (path.isNonLocal()) { // explicitly use local path for deleted items - so these items will be correctly displayed as deleted under local working copy node // and not as deleted under remote branch node (in ChangesBrowser) // NOTE, that content is still retrieved using remotePath. - return SvnRepositoryContentRevision - .create(myVcs, remotePath, status == FileStatus.DELETED ? localPath : null, SVNRevision.HEAD.getNumber()); + result = + SvnRepositoryContentRevision.create(myVcs, path, status == FileStatus.DELETED ? localPath : null, SVNRevision.HEAD.getNumber()); + } + else { + result = CurrentContentRevision.create(path); } - private static ContentRevision createLocalRevision(@NotNull FilePath path) { - return CurrentContentRevision.create(path); + return result; + } + + private static FilePath createFilePath(@NotNull SvnTarget target, boolean isDirectory) { + return target.isFile() + ? VcsUtil.getFilePath(target.getFile(), isDirectory) + : VcsUtil.getFilePathOnNonLocal(SvnUtil.toDecodedString(target), isDirectory); } @NotNull @@ -134,9 +148,7 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { // TODO: 3) Properties change is currently not added as part of result change like in SvnChangeProviderContext.patchWithPropertyChange SvnTarget subTarget1 = SvnUtil.append(target1, diffPath.path, true); - String relativePath = target1.isFile() - ? FileUtil.getRelativePath(target1.getFile(), subTarget1.getFile()) - : SvnUtil.getRelativeUrl(SvnUtil.toDecodedString(target1), SvnUtil.toDecodedString(subTarget1)); + String relativePath = SvnUtil.getRelativeUrl(SvnUtil.toDecodedString(target1), SvnUtil.toDecodedString(subTarget1)); if (relativePath == null) { throw new SvnBindException("Could not get relative path for " + target1 + " and " + subTarget1); @@ -144,26 +156,19 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { SvnTarget subTarget2 = SvnUtil.append(target2, FileUtil.toSystemIndependentName(relativePath)); - FilePath target1Path = target1.isFile() - ? VcsUtil.getFilePath(subTarget1.getFile(), diffPath.isDirectory()) - : VcsUtil.getFilePathOnNonLocal(SvnUtil.toDecodedString(subTarget1), diffPath.isDirectory()); - FilePath target2Path = VcsUtil.getFilePathOnNonLocal(SvnUtil.toDecodedString(subTarget2), diffPath.isDirectory()); + FilePath target1Path = createFilePath(subTarget1, diffPath.isDirectory()); + FilePath target2Path = createFilePath(subTarget2, diffPath.isDirectory()); FileStatus status = SvnStatusConvertor .convertStatus(SvnStatusHandler.getStatus(diffPath.itemStatus), SvnStatusHandler.getStatus(diffPath.propertiesStatus)); - // for "file + url" pair - statuses determine changes needs to be done to "url" to get "file" state - // for "url1 + url2" pair - statuses determine changes needs to be done to "url1" to get "url2" state + // statuses determine changes needs to be done to "target1" to get "target2" state ContentRevision beforeRevision = status == FileStatus.ADDED ? null - : target1.isFile() - ? createRemoteRevision(target2Path, target1Path, status) - : createRemoteRevision(target1Path, target2Path, status); + : createRevision(target1Path, target2Path, status); ContentRevision afterRevision = status == FileStatus.DELETED ? null - : target1.isFile() - ? createLocalRevision(target1Path) - : createRemoteRevision(target2Path, target1Path, status); + : createRevision(target2Path, target1Path, status); return createChange(status, beforeRevision, afterRevision); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java index 35c35e9313f1..0904ff24f582 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java @@ -29,6 +29,10 @@ import java.util.List; */ public interface DiffClient extends SvnClient { + /** + * @param target1 Should always be url. + * @param target2 Could be either url or file. And should be directory if file. + */ List compare(@NotNull SvnTarget target1, @NotNull SvnTarget target2) throws VcsException; void unifiedDiff(@NotNull SvnTarget target1, @NotNull SvnTarget target2, @NotNull OutputStream output) throws VcsException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DirectoryWithBranchComparer.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DirectoryWithBranchComparer.java index 0f0e6a714037..3fe346fb7797 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DirectoryWithBranchComparer.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DirectoryWithBranchComparer.java @@ -53,8 +53,8 @@ public class DirectoryWithBranchComparer extends ElementWithBranchComparer { titleBuilder.append(SvnBundle.message("repository.browser.compare.title", myElementUrl, FileUtil.toSystemDependentName(myVirtualFile.getPresentableUrl()))); - SvnTarget target1 = SvnTarget.fromFile(new File(myVirtualFile.getPath())); - SvnTarget target2 = SvnTarget.fromURL(myElementUrl); + SvnTarget target1 = SvnTarget.fromURL(myElementUrl); + SvnTarget target2 = SvnTarget.fromFile(new File(myVirtualFile.getPath())); changes.addAll(getClientFactory().createDiffClient().compare(target1, target2)); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java index 0a1b14837b7e..cd1a72dfb436 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java @@ -99,12 +99,12 @@ public class SvnKitDiffClient extends BaseSvnClient implements DiffClient { } public void run() throws SVNException { - assertUrl(myTarget2); + assertUrl(myTarget1); - if (myTarget1.isFile()) { - assertDirectory(myTarget1); + if (myTarget2.isFile()) { + assertDirectory(myTarget2); - WorkingCopyFormat format = myVcs.getWorkingCopyFormat(myTarget1.getFile()); + WorkingCopyFormat format = myVcs.getWorkingCopyFormat(myTarget2.getFile()); myChanges.addAll(WorkingCopyFormat.ONE_DOT_SIX.equals(format) ? run16Diff() : run17Diff()); } else { @@ -142,20 +142,20 @@ public class SvnKitDiffClient extends BaseSvnClient implements DiffClient { } private Collection run17Diff() throws SVNException { - final Info info1 = myVcs.getInfo(myTarget1.getFile(), SVNRevision.HEAD); + final Info info1 = myVcs.getInfo(myTarget2.getFile(), SVNRevision.HEAD); if (info1 == null) { SVNErrorMessage err = - SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "''{0}'' is not under version control", myTarget1); + SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "''{0}'' is not under version control", myTarget2); SVNErrorManager.error(err, SVNLogType.WC); } else if (info1.getURL() == null) { - SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "''{0}'' has no URL", myTarget1); + SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "''{0}'' has no URL", myTarget2); SVNErrorManager.error(err, SVNLogType.WC); } final SVNReporter17 reporter17 = - new SVNReporter17(myTarget1.getFile(), new SVNWCContext(myVcs.getSvnKitManager().getSvnOptions(), new ISVNEventHandler() { + new SVNReporter17(myTarget2.getFile(), new SVNWCContext(myVcs.getSvnKitManager().getSvnOptions(), new ISVNEventHandler() { @Override public void handleEvent(SVNEvent event, double progress) throws SVNException { } @@ -169,9 +169,9 @@ public class SvnKitDiffClient extends BaseSvnClient implements DiffClient { try { repository = myVcs.getSvnKitManager().createRepository(info1.getURL()); long rev = repository.getLatestRevision(); - repository2 = myVcs.getSvnKitManager().createRepository(myTarget2.getURL()); - SvnDiffEditor diffEditor = new SvnDiffEditor(myTarget1.getFile(), repository2, rev, true); - repository.diff(myTarget2.getURL(), rev, rev, null, true, SVNDepth.INFINITY, false, reporter17, + repository2 = myVcs.getSvnKitManager().createRepository(myTarget1.getURL()); + SvnDiffEditor diffEditor = new SvnDiffEditor(myTarget2.getFile(), repository2, rev, true); + repository.diff(myTarget1.getURL(), rev, rev, null, true, SVNDepth.INFINITY, false, reporter17, SVNCancellableEditor.newInstance(diffEditor, new SvnKitProgressCanceller(), null)); return diffEditor.getChangesMap().values(); @@ -193,7 +193,7 @@ public class SvnKitDiffClient extends BaseSvnClient implements DiffClient { SVNRepository repository = null; SVNRepository repository2 = null; try { - SVNAdminAreaInfo info = wcAccess.openAnchor(myTarget1.getFile(), false, SVNWCAccess.INFINITE_DEPTH); + SVNAdminAreaInfo info = wcAccess.openAnchor(myTarget2.getFile(), false, SVNWCAccess.INFINITE_DEPTH); File anchorPath = info.getAnchor().getRoot(); String target = "".equals(info.getTargetName()) ? null : info.getTargetName(); @@ -215,10 +215,10 @@ public class SvnKitDiffClient extends BaseSvnClient implements DiffClient { repository = myVcs.getSvnKitManager().createRepository(anchorURL.toString()); long rev = repository.getLatestRevision(); repository2 = - myVcs.getSvnKitManager().createRepository((target == null) ? myTarget2.getURL() : myTarget2.getURL().removePathTail()); + myVcs.getSvnKitManager().createRepository((target == null) ? myTarget1.getURL() : myTarget1.getURL().removePathTail()); SvnDiffEditor diffEditor = - new SvnDiffEditor(target == null ? myTarget1.getFile() : myTarget1.getFile().getParentFile(), repository2, rev, true); - repository.diff(myTarget2.getURL(), rev, rev, target, true, true, false, reporter, + new SvnDiffEditor(target == null ? myTarget2.getFile() : myTarget2.getFile().getParentFile(), repository2, rev, true); + repository.diff(myTarget1.getURL(), rev, rev, target, true, true, false, reporter, SVNCancellableEditor.newInstance(diffEditor, new SvnKitProgressCanceller(), null)); return diffEditor.getChangesMap().values(); From 2f92d2a37b1791697c744af43436a9d1ae2218ad Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 13:57:45 +0400 Subject: [PATCH 02/85] svn: Refactored DiffClient and implementations - @NotNull --- .../svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java | 2 ++ .../svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java | 1 + .../src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java | 1 + 3 files changed, 4 insertions(+) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java index e561a2898d7d..40b28eac4ba9 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java @@ -52,6 +52,7 @@ import java.util.List; */ public class CmdDiffClient extends BaseSvnClient implements DiffClient { + @NotNull @Override public List compare(@NotNull SvnTarget target1, @NotNull SvnTarget target2) throws VcsException { assertUrl(target1); @@ -95,6 +96,7 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { } } + @NotNull private List parseOutput(@NotNull SvnTarget target1, @NotNull SvnTarget target2, @NotNull CommandExecutor executor) throws SvnBindException { try { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java index 0904ff24f582..dfa43ddcb4bd 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DiffClient.java @@ -33,6 +33,7 @@ public interface DiffClient extends SvnClient { * @param target1 Should always be url. * @param target2 Could be either url or file. And should be directory if file. */ + @NotNull List compare(@NotNull SvnTarget target1, @NotNull SvnTarget target2) throws VcsException; void unifiedDiff(@NotNull SvnTarget target1, @NotNull SvnTarget target2, @NotNull OutputStream output) throws VcsException; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java index cd1a72dfb436..ec4c51288cc2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/SvnKitDiffClient.java @@ -54,6 +54,7 @@ import java.util.List; */ public class SvnKitDiffClient extends BaseSvnClient implements DiffClient { + @NotNull @Override public List compare(@NotNull SvnTarget target1, @NotNull SvnTarget target2) throws VcsException { DiffExecutor executor = new DiffExecutor(target1, target2); From 7cfc1d15631c651358d81e9e0724ea5a987cb0ed Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 14:26:41 +0400 Subject: [PATCH 03/85] svn: Make CmdDiffClient correctly handle peg revisions of passed targets --- .../src/org/jetbrains/idea/svn/diff/CmdDiffClient.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java index 40b28eac4ba9..be084b0986d0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/CmdDiffClient.java @@ -119,6 +119,7 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { @NotNull private ContentRevision createRevision(@NotNull FilePath path, @NotNull FilePath localPath, + @NotNull SVNRevision revision, @NotNull FileStatus status) { ContentRevision result; @@ -126,8 +127,7 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { // explicitly use local path for deleted items - so these items will be correctly displayed as deleted under local working copy node // and not as deleted under remote branch node (in ChangesBrowser) // NOTE, that content is still retrieved using remotePath. - result = - SvnRepositoryContentRevision.create(myVcs, path, status == FileStatus.DELETED ? localPath : null, SVNRevision.HEAD.getNumber()); + result = SvnRepositoryContentRevision.create(myVcs, path, status == FileStatus.DELETED ? localPath : null, revision.getNumber()); } else { result = CurrentContentRevision.create(path); @@ -167,11 +167,10 @@ public class CmdDiffClient extends BaseSvnClient implements DiffClient { // statuses determine changes needs to be done to "target1" to get "target2" state ContentRevision beforeRevision = status == FileStatus.ADDED ? null - : createRevision(target1Path, target2Path, status); + : createRevision(target1Path, target2Path, target1.getPegRevision(), status); ContentRevision afterRevision = status == FileStatus.DELETED ? null - : createRevision(target2Path, target1Path, status); - + : createRevision(target2Path, target1Path, target2.getPegRevision(), status); return createChange(status, beforeRevision, afterRevision); } From 3fa1cdd733911d12a37ed18d8a3e544a288e547a Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 15:13:24 +0400 Subject: [PATCH 04/85] svn: Refactored GitDiffFromHistoryHandler and HgDiffFromHistoryHandler - common logic extracted to base BaseDiffFromHistoryHandler class --- .../history/BaseDiffFromHistoryHandler.java | 197 ++++++++++++++++++ .../history/GitDiffFromHistoryHandler.java | 142 +++---------- .../provider/HgDiffFromHistoryHandler.java | 121 ++--------- 3 files changed, 248 insertions(+), 212 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/history/BaseDiffFromHistoryHandler.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/BaseDiffFromHistoryHandler.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/BaseDiffFromHistoryHandler.java new file mode 100644 index 000000000000..8a16ba6a481f --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/BaseDiffFromHistoryHandler.java @@ -0,0 +1,197 @@ +/* + * Copyright 2000-2014 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.history; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogBuilder; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.util.Couple; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ui.ChangesBrowser; +import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public abstract class BaseDiffFromHistoryHandler implements DiffFromHistoryHandler { + + private static final Logger LOG = Logger.getInstance(BaseDiffFromHistoryHandler.class); + + @NotNull protected final Project myProject; + + protected BaseDiffFromHistoryHandler(@NotNull Project project) { + myProject = project; + } + + @Override + public void showDiffForOne(@NotNull AnActionEvent e, + @NotNull FilePath filePath, + @NotNull VcsFileRevision previousRevision, + @NotNull VcsFileRevision revision) { + doShowDiff(filePath, previousRevision, revision, false); + } + + @Override + public void showDiffForTwo(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2) { + doShowDiff(filePath, revision1, revision2, true); + } + + @SuppressWarnings("unchecked") + protected void doShowDiff(@NotNull FilePath filePath, + @NotNull VcsFileRevision revision1, + @NotNull VcsFileRevision revision2, + boolean autoSort) { + if (!filePath.isDirectory()) { + VcsHistoryUtil.showDifferencesInBackground(myProject, filePath, revision1, revision2, autoSort); + } + else if (revision1.equals(VcsFileRevision.NULL)) { + T right = (T)revision2; + showAffectedChanges(filePath, right); + } + else if (revision2 instanceof CurrentRevision) { + T left = (T)revision1; + showChangesBetweenRevisions(filePath, left, null); + } + else { + T left = (T)revision1; + T right = (T)revision2; + if (autoSort) { + Couple pair = VcsHistoryUtil.sortRevisions(revision1, revision2); + left = (T)pair.first; + right = (T)pair.second; + } + showChangesBetweenRevisions(filePath, left, right); + } + } + + protected void showChangesBetweenRevisions(@NotNull final FilePath path, @NotNull final T rev1, @Nullable final T rev2) { + new CollectChangesTask("Comparing revisions...") { + + @NotNull + @Override + public List getChanges() throws VcsException { + return getChangesBetweenRevisions(path, rev1, rev2); + } + + @NotNull + @Override + public String getDialogTitle() { + return getChangesBetweenRevisionsDialogTitle(path, rev1, rev2); + } + }.queue(); + } + + protected void showAffectedChanges(@NotNull final FilePath path, @NotNull final T rev) { + new CollectChangesTask("Collecting affected changes...") { + + @NotNull + @Override + public List getChanges() throws VcsException { + return getAffectedChanges(path, rev); + } + + @NotNull + @Override + public String getDialogTitle() { + return getAffectedChangesDialogTitle(path, rev); + } + }.queue(); + } + + // rev2 == null -> compare rev1 with local + // rev2 != null -> compare rev1 with rev2 + @NotNull + protected abstract List getChangesBetweenRevisions(@NotNull final FilePath path, @NotNull final T rev1, @Nullable final T rev2) + throws VcsException; + + @NotNull + protected abstract List getAffectedChanges(@NotNull final FilePath path, @NotNull final T rev) throws VcsException; + + @NotNull + protected abstract String getPresentableName(@NotNull T revision); + + protected void showChangesDialog(@NotNull String title, @NotNull List changes) { + DialogBuilder dialogBuilder = new DialogBuilder(myProject); + + dialogBuilder.setTitle(title); + dialogBuilder.setActionDescriptors(new DialogBuilder.ActionDescriptor[]{new DialogBuilder.CloseDialogAction()}); + final ChangesBrowser changesBrowser = + new ChangesBrowser(myProject, null, changes, null, false, true, null, ChangesBrowser.MyUseCase.COMMITTED_CHANGES, null); + changesBrowser.setChangesToDisplay(changes); + dialogBuilder.setCenterPanel(changesBrowser); + dialogBuilder.showNotModal(); + } + + protected void showError(@NotNull VcsException e, @NotNull String logMessage) { + LOG.info(logMessage, e); + VcsBalloonProblemNotifier.showOverVersionControlView(myProject, e.getMessage(), MessageType.ERROR); + } + + @NotNull + protected String getChangesBetweenRevisionsDialogTitle(@NotNull final FilePath path, @NotNull final T rev1, @Nullable final T rev2) { + String rev1Title = getPresentableName(rev1); + + return rev2 != null + ? String.format("Difference between %s and %s in %s", rev1Title, getPresentableName(rev2), path.getName()) + : String.format("Difference between %s and local version in %s", rev1Title, path.getName()); + } + + @NotNull + protected String getAffectedChangesDialogTitle(@NotNull final FilePath path, @NotNull final T rev) { + return String.format("Initial commit %s in %s", getPresentableName(rev), path.getName()); + } + + protected abstract class CollectChangesTask extends Task.Backgroundable { + + private List myChanges; + + public CollectChangesTask(@NotNull String title) { + super(BaseDiffFromHistoryHandler.this.myProject, title); + } + + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + myChanges = getChanges(); + } + catch (VcsException e) { + showError(e, "Error during task: " + getDialogTitle()); + } + } + + @NotNull + public abstract List getChanges() throws VcsException; + + @NotNull + public abstract String getDialogTitle(); + + @Override + public void onSuccess() { + showChangesDialog(getDialogTitle(), ContainerUtil.notNullize(myChanges)); + } + } +} diff --git a/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java b/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java index 87edd30ae077..a6488d4ce3d0 100644 --- a/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java +++ b/plugins/git4idea/src/git4idea/history/GitDiffFromHistoryHandler.java @@ -22,25 +22,22 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; -import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ContentRevision; -import com.intellij.openapi.vcs.changes.ui.ChangesBrowser; -import com.intellij.openapi.vcs.history.CurrentRevision; +import com.intellij.openapi.vcs.history.BaseDiffFromHistoryHandler; import com.intellij.openapi.vcs.history.DiffFromHistoryHandler; import com.intellij.openapi.vcs.history.VcsFileRevision; -import com.intellij.openapi.vcs.history.VcsHistoryUtil; import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; +import com.intellij.util.containers.ContainerUtil; import git4idea.GitFileRevision; import git4idea.GitRevisionNumber; import git4idea.GitUtil; @@ -67,71 +64,59 @@ import java.util.List; * * @author Kirill Likhodedov */ -public class GitDiffFromHistoryHandler implements DiffFromHistoryHandler { +public class GitDiffFromHistoryHandler extends BaseDiffFromHistoryHandler { private static final Logger LOG = Logger.getInstance(GitDiffFromHistoryHandler.class); - @NotNull private final Project myProject; @NotNull private final Git myGit; @NotNull private final GitRepositoryManager myRepositoryManager; public GitDiffFromHistoryHandler(@NotNull Project project) { - myProject = project; + super(project); myGit = ServiceManager.getService(project, Git.class); myRepositoryManager = GitUtil.getRepositoryManager(project); } @Override - public void showDiffForOne(@NotNull AnActionEvent e, @NotNull FilePath filePath, - @NotNull VcsFileRevision previousRevision, @NotNull VcsFileRevision revision) { + public void showDiffForOne(@NotNull AnActionEvent e, + @NotNull FilePath filePath, + @NotNull VcsFileRevision previousRevision, + @NotNull VcsFileRevision revision) { GitFileRevision rev = (GitFileRevision)revision; Collection parents = rev.getParents(); if (parents.size() < 2) { - doShowDiff(filePath, previousRevision, revision, false); + super.showDiffForOne(e, filePath, previousRevision, revision); } else { // merge showDiffForMergeCommit(e, filePath, rev, parents); } } + @NotNull @Override - public void showDiffForTwo(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2) { - doShowDiff(filePath, revision1, revision2, true); - } - - private void doShowDiff(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2, - boolean autoSort) { - if (!filePath.isDirectory()) { - VcsHistoryUtil.showDifferencesInBackground(myProject, filePath, revision1, revision2, autoSort); - } - else if (revision2 instanceof CurrentRevision) { - GitFileRevision left = (GitFileRevision)revision1; - showDiffForDirectory(filePath, left.getHash(), null); - } - else if (revision1.equals(VcsFileRevision.NULL)) { - GitFileRevision right = (GitFileRevision)revision2; - showDiffForDirectory(filePath, null, right.getHash()); - } - else { - GitFileRevision left = (GitFileRevision)revision1; - GitFileRevision right = (GitFileRevision)revision2; - if (autoSort) { - Couple pair = VcsHistoryUtil.sortRevisions(revision1, revision2); - left = (GitFileRevision)pair.first; - right = (GitFileRevision)pair.second; - } - showDiffForDirectory(filePath, left.getHash(), right.getHash()); - } - } - - private void showDiffForDirectory(@NotNull final FilePath path, @Nullable final String hash1, @Nullable final String hash2) { + protected List getChangesBetweenRevisions(@NotNull FilePath path, @NotNull GitFileRevision rev1, @Nullable GitFileRevision rev2) + throws VcsException { GitRepository repository = getRepository(path); - calculateDiffInBackground(repository, path, hash1, hash2, new Consumer>() { - @Override - public void consume(List changes) { - showDirDiffDialog(path, hash1, hash2, changes); - } - }); + String hash1 = rev1.getHash(); + String hash2 = rev2 != null ? rev2.getHash() : null; + + return ContainerUtil + .newArrayList(GitChangeUtils.getDiff(repository.getProject(), repository.getRoot(), hash1, hash2, Collections.singletonList(path))); + } + + @NotNull + @Override + protected List getAffectedChanges(@NotNull FilePath path, @NotNull GitFileRevision rev) throws VcsException { + GitRepository repository = getRepository(path); + + return ContainerUtil.newArrayList( + GitChangeUtils.getRevisionChanges(repository.getProject(), repository.getRoot(), rev.getHash(), false, true, true).getChanges()); + } + + @NotNull + @Override + protected String getPresentableName(@NotNull GitFileRevision revision) { + return GitUtil.getShortHash(revision.getHash()); } @NotNull @@ -141,63 +126,6 @@ public class GitDiffFromHistoryHandler implements DiffFromHistoryHandler { return repository; } - // hash1 == null => hash2 is the initial commit - // hash2 == null => comparing hash1 with local - private void calculateDiffInBackground(@NotNull final GitRepository repository, @NotNull final FilePath path, - @Nullable final String hash1, @Nullable final String hash2, - final Consumer> successHandler) { - new Task.Backgroundable(myProject, "Comparing revisions...") { - private List myChanges; - @Override - public void run(@NotNull ProgressIndicator indicator) { - try { - if (hash1 != null) { - // diff - myChanges = new ArrayList(GitChangeUtils.getDiff(repository.getProject(), repository.getRoot(), hash1, hash2, - Collections.singletonList(path))); - } - else { - // show the initial commit - myChanges = new ArrayList(GitChangeUtils.getRevisionChanges(repository.getProject(), repository.getRoot(), hash2, false, - true, true).getChanges()); - } - } - catch (VcsException e) { - showError(e, "Error during requesting diff for directory"); - } - } - - @Override - public void onSuccess() { - successHandler.consume(myChanges); - } - }.queue(); - } - - private void showDirDiffDialog(@NotNull FilePath path, @Nullable String hash1, @Nullable String hash2, @NotNull List diff) { - DialogBuilder dialogBuilder = new DialogBuilder(myProject); - String title; - if (hash2 != null) { - if (hash1 != null) { - title = String.format("Difference between %s and %s in %s", GitUtil.getShortHash(hash1), GitUtil.getShortHash(hash2), path.getName()); - } - else { - title = String.format("Initial commit %s in %s", GitUtil.getShortHash(hash2), path.getName()); - } - } - else { - LOG.assertTrue(hash1 != null, "hash1 and hash2 can't both be null. Path: " + path); - title = String.format("Difference between %s and local version in %s", GitUtil.getShortHash(hash1), path.getName()); - } - dialogBuilder.setTitle(title); - dialogBuilder.setActionDescriptors(new DialogBuilder.ActionDescriptor[] { new DialogBuilder.CloseDialogAction()}); - final ChangesBrowser changesBrowser = new ChangesBrowser(myProject, null, diff, null, false, true, - null, ChangesBrowser.MyUseCase.COMMITTED_CHANGES, null); - changesBrowser.setChangesToDisplay(diff); - dialogBuilder.setCenterPanel(changesBrowser); - dialogBuilder.showNotModal(); - } - private void showDiffForMergeCommit(@NotNull final AnActionEvent event, @NotNull final FilePath filePath, @NotNull final GitFileRevision rev, @NotNull final Collection parents) { @@ -299,12 +227,6 @@ public class GitDiffFromHistoryHandler implements DiffFromHistoryHandler { return makeRevisionFromHash(currentRevisionPath, parentHash); } - - private void showError(VcsException e, String logMessage) { - LOG.info(logMessage, e); - VcsBalloonProblemNotifier.showOverVersionControlView(this.myProject, e.getMessage(), MessageType.ERROR); - } - private void showPopup(@NotNull AnActionEvent event, @NotNull GitFileRevision rev, @NotNull FilePath filePath, @NotNull Collection parents) { ActionGroup parentActions = createActionGroup(rev, filePath, parents); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgDiffFromHistoryHandler.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgDiffFromHistoryHandler.java index 61d08bc94e8c..1d556a8b19cb 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgDiffFromHistoryHandler.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgDiffFromHistoryHandler.java @@ -15,22 +15,13 @@ */ package org.zmlx.hg4idea.provider; -import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogBuilder; -import com.intellij.openapi.util.Couple; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ui.ChangesBrowser; -import com.intellij.openapi.vcs.history.CurrentRevision; -import com.intellij.openapi.vcs.history.DiffFromHistoryHandler; -import com.intellij.openapi.vcs.history.VcsFileRevision; -import com.intellij.openapi.vcs.history.VcsHistoryUtil; +import com.intellij.openapi.vcs.history.BaseDiffFromHistoryHandler; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.Consumer; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,111 +35,37 @@ import java.util.List; * * @author Nadya Zabrodina */ -public class HgDiffFromHistoryHandler implements DiffFromHistoryHandler { +public class HgDiffFromHistoryHandler extends BaseDiffFromHistoryHandler { private static final Logger LOG = Logger.getInstance(HgDiffFromHistoryHandler.class); - @NotNull private final Project myProject; - public HgDiffFromHistoryHandler(@NotNull Project project) { - myProject = project; + super(project); } + @NotNull @Override - public void showDiffForOne(@NotNull AnActionEvent e, @NotNull FilePath filePath, - @NotNull VcsFileRevision previousRevision, @NotNull VcsFileRevision revision) { - doShowDiff(filePath, previousRevision, revision, false); + protected List getChangesBetweenRevisions(@NotNull FilePath path, @NotNull HgFileRevision rev1, @Nullable HgFileRevision rev2) { + return executeDiff(path, rev1, rev2); } - + @NotNull @Override - public void showDiffForTwo(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2) { - doShowDiff(filePath, revision1, revision2, true); + protected List getAffectedChanges(@NotNull FilePath path, @NotNull HgFileRevision rev) throws VcsException { + return executeDiff(path, null, rev); } - private void doShowDiff(@NotNull FilePath filePath, @NotNull VcsFileRevision revision1, @NotNull VcsFileRevision revision2, - boolean autoSort) { - if (!filePath.isDirectory()) { - VcsHistoryUtil.showDifferencesInBackground(myProject, filePath, revision1, revision2, autoSort); - } - else if (revision2 instanceof CurrentRevision) { - HgFileRevision left = (HgFileRevision)revision1; - showDiffForDirectory(filePath, left, null); - } - else if (revision1.equals(VcsFileRevision.NULL)) { - HgFileRevision right = (HgFileRevision)revision2; - showDiffForDirectory(filePath, null, right); - } - else { - HgFileRevision left = (HgFileRevision)revision1; - HgFileRevision right = (HgFileRevision)revision2; - if (autoSort) { - Couple pair = VcsHistoryUtil.sortRevisions(revision1, revision2); - left = (HgFileRevision)pair.first; - right = (HgFileRevision)pair.second; - } - showDiffForDirectory(filePath, left, right); - } + @NotNull + @Override + protected String getPresentableName(@NotNull HgFileRevision revision) { + return revision.getRevisionNumber().getChangeset(); } - private void showDiffForDirectory(@NotNull final FilePath path, - @Nullable final HgFileRevision rev1, - @Nullable final HgFileRevision rev2) { + @NotNull + private List executeDiff(@NotNull FilePath path, @Nullable HgFileRevision rev1, @Nullable HgFileRevision rev2) { VirtualFile root = VcsUtil.getVcsRootFor(myProject, path); LOG.assertTrue(root != null, "Repository is null for " + path); - calculateDiffInBackground(root, path, rev1, rev2, new Consumer>() { - @Override - public void consume(List changes) { - showDirDiffDialog(path, rev1 != null ? rev1.getRevisionNumber().getChangeset() : null, - rev2 != null ? rev2.getRevisionNumber().getChangeset() : null, changes); - } - }); + + return HgUtil.getDiff(myProject, root, path, rev1, rev2); } - - // rev1 == null => rev2 is the initial commit - // rev2 == null => comparing rev1 with local - private void calculateDiffInBackground(@NotNull final VirtualFile root, @NotNull final FilePath path, - @Nullable final HgFileRevision rev1, @Nullable final HgFileRevision rev2, - final Consumer> successHandler) { - new Task.Backgroundable(myProject, "Comparing revisions...") { - private List myChanges; - - @Override - public void run(@NotNull ProgressIndicator indicator) { - if (myProject != null) { - myChanges = HgUtil.getDiff(HgDiffFromHistoryHandler.this.myProject, root, path, rev1, rev2); - } - } - - @Override - public void onSuccess() { - successHandler.consume(myChanges); - } - }.queue(); - } - - private void showDirDiffDialog(@NotNull FilePath path, @Nullable String hash1, @Nullable String hash2, @NotNull List diff) { - DialogBuilder dialogBuilder = new DialogBuilder(myProject); - String title; - if (hash2 != null) { - if (hash1 != null) { - title = String.format("Difference between %s and %s in %s", hash1, hash2, path.getName()); - } - else { - title = String.format("Initial commit %s in %s", hash2, path.getName()); - } - } - else { - LOG.assertTrue(hash1 != null, "hash1 and hash2 can't both be null. Path: " + path); - title = String.format("Difference between %s and local version in %s", hash1, path.getName()); - } - dialogBuilder.setTitle(title); - dialogBuilder.setActionDescriptors(new DialogBuilder.ActionDescriptor[]{new DialogBuilder.CloseDialogAction()}); - final ChangesBrowser changesBrowser = new ChangesBrowser(myProject, null, diff, null, false, true, - null, ChangesBrowser.MyUseCase.COMMITTED_CHANGES, null); - changesBrowser.setChangesToDisplay(diff); - dialogBuilder.setCenterPanel(changesBrowser); - dialogBuilder.showNotModal(); - } -} - +} \ No newline at end of file From 7e6e3b1cabbf7786052261e878a87ee0cb0a896e Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 15:24:26 +0400 Subject: [PATCH 05/85] svn: Allow explicitly specified zero peg revision to be passed to svn executable --- .../src/org/jetbrains/idea/svn/commandLine/CommandUtil.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java index eee798737846..eb2e6cf976cb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandUtil.java @@ -58,8 +58,7 @@ public class CommandUtil { boolean hasPegRevision = pegRevision != null && !SVNRevision.UNDEFINED.equals(pegRevision) && !SVNRevision.WORKING.equals(pegRevision) && - pegRevision.isValid() && - pegRevision.getNumber() != 0; + pegRevision.isValid(); if (hasPegRevision || hasAtSymbol) { // add '@' to correctly handle paths that contain '@' symbol From 04f3d7890858738df993d57157ce255c56192e04 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 15:43:57 +0400 Subject: [PATCH 06/85] IDEA-55335 Implemented directory revisions comparison from "history panel" --- .../svn/diff/DirectoryWithBranchComparer.java | 10 ++- .../history/SvnDiffFromHistoryHandler.java | 80 +++++++++++++++++++ .../idea/svn/history/SvnHistoryProvider.java | 2 +- .../idea/svn/history/SvnHistorySession.java | 5 -- 4 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnDiffFromHistoryHandler.java diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DirectoryWithBranchComparer.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DirectoryWithBranchComparer.java index 3fe346fb7797..784548d9e661 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DirectoryWithBranchComparer.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/diff/DirectoryWithBranchComparer.java @@ -24,6 +24,7 @@ import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnBundle; +import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.WorkingCopyFormat; import org.jetbrains.idea.svn.api.ClientFactory; import org.tmatesoft.svn.core.SVNException; @@ -61,11 +62,16 @@ public class DirectoryWithBranchComparer extends ElementWithBranchComparer { @NotNull private ClientFactory getClientFactory() { - WorkingCopyFormat format = myVcs.getWorkingCopyFormat(VfsUtilCore.virtualToIoFile(myVirtualFile)); + return getClientFactory(myVcs, VfsUtilCore.virtualToIoFile(myVirtualFile)); + } + + @NotNull + public static ClientFactory getClientFactory(@NotNull SvnVcs vcs, @NotNull File file) { + WorkingCopyFormat format = vcs.getWorkingCopyFormat(file); // svn 1.7 command line "--summarize" option for "diff" command does not support comparing working copy directories with repository // directories - that is why command line is only used explicitly for svn 1.8 - return format.isOrGreater(WorkingCopyFormat.ONE_DOT_EIGHT) ? myVcs.getCommandLineFactory() : myVcs.getSvnKitFactory(); + return format.isOrGreater(WorkingCopyFormat.ONE_DOT_EIGHT) ? vcs.getCommandLineFactory() : vcs.getSvnKitFactory(); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnDiffFromHistoryHandler.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnDiffFromHistoryHandler.java new file mode 100644 index 000000000000..be112127ecc9 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnDiffFromHistoryHandler.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.svn.history; + +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.history.BaseDiffFromHistoryHandler; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnUtil; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.api.ClientFactory; +import org.jetbrains.idea.svn.diff.DirectoryWithBranchComparer; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.io.File; +import java.util.List; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnDiffFromHistoryHandler extends BaseDiffFromHistoryHandler { + + @NotNull private final SvnVcs myVcs; + + public SvnDiffFromHistoryHandler(@NotNull SvnVcs vcs) { + super(vcs.getProject()); + myVcs = vcs; + } + + @NotNull + @Override + protected List getChangesBetweenRevisions(@NotNull FilePath path, @NotNull SvnFileRevision rev1, @Nullable SvnFileRevision rev2) + throws VcsException { + File file = path.getIOFile(); + SvnTarget target1 = SvnTarget.fromURL(SvnUtil.createUrl(rev1.getURL()), rev1.getRevision()); + SvnTarget target2 = rev2 != null ? SvnTarget.fromURL(SvnUtil.createUrl(rev2.getURL()), rev2.getRevision()) : SvnTarget.fromFile(file); + + return executeDiff(path, target1, target2); + } + + @NotNull + @Override + protected List getAffectedChanges(@NotNull FilePath path, @NotNull SvnFileRevision rev) throws VcsException { + // Diff with zero revision is used here to get just affected changes under the path, and not all affected changes of the revision. + SvnTarget target1 = SvnTarget.fromURL(SvnUtil.createUrl(rev.getURL()), SVNRevision.create(0)); + SvnTarget target2 = SvnTarget.fromURL(SvnUtil.createUrl(rev.getURL()), rev.getRevision()); + + return executeDiff(path, target1, target2); + } + + @NotNull + @Override + protected String getPresentableName(@NotNull SvnFileRevision revision) { + return revision.getRevisionNumber().asString(); + } + + @NotNull + private List executeDiff(@NotNull FilePath path, @NotNull SvnTarget target1, @NotNull SvnTarget target2) throws VcsException { + File file = path.getIOFile(); + ClientFactory factory = target2.isURL() ? myVcs.getFactory(file) : DirectoryWithBranchComparer.getClientFactory(myVcs, file); + + return factory.createDiffClient().compare(target1, target2); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java index 9e9dd7e1befc..b23b57772535 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java @@ -73,7 +73,7 @@ public class SvnHistoryProvider @Override public DiffFromHistoryHandler getHistoryDiffHandler() { - return null; + return new SvnDiffFromHistoryHandler(myVcs); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java index f2f91bceec14..8ae2da7cf8b3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java @@ -77,11 +77,6 @@ public class SvnHistorySession extends VcsAbstractHistorySession { return myCommittedPath; } - @Override - public boolean isContentAvailable(final VcsFileRevision revision) { - return !myCommittedPath.isDirectory(); - } - public boolean isHaveMergeSources() { return myHaveMergeSources; } From 28355b6ec105aa8b5762d7ae6f0416268889bdb1 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 16:10:49 +0400 Subject: [PATCH 07/85] svn: Swap before and after revisions during properties comparison only if before revision is strictly greater than after (otherwise there could be inconsistencies with content comparison - for instance, when comparing folders from "SVN Repositories" tool window) --- .../idea/svn/actions/AbstractShowPropertiesDiffAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java index 97e6a618ba7c..dc846a969f82 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java @@ -179,7 +179,7 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen } if (myBeforeContent != null && myAfterContent != null && myBeforeRevisionValue != null && myAfterRevision != null) { final SimpleDiffRequest diffRequest = new SimpleDiffRequest(myProject, getDiffWindowTitle(myChange)); - if (compareRevisions(myBeforeRevisionValue, myAfterRevision) >= 0) { + if (compareRevisions(myBeforeRevisionValue, myAfterRevision) > 0) { // before ahead diffRequest.setContents(new SimpleContent(myAfterContent), new SimpleContent(myBeforeContent)); diffRequest.setContentTitles(revisionToString(myAfterRevision), revisionToString(myBeforeRevisionValue)); From 86f6f3bcb20a85fa7a6a7f51dee754fbccb410f3 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 18:24:06 +0400 Subject: [PATCH 08/85] IDEA-118571 Explicitly set locale environment variables when running svn executable - use value from "svn.executable.locale" registry key --- .../src/misc/registry.properties | 1 + .../idea/svn/commandLine/CommandExecutor.java | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 0f430e0fee58..a78c26c7e9c8 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -316,6 +316,7 @@ diff.patience.alg=false svn.use.terminal=false svn.use.incoming.optimization=false +svn.executable.locale=C.UTF-8 completion.enable.relevant.method.chain.suggestions=false ide.mac.message.sheets.java.emulation=false diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandExecutor.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandExecutor.java index 33e06739ad0b..0b484e41d1c5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandExecutor.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/CommandExecutor.java @@ -23,6 +23,7 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.EventDispatcher; @@ -36,6 +37,7 @@ import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -152,9 +154,20 @@ public class CommandExecutor { protected void beforeCreateProcess() throws SvnBindException { EncodingEnvironmentUtil.fixDefaultEncodingIfMac(myCommandLine, null); + setupLocale(); ensureMessageFile(); } + private void setupLocale() { + String locale = Registry.stringValue("svn.executable.locale"); + Map environment = myCommandLine.getEnvironment(); + + // TODO: check if we need to set LC_ALL to configured locale or just clear it + environment.put("LC_ALL", ""); + environment.put("LC_MESSAGES", locale); + environment.put("LANG", locale); + } + private void ensureMessageFile() throws SvnBindException { if (myMessage != null) { myMessageFile = createTempFile("commit-message", ".txt"); From 8d4e4996ea58ab573b5359199e521f173e30d542 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 23 Jul 2014 19:22:51 +0400 Subject: [PATCH 09/85] IDEA-127736 Fixed tab title by tab component detection (while editing svn network options) --- .../idea/svn/config/SvnConfigureProxiesDialog.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/config/SvnConfigureProxiesDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/config/SvnConfigureProxiesDialog.java index a70a4f28d0fd..40e3c80d1fc6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/config/SvnConfigureProxiesDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/config/SvnConfigureProxiesDialog.java @@ -63,16 +63,10 @@ public class SvnConfigureProxiesDialog extends DialogWrapper implements Validati public void onError(final String text, final JComponent component, final boolean forbidSave) { myTabbedPane.setSelectedComponent(component); - String prefixString = ""; - for (int i = 0; i < myTabbedPane.getComponentCount(); i++) { - final Component currentComponent = myTabbedPane.getComponentAt(i); - // compare referencies - same objects - if (currentComponent == component) { - prefixString = myTabbedPane.getTitleAt(i) + ": "; - } - } + String errorPrefix = myTabbedPane.getTitleAt(myTabbedPane.indexOfComponent(component)) + ": "; + setOKActionEnabled(! forbidSave); - setInvalid(prefixString + text); + setInvalid(errorPrefix + text); } public void onSuccess() { From 0131b983f62b590a29c58a7b690b8975f2d0f718 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Thu, 24 Jul 2014 23:39:57 +0400 Subject: [PATCH 10/85] svn: Refactored SvnFormatSelector - ISVNAdminAreaFactorySelector (from SVNKit) implementation logic extracted to separate class --- .../jetbrains/idea/svn/SvnFormatSelector.java | 109 +------------- .../SvnKitAdminAreaFactorySelector.java | 138 ++++++++++++++++++ .../idea/svn/svnkit/SvnKitManager.java | 3 +- 3 files changed, 140 insertions(+), 110 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitAdminAreaFactorySelector.java diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java index 1719f781a34a..977c6add0c1f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnFormatSelector.java @@ -15,122 +15,15 @@ */ package org.jetbrains.idea.svn; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.tmatesoft.svn.core.SVNErrorCode; -import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.internal.wc.admin.ISVNAdminAreaFactorySelector; import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory; import org.tmatesoft.svn.core.internal.wc2.SvnWcGeneration; import org.tmatesoft.svn.core.wc2.SvnOperationFactory; import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -public class SvnFormatSelector implements ISVNAdminAreaFactorySelector { - - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnFormatSelector"); - - public Collection getEnabledFactories(File path, Collection factories, boolean writeAccess) throws SVNException { - if (ApplicationManager.getApplication().isUnitTestMode()) { - return factories; - } - - if (! writeAccess) { - return factories; - } - - Collection result = null; - final WorkingCopyFormat presetFormat = SvnWorkingCopyFormatHolder.getPresetFormat(); - if (presetFormat != null) { - result = format2Factories(presetFormat, factories); - } - - if (result == null) { - final WorkingCopyFormat format = getWorkingCopyFormat(path); - result = format2Factories(format, factories); - } - - if (result == null) { - throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY)); - } - return result; - } - - @Nullable - static Collection format2Factories(final WorkingCopyFormat format, final Collection factories) { - if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { - return factories; - } else if (WorkingCopyFormat.ONE_DOT_SIX.equals(format)) { - return factoriesFor16(factories); - } else if (WorkingCopyFormat.ONE_DOT_FIVE.equals(format)) { - return factoriesFor15(factories); - } else if (WorkingCopyFormat.ONE_DOT_FOUR.equals(format)) { - return factoriesFor14(factories); - } else if (WorkingCopyFormat.ONE_DOT_THREE.equals(format)) { - return factoriesFor13(factories); - } - return null; - } - - private static Collection factoriesFor13(final Collection factories) { - for (Iterator iterator = factories.iterator(); iterator.hasNext();) { - final SVNAdminAreaFactory factory = (SVNAdminAreaFactory) iterator.next(); - final int supportedVersion = factory.getSupportedVersion(); - if (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) { - return Collections.singletonList(factory); - } - } - return Collections.emptyList(); - } - - private static Collection factoriesFor14(final Collection factories) { - final Collection result = new ArrayList(2); - for (Iterator iterator = factories.iterator(); iterator.hasNext();) { - final SVNAdminAreaFactory factory = (SVNAdminAreaFactory) iterator.next(); - final int supportedVersion = factory.getSupportedVersion(); - if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || - (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion)) { - result.add(factory); - } - } - return result; - } - - private static Collection factoriesFor15(final Collection factories) { - final Collection result = new ArrayList(2); - for (Iterator iterator = factories.iterator(); iterator.hasNext();) { - final SVNAdminAreaFactory factory = (SVNAdminAreaFactory) iterator.next(); - final int supportedVersion = factory.getSupportedVersion(); - if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || - (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) || - (WorkingCopyFormat.ONE_DOT_FIVE.getFormat() == supportedVersion)) { - result.add(factory); - } - } - return result; - } - - private static Collection factoriesFor16(final Collection factories) { - final Collection result = new ArrayList(2); - for (Iterator iterator = factories.iterator(); iterator.hasNext();) { - final SVNAdminAreaFactory factory = (SVNAdminAreaFactory) iterator.next(); - final int supportedVersion = factory.getSupportedVersion(); - if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || - (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) || - (WorkingCopyFormat.ONE_DOT_FIVE.getFormat() == supportedVersion) || - (WorkingCopyFormat.ONE_DOT_SIX.getFormat() == supportedVersion)) { - result.add(factory); - } - } - return result; - } +public class SvnFormatSelector { @NotNull public static WorkingCopyFormat findRootAndGetFormat(final File path) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitAdminAreaFactorySelector.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitAdminAreaFactorySelector.java new file mode 100644 index 000000000000..164e90b68fae --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitAdminAreaFactorySelector.java @@ -0,0 +1,138 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.svn.svnkit; + +import com.intellij.openapi.application.ApplicationManager; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnFormatSelector; +import org.jetbrains.idea.svn.SvnWorkingCopyFormatHolder; +import org.jetbrains.idea.svn.WorkingCopyFormat; +import org.tmatesoft.svn.core.SVNErrorCode; +import org.tmatesoft.svn.core.SVNErrorMessage; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.internal.wc.admin.ISVNAdminAreaFactorySelector; +import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +/** + * @author Konstantin Kolosovsky. + */ +public class SvnKitAdminAreaFactorySelector implements ISVNAdminAreaFactorySelector { + + public Collection getEnabledFactories(File path, Collection factories, boolean writeAccess) throws SVNException { + if (ApplicationManager.getApplication().isUnitTestMode()) { + return factories; + } + + if (!writeAccess) { + return factories; + } + + Collection result = null; + final WorkingCopyFormat presetFormat = SvnWorkingCopyFormatHolder.getPresetFormat(); + if (presetFormat != null) { + result = format2Factories(presetFormat, factories); + } + + if (result == null) { + final WorkingCopyFormat format = SvnFormatSelector.getWorkingCopyFormat(path); + result = format2Factories(format, factories); + } + + if (result == null) { + throw new SVNException(SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY)); + } + return result; + } + + @Nullable + static Collection format2Factories(final WorkingCopyFormat format, final Collection factories) { + if (WorkingCopyFormat.ONE_DOT_SEVEN.equals(format)) { + return factories; + } + else if (WorkingCopyFormat.ONE_DOT_SIX.equals(format)) { + return factoriesFor16(factories); + } + else if (WorkingCopyFormat.ONE_DOT_FIVE.equals(format)) { + return factoriesFor15(factories); + } + else if (WorkingCopyFormat.ONE_DOT_FOUR.equals(format)) { + return factoriesFor14(factories); + } + else if (WorkingCopyFormat.ONE_DOT_THREE.equals(format)) { + return factoriesFor13(factories); + } + return null; + } + + private static Collection factoriesFor13(final Collection factories) { + for (Iterator iterator = factories.iterator(); iterator.hasNext(); ) { + final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)iterator.next(); + final int supportedVersion = factory.getSupportedVersion(); + if (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) { + return Collections.singletonList(factory); + } + } + return Collections.emptyList(); + } + + private static Collection factoriesFor14(final Collection factories) { + final Collection result = new ArrayList(2); + for (Iterator iterator = factories.iterator(); iterator.hasNext(); ) { + final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)iterator.next(); + final int supportedVersion = factory.getSupportedVersion(); + if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || + (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion)) { + result.add(factory); + } + } + return result; + } + + private static Collection factoriesFor15(final Collection factories) { + final Collection result = new ArrayList(2); + for (Iterator iterator = factories.iterator(); iterator.hasNext(); ) { + final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)iterator.next(); + final int supportedVersion = factory.getSupportedVersion(); + if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || + (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) || + (WorkingCopyFormat.ONE_DOT_FIVE.getFormat() == supportedVersion)) { + result.add(factory); + } + } + return result; + } + + private static Collection factoriesFor16(final Collection factories) { + final Collection result = new ArrayList(2); + for (Iterator iterator = factories.iterator(); iterator.hasNext(); ) { + final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)iterator.next(); + final int supportedVersion = factory.getSupportedVersion(); + if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || + (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) || + (WorkingCopyFormat.ONE_DOT_FIVE.getFormat() == supportedVersion) || + (WorkingCopyFormat.ONE_DOT_SIX.getFormat() == supportedVersion)) { + result.add(factory); + } + } + return result; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitManager.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitManager.java index a446b58bb1cb..757100026797 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitManager.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitManager.java @@ -26,7 +26,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnConfiguration; -import org.jetbrains.idea.svn.SvnFormatSelector; import org.jetbrains.idea.svn.SvnHttpAuthMethodsDefaultChecker; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; @@ -72,7 +71,7 @@ public class SvnKitManager { SVNJNAUtil.setJNAEnabled(true); SvnHttpAuthMethodsDefaultChecker.check(); - SVNAdminAreaFactory.setSelector(new SvnFormatSelector()); + SVNAdminAreaFactory.setSelector(new SvnKitAdminAreaFactorySelector()); DAVRepositoryFactory.setup(); SVNRepositoryFactoryImpl.setup(); From e10cc2bcee976cf15ee5209ffda761eede38a329 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Thu, 24 Jul 2014 23:42:19 +0400 Subject: [PATCH 11/85] svn: Refactored SvnKitAdminAreaFactorySelector - warnings fixed --- .../svnkit/SvnKitAdminAreaFactorySelector.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitAdminAreaFactorySelector.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitAdminAreaFactorySelector.java index 164e90b68fae..0d1a4c0106e0 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitAdminAreaFactorySelector.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/svnkit/SvnKitAdminAreaFactorySelector.java @@ -30,7 +30,6 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Iterator; /** * @author Konstantin Kolosovsky. @@ -84,8 +83,8 @@ public class SvnKitAdminAreaFactorySelector implements ISVNAdminAreaFactorySelec } private static Collection factoriesFor13(final Collection factories) { - for (Iterator iterator = factories.iterator(); iterator.hasNext(); ) { - final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)iterator.next(); + for (Object item : factories) { + final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)item; final int supportedVersion = factory.getSupportedVersion(); if (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) { return Collections.singletonList(factory); @@ -96,8 +95,8 @@ public class SvnKitAdminAreaFactorySelector implements ISVNAdminAreaFactorySelec private static Collection factoriesFor14(final Collection factories) { final Collection result = new ArrayList(2); - for (Iterator iterator = factories.iterator(); iterator.hasNext(); ) { - final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)iterator.next(); + for (Object item : factories) { + final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)item; final int supportedVersion = factory.getSupportedVersion(); if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion)) { @@ -109,8 +108,8 @@ public class SvnKitAdminAreaFactorySelector implements ISVNAdminAreaFactorySelec private static Collection factoriesFor15(final Collection factories) { final Collection result = new ArrayList(2); - for (Iterator iterator = factories.iterator(); iterator.hasNext(); ) { - final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)iterator.next(); + for (Object item : factories) { + final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)item; final int supportedVersion = factory.getSupportedVersion(); if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) || @@ -123,8 +122,8 @@ public class SvnKitAdminAreaFactorySelector implements ISVNAdminAreaFactorySelec private static Collection factoriesFor16(final Collection factories) { final Collection result = new ArrayList(2); - for (Iterator iterator = factories.iterator(); iterator.hasNext(); ) { - final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)iterator.next(); + for (Object item : factories) { + final SVNAdminAreaFactory factory = (SVNAdminAreaFactory)item; final int supportedVersion = factory.getSupportedVersion(); if ((WorkingCopyFormat.ONE_DOT_FOUR.getFormat() == supportedVersion) || (WorkingCopyFormat.ONE_DOT_THREE.getFormat() == supportedVersion) || From 5573a7022ba9ad75db020d0c9ec2c5a91da4f020 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Mon, 28 Jul 2014 18:13:57 +0400 Subject: [PATCH 12/85] IDEA-125320 Make single async refresh of svn internal files after status operation finish (for 1.6 and before working copies) - instead of scheduling async refresh on each change found --- .../jetbrains/idea/svn/NestedCopiesBuilder.java | 4 ++++ .../org/jetbrains/idea/svn/StatusReceiver.java | 1 + .../org/jetbrains/idea/svn/SvnChangeProvider.java | 2 ++ .../idea/svn/SvnChangeProviderContext.java | 15 ++++++++++++--- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/NestedCopiesBuilder.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/NestedCopiesBuilder.java index d0eafb77bc4b..062666fbd402 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/NestedCopiesBuilder.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/NestedCopiesBuilder.java @@ -86,6 +86,10 @@ public class NestedCopiesBuilder implements StatusReceiver { } } + @Override + public void finish() { + } + public Set getCopies() { return mySet; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusReceiver.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusReceiver.java index ac165c48f5c3..f6515de0ebf1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusReceiver.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/StatusReceiver.java @@ -29,4 +29,5 @@ public interface StatusReceiver extends EventListener { void processUnversioned(final VirtualFile vFile); void processCopyRoot(VirtualFile file, SVNURL url, WorkingCopyFormat format, SVNURL rootURL); void bewareRoot(VirtualFile vf, SVNURL url); + void finish(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java index 073826f6b035..41461ed623c6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProvider.java @@ -95,6 +95,8 @@ public class SvnChangeProvider implements ChangeProvider { walker.go(item.getDir(), Depth.IMMEDIATES); } + statusReceiver.getMulticaster().finish(); + processCopiedAndDeleted(context, dirtyScope); processUnsaved(dirtyScope, addGate, context); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java index 716abbe1b20b..9ae90c722cb3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java @@ -23,6 +23,7 @@ import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.actions.AbstractShowPropertiesDiffAction; @@ -48,6 +49,7 @@ class SvnChangeProviderContext implements StatusReceiver { private Map myCopyFromURLs = null; private final SvnVcs myVcs; private final SvnBranchConfigurationManager myBranchConfigurationManager; + private final List filesToRefresh = ContainerUtil.newArrayList(); private final ProgressIndicator myProgress; @@ -79,6 +81,11 @@ class SvnChangeProviderContext implements StatusReceiver { public void bewareRoot(VirtualFile vf, SVNURL url) { } + @Override + public void finish() { + LocalFileSystem.getInstance().refreshIoFiles(filesToRefresh, true, false, null); + } + public ChangelistBuilder getBuilder() { return myChangelistBuilder; } @@ -302,7 +309,7 @@ class SvnChangeProviderContext implements StatusReceiver { * * @param filePath the path of a changed file. */ - private static void loadEntriesFile(final FilePath filePath) { + private void loadEntriesFile(final FilePath filePath) { final FilePath parentPath = filePath.getParentPath(); if (parentPath == null) { return; @@ -313,9 +320,11 @@ class SvnChangeProviderContext implements StatusReceiver { } } - private static void refreshDotSvnAndEntries(FilePath filePath) { + private void refreshDotSvnAndEntries(FilePath filePath) { final File svn = new File(filePath.getPath(), SvnUtil.SVN_ADMIN_DIR_NAME); - LocalFileSystem.getInstance().refreshIoFiles(Arrays.asList(svn, new File(svn, SvnUtil.ENTRIES_FILE_NAME)), true, false, null); + + filesToRefresh.add(svn); + filesToRefresh.add(new File(svn, SvnUtil.ENTRIES_FILE_NAME)); } // seems here we can only have a tree conflict; which can be marked on either path (?) From d9596d34550af8e2155c6e2ed889825204459a11 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Mon, 28 Jul 2014 20:22:00 +0400 Subject: [PATCH 13/85] svn: Fixed SvnKitPropertyClient.setProperties() - correctly apply passed properties (instead of anonymous class method argument with same name) --- .../jetbrains/idea/svn/properties/SvnKitPropertyClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java index 4ad3f10afccc..fcd2df17f7a2 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -80,11 +80,12 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien @Override public void setProperties(@NotNull File file, @NotNull SVNProperties properties) throws VcsException { + final SVNProperties propertiesToSet = properties; try { createClient().doSetProperty(file, new ISVNPropertyValueProvider() { @Override public SVNProperties providePropertyValues(File path, SVNProperties properties) throws SVNException { - return properties; + return propertiesToSet; } }, true, SVNDepth.EMPTY, null, null); } From 31e31cde52781fa4cbc0f082f2cee875e0e848a2 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Mon, 28 Jul 2014 20:37:27 +0400 Subject: [PATCH 14/85] svn: Replaced SVNProperties (from SVNKit) with custom PropertiesMap class --- .../svn/properties/CmdPropertyClient.java | 15 +++++------ .../idea/svn/properties/PropertiesMap.java | 25 +++++++++++++++++++ .../idea/svn/properties/PropertyClient.java | 2 +- .../svn/properties/SvnKitPropertyClient.java | 16 ++++++++++-- .../svn/rollback/SvnRollbackEnvironment.java | 13 +++++----- 5 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertiesMap.java diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java index e855abe1ec2e..98ff3111c8c6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlValue; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * @author Konstantin Kolosovsky. @@ -97,24 +98,24 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { } @Override - public void setProperties(@NotNull File file, @NotNull SVNProperties properties) throws VcsException { - SVNProperties currentProperties = collectPropertiesToDelete(file); + public void setProperties(@NotNull File file, @NotNull PropertiesMap properties) throws VcsException { + PropertiesMap currentProperties = collectPropertiesToDelete(file); currentProperties.putAll(properties); - for (String propertyName : currentProperties.nameSet()) { - setProperty(file, propertyName, currentProperties.getSVNPropertyValue(propertyName), Depth.EMPTY, true); + for (Map.Entry entry : currentProperties.entrySet()) { + setProperty(file, entry.getKey(), entry.getValue(), Depth.EMPTY, true); } } @NotNull - private SVNProperties collectPropertiesToDelete(@NotNull File file) throws VcsException { - final SVNProperties result = new SVNProperties(); + private PropertiesMap collectPropertiesToDelete(@NotNull File file) throws VcsException { + final PropertiesMap result = new PropertiesMap(); list(SvnTarget.fromFile(file), null, Depth.EMPTY, new ISVNPropertyHandler() { @Override public void handleProperty(File path, SVNPropertyData property) throws SVNException { // null indicates property will be deleted - result.put(property.getName(), (SVNPropertyValue)null); + result.put(property.getName(), null); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertiesMap.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertiesMap.java new file mode 100644 index 000000000000..76d3f4e7b7a6 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertiesMap.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.svn.properties; + +import com.intellij.util.containers.HashMap; +import org.tmatesoft.svn.core.SVNPropertyValue; + +/** + * @author Konstantin Kolosovsky. + */ +public class PropertiesMap extends HashMap { +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java index 1b61d59c0e20..90c525427e89 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java @@ -52,7 +52,7 @@ public interface PropertyClient extends SvnClient { @Nullable Depth depth, boolean force) throws VcsException; - void setProperties(@NotNull File file, @NotNull SVNProperties properties) throws VcsException; + void setProperties(@NotNull File file, @NotNull PropertiesMap properties) throws VcsException; void setRevisionProperty(@NotNull SvnTarget target, @NotNull String property, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java index fcd2df17f7a2..29a655dd52a3 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -11,6 +11,7 @@ import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; +import java.util.Map; /** * @author Konstantin Kolosovsky. @@ -79,8 +80,8 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien } @Override - public void setProperties(@NotNull File file, @NotNull SVNProperties properties) throws VcsException { - final SVNProperties propertiesToSet = properties; + public void setProperties(@NotNull File file, @NotNull PropertiesMap properties) throws VcsException { + final SVNProperties propertiesToSet = toSvnProperties(properties); try { createClient().doSetProperty(file, new ISVNPropertyValueProvider() { @Override @@ -113,6 +114,17 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien } } + @NotNull + private static SVNProperties toSvnProperties(@NotNull PropertiesMap properties) { + SVNProperties result = new SVNProperties(); + + for (Map.Entry entry : properties.entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + + return result; + } + private void runGetProperty(@NotNull SvnTarget target, @Nullable String property, @Nullable SVNRevision revision, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java index 237d05bd5f7a..896065407c30 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java @@ -34,6 +34,7 @@ import org.jetbrains.idea.svn.api.ProgressEvent; import org.jetbrains.idea.svn.api.ProgressTracker; import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.jetbrains.idea.svn.info.Info; +import org.jetbrains.idea.svn.properties.PropertiesMap; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -100,7 +101,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { }; final List fromToModified = new ArrayList(); - final Map properties = ContainerUtil.newHashMap(); + final Map properties = ContainerUtil.newHashMap(); moveRenamesToTmp(exceptions, fromToModified, properties, collector); // adds (deletes) // deletes (adds) @@ -123,7 +124,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { private void moveRenamesToTmp(List exceptions, List fromToModified, - final Map properties, + final Map properties, final UnversionedAndNotTouchedFilesGroupCollector collector) { final Map fromTo = collector.getFromTo(); try { @@ -134,7 +135,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { final ThroughRenameInfo info = collector.findToFile(new FilePathImpl(path, path.isDirectory()), null); if (info != null) { if (!properties.containsKey(info.getTo())) { - properties.put(info.getTo(), new SVNProperties()); + properties.put(info.getTo(), new PropertiesMap()); } properties.get(info.getTo()).put(property.getName(), property.getValue()); } @@ -181,7 +182,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { private void moveGroup(final List exceptions, List fromTo, - Map properties) { + Map properties) { Collections.sort(fromTo, new Comparator() { @Override public int compare(CopiedAsideInfo o1, CopiedAsideInfo o2) { @@ -240,8 +241,8 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { applyProperties(properties, exceptions); } - private void applyProperties(Map propertiesMap, final List exceptions) { - for (Map.Entry entry : propertiesMap.entrySet()) { + private void applyProperties(Map propertiesMap, final List exceptions) { + for (Map.Entry entry : propertiesMap.entrySet()) { File file = entry.getKey(); try { mySvnVcs.getFactory(file).createPropertyClient().setProperties(file, entry.getValue()); From 58a023cc66e3fc09782eb3e219a81076e5b96355 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Mon, 28 Jul 2014 22:11:18 +0400 Subject: [PATCH 15/85] svn: Replaced SVNPropertyData (from SVNKit) with custom PropertyData class --- .../jetbrains/idea/svn/SvnDiffProvider.java | 3 +- .../idea/svn/SvnEditFileProvider.java | 4 +- .../src/org/jetbrains/idea/svn/SvnVcs.java | 4 +- .../AbstractShowPropertiesDiffAction.java | 30 ++++----- .../svn/actions/CreateExternalAction.java | 3 +- .../idea/svn/actions/SvnMergeProvider.java | 3 +- .../idea/svn/dialogs/PropertiesComponent.java | 14 ++-- .../idea/svn/dialogs/SetPropertyDialog.java | 18 ++--- .../idea/svn/ignore/SvnPropertyService.java | 4 +- .../integrate/GatheringChangelistBuilder.java | 6 +- .../idea/svn/mergeinfo/BranchInfo.java | 6 +- .../OneRecursiveShotMergeInfoWorker.java | 12 ++-- .../svn/properties/CmdPropertyClient.java | 50 +++++++------- .../idea/svn/properties/PropertyClient.java | 26 ++------ .../idea/svn/properties/PropertyConsumer.java | 33 ++++++++++ .../idea/svn/properties/PropertyData.java | 54 +++++++++++++++ .../svn/properties/SvnKitPropertyClient.java | 65 +++++++++++++++---- .../svn/rollback/SvnRollbackEnvironment.java | 10 +-- 18 files changed, 230 insertions(+), 115 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyConsumer.java create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyData.java diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java index 49ea87d44f6e..9880dc2cb349 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java @@ -36,6 +36,7 @@ import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.jetbrains.idea.svn.history.LatestExistentSearcher; import org.jetbrains.idea.svn.info.InfoConsumer; import org.jetbrains.idea.svn.info.Info; +import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.status.Status; import org.jetbrains.idea.svn.status.StatusType; import org.tmatesoft.svn.core.SVNException; @@ -163,7 +164,7 @@ public class SvnDiffProvider extends DiffProviderEx implements DiffProvider, Dif } private String getCommitMessage(File path) throws VcsException { - SVNPropertyData property = + PropertyData property = myVcs.getFactory(path).createPropertyClient().getProperty(SvnTarget.fromFile(path), COMMIT_MESSAGE, true, SVNRevision.BASE); return property != null ? SVNPropertyValue.getPropertyAsString(property.getValue()) : null; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java index 64248892614a..89af16b2a409 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java @@ -19,7 +19,7 @@ import com.intellij.openapi.vcs.EditFileProvider; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.idea.svn.properties.PropertyClient; -import org.tmatesoft.svn.core.wc.SVNPropertyData; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -42,7 +42,7 @@ public class SvnEditFileProvider implements EditFileProvider { ioFiles[i] = new File(files[i].getPath()); PropertyClient client = myVCS.getFactory(ioFiles[i]).createPropertyClient(); - SVNPropertyData property = client.getProperty(SvnTarget.fromFile(ioFiles[i], SVNRevision.WORKING), SvnPropertyKeys.SVN_NEEDS_LOCK, + PropertyData property = client.getProperty(SvnTarget.fromFile(ioFiles[i], SVNRevision.WORKING), SvnPropertyKeys.SVN_NEEDS_LOCK, false, SVNRevision.WORKING); if (property == null || property.getValue() == null) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 5010ac580750..cf9033ed0dbd 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -81,6 +81,7 @@ import org.jetbrains.idea.svn.history.SvnHistoryProvider; import org.jetbrains.idea.svn.info.Info; import org.jetbrains.idea.svn.info.InfoConsumer; import org.jetbrains.idea.svn.properties.PropertyClient; +import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment; import org.jetbrains.idea.svn.status.Status; import org.jetbrains.idea.svn.status.StatusType; @@ -89,7 +90,6 @@ import org.jetbrains.idea.svn.update.SvnIntegrateEnvironment; import org.jetbrains.idea.svn.update.SvnUpdateEnvironment; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.wc.SVNAdminUtil; -import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -582,7 +582,7 @@ public class SvnVcs extends AbstractVcs { } PropertyClient client = getFactory(ioFile).createPropertyClient(); - final SVNPropertyData value = client.getProperty(SvnTarget.fromFile(ioFile, SVNRevision.WORKING), propName, false, SVNRevision.WORKING); + final PropertyData value = client.getProperty(SvnTarget.fromFile(ioFile, SVNRevision.WORKING), propName, false, SVNRevision.WORKING); final SVNPropertyValue propValue = value == null ? null : value.getValue(); if (cachedMap == null) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java index dc846a969f82..bb87e12fff73 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java @@ -42,9 +42,9 @@ import org.jetbrains.idea.svn.SvnRevisionNumber; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision; +import org.jetbrains.idea.svn.properties.PropertyConsumer; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; -import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -273,35 +273,35 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen private static String getPropertyList(@NotNull SvnVcs vcs, @NotNull SvnTarget target, @Nullable SVNRevision revision) throws VcsException { - final List lines = new ArrayList(); - final ISVNPropertyHandler propertyHandler = createHandler(revision, lines); + final List lines = new ArrayList(); + final PropertyConsumer propertyHandler = createHandler(revision, lines); vcs.getFactory(target).createPropertyClient().list(target, revision, Depth.EMPTY, propertyHandler); return toSortedStringPresentation(lines); } - private static ISVNPropertyHandler createHandler(SVNRevision revision, final List lines) { + private static PropertyConsumer createHandler(SVNRevision revision, final List lines) { final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); if (indicator != null) { indicator.checkCanceled(); indicator.setText(SvnBundle.message("show.properties.diff.progress.text.revision.information", revision.toString())); } - return new ISVNPropertyHandler() { - public void handleProperty(final File path, final SVNPropertyData property) throws SVNException { + return new PropertyConsumer() { + public void handleProperty(final File path, final PropertyData property) throws SVNException { registerProperty(property); } - public void handleProperty(final SVNURL url, final SVNPropertyData property) throws SVNException { + public void handleProperty(final SVNURL url, final PropertyData property) throws SVNException { registerProperty(property); } - public void handleProperty(final long revision, final SVNPropertyData property) throws SVNException { + public void handleProperty(final long revision, final PropertyData property) throws SVNException { // revision properties here } - private void registerProperty(@NotNull SVNPropertyData property) { + private void registerProperty(@NotNull PropertyData property) { if (indicator != null) { indicator.checkCanceled(); indicator.setText2(SvnBundle.message("show.properties.diff.progress.text2.property.information", property.getName())); @@ -311,23 +311,23 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen }; } - private static String toSortedStringPresentation(List lines) { + private static String toSortedStringPresentation(List lines) { StringBuilder sb = new StringBuilder(); - Collections.sort(lines, new Comparator() { - public int compare(final SVNPropertyData o1, final SVNPropertyData o2) { + Collections.sort(lines, new Comparator() { + public int compare(final PropertyData o1, final PropertyData o2) { return o1.getName().compareTo(o2.getName()); } }); - for (SVNPropertyData line : lines) { + for (PropertyData line : lines) { addPropertyPresentation(line, sb); } return sb.toString(); } - private static void addPropertyPresentation(final SVNPropertyData property, final StringBuilder sb) { + private static void addPropertyPresentation(final PropertyData property, final StringBuilder sb) { if (sb.length() != 0) { sb.append(ourPropertiesDelimiter); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java index 31206eec045c..98a9dca1deb1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java @@ -42,6 +42,7 @@ import org.jetbrains.idea.svn.api.ProgressEvent; import org.jetbrains.idea.svn.api.ProgressTracker; import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.dialogs.SelectCreateExternalTargetDialog; +import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.update.UpdateClient; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNException; @@ -132,7 +133,7 @@ public class CreateExternalAction extends DumbAwareAction { public static boolean addToExternalProperty(@NotNull SvnVcs vcs, @NotNull File ioFile, String target, String url) throws SVNException, VcsException { ClientFactory factory = vcs.getFactory(ioFile); - SVNPropertyData propertyData = factory.createPropertyClient().getProperty(SvnTarget.fromFile(ioFile), SvnPropertyKeys.SVN_EXTERNALS, + PropertyData propertyData = factory.createPropertyClient().getProperty(SvnTarget.fromFile(ioFile), SvnPropertyKeys.SVN_EXTERNALS, false, SVNRevision.UNDEFINED); String newValue; if (propertyData != null && propertyData.getValue() != null && ! StringUtil.isEmptyOrSpaces(propertyData.getValue().getString())) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java index 912bd970f7b0..8a13aaa1e5d1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java @@ -32,6 +32,7 @@ import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.info.Info; import org.jetbrains.idea.svn.properties.PropertyClient; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.wc.*; @@ -162,7 +163,7 @@ public class SvnMergeProvider implements MergeProvider { File ioFile = new File(file.getPath()); PropertyClient client = vcs.getFactory(ioFile).createPropertyClient(); - SVNPropertyData svnPropertyData = client.getProperty(SvnTarget.fromFile(ioFile), SVNProperty.MIME_TYPE, false, SVNRevision.WORKING); + PropertyData svnPropertyData = client.getProperty(SvnTarget.fromFile(ioFile), SVNProperty.MIME_TYPE, false, SVNRevision.WORKING); if (svnPropertyData != null && SVNProperty.isBinaryMimeType(SVNPropertyValue.getPropertyAsString(svnPropertyData.getValue()))) { return true; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java index 860c524ed591..b0a6188cf536 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java @@ -37,9 +37,9 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; +import org.jetbrains.idea.svn.properties.PropertyConsumer; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; -import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -153,18 +153,18 @@ public class PropertiesComponent extends JPanel { private void collectProperties(@NotNull SvnVcs vcs, @NotNull File file, @NotNull final Map props) { try { - ISVNPropertyHandler handler = new ISVNPropertyHandler() { - public void handleProperty(File path, SVNPropertyData property) throws SVNException { + PropertyConsumer handler = new PropertyConsumer() { + public void handleProperty(File path, PropertyData property) throws SVNException { final SVNPropertyValue value = property.getValue(); if (value != null) { props.put(property.getName(), SVNPropertyValue.getPropertyAsString(property.getValue())); } } - public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { + public void handleProperty(SVNURL url, PropertyData property) throws SVNException { } - public void handleProperty(long revision, SVNPropertyData property) throws SVNException { + public void handleProperty(long revision, PropertyData property) throws SVNException { } }; vcs.getFactory(file).createPropertyClient().list(SvnTarget.fromFile(file, SVNRevision.UNDEFINED), SVNRevision.WORKING, Depth.EMPTY, @@ -302,7 +302,7 @@ public class PropertiesComponent extends JPanel { public void actionPerformed(AnActionEvent e) { Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); - SVNPropertyData propValue = null; + PropertyData propValue = null; try { propValue = myVcs.getFactory(myFile).createPropertyClient() .getProperty(SvnTarget.fromFile(myFile), SVNProperty.KEYWORDS, false, SVNRevision.WORKING); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java index 28375d28b7ba..698bc8507981 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java @@ -29,10 +29,10 @@ import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.properties.PropertyClient; +import org.jetbrains.idea.svn.properties.PropertyConsumer; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; -import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -154,7 +154,7 @@ public class SetPropertyDialog extends DialogWrapper { return; } File file = myFiles[0]; - SVNPropertyData property = !StringUtil.isEmpty(name) ? getProperty(file, name) : null; + PropertyData property = !StringUtil.isEmpty(name) ? getProperty(file, name) : null; if (property != null) { myValueText.setText(SVNPropertyValue.getPropertyAsString(property.getValue())); @@ -165,8 +165,8 @@ public class SetPropertyDialog extends DialogWrapper { } } - private SVNPropertyData getProperty(@NotNull File file, @NotNull String name) { - SVNPropertyData property; + private PropertyData getProperty(@NotNull File file, @NotNull String name) { + PropertyData property; try { PropertyClient client = myVCS.getFactory(file).createPropertyClient(); @@ -205,18 +205,18 @@ public class SetPropertyDialog extends DialogWrapper { if (files.length == 1) { File file = files[0]; try { - ISVNPropertyHandler handler = new ISVNPropertyHandler() { - public void handleProperty(File path, SVNPropertyData property) { + PropertyConsumer handler = new PropertyConsumer() { + public void handleProperty(File path, PropertyData property) { String name = property.getName(); if (name != null) { names.add(name); } } - public void handleProperty(SVNURL url, SVNPropertyData property) { + public void handleProperty(SVNURL url, PropertyData property) { } - public void handleProperty(long revision, SVNPropertyData property) { + public void handleProperty(long revision, PropertyData property) { } }; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java index 8a9dbf760922..d37f8b6ae7f6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java @@ -24,8 +24,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.SVNPropertyValue; -import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -96,7 +96,7 @@ public class SvnPropertyService { if (myCanUseCachedProperty) { value = myVcs.getPropertyWithCaching(entry.getKey(), SvnPropertyKeys.SVN_IGNORE); } else { - final SVNPropertyData data = + final PropertyData data = myVcs.getFactory(dir).createPropertyClient() .getProperty(SvnTarget.fromFile(dir), SvnPropertyKeys.SVN_IGNORE, false, SVNRevision.WORKING); value = data == null ? null : data.getValue(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/GatheringChangelistBuilder.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/GatheringChangelistBuilder.java index b6228fca921d..054f27daca58 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/GatheringChangelistBuilder.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/GatheringChangelistBuilder.java @@ -28,7 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; -import org.tmatesoft.svn.core.wc.SVNPropertyData; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -92,9 +92,9 @@ public class GatheringChangelistBuilder extends EmptyChangelistBuilder { SvnTarget target = SvnTarget.fromFile(file); try { - SVNPropertyData current = + PropertyData current = myVcs.getFactory(target).createPropertyClient().getProperty(target, SvnPropertyKeys.MERGE_INFO, false, SVNRevision.WORKING); - SVNPropertyData base = + PropertyData base = myVcs.getFactory(target).createPropertyClient().getProperty(target, SvnPropertyKeys.MERGE_INFO, false, SVNRevision.BASE); if (current != null) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java index 62638291b485..476e67222da8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java @@ -22,10 +22,10 @@ import com.intellij.util.containers.MultiMap; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.info.Info; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; -import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -205,7 +205,7 @@ public class BranchInfo { return SvnMergeInfoCache.MergeCheckResult.getInstance(mergeInfo.contains(revisionAsked)); } - final SVNPropertyData mergeinfoProperty; + final PropertyData mergeinfoProperty; SvnTarget target = SvnTarget.fromURL(branchUrl); try { @@ -277,7 +277,7 @@ public class BranchInfo { return SvnMergeInfoCache.MergeCheckResult.getInstance(merged); } - final SVNPropertyData mergeinfoProperty; + final PropertyData mergeinfoProperty; try { if (actualRevision == targetRevisionCorrected) { // look in WC diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java index 0ea95c99c4d4..8b8197f30697 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java @@ -23,11 +23,11 @@ import com.intellij.util.PairProcessor; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.dialogs.MergeContext; +import org.jetbrains.idea.svn.properties.PropertyConsumer; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; -import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; -import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -63,8 +63,8 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker { public void prepare() throws VcsException { final Depth depth = Depth.allOrEmpty(myMergeContext.getVcs().getSvnConfiguration().isCheckNestedForQuickMerge()); - ISVNPropertyHandler handler = new ISVNPropertyHandler() { - public void handleProperty(File path, SVNPropertyData property) throws SVNException { + PropertyConsumer handler = new PropertyConsumer() { + public void handleProperty(File path, PropertyData property) throws SVNException { final String key = keyFromFile(path); synchronized (myLock) { myDataMap.put(key, SVNMergeInfoUtil @@ -72,10 +72,10 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker { } } - public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { + public void handleProperty(SVNURL url, PropertyData property) throws SVNException { } - public void handleProperty(long revision, SVNPropertyData property) throws SVNException { + public void handleProperty(long revision, PropertyData property) throws SVNException { } }; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java index 98ff3111c8c6..e15657ad7e4a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -10,9 +10,9 @@ import org.jetbrains.idea.svn.commandLine.CommandExecutor; import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.commandLine.SvnCommandName; import org.jetbrains.idea.svn.info.Info; -import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; -import org.tmatesoft.svn.core.wc.SVNPropertyData; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNPropertyValue; +import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -33,10 +33,10 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { @Nullable @Override - public SVNPropertyData getProperty(@NotNull SvnTarget target, - @NotNull String property, - boolean revisionProperty, - @Nullable SVNRevision revision) + public PropertyData getProperty(@NotNull SvnTarget target, + @NotNull String property, + boolean revisionProperty, + @Nullable SVNRevision revision) throws VcsException { List parameters = new ArrayList(); @@ -66,7 +66,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { @NotNull String property, @Nullable SVNRevision revision, @Nullable Depth depth, - @Nullable ISVNPropertyHandler handler) throws VcsException { + @Nullable PropertyConsumer handler) throws VcsException { List parameters = new ArrayList(); parameters.add(property); @@ -80,7 +80,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { public void list(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable Depth depth, - @Nullable ISVNPropertyHandler handler) throws VcsException { + @Nullable PropertyConsumer handler) throws VcsException { List parameters = new ArrayList(); fillListParameters(target, revision, depth, parameters, true); @@ -111,19 +111,19 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { private PropertiesMap collectPropertiesToDelete(@NotNull File file) throws VcsException { final PropertiesMap result = new PropertiesMap(); - list(SvnTarget.fromFile(file), null, Depth.EMPTY, new ISVNPropertyHandler() { + list(SvnTarget.fromFile(file), null, Depth.EMPTY, new PropertyConsumer() { @Override - public void handleProperty(File path, SVNPropertyData property) throws SVNException { + public void handleProperty(File path, PropertyData property) throws SVNException { // null indicates property will be deleted result.put(property.getName(), null); } @Override - public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { + public void handleProperty(SVNURL url, PropertyData property) throws SVNException { } @Override - public void handleProperty(long revision, SVNPropertyData property) throws SVNException { + public void handleProperty(long revision, PropertyData property) throws SVNException { } }); @@ -181,21 +181,21 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { CommandUtil.put(parameters, verbose, "--verbose"); } - private SVNPropertyData parseSingleProperty(SvnTarget target, String output) throws VcsException { - final SVNPropertyData[] data = new SVNPropertyData[1]; - ISVNPropertyHandler handler = new ISVNPropertyHandler() { + private PropertyData parseSingleProperty(SvnTarget target, String output) throws VcsException { + final PropertyData[] data = new PropertyData[1]; + PropertyConsumer handler = new PropertyConsumer() { @Override - public void handleProperty(File path, SVNPropertyData property) throws SVNException { + public void handleProperty(File path, PropertyData property) throws SVNException { data[0] = property; } @Override - public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { + public void handleProperty(SVNURL url, PropertyData property) throws SVNException { data[0] = property; } @Override - public void handleProperty(long revision, SVNPropertyData property) throws SVNException { + public void handleProperty(long revision, PropertyData property) throws SVNException { data[0] = property; } }; @@ -205,7 +205,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { return data[0]; } - private static void parseOutput(SvnTarget target, String output, ISVNPropertyHandler handler) throws VcsException { + private static void parseOutput(SvnTarget target, String output, PropertyConsumer handler) throws VcsException { try { Properties properties = CommandUtil.parse(output, Properties.class); @@ -232,7 +232,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { } } - private static void invokeHandler(@NotNull SvnTarget target, @Nullable SVNPropertyData data, @Nullable ISVNPropertyHandler handler) + private static void invokeHandler(@NotNull SvnTarget target, @Nullable PropertyData data, @Nullable PropertyConsumer handler) throws SVNException { if (handler != null && data != null) { if (target.isFile()) { @@ -243,7 +243,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { } } - private static void invokeHandler(long revision, @Nullable SVNPropertyData data, @Nullable ISVNPropertyHandler handler) + private static void invokeHandler(long revision, @Nullable PropertyData data, @Nullable PropertyConsumer handler) throws SVNException { if (handler != null && data != null) { handler.handleProperty(revision, data); @@ -251,13 +251,13 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { } @Nullable - private static SVNPropertyData create(@NotNull String property, @Nullable String value) { - SVNPropertyData result = null; + private static PropertyData create(@NotNull String property, @Nullable String value) { + PropertyData result = null; // such behavior is required to compatibility with SVNKit as some logic in merge depends on // whether null property data or property data with empty string value is returned if (value != null) { - result = new SVNPropertyData(property, SVNPropertyValue.create(value.trim()), LF_SEPARATOR_OPTIONS); + result = new PropertyData(property, SVNPropertyValue.create(value.trim())); } return result; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java index 90c525427e89..0f874a565e56 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java @@ -1,18 +1,11 @@ package org.jetbrains.idea.svn.properties; import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vfs.CharsetToolkit; -import com.intellij.util.LineSeparator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.api.SvnClient; -import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.SVNPropertyValue; -import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; -import org.tmatesoft.svn.core.wc.ISVNOptions; -import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; -import org.tmatesoft.svn.core.wc.SVNPropertyData; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -23,28 +16,21 @@ import java.io.File; */ public interface PropertyClient extends SvnClient { - ISVNOptions LF_SEPARATOR_OPTIONS = new DefaultSVNOptions() { - @Override - public byte[] getNativeEOL() { - return CharsetToolkit.getUtf8Bytes(LineSeparator.LF.getSeparatorString()); - } - }; - @Nullable - SVNPropertyData getProperty(@NotNull final SvnTarget target, - @NotNull final String property, - boolean revisionProperty, - @Nullable SVNRevision revision) throws VcsException; + PropertyData getProperty(@NotNull final SvnTarget target, + @NotNull final String property, + boolean revisionProperty, + @Nullable SVNRevision revision) throws VcsException; void getProperty(@NotNull SvnTarget target, @NotNull String property, @Nullable SVNRevision revision, @Nullable Depth depth, - @Nullable ISVNPropertyHandler handler) throws VcsException; + @Nullable PropertyConsumer handler) throws VcsException; void list(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable Depth depth, - @Nullable ISVNPropertyHandler handler) throws VcsException; + @Nullable PropertyConsumer handler) throws VcsException; void setProperty(@NotNull File file, @NotNull String property, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyConsumer.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyConsumer.java new file mode 100644 index 000000000000..f45e09df39d5 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyConsumer.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.svn.properties; + +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; + +import java.io.File; + +/** + * @author Konstantin Kolosovsky. + */ +public interface PropertyConsumer { + + void handleProperty(File path, PropertyData property) throws SVNException; + + void handleProperty(SVNURL url, PropertyData property) throws SVNException; + + void handleProperty(long revision, PropertyData property) throws SVNException; +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyData.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyData.java new file mode 100644 index 000000000000..aeed27589d93 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyData.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.svn.properties; + +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNPropertyValue; +import org.tmatesoft.svn.core.wc.SVNPropertyData; + +/** + * @author Konstantin Kolosovsky. + */ +public class PropertyData { + + private final SVNPropertyValue myValue; + + private final String myName; + + public PropertyData(String name, SVNPropertyValue value) { + myName = name; + myValue = value; + } + + public String getName() { + return myName; + } + + public SVNPropertyValue getValue() { + return myValue; + } + + @Nullable + public static PropertyData create(@Nullable SVNPropertyData data) { + PropertyData result = null; + + if (data != null) { + result = new PropertyData(data.getName(), data.getValue()); + } + + return result; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java index 29a655dd52a3..102dcfce6768 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -1,12 +1,15 @@ package org.jetbrains.idea.svn.properties; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.util.LineSeparator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -18,18 +21,25 @@ import java.util.Map; */ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClient { + public static final ISVNOptions LF_SEPARATOR_OPTIONS = new DefaultSVNOptions() { + @Override + public byte[] getNativeEOL() { + return CharsetToolkit.getUtf8Bytes(LineSeparator.LF.getSeparatorString()); + } + }; + @Nullable @Override - public SVNPropertyData getProperty(@NotNull SvnTarget target, - @NotNull String property, - boolean revisionProperty, - @Nullable SVNRevision revision) throws VcsException { + public PropertyData getProperty(@NotNull SvnTarget target, + @NotNull String property, + boolean revisionProperty, + @Nullable SVNRevision revision) throws VcsException { try { if (!revisionProperty) { if (target.isFile()) { - return createClient().doGetProperty(target.getFile(), property, target.getPegRevision(), revision); + return PropertyData.create(createClient().doGetProperty(target.getFile(), property, target.getPegRevision(), revision)); } else { - return createClient().doGetProperty(target.getURL(), property, target.getPegRevision(), revision); + return PropertyData.create(createClient().doGetProperty(target.getURL(), property, target.getPegRevision(), revision)); } } else { return getRevisionProperty(target, property, revision); @@ -53,7 +63,7 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien @NotNull String property, @Nullable SVNRevision revision, @Nullable Depth depth, - @Nullable ISVNPropertyHandler handler) throws VcsException { + @Nullable PropertyConsumer handler) throws VcsException { runGetProperty(target, property, revision, depth, handler); } @@ -61,7 +71,7 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien public void list(@NotNull SvnTarget target, @Nullable SVNRevision revision, @Nullable Depth depth, - @Nullable ISVNPropertyHandler handler) throws VcsException { + @Nullable PropertyConsumer handler) throws VcsException { runGetProperty(target, null, revision, depth, handler); } @@ -129,23 +139,24 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien @Nullable String property, @Nullable SVNRevision revision, @Nullable Depth depth, - @Nullable ISVNPropertyHandler handler) throws VcsException { + @Nullable PropertyConsumer handler) throws VcsException { SVNWCClient client = createClient(); try { if (target.isURL()) { - client.doGetProperty(target.getURL(), property, target.getPegRevision(), revision, toDepth(depth), handler); + client.doGetProperty(target.getURL(), property, target.getPegRevision(), revision, toDepth(depth), toHandler(handler)); } else { - client.doGetProperty(target.getFile(), property, target.getPegRevision(), revision, toDepth(depth), handler, null); + client.doGetProperty(target.getFile(), property, target.getPegRevision(), revision, toDepth(depth), toHandler(handler), null); } } catch (SVNException e) { throw new VcsException(e); } } - private SVNPropertyData getRevisionProperty(@NotNull SvnTarget target, @NotNull final String property, @Nullable SVNRevision revision) throws SVNException{ + private PropertyData getRevisionProperty(@NotNull SvnTarget target, @NotNull final String property, @Nullable SVNRevision revision) + throws SVNException { final SVNWCClient client = createClient(); - final SVNPropertyData[] result = new SVNPropertyData[1]; + final PropertyData[] result = new PropertyData[1]; ISVNPropertyHandler handler = new ISVNPropertyHandler() { @Override public void handleProperty(File path, SVNPropertyData property) throws SVNException { @@ -164,7 +175,7 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien private void handle(@NotNull SVNPropertyData data) { if (property.equals(data.getName())) { - result[0] = data; + result[0] = PropertyData.create(data); } } }; @@ -177,4 +188,30 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien return result[0]; } + + @Nullable + private static ISVNPropertyHandler toHandler(@Nullable final PropertyConsumer consumer) { + ISVNPropertyHandler result = null; + + if (consumer != null) { + result = new ISVNPropertyHandler() { + @Override + public void handleProperty(File path, SVNPropertyData property) throws SVNException { + consumer.handleProperty(path, PropertyData.create(property)); + } + + @Override + public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { + consumer.handleProperty(url, PropertyData.create(property)); + } + + @Override + public void handleProperty(long revision, SVNPropertyData property) throws SVNException { + consumer.handleProperty(revision, PropertyData.create(property)); + } + }; + } + + return result; + } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java index 896065407c30..7b09461b3766 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java @@ -35,6 +35,8 @@ import org.jetbrains.idea.svn.api.ProgressTracker; import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.jetbrains.idea.svn.info.Info; import org.jetbrains.idea.svn.properties.PropertiesMap; +import org.jetbrains.idea.svn.properties.PropertyConsumer; +import org.jetbrains.idea.svn.properties.PropertyData; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -129,9 +131,9 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { final Map fromTo = collector.getFromTo(); try { final File tmp = FileUtil.createTempDirectory("forRename", ""); - final ISVNPropertyHandler handler = new ISVNPropertyHandler() { + final PropertyConsumer handler = new PropertyConsumer() { @Override - public void handleProperty(File path, SVNPropertyData property) throws SVNException { + public void handleProperty(File path, PropertyData property) throws SVNException { final ThroughRenameInfo info = collector.findToFile(new FilePathImpl(path, path.isDirectory()), null); if (info != null) { if (!properties.containsKey(info.getTo())) { @@ -142,11 +144,11 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { } @Override - public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { + public void handleProperty(SVNURL url, PropertyData property) throws SVNException { } @Override - public void handleProperty(long revision, SVNPropertyData property) throws SVNException { + public void handleProperty(long revision, PropertyData property) throws SVNException { } }; From 04026103737a4d00770287735640edb7d5770699 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 30 Jul 2014 15:38:30 +0400 Subject: [PATCH 16/85] svn: Replaced SVNPropertyValue (from SVNKit) with custom PropertyValue class --- .../jetbrains/idea/svn/SvnDiffProvider.java | 4 +- .../src/org/jetbrains/idea/svn/SvnVcs.java | 15 +++--- .../AbstractShowPropertiesDiffAction.java | 3 +- .../svn/actions/CreateExternalAction.java | 11 ++-- .../idea/svn/actions/SetPropertyAction.java | 4 +- .../idea/svn/actions/SvnMergeProvider.java | 4 +- .../idea/svn/dialogs/PropertiesComponent.java | 15 +++--- .../idea/svn/dialogs/SetPropertyDialog.java | 4 +- .../history/SvnEditCommitMessageAction.java | 4 +- .../idea/svn/ignore/SvnPropertyService.java | 24 ++++----- .../idea/svn/mergeinfo/BranchInfo.java | 5 +- .../OneRecursiveShotMergeInfoWorker.java | 3 +- .../svn/properties/CmdPropertyClient.java | 13 +++-- .../idea/svn/properties/PropertiesMap.java | 3 +- .../idea/svn/properties/PropertyClient.java | 5 +- .../idea/svn/properties/PropertyData.java | 9 ++-- .../idea/svn/properties/PropertyValue.java | 53 +++++++++++++++++++ .../svn/properties/SvnKitPropertyClient.java | 25 ++++++--- 18 files changed, 135 insertions(+), 69 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyValue.java diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java index 9880dc2cb349..c37582bbd6b8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java @@ -37,10 +37,10 @@ import org.jetbrains.idea.svn.history.LatestExistentSearcher; import org.jetbrains.idea.svn.info.InfoConsumer; import org.jetbrains.idea.svn.info.Info; import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.jetbrains.idea.svn.status.Status; import org.jetbrains.idea.svn.status.StatusType; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -167,7 +167,7 @@ public class SvnDiffProvider extends DiffProviderEx implements DiffProvider, Dif PropertyData property = myVcs.getFactory(path).createPropertyClient().getProperty(SvnTarget.fromFile(path), COMMIT_MESSAGE, true, SVNRevision.BASE); - return property != null ? SVNPropertyValue.getPropertyAsString(property.getValue()) : null; + return property != null ? PropertyValue.toString(property.getValue()) : null; } private static ItemLatestState defaultResult() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index cf9033ed0dbd..b295433c4c9b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -82,6 +82,7 @@ import org.jetbrains.idea.svn.info.Info; import org.jetbrains.idea.svn.info.InfoConsumer; import org.jetbrains.idea.svn.properties.PropertyClient; import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.jetbrains.idea.svn.rollback.SvnRollbackEnvironment; import org.jetbrains.idea.svn.status.Status; import org.jetbrains.idea.svn.status.StatusType; @@ -108,8 +109,8 @@ public class SvnVcs extends AbstractVcs { private static final VcsKey ourKey = createKey(VCS_NAME); public static final Topic WC_CONVERTED = new Topic("WC_CONVERTED", Runnable.class); - private final Map>>> myPropertyCache = - new SoftHashMap>>>(); + private final Map>>> myPropertyCache = + new SoftHashMap>>>(); private final SvnConfiguration myConfiguration; private final SvnEntriesFileListener myEntriesFileListener; @@ -567,9 +568,9 @@ public class SvnVcs extends AbstractVcs { } @Nullable - public SVNPropertyValue getPropertyWithCaching(final VirtualFile file, final String propName) throws VcsException { - Map>> cachedMap = myPropertyCache.get(keyForVf(file)); - final Pair> cachedValue = cachedMap == null ? null : cachedMap.get(propName); + public PropertyValue getPropertyWithCaching(final VirtualFile file, final String propName) throws VcsException { + Map>> cachedMap = myPropertyCache.get(keyForVf(file)); + final Pair> cachedValue = cachedMap == null ? null : cachedMap.get(propName); final File ioFile = new File(file.getPath()); final Trinity tsTrinity = getTimestampForPropertiesChange(ioFile, file.isDirectory()); @@ -583,10 +584,10 @@ public class SvnVcs extends AbstractVcs { PropertyClient client = getFactory(ioFile).createPropertyClient(); final PropertyData value = client.getProperty(SvnTarget.fromFile(ioFile, SVNRevision.WORKING), propName, false, SVNRevision.WORKING); - final SVNPropertyValue propValue = value == null ? null : value.getValue(); + final PropertyValue propValue = value == null ? null : value.getValue(); if (cachedMap == null) { - cachedMap = new HashMap>>(); + cachedMap = new HashMap>>(); myPropertyCache.put(keyForVf(file), cachedMap); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java index bb87e12fff73..ff6bd5ee00e5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java @@ -44,6 +44,7 @@ import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision; import org.jetbrains.idea.svn.properties.PropertyConsumer; import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -331,6 +332,6 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen if (sb.length() != 0) { sb.append(ourPropertiesDelimiter); } - sb.append(property.getName()).append("=").append((property.getValue() == null) ? "" : SVNPropertyValue.getPropertyAsString(property.getValue())); + sb.append(property.getName()).append("=").append((property.getValue() == null) ? "" : PropertyValue.toString(property.getValue())); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java index 98a9dca1deb1..0e238587c543 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java @@ -43,10 +43,10 @@ import org.jetbrains.idea.svn.api.ProgressTracker; import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.dialogs.SelectCreateExternalTargetDialog; import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.jetbrains.idea.svn.update.UpdateClient; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.internal.wc.SVNExternal; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -136,8 +136,8 @@ public class CreateExternalAction extends DumbAwareAction { PropertyData propertyData = factory.createPropertyClient().getProperty(SvnTarget.fromFile(ioFile), SvnPropertyKeys.SVN_EXTERNALS, false, SVNRevision.UNDEFINED); String newValue; - if (propertyData != null && propertyData.getValue() != null && ! StringUtil.isEmptyOrSpaces(propertyData.getValue().getString())) { - final SVNExternal[] externals = SVNExternal.parseExternals("Create External", propertyData.getValue().getString()); + if (propertyData != null && propertyData.getValue() != null && ! StringUtil.isEmptyOrSpaces(propertyData.getValue().toString())) { + final SVNExternal[] externals = SVNExternal.parseExternals("Create External", propertyData.getValue().toString()); for (SVNExternal external : externals) { if (Comparing.equal(external.getPath(), target)) { AbstractVcsHelper @@ -146,12 +146,11 @@ public class CreateExternalAction extends DumbAwareAction { } } final String string = createExternalDefinitionString(url, target); - newValue = propertyData.getValue().getString().trim() + "\n" + string; + newValue = propertyData.getValue().toString().trim() + "\n" + string; } else { newValue = createExternalDefinitionString(url, target); } - factory.createPropertyClient().setProperty(ioFile, SvnPropertyKeys.SVN_EXTERNALS, SVNPropertyValue.create(newValue), Depth.EMPTY, - false); + factory.createPropertyClient().setProperty(ioFile, SvnPropertyKeys.SVN_EXTERNALS, PropertyValue.create(newValue), Depth.EMPTY, false); return false; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SetPropertyAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SetPropertyAction.java index cdc738cf2d40..5e53cc956e92 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SetPropertyAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SetPropertyAction.java @@ -30,7 +30,7 @@ import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.dialogs.SetPropertyDialog; import org.jetbrains.idea.svn.properties.PropertyClient; -import org.tmatesoft.svn.core.SVNPropertyValue; +import org.jetbrains.idea.svn.properties.PropertyValue; import java.io.File; @@ -79,7 +79,7 @@ public class SetPropertyAction extends BasicAction { // TODO: most likely SVNDepth.getInfinityOrEmptyDepth should be used instead of SVNDepth.fromRecursive - to have either "infinity" // TODO: or "empty" depth, and not "infinity" or "files" depth. But previous logic used SVNDepth.fromRecursive implicitly - client.setProperty(ioFile, name, SVNPropertyValue.create(value), Depth.allOrFiles(recursive), false); + client.setProperty(ioFile, name, PropertyValue.create(value), Depth.allOrFiles(recursive), false); } for(int i = 0; i < file.length; i++) { if (recursive && file[i].isDirectory()) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java index 8a13aaa1e5d1..bf00d03755c4 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java @@ -33,8 +33,8 @@ import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.info.Info; import org.jetbrains.idea.svn.properties.PropertyClient; import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.SVNProperty; -import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -164,7 +164,7 @@ public class SvnMergeProvider implements MergeProvider { PropertyClient client = vcs.getFactory(ioFile).createPropertyClient(); PropertyData svnPropertyData = client.getProperty(SvnTarget.fromFile(ioFile), SVNProperty.MIME_TYPE, false, SVNRevision.WORKING); - if (svnPropertyData != null && SVNProperty.isBinaryMimeType(SVNPropertyValue.getPropertyAsString(svnPropertyData.getValue()))) { + if (svnPropertyData != null && SVNProperty.isBinaryMimeType(PropertyValue.toString(svnPropertyData.getValue()))) { return true; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java index b0a6188cf536..4ea8032a707a 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java @@ -39,7 +39,10 @@ import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.properties.PropertyConsumer; import org.jetbrains.idea.svn.properties.PropertyData; -import org.tmatesoft.svn.core.*; +import org.jetbrains.idea.svn.properties.PropertyValue; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNProperty; +import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -155,9 +158,9 @@ public class PropertiesComponent extends JPanel { try { PropertyConsumer handler = new PropertyConsumer() { public void handleProperty(File path, PropertyData property) throws SVNException { - final SVNPropertyValue value = property.getValue(); + final PropertyValue value = property.getValue(); if (value != null) { - props.put(property.getName(), SVNPropertyValue.getPropertyAsString(property.getValue())); + props.put(property.getName(), PropertyValue.toString(property.getValue())); } } @@ -272,7 +275,7 @@ public class PropertiesComponent extends JPanel { if (!StringUtil.isEmpty(property)) { try { myVcs.getFactory(myFile).createPropertyClient() - .setProperty(myFile, property, value != null ? SVNPropertyValue.create(value) : null, + .setProperty(myFile, property, value != null ? PropertyValue.create(value) : null, Depth.allOrEmpty(recursive), force); } catch (VcsException error) { @@ -311,8 +314,8 @@ public class PropertiesComponent extends JPanel { // show erorr message } - SetKeywordsDialog dialog = new SetKeywordsDialog(project, - propValue != null ? SVNPropertyValue.getPropertyAsString(propValue.getValue()) : null); + SetKeywordsDialog dialog = + new SetKeywordsDialog(project, propValue != null ? PropertyValue.toString(propValue.getValue()) : null); dialog.show(); if (dialog.isOK()) { setProperty(SvnPropertyKeys.SVN_KEYWORDS, dialog.getKeywords(), false, false); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java index 698bc8507981..95608f410d01 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java @@ -31,7 +31,7 @@ import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.properties.PropertyClient; import org.jetbrains.idea.svn.properties.PropertyConsumer; import org.jetbrains.idea.svn.properties.PropertyData; -import org.tmatesoft.svn.core.SVNPropertyValue; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -157,7 +157,7 @@ public class SetPropertyDialog extends DialogWrapper { PropertyData property = !StringUtil.isEmpty(name) ? getProperty(file, name) : null; if (property != null) { - myValueText.setText(SVNPropertyValue.getPropertyAsString(property.getValue())); + myValueText.setText(PropertyValue.toString(property.getValue())); myValueText.selectAll(); } else { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java index d2b820607ff4..8d1830763fc6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnEditCommitMessageAction.java @@ -40,8 +40,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnUtil; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -145,7 +145,7 @@ public class SvnEditCommitMessageAction extends AnAction { } SvnTarget target = SvnTarget.fromURL(root); myVcs.getFactory(target).createPropertyClient() - .setRevisionProperty(target, SvnPropertyKeys.LOG, SVNRevision.create(myNumber), SVNPropertyValue.create(myNewMessage), false); + .setRevisionProperty(target, SvnPropertyKeys.LOG, SVNRevision.create(myNumber), PropertyValue.create(myNewMessage), false); } catch (SVNException e) { myException = new VcsException(e); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java index d37f8b6ae7f6..4d7b54fb68ee 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java @@ -25,7 +25,7 @@ import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.properties.PropertyData; -import org.tmatesoft.svn.core.SVNPropertyValue; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -68,7 +68,7 @@ public class SvnPropertyService { protected final boolean myCanUseCachedProperty; protected abstract void processFolder(final VirtualFile folder, final File folderDir, final Set data, - final SVNPropertyValue propertyValue) throws VcsException; + final PropertyValue propertyValue) throws VcsException; protected abstract void onAfterProcessing(final VirtualFile[] file) throws VcsException; @@ -92,7 +92,7 @@ public class SvnPropertyService { } final File dir = new File(entry.getKey().getPath()); try { - final SVNPropertyValue value; + final PropertyValue value; if (myCanUseCachedProperty) { value = myVcs.getPropertyWithCaching(entry.getKey(), SvnPropertyKeys.SVN_IGNORE); } else { @@ -127,7 +127,7 @@ public class SvnPropertyService { return (! myFilesOk) && (! myExtensionOk); } - protected void processFolder(final VirtualFile folder, final File folderDir, final Set data, final SVNPropertyValue propertyValue) + protected void processFolder(final VirtualFile folder, final File folderDir, final Set data, final PropertyValue propertyValue) throws VcsException { if (propertyValue == null) { myFilesOk = false; @@ -135,7 +135,7 @@ public class SvnPropertyService { return; } final Set ignorePatterns = new HashSet(); - final StringTokenizer st = new StringTokenizer(SVNPropertyValue.getPropertyAsString(propertyValue), "\r\n "); + final StringTokenizer st = new StringTokenizer(PropertyValue.toString(propertyValue), "\r\n "); while (st.hasMoreElements()) { final String ignorePattern = (String)st.nextElement(); ignorePatterns.add(ignorePattern); @@ -180,14 +180,14 @@ public class SvnPropertyService { return false; } - protected abstract String getNewPropertyValue(final Set data, final SVNPropertyValue propertyValue); + protected abstract String getNewPropertyValue(final Set data, final PropertyValue propertyValue); - protected void processFolder(final VirtualFile folder, final File folderDir, final Set data, final SVNPropertyValue propertyValue) + protected void processFolder(final VirtualFile folder, final File folderDir, final Set data, final PropertyValue propertyValue) throws VcsException { String newValue = getNewPropertyValue(data, propertyValue); newValue = (newValue.trim().isEmpty()) ? null : newValue; myVcs.getFactory(folderDir).createPropertyClient() - .setProperty(folderDir, SvnPropertyKeys.SVN_IGNORE, SVNPropertyValue.create(newValue), Depth.EMPTY, false); + .setProperty(folderDir, SvnPropertyKeys.SVN_IGNORE, PropertyValue.create(newValue), Depth.EMPTY, false); if (myUseCommonExtension) { dirtyScopeManager.dirDirtyRecursively(folder); @@ -216,9 +216,9 @@ public class SvnPropertyService { super(activeVcs, project, useCommonExtension); } - protected String getNewPropertyValue(final Set data, final SVNPropertyValue propertyValue) { + protected String getNewPropertyValue(final Set data, final PropertyValue propertyValue) { if (propertyValue != null) { - return getNewPropertyValueForRemove(data, SVNPropertyValue.getPropertyAsString(propertyValue)); + return getNewPropertyValueForRemove(data, PropertyValue.toString(propertyValue)); } return ""; } @@ -241,7 +241,7 @@ public class SvnPropertyService { super(activeVcs, project, useCommonExtension); } - protected String getNewPropertyValue(final Set data, final SVNPropertyValue propertyValue) { + protected String getNewPropertyValue(final Set data, final PropertyValue propertyValue) { final String ignoreString; if (data.size() == 1) { ignoreString = data.iterator().next(); @@ -252,7 +252,7 @@ public class SvnPropertyService { } ignoreString = sb.toString(); } - return (propertyValue == null) ? ignoreString : (SVNPropertyValue.getPropertyAsString(propertyValue) + '\n' + ignoreString); + return (propertyValue == null) ? ignoreString : (PropertyValue.toString(propertyValue) + '\n' + ignoreString); } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java index 476e67222da8..e4222331d0fb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java @@ -23,6 +23,7 @@ import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.info.Info; import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; @@ -306,7 +307,7 @@ public class BranchInfo { } private SvnMergeInfoCache.MergeCheckResult processMergeinfoProperty(final String pathWithRevisionNumber, final long revisionAsked, - final SVNPropertyValue value, final String trunkRelativeUrl, + final PropertyValue value, final String trunkRelativeUrl, final boolean self) { final String valueAsString = value.toString().trim(); @@ -318,7 +319,7 @@ public class BranchInfo { final Map map; try { - map = SVNMergeInfoUtil.parseMergeInfo(new StringBuffer(replaceSeparators(value.getString())), null); + map = SVNMergeInfoUtil.parseMergeInfo(new StringBuffer(replaceSeparators(value.toString())), null); } catch (SVNException e) { LOG.info(e); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java index 8b8197f30697..225bc6ad0396 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/OneRecursiveShotMergeInfoWorker.java @@ -25,6 +25,7 @@ import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.dialogs.MergeContext; import org.jetbrains.idea.svn.properties.PropertyConsumer; import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; @@ -68,7 +69,7 @@ public class OneRecursiveShotMergeInfoWorker implements MergeInfoWorker { final String key = keyFromFile(path); synchronized (myLock) { myDataMap.put(key, SVNMergeInfoUtil - .parseMergeInfo(new StringBuffer(replaceSeparators(SVNPropertyValue.getPropertyAsString(property.getValue()))), null)); + .parseMergeInfo(new StringBuffer(replaceSeparators(PropertyValue.toString(property.getValue()))), null)); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java index e15657ad7e4a..d78febd578dc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -11,7 +11,6 @@ import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.commandLine.SvnCommandName; import org.jetbrains.idea.svn.info.Info; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -91,7 +90,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { @Override public void setProperty(@NotNull File file, @NotNull String property, - @Nullable SVNPropertyValue value, + @Nullable PropertyValue value, @Nullable Depth depth, boolean force) throws VcsException { runSetProperty(SvnTarget.fromFile(file), property, null, depth, value, force); @@ -102,7 +101,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { PropertiesMap currentProperties = collectPropertiesToDelete(file); currentProperties.putAll(properties); - for (Map.Entry entry : currentProperties.entrySet()) { + for (Map.Entry entry : currentProperties.entrySet()) { setProperty(file, entry.getKey(), entry.getValue(), Depth.EMPTY, true); } } @@ -134,7 +133,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { public void setRevisionProperty(@NotNull SvnTarget target, @NotNull String property, @NotNull SVNRevision revision, - @Nullable SVNPropertyValue value, + @Nullable PropertyValue value, boolean force) throws VcsException { runSetProperty(target, property, revision, null, value, force); } @@ -143,7 +142,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { @NotNull String property, @Nullable SVNRevision revision, @Nullable Depth depth, - @Nullable SVNPropertyValue value, + @Nullable PropertyValue value, boolean force) throws VcsException { List parameters = new ArrayList(); boolean isDelete = value == null; @@ -154,7 +153,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { CommandUtil.put(parameters, revision); } if (!isDelete) { - parameters.add(SVNPropertyValue.getPropertyAsString(value)); + parameters.add(PropertyValue.toString(value)); // --force could only be used in "propset" command, but not in "propdel" command CommandUtil.put(parameters, force, "--force"); } @@ -257,7 +256,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { // such behavior is required to compatibility with SVNKit as some logic in merge depends on // whether null property data or property data with empty string value is returned if (value != null) { - result = new PropertyData(property, SVNPropertyValue.create(value.trim())); + result = new PropertyData(property, PropertyValue.create(value.trim())); } return result; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertiesMap.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertiesMap.java index 76d3f4e7b7a6..c5857aa4b345 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertiesMap.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertiesMap.java @@ -16,10 +16,9 @@ package org.jetbrains.idea.svn.properties; import com.intellij.util.containers.HashMap; -import org.tmatesoft.svn.core.SVNPropertyValue; /** * @author Konstantin Kolosovsky. */ -public class PropertiesMap extends HashMap { +public class PropertiesMap extends HashMap { } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java index 0f874a565e56..4ca1329abed5 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java @@ -5,7 +5,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.api.SvnClient; -import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -34,7 +33,7 @@ public interface PropertyClient extends SvnClient { void setProperty(@NotNull File file, @NotNull String property, - @Nullable SVNPropertyValue value, + @Nullable PropertyValue value, @Nullable Depth depth, boolean force) throws VcsException; @@ -43,6 +42,6 @@ public interface PropertyClient extends SvnClient { void setRevisionProperty(@NotNull SvnTarget target, @NotNull String property, @NotNull SVNRevision revision, - @Nullable SVNPropertyValue value, + @Nullable PropertyValue value, boolean force) throws VcsException; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyData.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyData.java index aeed27589d93..3953636d870c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyData.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyData.java @@ -16,7 +16,6 @@ package org.jetbrains.idea.svn.properties; import org.jetbrains.annotations.Nullable; -import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.wc.SVNPropertyData; /** @@ -24,11 +23,11 @@ import org.tmatesoft.svn.core.wc.SVNPropertyData; */ public class PropertyData { - private final SVNPropertyValue myValue; + private final PropertyValue myValue; private final String myName; - public PropertyData(String name, SVNPropertyValue value) { + public PropertyData(String name, PropertyValue value) { myName = name; myValue = value; } @@ -37,7 +36,7 @@ public class PropertyData { return myName; } - public SVNPropertyValue getValue() { + public PropertyValue getValue() { return myValue; } @@ -46,7 +45,7 @@ public class PropertyData { PropertyData result = null; if (data != null) { - result = new PropertyData(data.getName(), data.getValue()); + result = new PropertyData(data.getName(), PropertyValue.create(data.getValue())); } return result; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyValue.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyValue.java new file mode 100644 index 000000000000..62cb7cbd0340 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyValue.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.svn.properties; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.tmatesoft.svn.core.SVNPropertyValue; + +/** + * TODO: Add correct support of binary properties - support in api, diff, etc. + * + * @author Konstantin Kolosovsky. + */ +public class PropertyValue { + + @NotNull private final String myValue; + + @Nullable + public static PropertyValue create(@Nullable SVNPropertyValue value) { + return create(SVNPropertyValue.getPropertyAsString(value)); + } + + private PropertyValue(@NotNull String propertyValue) { + myValue = propertyValue; + } + + @Nullable + public static PropertyValue create(@Nullable String propertyValue) { + return propertyValue == null ? null : new PropertyValue(propertyValue); + } + + @Nullable + public static String toString(@Nullable PropertyValue value) { + return value == null ? null : value.myValue; + } + + public String toString() { + return myValue; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java index 102dcfce6768..97a70520cae9 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -78,11 +78,11 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien @Override public void setProperty(@NotNull File file, @NotNull String property, - @Nullable SVNPropertyValue value, + @Nullable PropertyValue value, @Nullable Depth depth, boolean force) throws VcsException { try { - createClient().doSetProperty(file, property, value, force, toDepth(depth), null, null); + createClient().doSetProperty(file, property, toPropertyValue(value), force, toDepth(depth), null, null); } catch (SVNException e) { throw new SvnBindException(e); @@ -109,14 +109,14 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien public void setRevisionProperty(@NotNull SvnTarget target, @NotNull String property, @NotNull SVNRevision revision, - @Nullable SVNPropertyValue value, + @Nullable PropertyValue value, boolean force) throws VcsException { try { if (target.isFile()) { - createClient().doSetRevisionProperty(target.getFile(), revision, property, value, force, null); + createClient().doSetRevisionProperty(target.getFile(), revision, property, toPropertyValue(value), force, null); } else { - createClient().doSetRevisionProperty(target.getURL(), revision, property, value, force, null); + createClient().doSetRevisionProperty(target.getURL(), revision, property, toPropertyValue(value), force, null); } } catch (SVNException e) { @@ -128,8 +128,8 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien private static SVNProperties toSvnProperties(@NotNull PropertiesMap properties) { SVNProperties result = new SVNProperties(); - for (Map.Entry entry : properties.entrySet()) { - result.put(entry.getKey(), entry.getValue()); + for (Map.Entry entry : properties.entrySet()) { + result.put(entry.getKey(), toPropertyValue(entry.getValue())); } return result; @@ -214,4 +214,15 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien return result; } + + @Nullable + private static SVNPropertyValue toPropertyValue(@Nullable PropertyValue value) { + SVNPropertyValue result = null; + + if (value != null) { + result = SVNPropertyValue.create(value.toString()); + } + + return result; + } } From 7f2d0956325924496cbd1b8566a135a37798c4af Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 30 Jul 2014 16:13:28 +0400 Subject: [PATCH 17/85] svn: Refactored PropertyClient.getProperty() - make return PropertyValue instance (instead of PropertyData) --- .../jetbrains/idea/svn/SvnDiffProvider.java | 10 +++++----- .../idea/svn/SvnEditFileProvider.java | 6 +++--- .../src/org/jetbrains/idea/svn/SvnVcs.java | 7 +++---- .../idea/svn/actions/CreateExternalAction.java | 11 +++++------ .../idea/svn/actions/SvnMergeProvider.java | 7 +++---- .../idea/svn/dialogs/PropertiesComponent.java | 5 ++--- .../idea/svn/dialogs/SetPropertyDialog.java | 16 +++++++++------- .../idea/svn/ignore/SvnPropertyService.java | 7 ++----- .../integrate/GatheringChangelistBuilder.java | 17 +++++++++++------ .../idea/svn/mergeinfo/BranchInfo.java | 9 ++++----- .../idea/svn/properties/CmdPropertyClient.java | 13 ++++++++----- .../idea/svn/properties/PropertyClient.java | 8 ++++---- .../svn/properties/SvnKitPropertyClient.java | 18 +++++++++++------- 13 files changed, 70 insertions(+), 64 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java index c37582bbd6b8..5f5edd12eea7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnDiffProvider.java @@ -34,15 +34,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.jetbrains.idea.svn.history.LatestExistentSearcher; -import org.jetbrains.idea.svn.info.InfoConsumer; import org.jetbrains.idea.svn.info.Info; -import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.info.InfoConsumer; import org.jetbrains.idea.svn.properties.PropertyValue; import org.jetbrains.idea.svn.status.Status; import org.jetbrains.idea.svn.status.StatusType; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; @@ -163,11 +162,12 @@ public class SvnDiffProvider extends DiffProviderEx implements DiffProvider, Dif } } + @Nullable private String getCommitMessage(File path) throws VcsException { - PropertyData property = + PropertyValue property = myVcs.getFactory(path).createPropertyClient().getProperty(SvnTarget.fromFile(path), COMMIT_MESSAGE, true, SVNRevision.BASE); - return property != null ? PropertyValue.toString(property.getValue()) : null; + return PropertyValue.toString(property); } private static ItemLatestState defaultResult() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java index 89af16b2a409..c8b47aa0928e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnEditFileProvider.java @@ -19,7 +19,7 @@ import com.intellij.openapi.vcs.EditFileProvider; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.idea.svn.properties.PropertyClient; -import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -42,10 +42,10 @@ public class SvnEditFileProvider implements EditFileProvider { ioFiles[i] = new File(files[i].getPath()); PropertyClient client = myVCS.getFactory(ioFiles[i]).createPropertyClient(); - PropertyData property = client.getProperty(SvnTarget.fromFile(ioFiles[i], SVNRevision.WORKING), SvnPropertyKeys.SVN_NEEDS_LOCK, + PropertyValue property = client.getProperty(SvnTarget.fromFile(ioFiles[i], SVNRevision.WORKING), SvnPropertyKeys.SVN_NEEDS_LOCK, false, SVNRevision.WORKING); - if (property == null || property.getValue() == null) { + if (property == null) { throw new VcsException(SvnBundle.message("exception.text.file.miss.svn", ioFiles[i].getName())); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index b295433c4c9b..b44fd16f979f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -583,17 +583,16 @@ public class SvnVcs extends AbstractVcs { } PropertyClient client = getFactory(ioFile).createPropertyClient(); - final PropertyData value = client.getProperty(SvnTarget.fromFile(ioFile, SVNRevision.WORKING), propName, false, SVNRevision.WORKING); - final PropertyValue propValue = value == null ? null : value.getValue(); + final PropertyValue value = client.getProperty(SvnTarget.fromFile(ioFile, SVNRevision.WORKING), propName, false, SVNRevision.WORKING); if (cachedMap == null) { cachedMap = new HashMap>>(); myPropertyCache.put(keyForVf(file), cachedMap); } - cachedMap.put(propName, Pair.create(propValue, tsTrinity)); + cachedMap.put(propName, Pair.create(value, tsTrinity)); - return propValue; + return value; } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java index 0e238587c543..4ee5d79d662c 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java @@ -42,13 +42,12 @@ import org.jetbrains.idea.svn.api.ProgressEvent; import org.jetbrains.idea.svn.api.ProgressTracker; import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.dialogs.SelectCreateExternalTargetDialog; -import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.properties.PropertyValue; import org.jetbrains.idea.svn.update.UpdateClient; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.wc.SVNExternal; -import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; @@ -133,11 +132,11 @@ public class CreateExternalAction extends DumbAwareAction { public static boolean addToExternalProperty(@NotNull SvnVcs vcs, @NotNull File ioFile, String target, String url) throws SVNException, VcsException { ClientFactory factory = vcs.getFactory(ioFile); - PropertyData propertyData = factory.createPropertyClient().getProperty(SvnTarget.fromFile(ioFile), SvnPropertyKeys.SVN_EXTERNALS, + PropertyValue propertyValue = factory.createPropertyClient().getProperty(SvnTarget.fromFile(ioFile), SvnPropertyKeys.SVN_EXTERNALS, false, SVNRevision.UNDEFINED); String newValue; - if (propertyData != null && propertyData.getValue() != null && ! StringUtil.isEmptyOrSpaces(propertyData.getValue().toString())) { - final SVNExternal[] externals = SVNExternal.parseExternals("Create External", propertyData.getValue().toString()); + if (propertyValue != null && !StringUtil.isEmptyOrSpaces(propertyValue.toString())) { + final SVNExternal[] externals = SVNExternal.parseExternals("Create External", propertyValue.toString()); for (SVNExternal external : externals) { if (Comparing.equal(external.getPath(), target)) { AbstractVcsHelper @@ -146,7 +145,7 @@ public class CreateExternalAction extends DumbAwareAction { } } final String string = createExternalDefinitionString(url, target); - newValue = propertyData.getValue().toString().trim() + "\n" + string; + newValue = propertyValue.toString().trim() + "\n" + string; } else { newValue = createExternalDefinitionString(url, target); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java index bf00d03755c4..ddd9bd572f0e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/SvnMergeProvider.java @@ -32,10 +32,9 @@ import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.info.Info; import org.jetbrains.idea.svn.properties.PropertyClient; -import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.SVNProperty; -import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.ByteArrayOutputStream; @@ -163,8 +162,8 @@ public class SvnMergeProvider implements MergeProvider { File ioFile = new File(file.getPath()); PropertyClient client = vcs.getFactory(ioFile).createPropertyClient(); - PropertyData svnPropertyData = client.getProperty(SvnTarget.fromFile(ioFile), SVNProperty.MIME_TYPE, false, SVNRevision.WORKING); - if (svnPropertyData != null && SVNProperty.isBinaryMimeType(PropertyValue.toString(svnPropertyData.getValue()))) { + PropertyValue value = client.getProperty(SvnTarget.fromFile(ioFile), SVNProperty.MIME_TYPE, false, SVNRevision.WORKING); + if (value != null && SVNProperty.isBinaryMimeType(value.toString())) { return true; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java index 4ea8032a707a..16ee29c0a6fd 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java @@ -305,7 +305,7 @@ public class PropertiesComponent extends JPanel { public void actionPerformed(AnActionEvent e) { Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); - PropertyData propValue = null; + PropertyValue propValue = null; try { propValue = myVcs.getFactory(myFile).createPropertyClient() .getProperty(SvnTarget.fromFile(myFile), SVNProperty.KEYWORDS, false, SVNRevision.WORKING); @@ -314,8 +314,7 @@ public class PropertiesComponent extends JPanel { // show erorr message } - SetKeywordsDialog dialog = - new SetKeywordsDialog(project, propValue != null ? PropertyValue.toString(propValue.getValue()) : null); + SetKeywordsDialog dialog = new SetKeywordsDialog(project, PropertyValue.toString(propValue)); dialog.show(); if (dialog.isOK()) { setProperty(SvnPropertyKeys.SVN_KEYWORDS, dialog.getKeywords(), false, false); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java index 95608f410d01..f2a58a0bd2da 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SetPropertyDialog.java @@ -24,6 +24,7 @@ import com.intellij.openapi.vcs.VcsException; import com.intellij.ui.DocumentAdapter; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; @@ -154,10 +155,10 @@ public class SetPropertyDialog extends DialogWrapper { return; } File file = myFiles[0]; - PropertyData property = !StringUtil.isEmpty(name) ? getProperty(file, name) : null; + PropertyValue property = !StringUtil.isEmpty(name) ? getProperty(file, name) : null; if (property != null) { - myValueText.setText(PropertyValue.toString(property.getValue())); + myValueText.setText(property.toString()); myValueText.selectAll(); } else { @@ -165,19 +166,20 @@ public class SetPropertyDialog extends DialogWrapper { } } - private PropertyData getProperty(@NotNull File file, @NotNull String name) { - PropertyData property; + @Nullable + private PropertyValue getProperty(@NotNull File file, @NotNull String name) { + PropertyValue result; try { PropertyClient client = myVCS.getFactory(file).createPropertyClient(); - property = client.getProperty(SvnTarget.fromFile(file, SVNRevision.WORKING), name, false, SVNRevision.WORKING); + result = client.getProperty(SvnTarget.fromFile(file, SVNRevision.WORKING), name, false, SVNRevision.WORKING); } catch (VcsException e) { LOG.info(e); - property = null; + result = null; } - return property; + return result; } protected JComponent createCenterPanel() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java index 4d7b54fb68ee..e0bdc15f78b1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/ignore/SvnPropertyService.java @@ -24,7 +24,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; -import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -96,10 +95,8 @@ public class SvnPropertyService { if (myCanUseCachedProperty) { value = myVcs.getPropertyWithCaching(entry.getKey(), SvnPropertyKeys.SVN_IGNORE); } else { - final PropertyData data = - myVcs.getFactory(dir).createPropertyClient() - .getProperty(SvnTarget.fromFile(dir), SvnPropertyKeys.SVN_IGNORE, false, SVNRevision.WORKING); - value = data == null ? null : data.getValue(); + value = myVcs.getFactory(dir).createPropertyClient() + .getProperty(SvnTarget.fromFile(dir), SvnPropertyKeys.SVN_IGNORE, false, SVNRevision.WORKING); } processFolder(entry.getKey(), dir, entry.getValue(), value); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/GatheringChangelistBuilder.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/GatheringChangelistBuilder.java index 054f27daca58..1e031e20d408 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/GatheringChangelistBuilder.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/GatheringChangelistBuilder.java @@ -20,7 +20,10 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsKey; -import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeList; +import com.intellij.openapi.vcs.changes.ChangesUtil; +import com.intellij.openapi.vcs.changes.EmptyChangelistBuilder; import com.intellij.openapi.vcs.update.UpdatedFilesReverseSide; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; @@ -28,12 +31,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnPropertyKeys; import org.jetbrains.idea.svn.SvnVcs; -import org.jetbrains.idea.svn.properties.PropertyData; +import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; -import java.util.*; +import java.util.Iterator; +import java.util.List; +import java.util.Set; public class GatheringChangelistBuilder extends EmptyChangelistBuilder { @@ -92,13 +97,13 @@ public class GatheringChangelistBuilder extends EmptyChangelistBuilder { SvnTarget target = SvnTarget.fromFile(file); try { - PropertyData current = + PropertyValue current = myVcs.getFactory(target).createPropertyClient().getProperty(target, SvnPropertyKeys.MERGE_INFO, false, SVNRevision.WORKING); - PropertyData base = + PropertyValue base = myVcs.getFactory(target).createPropertyClient().getProperty(target, SvnPropertyKeys.MERGE_INFO, false, SVNRevision.BASE); if (current != null) { - return base == null || !Comparing.equal(current.getValue(), base.getValue()); + return base == null || !Comparing.equal(current, base); } } catch (VcsException e) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java index e4222331d0fb..701bb2575001 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/BranchInfo.java @@ -22,7 +22,6 @@ import com.intellij.util.containers.MultiMap; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.info.Info; -import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.properties.PropertyValue; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil; @@ -206,7 +205,7 @@ public class BranchInfo { return SvnMergeInfoCache.MergeCheckResult.getInstance(mergeInfo.contains(revisionAsked)); } - final PropertyData mergeinfoProperty; + final PropertyValue mergeinfoProperty; SvnTarget target = SvnTarget.fromURL(branchUrl); try { @@ -236,7 +235,7 @@ public class BranchInfo { return goUpInRepo(revisionAsked, targetRevision, newBranchUrl, newTrunkUrl); } // process - return processMergeinfoProperty(keyString, revisionAsked, mergeinfoProperty.getValue(), trunkUrl, false); + return processMergeinfoProperty(keyString, revisionAsked, mergeinfoProperty, trunkUrl, false); } private Info getInfo(final File pathFile) { @@ -278,7 +277,7 @@ public class BranchInfo { return SvnMergeInfoCache.MergeCheckResult.getInstance(merged); } - final PropertyData mergeinfoProperty; + final PropertyValue mergeinfoProperty; try { if (actualRevision == targetRevisionCorrected) { // look in WC @@ -303,7 +302,7 @@ public class BranchInfo { return goUp(revisionAsked, targetRevisionCorrected, branchRootPath, path, trunkUrl); } // process - return processMergeinfoProperty(keyString, revisionAsked, mergeinfoProperty.getValue(), trunkUrl, self); + return processMergeinfoProperty(keyString, revisionAsked, mergeinfoProperty, trunkUrl, self); } private SvnMergeInfoCache.MergeCheckResult processMergeinfoProperty(final String pathWithRevisionNumber, final long revisionAsked, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java index d78febd578dc..e053776d14ce 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -32,10 +32,10 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { @Nullable @Override - public PropertyData getProperty(@NotNull SvnTarget target, - @NotNull String property, - boolean revisionProperty, - @Nullable SVNRevision revision) + public PropertyValue getProperty(@NotNull SvnTarget target, + @NotNull String property, + boolean revisionProperty, + @Nullable SVNRevision revision) throws VcsException { List parameters = new ArrayList(); @@ -57,7 +57,9 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { parameters.add("--xml"); CommandExecutor command = execute(myVcs, target, SvnCommandName.propget, parameters, null); - return parseSingleProperty(target, command.getOutput()); + PropertyData data = parseSingleProperty(target, command.getOutput()); + + return data != null ? data.getValue() : null; } @Override @@ -180,6 +182,7 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { CommandUtil.put(parameters, verbose, "--verbose"); } + @Nullable private PropertyData parseSingleProperty(SvnTarget target, String output) throws VcsException { final PropertyData[] data = new PropertyData[1]; PropertyConsumer handler = new PropertyConsumer() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java index 4ca1329abed5..42acfc8109d7 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java @@ -16,10 +16,10 @@ import java.io.File; public interface PropertyClient extends SvnClient { @Nullable - PropertyData getProperty(@NotNull final SvnTarget target, - @NotNull final String property, - boolean revisionProperty, - @Nullable SVNRevision revision) throws VcsException; + PropertyValue getProperty(@NotNull final SvnTarget target, + @NotNull final String property, + boolean revisionProperty, + @Nullable SVNRevision revision) throws VcsException; void getProperty(@NotNull SvnTarget target, @NotNull String property, @Nullable SVNRevision revision, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java index 97a70520cae9..23fedd3c7ac8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -30,24 +30,28 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien @Nullable @Override - public PropertyData getProperty(@NotNull SvnTarget target, - @NotNull String property, - boolean revisionProperty, - @Nullable SVNRevision revision) throws VcsException { + public PropertyValue getProperty(@NotNull SvnTarget target, + @NotNull String property, + boolean revisionProperty, + @Nullable SVNRevision revision) throws VcsException { + PropertyData resultData; + try { if (!revisionProperty) { if (target.isFile()) { - return PropertyData.create(createClient().doGetProperty(target.getFile(), property, target.getPegRevision(), revision)); + resultData = PropertyData.create(createClient().doGetProperty(target.getFile(), property, target.getPegRevision(), revision)); } else { - return PropertyData.create(createClient().doGetProperty(target.getURL(), property, target.getPegRevision(), revision)); + resultData = PropertyData.create(createClient().doGetProperty(target.getURL(), property, target.getPegRevision(), revision)); } } else { - return getRevisionProperty(target, property, revision); + resultData = getRevisionProperty(target, property, revision); } } catch (SVNException e) { throw new VcsException(e); } + + return resultData != null ? resultData.getValue() : null; } @NotNull From f924793483f5aa773df1939b224fdee2371bfb2a Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 30 Jul 2014 17:49:46 +0400 Subject: [PATCH 18/85] svn: Refactored AbstractShowPropertiesDiffAction - @NotNull, removed unused code, code simplified, warnings fixed --- .../AbstractShowPropertiesDiffAction.java | 71 ++++++++----------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java index ff6bd5ee00e5..395dc36b7371 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/AbstractShowPropertiesDiffAction.java @@ -19,12 +19,15 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diff.DiffManager; import com.intellij.openapi.diff.SimpleContent; import com.intellij.openapi.diff.SimpleDiffRequest; +import com.intellij.openapi.progress.PerformInBackgroundOption; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsDataKeys; @@ -34,18 +37,20 @@ import com.intellij.openapi.vcs.changes.ChangesUtil; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.MarkerVcsContentRevision; import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NonNls; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; -import org.jetbrains.idea.svn.SvnRevisionNumber; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.history.SvnRepositoryContentRevision; import org.jetbrains.idea.svn.properties.PropertyConsumer; import org.jetbrains.idea.svn.properties.PropertyData; import org.jetbrains.idea.svn.properties.PropertyValue; -import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.SVNErrorCode; +import org.tmatesoft.svn.core.SVNErrorMessage; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -70,8 +75,6 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen @Override public void update(final AnActionEvent e) { final DataContext dataContext = e.getDataContext(); - final Project project = CommonDataKeys.PROJECT.getData(dataContext); - final Presentation presentation = e.getPresentation(); final Change[] data = VcsDataKeys.CHANGES.getData(dataContext); boolean showAction = checkThatChangesAreUnderSvn(data); @@ -79,38 +82,23 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen presentation.setEnabled(showAction); } - private boolean checkThatChangesAreUnderSvn(Change[] data) { - boolean showAction = false; - if (data != null) { - for (Change change : data) { - final ContentRevision before = change.getBeforeRevision(); - if (before != null) { - showAction = showAction || before instanceof MarkerVcsContentRevision && SvnVcs.getKey().equals(((MarkerVcsContentRevision)before).getVcsKey()); + private static boolean checkThatChangesAreUnderSvn(@Nullable Change[] changes) { + boolean result = false; + + if (changes != null) { + result = ContainerUtil.or(changes, new Condition() { + @Override + public boolean value(Change change) { + return isUnderSvn(change.getBeforeRevision()) || isUnderSvn(change.getAfterRevision()); } - final ContentRevision after = change.getAfterRevision(); - if (after != null) { - showAction = showAction || after instanceof MarkerVcsContentRevision && SvnVcs.getKey().equals(((MarkerVcsContentRevision)after).getVcsKey()); - } - if (showAction) break; - } + }); } - return showAction; + + return result; } - private boolean enabled(final Project project, final Change[] changes) { - final boolean noChange = (project == null) || (changes == null) || (changes.length != 1); - if (noChange) { - return false; - } else { - final Change change = changes[0]; - - final ContentRevision revision = (change.getBeforeRevision() != null) ? change.getBeforeRevision() : change.getAfterRevision(); - if ((revision == null) || (! (revision.getRevisionNumber() instanceof SvnRevisionNumber))) { - return false; - } - - return checkVcs(project, change); - } + private static boolean isUnderSvn(@Nullable ContentRevision revision) { + return revision instanceof MarkerVcsContentRevision && SvnVcs.getKey().equals(((MarkerVcsContentRevision)revision).getVcsKey()); } protected boolean checkVcs(final Project project, final Change change) { @@ -147,7 +135,7 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen private final String myErrorTitle; private CalculateAndShow(@Nullable final Project project, final Change change, final String errorTitle) { - super(project, SvnBundle.message("fetching.properties.contents.progress.title"), true, Backgroundable.DEAF); + super(project, SvnBundle.message("fetching.properties.contents.progress.title"), true, PerformInBackgroundOption.DEAF); myChange = change; myErrorTitle = errorTitle; } @@ -195,7 +183,8 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen } } - private String getDiffWindowTitle(final Change change) { + @NotNull + private static String getDiffWindowTitle(@NotNull Change change) { if (change.isMoved() || change.isRenamed()) { final FilePath beforeFilePath = ChangesUtil.getBeforePath(change); final FilePath afterFilePath = ChangesUtil.getAfterPath(change); @@ -208,7 +197,7 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen } } - private int compareRevisions(@NonNls final SVNRevision revision1, @NonNls final SVNRevision revision2) { + private static int compareRevisions(@NotNull SVNRevision revision1, @NotNull SVNRevision revision2) { if (revision1.equals(revision2)) { return 0; } @@ -228,11 +217,9 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen return revision1.getNumber() > revision2.getNumber() ? 1 : -1; } - private String revisionToString(final SVNRevision revision) { - if (revision == null) { - return "not exists"; - } - return revision.toString(); + @NotNull + private static String revisionToString(@Nullable SVNRevision revision) { + return revision == null ? "not exists" : revision.toString(); } private final static String ourPropertiesDelimiter = "\n"; @@ -332,6 +319,6 @@ public abstract class AbstractShowPropertiesDiffAction extends AnAction implemen if (sb.length() != 0) { sb.append(ourPropertiesDelimiter); } - sb.append(property.getName()).append("=").append((property.getValue() == null) ? "" : PropertyValue.toString(property.getValue())); + sb.append(property.getName()).append("=").append(StringUtil.notNullize(PropertyValue.toString(property.getValue()))); } } From 17bec1aeae1680301f0d0f35b99ef7da2a61c67a Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Wed, 30 Jul 2014 18:21:03 +0400 Subject: [PATCH 19/85] svn: Refactored PropertiesComponent - code simplified, warnings fixed --- .../idea/svn/dialogs/PropertiesComponent.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java index 16ee29c0a6fd..cd504be56077 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/PropertiesComponent.java @@ -154,7 +154,7 @@ public class PropertiesComponent extends JPanel { } } - private void collectProperties(@NotNull SvnVcs vcs, @NotNull File file, @NotNull final Map props) { + private static void collectProperties(@NotNull SvnVcs vcs, @NotNull File file, @NotNull final Map props) { try { PropertyConsumer handler = new PropertyConsumer() { public void handleProperty(File path, PropertyData property) throws SVNException { @@ -232,10 +232,12 @@ public class PropertiesComponent extends JPanel { String url = "file://" + myFile.getPath().replace(File.separatorChar, '/'); VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url); if (file != null) { + VcsDirtyScopeManager dirtyScopeManager = VcsDirtyScopeManager.getInstance(myVcs.getProject()); + if (recursive && file.isDirectory()) { - VcsDirtyScopeManager.getInstance(myVcs.getProject()).dirDirtyRecursively(file, true); + dirtyScopeManager.dirDirtyRecursively(file); } else { - VcsDirtyScopeManager.getInstance(myVcs.getProject()).fileDirty(file); + dirtyScopeManager.fileDirty(file); } } } @@ -275,8 +277,7 @@ public class PropertiesComponent extends JPanel { if (!StringUtil.isEmpty(property)) { try { myVcs.getFactory(myFile).createPropertyClient() - .setProperty(myFile, property, value != null ? PropertyValue.create(value) : null, - Depth.allOrEmpty(recursive), force); + .setProperty(myFile, property, PropertyValue.create(value), Depth.allOrEmpty(recursive), force); } catch (VcsException error) { VcsBalloonProblemNotifier From 579e11484498dff30e573d3c4a58f5adce8100dc Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Thu, 31 Jul 2014 12:05:17 +0400 Subject: [PATCH 20/85] svn: Implemented custom "svn:externals" parsing logic and removed SVNExternal (from SVNKit) usages --- .../svn/actions/CreateExternalAction.java | 18 +-- .../properties/ExternalsDefinitionParser.java | 106 ++++++++++++++++++ .../svn/ExternalsDefinitionParserTest.java | 62 ++++++++++ 3 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/properties/ExternalsDefinitionParser.java create mode 100644 plugins/svn4idea/testSource/org/jetbrains/idea/svn/ExternalsDefinitionParserTest.java diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java index 4ee5d79d662c..d6d9c3152dde 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/actions/CreateExternalAction.java @@ -24,7 +24,6 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; @@ -42,15 +41,16 @@ import org.jetbrains.idea.svn.api.ProgressEvent; import org.jetbrains.idea.svn.api.ProgressTracker; import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.dialogs.SelectCreateExternalTargetDialog; +import org.jetbrains.idea.svn.properties.ExternalsDefinitionParser; import org.jetbrains.idea.svn.properties.PropertyValue; import org.jetbrains.idea.svn.update.UpdateClient; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.internal.wc.SVNExternal; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; +import java.util.Map; /** * Created with IntelliJ IDEA. @@ -136,13 +136,13 @@ public class CreateExternalAction extends DumbAwareAction { false, SVNRevision.UNDEFINED); String newValue; if (propertyValue != null && !StringUtil.isEmptyOrSpaces(propertyValue.toString())) { - final SVNExternal[] externals = SVNExternal.parseExternals("Create External", propertyValue.toString()); - for (SVNExternal external : externals) { - if (Comparing.equal(external.getPath(), target)) { - AbstractVcsHelper - .getInstance(vcs.getProject()).showError(new VcsException("Selected destination conflicts with existing: " + external.toString()), "Create External"); - return true; - } + Map externalsMap = ExternalsDefinitionParser.parseExternalsProperty(propertyValue.toString()); + String externalsForTarget = externalsMap.get(target); + + if (externalsForTarget != null) { + AbstractVcsHelper.getInstance(vcs.getProject()).showError( + new VcsException("Selected destination conflicts with existing: " + externalsForTarget), "Create External"); + return true; } final String string = createExternalDefinitionString(url, target); newValue = propertyValue.toString().trim() + "\n" + string; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/ExternalsDefinitionParser.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/ExternalsDefinitionParser.java new file mode 100644 index 000000000000..b3ec74e397cb --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/ExternalsDefinitionParser.java @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.svn.properties; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.commandLine.SvnBindException; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Konstantin Kolosovsky. + */ +public class ExternalsDefinitionParser { + + /** + * Parses "svn:externals" property in format starting from svn 1.5. + * + * @return map of externals definitions: key - relative directory, value - corresponding complete externals definition. + */ + @NotNull + public static Map parseExternalsProperty(@NotNull String externals) throws SvnBindException { + HashMap map = ContainerUtil.newHashMap(); + + for (String external : StringUtil.splitByLines(externals, true)) { + map.put(parseRelativeDirectory(external), external); + } + + return map; + } + + /** + * Parses relative directory from externals definition (in format starting from svn 1.5). Restrictions for relative directory: + * - is at the end of externals definition separated from other parameters by ' ' char + * - could be quoted with '"' char + * - certain chars could be escaped with '\' char + */ + @NotNull + public static String parseRelativeDirectory(@NotNull String s) throws SvnBindException { + s = s.trim(); + + int length = s.length(); + String result; + + if (isUnescapedQuote(s, length - 1)) { + int index = lastUnescapedIndexOf(s, length - 1, '"'); + assertIndex(s, index, "Could not find start quote"); + result = s.substring(index + 1, length - 1); + } + else { + int index = lastUnescapedIndexOf(s, length, ' '); + assertIndex(s, index, "Could not find separating space"); + result = s.substring(index + 1); + } + + return unescape(result); + } + + private static void assertIndex(@NotNull String s, int index, @NotNull String message) throws SvnBindException { + if (index < 0) { + throw new SvnBindException(message + " - " + s); + } + } + + @NotNull + private static String unescape(@NotNull String s) { + return s.replace("\\", ""); + } + + /** + * "from" index is excluded. + */ + private static int lastUnescapedIndexOf(@NotNull String s, int from, char c) { + int result = from; + + do { + result = s.lastIndexOf(c, result - 1); + } + while (result != -1 && !isUnescaped(s, result, c)); + + return result; + } + + private static boolean isUnescapedQuote(@NotNull String s, int index) { + return isUnescaped(s, index, '"'); + } + + private static boolean isUnescaped(@NotNull String s, int index, char c) { + return StringUtil.isChar(s, index, c) && !StringUtil.isChar(s, index - 1, '\\'); + } +} diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ExternalsDefinitionParserTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ExternalsDefinitionParserTest.java new file mode 100644 index 000000000000..ff24c79a6b4c --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ExternalsDefinitionParserTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.svn; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.properties.ExternalsDefinitionParser; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Konstantin Kolosovsky. + */ +public class ExternalsDefinitionParserTest { + + @Test + public void test_no_space_directory_with_revision() throws Exception { + assertRelativeDirectory("third-party/skins", "-r148 http://svn.example.com/skinproj third-party/skins"); + } + + @Test + public void test_no_space_directory_with_peg_revision() throws Exception { + assertRelativeDirectory("third-party/skins", "http://svn.example.com/skinproj@148 third-party/skins"); + } + + @Test + public void test_no_space_directory_without_revision() throws Exception { + assertRelativeDirectory("third-party/sounds", " http://svn.example.com/repos/sounds third-party/sounds"); + } + + @Test + public void test_quoted_no_space_directory() throws Exception { + assertRelativeDirectory("third-party/skins", "-r148 http://svn.example.com/skinproj \"third-party/skins\""); + } + + @Test + public void test_quoted_with_space_directory() throws Exception { + assertRelativeDirectory("My Project", "http://svn.thirdparty.com/repos/My%20Project \"My Project\""); + } + + @Test + public void test_escaped_with_space_and_quotes_directory() throws Exception { + assertRelativeDirectory("\"Quotes Too\"", "http://svn.thirdparty.com/repos/%22Quotes%20Too%22 \\\"Quotes\\ Too\\\""); + } + + private static void assertRelativeDirectory(@NotNull String expected, @NotNull String external) throws Exception { + assertEquals(expected, ExternalsDefinitionParser.parseRelativeDirectory(external)); + } +} From efcdf14f4e6e02bf6be12101bbad623ddfc53d62 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 31 Jul 2014 14:45:07 +0200 Subject: [PATCH 21/85] highlight "Simplifiable annotation" inspection like unused symbol --- .../siyeh/InspectionGadgetsBundle.properties | 5 +- .../com/siyeh/ig/BaseInspectionVisitor.java | 13 ++- .../SimplifiableAnnotationInspection.java | 79 ++++++++++--------- .../SimplifiableAnnotation.java | 10 +-- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index e08d3dae9744..715bf898c50a 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1841,8 +1841,9 @@ double.literal.may.be.float.literal.problem.descriptor=#ref could b double.literal.may.be.float.literal.quickfix=Replace with ''{0}'' multiple.declaration.option=Ignore 'for' loop declarations simplifiable.annotation.display.name=Simplifiable annotation -simplifiable.annotation.problem.descriptor=Annotation #ref can be simplified #loc -simplifiable.annotation.quickfix=Replace with ''{0}'' +simplifiable.annotation.problem.descriptor=Unnecessary #ref in annotation #loc +simplifiable.annotation.whitespace.problem.descriptor=Unnecessary whitespace in annotation #loc +simplifiable.annotation.quickfix=Simplify annotation overloaded.methods.with.same.number.parameters.option=Ignore overloaded methods whose parameter types are definitely incompatible string.concatenation.in.format.call.display.name=String concatenation as argument to 'format()' call string.concatenation.in.format.call.problem.descriptor=#ref() call has a String concatenation argument #loc diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseInspectionVisitor.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseInspectionVisitor.java index 1ba34a6235d8..d792a1ae1101 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseInspectionVisitor.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/BaseInspectionVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -191,8 +191,13 @@ public abstract class BaseInspectionVisitor extends JavaElementVisitor { holder.registerProblem(location, description, highlightType, fixes); } - protected final void registerErrorAtOffset(@NotNull PsiElement location, - int offset, int length, Object... infos) { + protected final void registerErrorAtOffset(@NotNull PsiElement location, int offset, int length, Object... infos) { + registerErrorAtOffset(location, offset, length, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, infos); + } + + protected final void registerErrorAtOffset(@NotNull PsiElement location, int offset, int length, + ProblemHighlightType highlightType, + Object... infos) { if (location.getTextLength() == 0 || length == 0) { return; } @@ -202,7 +207,7 @@ public abstract class BaseInspectionVisitor extends JavaElementVisitor { } final String description = inspection.buildErrorString(infos); final TextRange range = new TextRange(offset, offset + length); - holder.registerProblem(location, range, description, fixes); + holder.registerProblem(location, description, highlightType, range, fixes); } @NotNull diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SimplifiableAnnotationInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SimplifiableAnnotationInspection.java index ed2aec66b42c..007280f6da74 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SimplifiableAnnotationInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SimplifiableAnnotationInspection.java @@ -16,9 +16,10 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.IncorrectOperationException; @@ -46,30 +47,30 @@ public class SimplifiableAnnotationInspection extends BaseInspection { @NotNull @Override protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message("simplifiable.annotation.problem.descriptor"); + if (((Boolean)infos[0]).booleanValue()) { + return InspectionGadgetsBundle.message("simplifiable.annotation.whitespace.problem.descriptor"); + } + else { + return InspectionGadgetsBundle.message("simplifiable.annotation.problem.descriptor"); + } } @Override protected InspectionGadgetsFix buildFix(Object... infos) { - final PsiAnnotation annotation = (PsiAnnotation)infos[0]; - return new SimplifiableAnnotationFix(annotation); + return new SimplifiableAnnotationFix(); } private static class SimplifiableAnnotationFix extends InspectionGadgetsFix { - private final String replacement; - - public SimplifiableAnnotationFix(PsiAnnotation annotation) { - this.replacement = buildAnnotationText(annotation, new StringBuilder()).toString(); - } + public SimplifiableAnnotationFix() {} @Override @NotNull public String getName() { - return InspectionGadgetsBundle.message("simplifiable.annotation.quickfix", - StringUtil.shortenTextWithEllipsis(replacement, 50, 0, true)); + return InspectionGadgetsBundle.message("simplifiable.annotation.quickfix"); } - @Override + + @Override @NotNull public String getFamilyName() { return getName(); @@ -78,10 +79,10 @@ public class SimplifiableAnnotationInspection extends BaseInspection { @Override protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { final PsiElement element = descriptor.getPsiElement(); - if (!(element instanceof PsiAnnotation)) { + final PsiAnnotation annotation = PsiTreeUtil.getParentOfType(element, PsiAnnotation.class); + if (annotation == null) { return; } - final PsiAnnotation annotation = (PsiAnnotation)element; final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); final String annotationText = buildAnnotationText(annotation, new StringBuilder()).toString(); final PsiAnnotation newAnnotation = factory.createAnnotationFromText(annotationText, element); @@ -153,36 +154,36 @@ public class SimplifiableAnnotationInspection extends BaseInspection { } final PsiNameValuePair[] attributes = parameterList.getAttributes(); final PsiElement[] annotationChildren = annotation.getChildren(); - if (annotationChildren.length >= 2 && annotationChildren[1] instanceof PsiWhiteSpace) { - if (!containsError(annotation)) { - registerError(annotation, annotation); - } + if (annotationChildren.length >= 2 && annotationChildren[1] instanceof PsiWhiteSpace && !containsError(annotation)) { + registerError(annotationChildren[1], Boolean.TRUE); } - else if (attributes.length == 0) { - final PsiElement[] children = parameterList.getChildren(); - if (children.length <= 0) { - return; - } - if (!containsError(annotation)) { - registerError(annotation, annotation); + if (attributes.length == 0) { + if (parameterList.getChildren().length > 0 && !containsError(annotation)) { + registerError(parameterList, ProblemHighlightType.LIKE_UNUSED_SYMBOL, Boolean.FALSE); } } else if (attributes.length == 1) { final PsiNameValuePair attribute = attributes[0]; - @NonNls final String name = attribute.getName(); + final PsiIdentifier identifier = attribute.getNameIdentifier(); final PsiAnnotationMemberValue attributeValue = attribute.getValue(); - if (!PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(name)) { - if (!(attributeValue instanceof PsiArrayInitializerMemberValue)) { - return; - } - final PsiArrayInitializerMemberValue arrayValue = (PsiArrayInitializerMemberValue)attributeValue; - final PsiAnnotationMemberValue[] initializers = arrayValue.getInitializers(); - if (initializers.length != 1) { - return; + if (identifier != null && attributeValue != null) { + @NonNls final String name = attribute.getName(); + if (PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(name) && !containsError(annotation)) { + registerErrorAtOffset(attribute, 0, attributeValue.getStartOffsetInParent(), ProblemHighlightType.LIKE_UNUSED_SYMBOL, + Boolean.FALSE); } } + if (!(attributeValue instanceof PsiArrayInitializerMemberValue)) { + return; + } + final PsiArrayInitializerMemberValue arrayValue = (PsiArrayInitializerMemberValue)attributeValue; + final PsiAnnotationMemberValue[] initializers = arrayValue.getInitializers(); + if (initializers.length != 1) { + return; + } if (!containsError(annotation)) { - registerError(annotation, annotation); + registerError(arrayValue.getFirstChild(), ProblemHighlightType.LIKE_UNUSED_SYMBOL, Boolean.FALSE); + registerError(arrayValue.getLastChild(), ProblemHighlightType.LIKE_UNUSED_SYMBOL, Boolean.FALSE); } } else if (attributes.length > 1) { @@ -191,15 +192,15 @@ public class SimplifiableAnnotationInspection extends BaseInspection { if (!(value instanceof PsiArrayInitializerMemberValue)) { continue; } - final PsiArrayInitializerMemberValue arrayInitializerMemberValue = (PsiArrayInitializerMemberValue)value; - final PsiAnnotationMemberValue[] initializers = arrayInitializerMemberValue.getInitializers(); + final PsiArrayInitializerMemberValue arrayValue = (PsiArrayInitializerMemberValue)value; + final PsiAnnotationMemberValue[] initializers = arrayValue.getInitializers(); if (initializers.length != 1) { continue; } if (!containsError(annotation)) { - registerError(annotation, annotation); + registerError(arrayValue.getFirstChild(), ProblemHighlightType.LIKE_UNUSED_SYMBOL, Boolean.FALSE); + registerError(arrayValue.getLastChild(), ProblemHighlightType.LIKE_UNUSED_SYMBOL, Boolean.FALSE); } - return; } } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/simplifiable_annotation/SimplifiableAnnotation.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/simplifiable_annotation/SimplifiableAnnotation.java index b40289507a08..1fe15e0f3eeb 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/simplifiable_annotation/SimplifiableAnnotation.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/simplifiable_annotation/SimplifiableAnnotation.java @@ -2,8 +2,8 @@ package com.siyeh.igtest.style.simplifiable_annotation; public class SimplifiableAnnotation { - @ SuppressWarnings(value = "blabla") - @ Deprecated() + @ SuppressWarnings(value = "blabla") + @ Deprecated() Object foo() { return null; } @@ -14,8 +14,8 @@ public class SimplifiableAnnotation { @interface ArrayAnnotation { String[] array(); } -@ValueAnnotation({"the value"}) -@ArrayAnnotation(array = {"first"}) +@ValueAnnotation({"the value"}) +@ArrayAnnotation(array = {"first"}) class MyClass { @ ValueAnnotation @@ -23,7 +23,7 @@ class MyClass { return -1; } - @Two(i={1}, j = 2) + @Two(i={1}, j = 2) String s; } @interface Two { From 036570baf699c16b7836827d5cc29db9b15e1be7 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Thu, 31 Jul 2014 16:56:31 +0400 Subject: [PATCH 22/85] detaching el functionality --- .../com/intellij/psi/impl/PsiImplUtil.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java index bcf0decb69b7..938b40f51bb8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -687,4 +687,33 @@ public class PsiImplUtil { return element instanceof LeafElement && tokenSet.contains(((LeafElement)element).getElementType()); } + public static PsiType buildTypeFromTypeString(@NotNull final String typeName, @NotNull final PsiElement context, @NotNull final PsiFile psiFile) { + PsiType resultType; + final PsiManager psiManager = psiFile.getManager(); + + if (typeName.indexOf('<') != -1 || typeName.indexOf('[') != -1 || typeName.indexOf('.') == -1) { + try { + return JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory().createTypeFromText(typeName, context); + } catch(Exception ex) {} // invalid syntax will produce unresolved class type + } + + PsiClass aClass = JavaPsiFacade.getInstance(psiManager.getProject()).findClass(typeName, context.getResolveScope()); + + if (aClass == null) { + final LightClassReference ref = new LightClassReference( + psiManager, + PsiNameHelper.getShortClassName(typeName), + typeName, + PsiSubstitutor.EMPTY, + psiFile + ); + resultType = new PsiClassReferenceType(ref, null); + } else { + PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory(); + PsiSubstitutor substitutor = factory.createRawSubstitutor(aClass); + resultType = factory.createType(aClass, substitutor); + } + + return resultType; + } } From 6859788abf5f66a7fde54429e35a99e0c8b9d079 Mon Sep 17 00:00:00 2001 From: Yaroslav Lepenkin Date: Thu, 31 Jul 2014 17:00:07 +0400 Subject: [PATCH 23/85] Java Rearranger: visiting reference expression to find initializing dependencies on another fields (IDEA-128071) --- .../arrangement/JavaArrangementVisitor.java | 1 + .../JavaRearrangerFieldReferenceTest.groovy | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaArrangementVisitor.java b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaArrangementVisitor.java index 3651fa1afe3a..ff1170b64bef 100644 --- a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaArrangementVisitor.java +++ b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaArrangementVisitor.java @@ -280,6 +280,7 @@ public class JavaArrangementVisitor extends JavaRecursiveElementVisitor { if (ref instanceof PsiField && containingClassFields.contains(ref)) { referencedElements.add((PsiField)ref); } + super.visitReferenceExpression(expression); } }); diff --git a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy index d11a34f6e376..7b1861b9daec 100644 --- a/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/psi/codeStyle/arrangement/JavaRearrangerFieldReferenceTest.groovy @@ -385,4 +385,28 @@ public class test { ] ) } + + void "test IDEA-128071"() { + doTest( + initial: ''' +public class FormatTest { + public int a = 3; + private static final String FACEBOOK_CLIENT_ID = ""; + public static final String FACEBOOK_OAUTH_URL = "".concat(FACEBOOK_CLIENT_ID).concat(""); +} +''', + expected: ''' +public class FormatTest { + private static final String FACEBOOK_CLIENT_ID = ""; + public static final String FACEBOOK_OAUTH_URL = "".concat(FACEBOOK_CLIENT_ID).concat(""); + public int a = 3; +} +''', + rules: [ + rule(PUBLIC, STATIC, FINAL), + rule(PRIVATE, STATIC, FINAL), + rule(PUBLIC) + ] + ) + } } From 8ffd4359836a426c99d23e024246ab0c674682dc Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Wed, 30 Jul 2014 19:35:45 +0400 Subject: [PATCH 24/85] IDEA-127046 Gradle: processing resources --- build/scripts/layouts.gant | 5 + .../externalSystem/model/DataNode.java | 12 + .../model/DefaultExternalFilter.java | 62 +++++ .../model/DefaultExternalPlugin.java | 46 +++ .../model/DefaultExternalProject.java | 243 ++++++++++++++++ .../DefaultExternalSourceDirectorySet.java | 124 +++++++++ .../model/DefaultExternalSourceSet.java | 66 +++++ .../model/DefaultExternalTask.java | 86 ++++++ .../externalSystem/model/ExternalFilter.java | 34 +++ .../externalSystem/model/ExternalPlugin.java | 30 ++ .../externalSystem/model/ExternalProject.java | 87 ++++++ .../model/ExternalSourceDirectorySet.java | 46 +++ .../model/ExternalSourceSet.java | 39 +++ .../externalSystem/model/ExternalTask.java | 36 +++ .../project/ExternalSystemSourceType.java | 70 +++-- .../project/IExternalSystemSourceType.java | 32 +++ .../util/ExternalSystemApiUtil.java | 5 + .../util/ExternalSystemConstants.java | 1 + .../external-system-impl.iml | 2 + .../project/ProjectStructureServices.java | 42 --- .../project/manage/ModuleDataService.java | 1 + .../project/manage/ProjectDataManager.java | 21 +- .../util/ExternalProjectSerializer.java | 262 ++++++++++++++++++ .../test/ExternalSystemTestCase.java | 137 ++++++++- .../roots/OrderEnumerationHandler.java | 9 + .../roots/impl/OrderEnumeratorBase.java | 32 +++ .../roots/impl/OrderRootsEnumeratorImpl.java | 1 + plugins/gradle/gradle.iml | 3 + .../gradle/jps-plugin/gradle-jps-plugin.iml | 44 +++ ...jps.gradle.model.JpsGradleExtensionService | 16 ++ ...g.jetbrains.jps.incremental.BuilderService | 16 ++ ...JavaDependenciesEnumerationHandler$Factory | 16 ++ ....serialization.JpsModelSerializerExtension | 16 ++ .../compiler/ChainingFilterTransformer.java | 111 ++++++++ .../gradle/compiler/GradleBuilderService.java | 44 +++ .../compiler/GradleResourceFileProcessor.java | 94 +++++++ .../compiler/GradleResourcesBuilder.java | 128 +++++++++ .../model/JpsGradleExtensionService.java | 49 ++++ .../model/JpsGradleModuleExtension.java | 25 ++ .../jps/gradle/model/impl/FilePattern.java | 38 +++ .../GradleModuleResourceConfiguration.java | 81 ++++++ .../impl/GradleProjectConfiguration.java | 35 +++ .../model/impl/GradleResourceFileFilter.java | 81 ++++++ .../impl/GradleResourceRootDescriptor.java | 88 ++++++ .../model/impl/GradleResourcesTarget.java | 161 +++++++++++ .../model/impl/GradleResourcesTargetType.java | 78 ++++++ ...sGradleDependenciesEnumerationHandler.java | 57 ++++ .../impl/JpsGradleExtensionServiceImpl.java | 136 +++++++++ .../JpsGradleModelSerializationExtension.java | 59 ++++ .../impl/JpsGradleModuleExtensionImpl.java | 43 +++ .../jps/gradle/model/impl/ModuleVersion.java | 64 +++++ .../model/impl/ResourceRootConfiguration.java | 59 ++++ .../gradle/model/impl/ResourceRootFilter.java | 75 +++++ .../src/META-INF/gradle-maven-integration.xml | 1 - plugins/gradle/src/META-INF/plugin.xml | 6 + .../GradleBuildProcessParametersProvider.java | 93 +++++++ ...esourceCompilerConfigurationGenerator.java | 186 +++++++++++++ .../GradleOrderEnumeratorHandler.java | 114 ++++++++ .../AbstractProjectResolverExtension.java | 6 + .../BaseGradleProjectResolverExtension.java | 83 +++++- .../project/GradleProjectResolver.java | 3 + .../GradleProjectResolverExtension.java | 3 + .../project/GradleStartupActivity.java | 22 +- .../project/ProjectResolverContext.java | 8 +- .../data/ExternalProjectDataService.java | 233 ++++++++++++++++ .../compiler/GradleCompilingTestCase.java | 89 ++++++ .../compiler/GradleResourceFilteringTest.java | 132 +++++++++ .../GradleResourceProcessingTest.java | 176 ++++++++++++ .../gradle/model/ModuleExtendedModel.java | 3 + .../gradle/model/ProjectImportAction.java | 51 ++-- .../gradle/tooling/ErrorMessageBuilder.java | 12 +- .../gradle-tooling-extension-impl.iml | 1 + ...plugins.gradle.tooling.ModelBuilderService | 1 + .../builder/ExternalProjectBuilderImpl.groovy | 231 +++++++++++++++ 74 files changed, 4484 insertions(+), 118 deletions(-) create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalFilter.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalPlugin.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalProject.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceDirectorySet.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceSet.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalTask.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalFilter.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalPlugin.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalProject.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceDirectorySet.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceSet.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalTask.java create mode 100644 platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/IExternalSystemSourceType.java delete mode 100644 platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/ProjectStructureServices.java create mode 100644 platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalProjectSerializer.java create mode 100644 plugins/gradle/jps-plugin/gradle-jps-plugin.iml create mode 100644 plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.gradle.model.JpsGradleExtensionService create mode 100644 plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.incremental.BuilderService create mode 100644 plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.java.impl.JpsJavaDependenciesEnumerationHandler$Factory create mode 100644 plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/ChainingFilterTransformer.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleBuilderService.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleResourceFileProcessor.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleResourcesBuilder.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/JpsGradleExtensionService.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/JpsGradleModuleExtension.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/FilePattern.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleModuleResourceConfiguration.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleProjectConfiguration.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourceFileFilter.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourceRootDescriptor.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourcesTarget.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourcesTargetType.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleDependenciesEnumerationHandler.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleExtensionServiceImpl.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleModelSerializationExtension.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleModuleExtensionImpl.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ModuleVersion.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ResourceRootConfiguration.java create mode 100644 plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ResourceRootFilter.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataService.java create mode 100644 plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleCompilingTestCase.java create mode 100644 plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceFilteringTest.java create mode 100644 plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceProcessingTest.java create mode 100644 plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index b0315724e8e7..ed22dfed5f99 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -336,6 +336,10 @@ public def layoutCommunityPlugins(String home) { } layoutPlugin("gradle") { + jar("gradle-jps-plugin.jar") { + module("gradle-jps-plugin") + } + jar("gradle-tooling-extension-api.jar") { module("gradle-tooling-extension-api") } @@ -753,6 +757,7 @@ def layoutJps(String home, String targetDir, String buildNumber, Closure additio jar("maven-jps-plugin.jar") { module("maven-jps-plugin") } + jar("gradle-jps-plugin.jar") { module("gradle-jps-plugin") } fileset(dir: "$home/plugins/maven/maven3-server-impl/lib/maven3/lib") {include(name: "plexus-utils-*.jar")} jar("eclipse-jps-plugin.jar") { diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java index 4efac184c2ee..10fc11acbf5f 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java @@ -68,6 +68,18 @@ public class DataNode implements Serializable { return result; } + @NotNull + public DataNode createOrReplaceChild(@NotNull Key key, @NotNull T data) { + for (Iterator> iterator = myChildren.iterator(); iterator.hasNext(); ) { + DataNode child = iterator.next(); + if (child.getKey().equals(key)) { + iterator.remove(); + break; + } + } + return createChild(key, data); + } + @NotNull public Key getKey() { return myKey; diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalFilter.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalFilter.java new file mode 100644 index 000000000000..6bc9cf9aed94 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalFilter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Vladislav.Soroka + * @since 7/22/2014 + */ +public class DefaultExternalFilter implements ExternalFilter { + private static final long serialVersionUID = 1L; + + @NotNull + private String myFilterType; + @NotNull + private String myPropertiesAsJsonMap; + + public DefaultExternalFilter() { + myPropertiesAsJsonMap = ""; + myFilterType = ""; + } + + + public DefaultExternalFilter(ExternalFilter filter) { + myPropertiesAsJsonMap = filter.getPropertiesAsJsonMap(); + myFilterType = filter.getFilterType(); + } + + @NotNull + @Override + public String getFilterType() { + return myFilterType; + } + + public void setFilterType(@NotNull String filterType) { + myFilterType = filterType; + } + + @Override + @NotNull + public String getPropertiesAsJsonMap() { + return myPropertiesAsJsonMap; + } + + public void setPropertiesAsJsonMap(@NotNull String propertiesAsJsonMap) { + myPropertiesAsJsonMap = propertiesAsJsonMap; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalPlugin.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalPlugin.java new file mode 100644 index 000000000000..8b7991b1d88c --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalPlugin.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Vladislav.Soroka + * @since 7/16/2014 + */ +public class DefaultExternalPlugin implements ExternalPlugin { + private static final long serialVersionUID = 1L; + + @NotNull + private String myId; + + public DefaultExternalPlugin() { + } + + public DefaultExternalPlugin(ExternalPlugin plugin) { + myId = plugin.getId(); + } + + @NotNull + @Override + public String getId() { + return myId; + } + + public void setId(@NotNull String id) { + myId = id; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalProject.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalProject.java new file mode 100644 index 000000000000..cecb9ad33c12 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalProject.java @@ -0,0 +1,243 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public class DefaultExternalProject implements ExternalProject { + + private static final long serialVersionUID = 1L; + + @NotNull + private String myName; + @NotNull + private String myQName; + @Nullable + private String myDescription; + @NotNull + private String myGroup; + @NotNull + private String myVersion; + @NotNull + private Map myChildProjects; + @NotNull + private File myProjectDir; + @NotNull + private File myBuildDir; + @Nullable + private File myBuildFile; + @NotNull + private Map myTasks; + @NotNull + private Map myProperties; + @NotNull + private Map mySourceSets; + @NotNull + private String myExternalSystemId; + @NotNull + private Map myPlugins; + + public DefaultExternalProject() { + myChildProjects = new HashMap(); + myTasks = new HashMap(); + myProperties = new HashMap(); + mySourceSets = new HashMap(); + myPlugins = new HashMap(); + } + + public DefaultExternalProject(@NotNull ExternalProject externalProject) { + this(); + myName = externalProject.getName(); + myQName = externalProject.getQName(); + myVersion = externalProject.getVersion(); + myGroup = externalProject.getGroup(); + myDescription = externalProject.getDescription(); + myProjectDir = externalProject.getProjectDir(); + myBuildDir = externalProject.getBuildDir(); + myBuildFile = externalProject.getBuildFile(); + myExternalSystemId = externalProject.getExternalSystemId(); + + for (Map.Entry entry : externalProject.getChildProjects().entrySet()) { + myChildProjects.put(entry.getKey(), new DefaultExternalProject(entry.getValue())); + } + + for (Map.Entry entry : externalProject.getTasks().entrySet()) { + myTasks.put(entry.getKey(), new DefaultExternalTask(entry.getValue())); + } + for (Map.Entry entry : externalProject.getSourceSets().entrySet()) { + mySourceSets.put(entry.getKey(), new DefaultExternalSourceSet(entry.getValue())); + } + for (Map.Entry entry : externalProject.getPlugins().entrySet()) { + myPlugins.put(entry.getKey(), new DefaultExternalPlugin(entry.getValue())); + } + } + + + @NotNull + @Override + public String getExternalSystemId() { + return myExternalSystemId; + } + + public void setExternalSystemId(@NotNull String externalSystemId) { + myExternalSystemId = externalSystemId; + } + + @NotNull + @Override + public String getName() { + return myName; + } + + public void setName(@NotNull String name) { + myName = name; + } + + @NotNull + @Override + public String getQName() { + return myQName; + } + + public void setQName(@NotNull String QName) { + myQName = QName; + } + + @Nullable + @Override + public String getDescription() { + return myDescription; + } + + public void setDescription(@Nullable String description) { + myDescription = description; + } + + @NotNull + @Override + public String getGroup() { + return myGroup; + } + + public void setGroup(@NotNull String group) { + myGroup = group; + } + + @NotNull + @Override + public String getVersion() { + return myVersion; + } + + public void setVersion(@NotNull String version) { + myVersion = version; + } + + @NotNull + @Override + public Map getChildProjects() { + return myChildProjects; + } + + public void setChildProjects(@NotNull Map childProjects) { + myChildProjects = childProjects; + } + + @NotNull + @Override + public File getProjectDir() { + return myProjectDir; + } + + public void setProjectDir(@NotNull File projectDir) { + myProjectDir = projectDir; + } + + @NotNull + @Override + public File getBuildDir() { + return myBuildDir; + } + + public void setBuildDir(@NotNull File buildDir) { + myBuildDir = buildDir; + } + + @Nullable + @Override + public File getBuildFile() { + return myBuildFile; + } + + public void setBuildFile(@Nullable File buildFile) { + myBuildFile = buildFile; + } + + @NotNull + @Override + public Map getTasks() { + return myTasks; + } + + public void setTasks(@NotNull Map tasks) { + myTasks = tasks; + } + + @NotNull + @Override + public Map getPlugins() { + return myPlugins; + } + + public void setPlugins(@NotNull Map plugins) { + myPlugins = plugins; + } + + @NotNull + @Override + public Map getProperties() { + return myProperties; + } + + public void setProperties(@NotNull Map properties) { + myProperties = properties; + } + + @Nullable + @Override + public Object getProperty(String name) { + return myProperties.get(name); + } + + @NotNull + @Override + public Map getSourceSets() { + return mySourceSets; + } + + public void setSourceSets(@NotNull Map sourceSets) { + mySourceSets = sourceSets; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceDirectorySet.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceDirectorySet.java new file mode 100644 index 000000000000..d6a8d7aa6291 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceDirectorySet.java @@ -0,0 +1,124 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public class DefaultExternalSourceDirectorySet implements ExternalSourceDirectorySet { + private static final long serialVersionUID = 1L; + + @NotNull + private String myName; + @NotNull + private Set mySrcDirs; + @NotNull + private File myOutputDir; + @NotNull + private Set myExcludes; + @NotNull + private Set myIncludes; + @NotNull + private List myFilters; + + public DefaultExternalSourceDirectorySet() { + mySrcDirs = new HashSet(); + myExcludes = new HashSet(); + myIncludes = new HashSet(); + myFilters = new ArrayList(); + } + + public DefaultExternalSourceDirectorySet(ExternalSourceDirectorySet sourceDirectorySet) { + this(); + myName = sourceDirectorySet.getName(); + mySrcDirs = new HashSet(sourceDirectorySet.getSrcDirs()); + myOutputDir = sourceDirectorySet.getOutputDir(); + myExcludes = new HashSet(sourceDirectorySet.getExcludes()); + myIncludes = new HashSet(sourceDirectorySet.getIncludes()); + for (ExternalFilter filter : sourceDirectorySet.getFilters()) { + myFilters.add(new DefaultExternalFilter(filter)); + } + } + + @NotNull + @Override + public String getName() { + return myName; + } + + public void setName(@NotNull String name) { + myName = name; + } + + @NotNull + @Override + public Set getSrcDirs() { + return mySrcDirs; + } + + public void setSrcDirs(@NotNull Set srcDirs) { + mySrcDirs = srcDirs; + } + + @NotNull + @Override + public File getOutputDir() { + return myOutputDir; + } + + @NotNull + @Override + public Set getIncludes() { + return myIncludes; + } + + public void setIncludes(@NotNull Set includes) { + myIncludes = includes; + } + + @NotNull + @Override + public Set getExcludes() { + return myExcludes; + } + + public void setExcludes(@NotNull Set excludes) { + myExcludes = excludes; + } + + @NotNull + @Override + public List getFilters() { + return myFilters; + } + + public void setFilters(@NotNull List filters) { + myFilters = filters; + } + + public void setOutputDir(@NotNull File outputDir) { + myOutputDir = outputDir; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceSet.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceSet.java new file mode 100644 index 000000000000..19d692dd1985 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceSet.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType; +import com.intellij.openapi.externalSystem.model.project.IExternalSystemSourceType; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public class DefaultExternalSourceSet implements ExternalSourceSet { + private static final long serialVersionUID = 1L; + + private String myName; + private Map mySources; + + public DefaultExternalSourceSet() { + mySources = new HashMap(); + } + + public DefaultExternalSourceSet(ExternalSourceSet sourceSet) { + this(); + myName = sourceSet.getName(); + for (Map.Entry entry : sourceSet.getSources().entrySet()) { + mySources.put(ExternalSystemSourceType.from(entry.getKey()), new DefaultExternalSourceDirectorySet(entry.getValue())); + } + } + + @NotNull + @Override + public String getName() { + return myName; + } + + public void setName(String name) { + myName = name; + } + + @NotNull + @Override + public Map getSources() { + return mySources; + } + + public void setSources(Map sources) { + mySources = sources; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalTask.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalTask.java new file mode 100644 index 000000000000..e9392eb52e84 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalTask.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public class DefaultExternalTask implements ExternalTask { + private static final long serialVersionUID = 1L; + + @NotNull + private String myName; + @NotNull + private String myQName; + @Nullable + private String myDescription; + @Nullable + private String myGroup; + + public DefaultExternalTask() { + } + + public DefaultExternalTask(ExternalTask externalTask) { + myName = externalTask.getName(); + myQName = externalTask.getQName(); + myDescription = externalTask.getDescription(); + myGroup = externalTask.getGroup(); + } + + @NotNull + @Override + public String getName() { + return myName; + } + + public void setName(@NotNull String name) { + myName = name; + } + + @NotNull + @Override + public String getQName() { + return myQName; + } + + public void setQName(@NotNull String QName) { + myQName = QName; + } + + @Nullable + @Override + public String getDescription() { + return myDescription; + } + + public void setDescription(@Nullable String description) { + myDescription = description; + } + + @Nullable + @Override + public String getGroup() { + return myGroup; + } + + public void setGroup(@Nullable String group) { + myGroup = group; + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalFilter.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalFilter.java new file mode 100644 index 000000000000..98fc003cb645 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalFilter.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public interface ExternalFilter extends Serializable { + @NotNull + String getFilterType(); + @NotNull + String getPropertiesAsJsonMap(); +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalPlugin.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalPlugin.java new file mode 100644 index 000000000000..edd3db771b3f --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalPlugin.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public interface ExternalPlugin extends Serializable { + @NotNull + String getId(); +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalProject.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalProject.java new file mode 100644 index 000000000000..6df2d413d0fd --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalProject.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.Serializable; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public interface ExternalProject extends Serializable { + + @NotNull + String getExternalSystemId(); + + @NotNull + String getName(); + + @NotNull + String getQName(); + + @Nullable + String getDescription(); + + @NotNull + String getGroup(); + + @NotNull + String getVersion(); + + @NotNull + Map getChildProjects(); + + @NotNull + File getProjectDir(); + + @NotNull + File getBuildDir(); + + @Nullable + File getBuildFile(); + + @NotNull + Map getTasks(); + + //@NotNull + //Map getConfigurations(); + + //@NotNull + //List getRepositories(); + + //@NotNull + //List getDependencies(); + + @NotNull + Map getPlugins(); + + //@NotNull + //ExternalProjectBuild getBuild(); + + @NotNull + Map getProperties(); + + @Nullable + Object getProperty(String name); + + @NotNull + Map getSourceSets(); +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceDirectorySet.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceDirectorySet.java new file mode 100644 index 000000000000..70f0bed3d177 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceDirectorySet.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public interface ExternalSourceDirectorySet extends Serializable { + @NotNull + String getName(); + + @NotNull + Set getSrcDirs(); + + @NotNull + File getOutputDir(); + + @NotNull + Set getExcludes(); + @NotNull + Set getIncludes(); + + @NotNull + List getFilters(); +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceSet.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceSet.java new file mode 100644 index 000000000000..b23c07bbafa6 --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceSet.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import com.intellij.openapi.externalSystem.model.project.IExternalSystemSourceType; +import org.jetbrains.annotations.NotNull; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public interface ExternalSourceSet extends Serializable { + @NotNull + String getName(); + //@NotNull + //ClasspathContainer getCompileClasspath(); + + //@NotNull + //ClasspathContainer getRuntimeClasspath(); + + @NotNull + Map getSources(); +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalTask.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalTask.java new file mode 100644 index 000000000000..b4c61c662acc --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalTask.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2014 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.externalSystem.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + * @since 7/14/2014 + */ +public interface ExternalTask extends Serializable { + @NotNull + String getName(); + @NotNull + String getQName(); + @Nullable + String getDescription(); + @Nullable + String getGroup(); +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalSystemSourceType.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalSystemSourceType.java index ffe8a52d4a06..0e112b1c2b14 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalSystemSourceType.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalSystemSourceType.java @@ -1,52 +1,62 @@ package com.intellij.openapi.externalSystem.model.project; -import org.jetbrains.annotations.NotNull; - -import java.io.Serializable; - /** * Enumerates module source types. * * @author Denis Zhdanov * @since 8/10/11 5:21 PM */ -public class ExternalSystemSourceType implements Serializable { +public enum ExternalSystemSourceType implements IExternalSystemSourceType { - @NotNull public static final ExternalSystemSourceType SOURCE = new ExternalSystemSourceType("SOURCE"); - @NotNull public static final ExternalSystemSourceType TEST = new ExternalSystemSourceType("TEST"); - @NotNull public static final ExternalSystemSourceType EXCLUDED = new ExternalSystemSourceType("EXCLUDED"); - @NotNull public static final ExternalSystemSourceType SOURCE_GENERATED = new ExternalSystemSourceType("SOURCE_GENERATED"); - @NotNull public static final ExternalSystemSourceType TEST_GENERATED = new ExternalSystemSourceType("TEST_GENERATED"); - @NotNull public static final ExternalSystemSourceType RESOURCE = new ExternalSystemSourceType("RESOURCE"); - @NotNull public static final ExternalSystemSourceType TEST_RESOURCE = new ExternalSystemSourceType("TEST_RESOURCE"); - - private static final long serialVersionUID = 1L; + SOURCE(false, false, false, false), + TEST(true, false, false, false), + EXCLUDED(false, false, false, true), + SOURCE_GENERATED(true, true, false, false), + TEST_GENERATED(true, true, false, false), + RESOURCE(false, false, true, false), + TEST_RESOURCE(true, false, true, false); - @NotNull private final String myId; + private final boolean isTest; + private final boolean isGenerated; + private final boolean isResource; + private final boolean isExcluded; - public ExternalSystemSourceType(@NotNull String id) { - myId = id; + ExternalSystemSourceType(boolean test, boolean generated, boolean resource, boolean excluded) { + isTest = test; + isGenerated = generated; + isResource = resource; + isExcluded = excluded; } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ExternalSystemSourceType type = (ExternalSystemSourceType)o; - - if (!myId.equals(type.myId)) return false; - - return true; + public boolean isTest() { + return isTest; } @Override - public int hashCode() { - return myId.hashCode(); + public boolean isGenerated() { + return isGenerated; } @Override - public String toString() { - return myId; + public boolean isResource() { + return isResource; + } + + @Override + public boolean isExcluded() { + return isExcluded; + } + + public static ExternalSystemSourceType from(IExternalSystemSourceType sourceType) { + for (ExternalSystemSourceType systemSourceType : ExternalSystemSourceType.values()) { + if (systemSourceType.isGenerated == sourceType.isGenerated() && + systemSourceType.isResource == sourceType.isResource() && + systemSourceType.isTest == sourceType.isTest() && + systemSourceType.isExcluded == sourceType.isExcluded()) { + return systemSourceType; + } + } + throw new IllegalArgumentException("Invalid source type: " + sourceType); } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/IExternalSystemSourceType.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/IExternalSystemSourceType.java new file mode 100644 index 000000000000..2ad8821b06cd --- /dev/null +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/IExternalSystemSourceType.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2014 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.externalSystem.model.project; + +import java.io.Serializable; + +/** + * @author Vladislav.Soroka + * @since 7/17/2014 + */ +public interface IExternalSystemSourceType extends Serializable { + boolean isTest(); + + boolean isGenerated(); + + boolean isResource(); + + boolean isExcluded(); +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java index a6cfc65eb759..fd3493447ced 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java @@ -681,4 +681,9 @@ public class ExternalSystemApiUtil { public static String getExternalProjectPath(@Nullable Module module) { return module != null ? module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY) : null; } + + @Nullable + public static String getExternalProjectId(@Nullable Module module) { + return module != null ? module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_ID_KEY) : null; + } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemConstants.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemConstants.java index 283067810bb0..160c95938378 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemConstants.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemConstants.java @@ -27,6 +27,7 @@ public class ExternalSystemConstants { @NonNls @NotNull public static final String EXTERNAL_SYSTEM_ID_KEY = "external.system.id"; @NonNls @NotNull public static final String LINKED_PROJECT_PATH_KEY = "external.linked.project.path"; @NonNls @NotNull public static final String ROOT_PROJECT_PATH_KEY = "external.root.project.path"; + @NonNls @NotNull public static final String LINKED_PROJECT_ID_KEY = "external.linked.project.id"; @NonNls @NotNull public static final String EXTERNAL_SYSTEM_MODULE_GROUP_KEY = "external.system.module.group"; @NonNls @NotNull public static final String EXTERNAL_SYSTEM_MODULE_VERSION_KEY = "external.system.module.version"; diff --git a/platform/external-system-impl/external-system-impl.iml b/platform/external-system-impl/external-system-impl.iml index aa68fdc4d7b6..a59eae8eea21 100644 --- a/platform/external-system-impl/external-system-impl.iml +++ b/platform/external-system-impl/external-system-impl.iml @@ -24,6 +24,8 @@ + + diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/ProjectStructureServices.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/ProjectStructureServices.java deleted file mode 100644 index 1e6dadd0d3b8..000000000000 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/ProjectStructureServices.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.intellij.openapi.externalSystem.service.project; - -import org.jetbrains.annotations.NotNull; - -/** - * Facades all services necessary for the 'sync project changes' processing. - *

- * Thread-safe. - * - * @author Denis Zhdanov - * @since 2/14/12 1:26 PM - */ -public class ProjectStructureServices { - - @NotNull private final ProjectStructureHelper myProjectStructureHelper; - @NotNull private final PlatformFacade myPlatformFacade; - @NotNull private final ExternalLibraryPathTypeMapper myLibraryPathTypeMapper; - - public ProjectStructureServices(@NotNull ProjectStructureHelper projectStructureHelper, - @NotNull PlatformFacade platformFacade, - @NotNull ExternalLibraryPathTypeMapper mapper) - { - myProjectStructureHelper = projectStructureHelper; - myPlatformFacade = platformFacade; - myLibraryPathTypeMapper = mapper; - } - - @NotNull - public ProjectStructureHelper getProjectStructureHelper() { - return myProjectStructureHelper; - } - - @NotNull - public PlatformFacade getPlatformFacade() { - return myPlatformFacade; - } - - @NotNull - public ExternalLibraryPathTypeMapper getLibraryPathTypeMapper() { - return myLibraryPathTypeMapper; - } -} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java index 4b1e3b13a22f..32919794eca4 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ModuleDataService.java @@ -274,6 +274,7 @@ public class ModuleDataService implements ProjectDataService module.putUserData(MODULE_DATA_KEY, moduleData); module.setOption(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY, moduleData.getOwner().toString()); + module.setOption(ExternalSystemConstants.LINKED_PROJECT_ID_KEY, moduleData.getId()); module.setOption(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY, moduleData.getLinkedExternalProjectPath()); final ProjectData projectData = moduleDataNode.getData(ProjectKeys.PROJECT); module.setOption(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY, projectData != null ? projectData.getLinkedExternalProjectPath() : ""); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java index 6575ffb107f0..c2dcfe57b010 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataManager.java @@ -25,6 +25,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -40,8 +41,10 @@ public class ProjectDataManager { private static final Logger LOG = Logger.getInstance("#" + ProjectDataManager.class.getName()); - @NotNull private final NotNullLazyValue, List>>> myServices = - new NotNullLazyValue, List>>>() { + @NotNull private final NotNullLazyValue, List>>> myServices; + + public ProjectDataManager() { + myServices = new NotNullLazyValue, List>>>() { @NotNull @Override protected Map, List>> compute() { @@ -57,10 +60,22 @@ public class ProjectDataManager { for (List> services : result.values()) { ExternalSystemApiUtil.orderAwareSort(services); } - return result; } }; + } + + @Nullable + public List> getDataServices(Key key) { + return myServices.getValue().get(key); + } + + @Nullable + public ProjectDataService getDataService(Key key) { + final List> dataServices = myServices.getValue().get(key); + assert dataServices == null || dataServices.isEmpty() || dataServices.size() == 1; + return ContainerUtil.getFirstItem(dataServices); + } @SuppressWarnings("unchecked") public void importData(@NotNull Collection> nodes, @NotNull Project project, boolean synchronous) { diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalProjectSerializer.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalProjectSerializer.java new file mode 100644 index 000000000000..0a863ed08852 --- /dev/null +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalProjectSerializer.java @@ -0,0 +1,262 @@ +/* + * Copyright 2000-2014 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.externalSystem.util; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.serializers.CollectionSerializer; +import com.esotericsoftware.kryo.serializers.DefaultSerializers; +import com.esotericsoftware.kryo.serializers.FieldSerializer; +import com.esotericsoftware.kryo.serializers.MapSerializer; +import com.esotericsoftware.minlog.Log; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.*; +import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.StreamUtil; +import gnu.trove.THashMap; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.objenesis.strategy.StdInstantiatorStrategy; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.*; + +/** + * @author Vladislav.Soroka + * @since 7/15/2014 + */ +public class ExternalProjectSerializer { + private static final Logger LOG = Logger.getInstance(ExternalProjectSerializer.class); + + private final Kryo myKryo; + + public ExternalProjectSerializer() { + myKryo = new Kryo() { + @Override + public T newInstance(Class type) { + LOG.error("Serializing default type: " + type); + return super.newInstance(type); + } + }; + configureKryo(); + } + + private void configureKryo() { + myKryo.setAutoReset(true); + + myKryo.setRegistrationRequired(true); + Log.set(Log.LEVEL_WARN); + + myKryo.register(ArrayList.class, new CollectionSerializer() { + @Override + protected Collection create(Kryo kryo, Input input, Class type) { + return new ArrayList(); + } + }); + myKryo.register(HashMap.class, new MapSerializer() { + @Override + protected Map create(Kryo kryo, Input input, Class type) { + return new HashMap(); + } + }); + myKryo.register(HashSet.class, new CollectionSerializer() { + @Override + protected Collection create(Kryo kryo, Input input, Class type) { + return new HashSet(); + } + }); + + myKryo.register(File.class, new FileSerializer()); + myKryo.register(DefaultExternalProject.class, new FieldSerializer(myKryo, DefaultExternalProject.class) { + @Override + protected DefaultExternalProject create(Kryo kryo, Input input, Class type) { + return new DefaultExternalProject(); + } + }); + + myKryo.register(DefaultExternalTask.class, new FieldSerializer(myKryo, DefaultExternalTask.class) { + @Override + protected DefaultExternalTask create(Kryo kryo, Input input, Class type) { + return new DefaultExternalTask(); + } + }); + + myKryo.register(DefaultExternalPlugin.class, new FieldSerializer(myKryo, DefaultExternalPlugin.class) { + @Override + protected DefaultExternalPlugin create(Kryo kryo, Input input, Class type) { + return new DefaultExternalPlugin(); + } + }); + + myKryo.register(DefaultExternalSourceSet.class, new FieldSerializer(myKryo, DefaultExternalSourceSet.class) { + @Override + protected DefaultExternalSourceSet create(Kryo kryo, Input input, Class type) { + return new DefaultExternalSourceSet(); + } + }); + + myKryo.register( + DefaultExternalSourceDirectorySet.class, + new FieldSerializer(myKryo, DefaultExternalSourceDirectorySet.class) { + @Override + protected DefaultExternalSourceDirectorySet create(Kryo kryo, Input input, Class type) { + return new DefaultExternalSourceDirectorySet(); + } + } + ); + + myKryo.register(DefaultExternalFilter.class, new FieldSerializer(myKryo, DefaultExternalFilter.class) { + @Override + protected DefaultExternalFilter create(Kryo kryo, Input input, Class type) { + return new DefaultExternalFilter(); + } + }); + + myKryo.register(ExternalSystemSourceType.class, new DefaultSerializers.EnumSerializer(ExternalSystemSourceType.class)); + + myKryo.register(LinkedHashSet.class, new CollectionSerializer() { + @Override + protected Collection create(Kryo kryo, Input input, Class type) { + return new LinkedHashSet(); + } + }); + myKryo.register(HashSet.class, new CollectionSerializer() { + @Override + protected Collection create(Kryo kryo, Input input, Class type) { + return new HashSet(); + } + }); + myKryo.register(THashSet.class, new CollectionSerializer() { + @Override + protected Collection create(Kryo kryo, Input input, Class type) { + return new THashSet(); + } + }); + myKryo.register(Set.class, new CollectionSerializer() { + @Override + protected Collection create(Kryo kryo, Input input, Class type) { + return new HashSet(); + } + }); + myKryo.register(THashMap.class, new MapSerializer() { + @Override + protected Map create(Kryo kryo, Input input, Class type) { + return new THashMap(); + } + }); + } + + + public void save(@NotNull ExternalProject externalProject) { + Output output = null; + try { + final String externalProjectPath = externalProject.getProjectDir().getPath(); + final File configurationFile = + getProjectConfigurationFile(new ProjectSystemId(externalProject.getExternalSystemId()), externalProjectPath); + if (!FileUtil.createParentDirs(configurationFile)) return; + + output = new Output(new FileOutputStream(configurationFile)); + myKryo.writeObject(output, externalProject); + } + catch (FileNotFoundException e) { + LOG.error(e); + } + finally { + StreamUtil.closeStream(output); + } + } + + @Nullable + public ExternalProject load(@NotNull ProjectSystemId externalSystemId, File externalProjectPath) { + Input input = null; + try { + final File configurationFile = getProjectConfigurationFile(externalSystemId, externalProjectPath.getPath()); + if (!configurationFile.isFile()) return null; + + input = new Input(new FileInputStream(configurationFile)); + return myKryo.readObject(input, DefaultExternalProject.class); + } + catch (Exception e) { + LOG.error(e); + } + finally { + StreamUtil.closeStream(input); + } + + return null; + } + + private static File getProjectConfigurationFile(ProjectSystemId externalSystemId, String externalProjectPath) { + return new File(getProjectConfigurationDir(externalSystemId), Integer.toHexString(externalProjectPath.hashCode()) + "/project.dat"); + } + + private static File getProjectConfigurationDir(ProjectSystemId externalSystemId) { + return getPluginSystemDir(externalSystemId, "Projects"); + } + + private static File getPluginSystemDir(ProjectSystemId externalSystemId, String folder) { + return new File(PathManager.getSystemPath(), externalSystemId.getId().toLowerCase() + "/" + folder).getAbsoluteFile(); + } + + private static class FileSerializer extends Serializer { + private final Kryo myStdKryo; + + public FileSerializer() { + myStdKryo = new Kryo(); + myStdKryo.register(File.class); + myStdKryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); + } + + @Override + public void write(Kryo kryo, Output output, File object) { + myStdKryo.writeObject(output, object); + } + + @Override + public File read(Kryo kryo, Input input, Class type) { + File file = myStdKryo.readObject(input, File.class); + return new File(file.getPath()); + } + } + + private static class StdSerializer extends Serializer { + private final Kryo myStdKryo; + + public StdSerializer(Class clazz) { + myStdKryo = new Kryo(); + myStdKryo.register(clazz); + myStdKryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); + } + + @Override + public void write(Kryo kryo, Output output, T object) { + myStdKryo.writeObject(output, object); + } + + @Override + public T read(Kryo kryo, Input input, Class type) { + return myStdKryo.readObject(input, type); + } + } +} diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemTestCase.java b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemTestCase.java index 22ec927eb30d..770578ae44f5 100644 --- a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemTestCase.java +++ b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemTestCase.java @@ -15,24 +15,41 @@ */ package com.intellij.openapi.externalSystem.test; +import com.intellij.compiler.CompilerTestUtil; +import com.intellij.compiler.CompilerWorkspaceConfiguration; +import com.intellij.compiler.artifacts.ArtifactsTestUtil; +import com.intellij.compiler.impl.ModuleCompileScope; +import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompileScope; +import com.intellij.openapi.compiler.CompileStatusNotification; +import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.module.StdModuleTypes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; +import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.packaging.impl.compiler.ArtifactCompileScope; import com.intellij.testFramework.IdeaTestCase; +import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.PsiTestUtil; import com.intellij.testFramework.UsefulTestCase; import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.util.concurrency.Semaphore; +import com.intellij.util.io.TestFileSystemItem; import com.intellij.util.ui.UIUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; @@ -40,6 +57,7 @@ import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Before; +import javax.swing.*; import java.awt.*; import java.io.File; import java.io.IOException; @@ -129,11 +147,11 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { @Override public void tearDown() throws Exception { try { - myProject = null; - UIUtil.invokeAndWaitIfNeeded(new Runnable() { + edt(new Runnable() { @Override public void run() { try { + CompilerTestUtil.disableExternalCompiler(myProject); tearDownFixtures(); } catch (Exception e) { @@ -141,6 +159,7 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { } } }); + myProject = null; if (!FileUtil.delete(myTestDir) && myTestDir.exists()) { System.err.println("Cannot delete " + myTestDir); //printDirectoryContent(myDir); @@ -319,7 +338,11 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { protected VirtualFile createProjectSubFile(String relativePath) throws IOException { File f = new File(getProjectPath(), relativePath); FileUtil.ensureExists(f.getParentFile()); - f.createNewFile(); + FileUtil.ensureCanCreateFile(f); + final boolean created = f.createNewFile(); + if(!created) { + throw new AssertionError("Unable to create the project sub file: " + f.getAbsolutePath()); + } return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f); } @@ -329,6 +352,114 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { return file; } + + protected void compileModules(final String... moduleNames) { + compile(createModulesCompileScope(moduleNames)); + } + + protected void buildArtifacts(String... artifactNames) { + compile(createArtifactsScope(artifactNames)); + } + + private void compile(final CompileScope scope) { + edt(new Runnable() { + @Override + public void run() { + for (Module module : scope.getAffectedModules()) { + setupJdkForModule(module.getName()); + } + } + }); + + CompilerWorkspaceConfiguration.getInstance(myProject).CLEAR_OUTPUT_DIRECTORY = true; + + final Semaphore semaphore = new Semaphore(); + semaphore.down(); + edt(new Runnable() { + @Override + public void run() { + CompilerTestUtil.enableExternalCompiler(); + CompilerManager.getInstance(myProject).make(scope, new CompileStatusNotification() { + @Override + public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) { + //assertFalse(aborted); + //assertEquals(collectMessages(compileContext, CompilerMessageCategory.ERROR), 0, errors); + //assertEquals(collectMessages(compileContext, CompilerMessageCategory.WARNING), 0, warnings); + semaphore.up(); + } + }); + } + }); + while (!semaphore.waitFor(100)) { + if (SwingUtilities.isEventDispatchThread()) { + UIUtil.dispatchAllInvocationEvents(); + } + } + if (SwingUtilities.isEventDispatchThread()) { + UIUtil.dispatchAllInvocationEvents(); + } + } + + private CompileScope createModulesCompileScope(final String[] moduleNames) { + final List modules = new ArrayList(); + for (String name : moduleNames) { + modules.add(getModule(name)); + } + return new ModuleCompileScope(myProject, modules.toArray(new Module[modules.size()]), false); + } + + private CompileScope createArtifactsScope(String[] artifactNames) { + List artifacts = new ArrayList(); + for (String name : artifactNames) { + artifacts.add(ArtifactsTestUtil.findArtifact(myProject, name)); + } + return ArtifactCompileScope.createArtifactsScope(myProject, artifacts); + } + + protected Sdk setupJdkForModule(final String moduleName) { + final Sdk sdk = true ? JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk() : createJdk("Java 1.5"); + ModuleRootModificationUtil.setModuleSdk(getModule(moduleName), sdk); + return sdk; + } + + protected static Sdk createJdk(String versionName) { + return IdeaTestUtil.getMockJdk17(versionName); + } + + protected Module getModule(final String name) { + AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock(); + try { + Module m = ModuleManager.getInstance(myProject).findModuleByName(name); + assertNotNull("Module " + name + " not found", m); + return m; + } + finally { + accessToken.finish(); + } + } + + protected void assertExplodedLayout(String artifactName, String expected) { + assertJarLayout(artifactName + " exploded", expected); + } + + protected void assertJarLayout(String artifactName, String expected) { + ArtifactsTestUtil.assertLayout(myProject, artifactName, expected); + } + + protected void assertArtifactOutputPath(final String artifactName, final String expected) { + ArtifactsTestUtil.assertOutputPath(myProject, artifactName, expected); + } + + protected void assertArtifactOutputFileName(final String artifactName, final String expected) { + ArtifactsTestUtil.assertOutputFileName(myProject, artifactName, expected); + } + + protected void assertArtifactOutput(String artifactName, TestFileSystemItem fs) { + final String outputPath = ArtifactsTestUtil.findArtifact(myProject, artifactName).getOutputPath(); + assert outputPath != null; + fs.assertDirectoryEqual(new File(outputPath)); + } + private static void setFileContent(final VirtualFile file, final String content, final boolean advanceStamps) throws IOException { new WriteAction() { @Override diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/OrderEnumerationHandler.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/OrderEnumerationHandler.java index 83984e11a80b..7a62c35d0390 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/OrderEnumerationHandler.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/OrderEnumerationHandler.java @@ -18,6 +18,7 @@ package com.intellij.openapi.roots; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -65,4 +66,12 @@ public abstract class OrderEnumerationHandler { @NotNull Collection urls) { return false; } + + public boolean addCustomModuleRoots(@NotNull OrderRootType type, + @NotNull ModuleRootModel rootModel, + @NotNull Collection result, + boolean includeProduction, + boolean includeTests) { + return false; + } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderEnumeratorBase.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderEnumeratorBase.java index 61a6aee9315b..b13df0560292 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderEnumeratorBase.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderEnumeratorBase.java @@ -356,6 +356,38 @@ abstract class OrderEnumeratorBase extends OrderEnumerator implements OrderEnume return false; } + boolean addCustomRootsForModule(OrderRootType type, + ModuleRootModel rootModel, + Collection result, + boolean includeProduction, + boolean includeTests) { + for (OrderEnumerationHandler handler : myCustomHandlers) { + final List urls = new ArrayList(); + final boolean added = handler.addCustomModuleRoots(type, rootModel, urls, includeProduction, includeTests); + for (String url : urls) { + ContainerUtil.addIfNotNull(VirtualFileManager.getInstance().findFileByUrl(url), result); + } + + if (added) return true; + } + return false; + } + + boolean addCustomRootUrlsForModule(OrderRootType type, + ModuleRootModel rootModel, + Collection result, + boolean includeProduction, + boolean includeTests) { + for (OrderEnumerationHandler handler : myCustomHandlers) { + final List urls = new ArrayList(); + final boolean added = handler.addCustomModuleRoots(type, rootModel, urls, includeProduction, includeTests); + result.addAll(urls); + + if (added) return true; + } + return false; + } + @Override public boolean isRuntimeOnly() { return myRuntimeOnly; diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderRootsEnumeratorImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderRootsEnumeratorImpl.java index ea5742cce176..fb9a75176255 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderRootsEnumeratorImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderRootsEnumeratorImpl.java @@ -240,6 +240,7 @@ public class OrderRootsEnumeratorImpl implements OrderRootsEnumerator { } } } + myOrderEnumerator.addCustomRootsForModule(type, rootModel, result, includeProduction, includeTests); } private void collectModuleRootsUrls(OrderRootType type, diff --git a/plugins/gradle/gradle.iml b/plugins/gradle/gradle.iml index 878ec399b87c..e7873ca13ead 100644 --- a/plugins/gradle/gradle.iml +++ b/plugins/gradle/gradle.iml @@ -14,6 +14,7 @@ + @@ -105,6 +106,8 @@ + + diff --git a/plugins/gradle/jps-plugin/gradle-jps-plugin.iml b/plugins/gradle/jps-plugin/gradle-jps-plugin.iml new file mode 100644 index 000000000000..0d1d5300e84b --- /dev/null +++ b/plugins/gradle/jps-plugin/gradle-jps-plugin.iml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.gradle.model.JpsGradleExtensionService b/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.gradle.model.JpsGradleExtensionService new file mode 100644 index 000000000000..526afcc9d0a9 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.gradle.model.JpsGradleExtensionService @@ -0,0 +1,16 @@ +# +# Copyright 2000-2014 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. +# +org.jetbrains.jps.gradle.model.impl.JpsGradleExtensionServiceImpl \ No newline at end of file diff --git a/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.incremental.BuilderService b/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.incremental.BuilderService new file mode 100644 index 000000000000..90e686daeaca --- /dev/null +++ b/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.incremental.BuilderService @@ -0,0 +1,16 @@ +# +# Copyright 2000-2014 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. +# +org.jetbrains.jps.gradle.compiler.GradleBuilderService \ No newline at end of file diff --git a/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.java.impl.JpsJavaDependenciesEnumerationHandler$Factory b/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.java.impl.JpsJavaDependenciesEnumerationHandler$Factory new file mode 100644 index 000000000000..f1518a33d329 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.java.impl.JpsJavaDependenciesEnumerationHandler$Factory @@ -0,0 +1,16 @@ +# +# Copyright 2000-2014 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. +# +org.jetbrains.jps.gradle.model.impl.JpsGradleDependenciesEnumerationHandler$GradleFactory \ No newline at end of file diff --git a/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension b/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension new file mode 100644 index 000000000000..a86ddcf7d451 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/META-INF/services/org.jetbrains.jps.model.serialization.JpsModelSerializerExtension @@ -0,0 +1,16 @@ +# +# Copyright 2000-2014 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. +# +org.jetbrains.jps.gradle.model.impl.JpsGradleModelSerializationExtension \ No newline at end of file diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/ChainingFilterTransformer.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/ChainingFilterTransformer.java new file mode 100644 index 000000000000..c717fe16c792 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/ChainingFilterTransformer.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.compiler; + +import com.intellij.openapi.util.Ref; +import org.gradle.api.Transformer; +import org.gradle.util.ConfigureUtil; +import org.jetbrains.jps.gradle.model.impl.ResourceRootFilter; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.messages.BuildMessage; +import org.jetbrains.jps.incremental.messages.CompilerMessage; + +import java.io.File; +import java.io.FilterReader; +import java.io.Reader; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.regex.Matcher; + +/** + * @author Vladislav.Soroka + * @since 7/24/2014 + */ +public class ChainingFilterTransformer implements Transformer { + private final Collection myFilters = new ArrayList(); + private final CompileContext myContext; + private final Ref myOutputFileRef; + + + public ChainingFilterTransformer(CompileContext context, Collection filters, Ref outputFileRef) { + myContext = context; + myOutputFileRef = outputFileRef; + myFilters.addAll(filters); + } + + public ChainingFilterTransformer(CompileContext context, Collection filters) { + this(context, filters, null); + } + + public void addAll(Collection filters) { + myFilters.addAll(filters); + } + + public void add(ResourceRootFilter... filters) { + Collections.addAll(myFilters, filters); + } + + @Override + public Reader transform(Reader original) { + Reader value = original; + for (ResourceRootFilter filter : myFilters) { + value = doTransform(filter, value); + } + return value; + } + + private Reader doTransform(ResourceRootFilter filter, Reader original) { + if ("RenamingCopyFilter" .equals(filter.filterType)) { + final Matcher matcher = (Matcher)filter.getProperties().get("matcher"); + final String replacement = (String)filter.getProperties().get("replacement"); + if (matcher == null || replacement == null) return original; + + matcher.reset(myOutputFileRef.get().getName()); + if (matcher.find()) { + final String newFileName = matcher.replaceFirst(replacement); + myOutputFileRef.set(new File(myOutputFileRef.get().getParentFile(), newFileName)); + } + return original; + } + try { + Class clazz = Class.forName(filter.filterType); + if (!FilterReader.class.isAssignableFrom(clazz)) { + myContext.processMessage( + new CompilerMessage( + GradleResourcesBuilder.BUILDER_NAME, BuildMessage.Kind.WARNING, + String.format("Error - Invalid filter specification for %s. It should extend java.io.FilterReader.", filter.filterType), null) + ); + } + Constructor constructor = clazz.getConstructor(Reader.class); + FilterReader result = (FilterReader)constructor.newInstance(original); + final Map properties = filter.getProperties(); + if (!properties.isEmpty()) { + ConfigureUtil.configureByMap(properties, result); + } + return result; + } + catch (Throwable th) { + myContext.processMessage(new CompilerMessage( + GradleResourcesBuilder.BUILDER_NAME, BuildMessage.Kind.WARNING, + String.format("Error - Failed to apply filter(%s): %s", filter.filterType, th.getMessage()), null) + ); + } + return original; + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleBuilderService.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleBuilderService.java new file mode 100644 index 000000000000..5219a2ccad7b --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleBuilderService.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.compiler; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.builders.BuildTargetType; +import org.jetbrains.jps.gradle.model.impl.GradleResourcesTargetType; +import org.jetbrains.jps.incremental.BuilderService; +import org.jetbrains.jps.incremental.TargetBuilder; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleBuilderService extends BuilderService { + @NotNull + @Override + public List> getTargetTypes() { + return Arrays.asList(GradleResourcesTargetType.PRODUCTION, GradleResourcesTargetType.TEST); + } + + @NotNull + @Override + public List> createBuilders() { + return Collections.singletonList(new GradleResourcesBuilder()); + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleResourceFileProcessor.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleResourceFileProcessor.java new file mode 100644 index 000000000000..dadfa21906a0 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleResourceFileProcessor.java @@ -0,0 +1,94 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.compiler; + +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.StreamUtil; +import org.apache.tools.ant.util.ReaderInputStream; +import org.jetbrains.jps.gradle.model.impl.GradleModuleResourceConfiguration; +import org.jetbrains.jps.gradle.model.impl.GradleProjectConfiguration; +import org.jetbrains.jps.gradle.model.impl.ResourceRootConfiguration; +import org.jetbrains.jps.gradle.model.impl.ResourceRootFilter; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.messages.BuildMessage; +import org.jetbrains.jps.incremental.messages.CompilerMessage; +import org.jetbrains.jps.model.JpsEncodingConfigurationService; +import org.jetbrains.jps.model.JpsEncodingProjectConfiguration; +import org.jetbrains.jps.model.JpsProject; + +import java.io.*; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleResourceFileProcessor { + private static final int FILTERING_SIZE_LIMIT = 10 * 1024 * 1024 /*10 mb*/; + protected final JpsEncodingProjectConfiguration myEncodingConfig; + protected final GradleProjectConfiguration myProjectConfig; + protected final GradleModuleResourceConfiguration myModuleConfiguration; + + public GradleResourceFileProcessor(GradleProjectConfiguration projectConfiguration, JpsProject project, + GradleModuleResourceConfiguration moduleConfiguration) { + myProjectConfig = projectConfiguration; + myEncodingConfig = JpsEncodingConfigurationService.getInstance().getEncodingConfiguration(project); + myModuleConfiguration = moduleConfiguration; + } + + public void copyFile(File file, Ref targetFileRef, ResourceRootConfiguration rootConfiguration, CompileContext context, + FileFilter filteringFilter) throws IOException { + boolean shouldFilter = rootConfiguration.isFiltered && !rootConfiguration.filters.isEmpty() && filteringFilter.accept(file); + if (shouldFilter && file.length() > FILTERING_SIZE_LIMIT) { + context.processMessage(new CompilerMessage( + GradleResourcesBuilder.BUILDER_NAME, BuildMessage.Kind.WARNING, + "File is too big to be filtered. Most likely it is a binary file and should be excluded from filtering", file.getPath()) + ); + shouldFilter = false; + } + if (shouldFilter) { + copyWithFiltering(file, targetFileRef, rootConfiguration.filters, context); + } + else { + FileUtil.copyContent(file, targetFileRef.get()); + } + } + + private static void copyWithFiltering(File file, Ref outputFileRef, List filters, CompileContext context) + throws IOException { + final FileInputStream originalInputStream = new FileInputStream(file); + try { + final InputStream inputStream = transform(filters, originalInputStream, outputFileRef, context); + FileUtil.createIfDoesntExist(outputFileRef.get()); + FileOutputStream outputStream = new FileOutputStream(outputFileRef.get()); + try { + FileUtil.copy(inputStream, outputStream); + } + finally { + StreamUtil.closeStream(inputStream); + StreamUtil.closeStream(outputStream); + } + } + finally { + StreamUtil.closeStream(originalInputStream); + } + } + + private static InputStream transform(List filters, FileInputStream original, Ref outputFileRef, CompileContext context) { + return new ReaderInputStream(new ChainingFilterTransformer(context, filters, outputFileRef).transform(new InputStreamReader(original))); + } +} \ No newline at end of file diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleResourcesBuilder.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleResourcesBuilder.java new file mode 100644 index 000000000000..295c8bab8133 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/compiler/GradleResourcesBuilder.java @@ -0,0 +1,128 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.compiler; + +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.builders.BuildOutputConsumer; +import org.jetbrains.jps.builders.DirtyFilesHolder; +import org.jetbrains.jps.builders.FileProcessor; +import org.jetbrains.jps.builders.storage.BuildDataPaths; +import org.jetbrains.jps.gradle.model.JpsGradleExtensionService; +import org.jetbrains.jps.gradle.model.impl.*; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.ProjectBuildException; +import org.jetbrains.jps.incremental.TargetBuilder; +import org.jetbrains.jps.incremental.messages.ProgressMessage; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleResourcesBuilder extends TargetBuilder { + public static final String BUILDER_NAME = "Gradle Resources Compiler"; + + public GradleResourcesBuilder() { + super(Arrays.asList(GradleResourcesTargetType.PRODUCTION, GradleResourcesTargetType.TEST)); + } + + @Override + public void build(@NotNull final GradleResourcesTarget target, + @NotNull final DirtyFilesHolder holder, + @NotNull final BuildOutputConsumer outputConsumer, + @NotNull final CompileContext context) throws ProjectBuildException, IOException { + final BuildDataPaths dataPaths = context.getProjectDescriptor().dataManager.getDataPaths(); + final GradleProjectConfiguration projectConfig = JpsGradleExtensionService.getInstance().getGradleProjectConfiguration(dataPaths); + final GradleModuleResourceConfiguration config = target.getModuleResourcesConfiguration(dataPaths); + if (config == null) return; + + final Map> files = new HashMap>(); + + holder.processDirtyFiles(new FileProcessor() { + + @Override + public boolean apply(GradleResourcesTarget t, File file, GradleResourceRootDescriptor rd) throws IOException { + assert target == t; + + List fileList = files.get(rd); + if (fileList == null) { + fileList = new ArrayList(); + files.put(rd, fileList); + } + + fileList.add(file); + return true; + } + }); + + GradleResourceRootDescriptor[] roots = files.keySet().toArray(new GradleResourceRootDescriptor[files.keySet().size()]); + Arrays.sort(roots, new Comparator() { + @Override + public int compare(GradleResourceRootDescriptor r1, GradleResourceRootDescriptor r2) { + int res = r1.getIndexInPom() - r2.getIndexInPom(); + if (r1.isOverwrite()) { + assert r2.isOverwrite(); + return res; + } + + if (r1.getConfiguration().isFiltered && !r2.getConfiguration().isFiltered) return 1; + if (!r1.getConfiguration().isFiltered && r2.getConfiguration().isFiltered) return -1; + + if (!r1.getConfiguration().isFiltered) { + res = -res; + } + + return res; + } + }); + + GradleResourceFileProcessor fileProcessor = new GradleResourceFileProcessor(projectConfig, target.getModule().getProject(), config); + + for (GradleResourceRootDescriptor rd : roots) { + for (File file : files.get(rd)) { + + String relPath = FileUtil.getRelativePath(rd.getRootFile(), file); + if (relPath == null) continue; + + final File outputDir = + GradleResourcesTarget.getOutputDir(target.getModuleOutputDir(), rd.getConfiguration(), config.outputDirectory); + if (outputDir == null) continue; + + context.processMessage(new ProgressMessage("Copying resources... [" + target.getModule().getName() + "]")); + + final Ref fileRef = Ref.create(new File(outputDir, relPath)); + fileProcessor.copyFile(file, fileRef, rd.getConfiguration(), context, FileUtilRt.ALL_FILES); + outputConsumer.registerOutputFile(fileRef.get(), Collections.singleton(file.getPath())); + + if (context.getCancelStatus().isCanceled()) return; + } + } + + context.checkCanceled(); + context.processMessage(new ProgressMessage("")); + } + + @NotNull + public String getPresentableName() { + return BUILDER_NAME; + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/JpsGradleExtensionService.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/JpsGradleExtensionService.java new file mode 100644 index 000000000000..126cf5758ac1 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/JpsGradleExtensionService.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.builders.storage.BuildDataPaths; +import org.jetbrains.jps.gradle.model.impl.GradleProjectConfiguration; +import org.jetbrains.jps.model.module.JpsDependencyElement; +import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.service.JpsServiceManager; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public abstract class JpsGradleExtensionService { + public static JpsGradleExtensionService getInstance() { + return JpsServiceManager.getInstance().getService(JpsGradleExtensionService.class); + } + + @Nullable + public abstract JpsGradleModuleExtension getExtension(@NotNull JpsModule module); + + @NotNull + public abstract JpsGradleModuleExtension getOrCreateExtension(@NotNull JpsModule module); + + public abstract void setProductionOnTestDependency(@NotNull JpsDependencyElement dependency, boolean value); + + public abstract boolean isProductionOnTestDependency(@NotNull JpsDependencyElement dependency); + + public abstract boolean hasGradleProjectConfiguration(@NotNull BuildDataPaths paths); + + @NotNull + public abstract GradleProjectConfiguration getGradleProjectConfiguration(BuildDataPaths paths); +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/JpsGradleModuleExtension.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/JpsGradleModuleExtension.java new file mode 100644 index 000000000000..c673e4c0d27c --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/JpsGradleModuleExtension.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model; + +import org.jetbrains.jps.model.JpsElement; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public interface JpsGradleModuleExtension extends JpsElement { +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/FilePattern.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/FilePattern.java new file mode 100644 index 000000000000..98938f596d05 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/FilePattern.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.Tag; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class FilePattern { + + @Tag("includes") + @AbstractCollection(surroundWithTag = false, elementTag = "pattern") + public Set includes = new HashSet(); + + @Tag("excludes") + @AbstractCollection(surroundWithTag = false, elementTag = "pattern") + public Set excludes = new HashSet(); + +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleModuleResourceConfiguration.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleModuleResourceConfiguration.java new file mode 100644 index 000000000000..9b6732a6d5d5 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleModuleResourceConfiguration.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.OptionTag; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleModuleResourceConfiguration { + @NotNull + @Tag("id") + public ModuleVersion id; + + @Nullable + @Tag("parentId") + public ModuleVersion parentId; + + @NotNull + @Tag("directory") + public String directory; + + @OptionTag + public boolean overwrite; + + @OptionTag + public String outputDirectory = null; + + @Tag("resources") + @AbstractCollection(surroundWithTag = false, elementTag = "resource") + public List resources = new ArrayList(); + + @Tag("test-resources") + @AbstractCollection(surroundWithTag = false, elementTag = "resource") + public List testResources = new ArrayList(); + + public int computeConfigurationHash(boolean forTestResources) { + int result = computeModuleConfigurationHash(); + + final List _resources = forTestResources ? testResources : resources; + result = 31 * result; + for (ResourceRootConfiguration resource : _resources) { + result += resource.computeConfigurationHash(); + } + return result; + } + + public int computeModuleConfigurationHash() { + int result = id.hashCode(); + result = 31 * result + (parentId != null ? parentId.hashCode() : 0); + result = 31 * result + directory.hashCode(); + result = 31 * result + (outputDirectory != null ? outputDirectory.hashCode() : 0); + result = 31 * result + (overwrite ? 1 : 0); + return result; + } +} + + + diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleProjectConfiguration.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleProjectConfiguration.java new file mode 100644 index 000000000000..083bba570d8a --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleProjectConfiguration.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.util.xmlb.annotations.MapAnnotation; +import com.intellij.util.xmlb.annotations.Tag; +import gnu.trove.THashMap; + +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleProjectConfiguration { + public static final String CONFIGURATION_FILE_RELATIVE_PATH = "gradle/configuration.xml"; + + @Tag("resource-processing") + @MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false, entryTagName = "gradle-module", + keyAttributeName = "name") + public Map moduleConfigurations = new THashMap(); +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourceFileFilter.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourceFileFilter.java new file mode 100644 index 000000000000..60e7fd295daa --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourceFileFilter.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.openapi.util.io.FileUtil; +import org.gradle.api.file.RelativePath; +import org.gradle.api.internal.file.pattern.PatternMatcherFactory; +import org.gradle.api.specs.Spec; +import org.gradle.api.specs.Specs; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleResourceFileFilter implements FileFilter { + private FilePattern myFilePattern; + private File myRoot; + private final Spec myFileFilterSpec; + + public GradleResourceFileFilter(@NotNull File rootFile, @NotNull FilePattern filePattern) { + myFilePattern = filePattern; + myRoot = rootFile; + myFileFilterSpec = getAsSpec(); + } + + @Override + public boolean accept(@NotNull File file) { + final String relPath = FileUtil.getRelativePath(myRoot, file); + return relPath != null && isIncluded(relPath); + } + + private boolean isIncluded(@NotNull String relativePath) { + RelativePath path = new RelativePath(true, relativePath.split(Pattern.quote(File.separator))); + return myFileFilterSpec.isSatisfiedBy(path); + } + + private Spec getAsSpec() { + return Specs.and(getAsIncludeSpec(true), Specs.not(getAsExcludeSpec(true))); + } + + private Spec getAsExcludeSpec(boolean caseSensitive) { + Collection allExcludes = new LinkedHashSet(myFilePattern.excludes); + List> matchers = new ArrayList>(); + for (String exclude : allExcludes) { + Spec patternMatcher = PatternMatcherFactory.getPatternMatcher(false, caseSensitive, exclude); + matchers.add(patternMatcher); + } + return Specs.or(false, matchers); + } + + private Spec getAsIncludeSpec(boolean caseSensitive) { + List> matchers = new ArrayList>(); + for (String include : myFilePattern.includes) { + Spec patternMatcher = PatternMatcherFactory.getPatternMatcher(true, caseSensitive, include); + matchers.add(patternMatcher); + } + return Specs.or(true, matchers); + } +} \ No newline at end of file diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourceRootDescriptor.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourceRootDescriptor.java new file mode 100644 index 000000000000..e8ae0d701200 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourceRootDescriptor.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.builders.BuildRootDescriptor; + +import java.io.File; +import java.io.FileFilter; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleResourceRootDescriptor extends BuildRootDescriptor { + private final GradleResourcesTarget myTarget; + private final ResourceRootConfiguration myConfig; + private final File myFile; + private final String myId; + private final boolean myOverwrite; + + private final int myIndexInPom; + + public GradleResourceRootDescriptor(@NotNull GradleResourcesTarget target, + ResourceRootConfiguration config, + int indexInPom, + boolean overwrite) { + myTarget = target; + myConfig = config; + final String path = FileUtil.toCanonicalPath(config.directory); + myFile = new File(path); + myId = path; + myIndexInPom = indexInPom; + myOverwrite = overwrite; + } + + public ResourceRootConfiguration getConfiguration() { + return myConfig; + } + + @Override + public String getRootId() { + return myId; + } + + @Override + public File getRootFile() { + return myFile; + } + + @Override + public GradleResourcesTarget getTarget() { + return myTarget; + } + + @NotNull + @Override + public FileFilter createFileFilter() { + return new GradleResourceFileFilter(myFile, myConfig); + } + + @Override + public boolean canUseFileCache() { + return true; + } + + public int getIndexInPom() { + return myIndexInPom; + } + + public boolean isOverwrite() { + return myOverwrite; + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourcesTarget.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourcesTarget.java new file mode 100644 index 000000000000..344c83080186 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourcesTarget.java @@ -0,0 +1,161 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.builders.*; +import org.jetbrains.jps.builders.storage.BuildDataPaths; +import org.jetbrains.jps.cmdline.ProjectDescriptor; +import org.jetbrains.jps.gradle.model.JpsGradleExtensionService; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.indices.IgnoredFileIndex; +import org.jetbrains.jps.indices.ModuleExcludeIndex; +import org.jetbrains.jps.model.JpsModel; +import org.jetbrains.jps.model.java.JpsJavaExtensionService; +import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.util.JpsPathUtil; + +import java.io.File; +import java.io.PrintWriter; +import java.util.*; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleResourcesTarget extends ModuleBasedTarget { + + GradleResourcesTarget(final GradleResourcesTargetType type, @NotNull JpsModule module) { + super(type, module); + } + + @Override + public String getId() { + return myModule.getName(); + } + + @Override + public Collection> computeDependencies(BuildTargetRegistry targetRegistry, TargetOutputIndex outputIndex) { + return Collections.emptyList(); + } + + @Override + public boolean isCompiledBeforeModuleLevelBuilders() { + return true; + } + + @NotNull + @Override + public List computeRootDescriptors(JpsModel model, ModuleExcludeIndex index, IgnoredFileIndex ignoredFileIndex, BuildDataPaths dataPaths) { + final List result = new ArrayList(); + + GradleProjectConfiguration projectConfig = JpsGradleExtensionService.getInstance().getGradleProjectConfiguration(dataPaths); + GradleModuleResourceConfiguration moduleConfig = projectConfig.moduleConfigurations.get(myModule.getName()); + if (moduleConfig == null) return Collections.emptyList(); + + int i = 0; + + for (ResourceRootConfiguration resource : getRootConfigurations(moduleConfig)) { + result.add(new GradleResourceRootDescriptor(this, resource, i++, moduleConfig.overwrite)); + } + return result; + } + + private Collection getRootConfigurations(@Nullable GradleModuleResourceConfiguration moduleConfig) { + if (moduleConfig != null) { + return isTests() ? moduleConfig.testResources : moduleConfig.resources; + } + return Collections.emptyList(); + } + + public GradleModuleResourceConfiguration getModuleResourcesConfiguration(BuildDataPaths dataPaths) { + final GradleProjectConfiguration projectConfig = JpsGradleExtensionService.getInstance().getGradleProjectConfiguration(dataPaths); + return projectConfig.moduleConfigurations.get(myModule.getName()); + } + + public boolean isTests() { + return ((GradleResourcesTargetType)getTargetType()).isTests(); + } + + @Nullable + @Override + public GradleResourceRootDescriptor findRootDescriptor(String rootId, BuildRootIndex rootIndex) { + for (GradleResourceRootDescriptor descriptor : rootIndex.getTargetRoots(this, null)) { + if (descriptor.getRootId().equals(rootId)) { + return descriptor; + } + } + return null; + } + + @NotNull + @Override + public String getPresentableName() { + return getTargetType().getTypeId() + ":" + myModule.getName(); + } + + @NotNull + @Override + public Collection getOutputRoots(CompileContext context) { + GradleModuleResourceConfiguration configuration = + getModuleResourcesConfiguration(context.getProjectDescriptor().dataManager.getDataPaths()); + final Set result = new THashSet(FileUtil.FILE_HASHING_STRATEGY); + final File moduleOutput = getModuleOutputDir(); + for (ResourceRootConfiguration resConfig : getRootConfigurations(configuration)) { + final File output = getOutputDir(moduleOutput, resConfig, configuration.outputDirectory); + if (output != null) { + result.add(output); + } + } + return result; + } + + @Nullable + public File getModuleOutputDir() { + return JpsJavaExtensionService.getInstance().getOutputDirectory(myModule, isTests()); + } + + @Nullable + public static File getOutputDir(@Nullable File moduleOutput, ResourceRootConfiguration config, @Nullable String outputDirectory) { + if(outputDirectory != null) { + moduleOutput = JpsPathUtil.urlToFile(outputDirectory); + } + + if (moduleOutput == null) { + return null; + } + String targetPath = config.targetPath; + if (StringUtil.isEmptyOrSpaces(targetPath)) { + return moduleOutput; + } + final File targetPathFile = new File(targetPath); + final File outputFile = targetPathFile.isAbsolute() ? targetPathFile : new File(moduleOutput, targetPath); + return new File(FileUtil.toCanonicalPath(outputFile.getPath())); + } + + @Override + public void writeConfiguration(ProjectDescriptor pd, PrintWriter out) { + final BuildDataPaths dataPaths = pd.getTargetsState().getDataPaths(); + final GradleModuleResourceConfiguration configuration = getModuleResourcesConfiguration(dataPaths); + if (configuration != null) { + out.write(Integer.toHexString(configuration.computeConfigurationHash(isTests()))); + } + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourcesTargetType.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourcesTargetType.java new file mode 100644 index 000000000000..bd399a164d31 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/GradleResourcesTargetType.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.builders.BuildTargetLoader; +import org.jetbrains.jps.builders.ModuleBasedBuildTargetType; +import org.jetbrains.jps.gradle.model.JpsGradleExtensionService; +import org.jetbrains.jps.model.JpsModel; +import org.jetbrains.jps.model.module.JpsModule; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleResourcesTargetType extends ModuleBasedBuildTargetType { + public static final GradleResourcesTargetType PRODUCTION = new GradleResourcesTargetType("gradle-resources-production", false); + public static final GradleResourcesTargetType TEST = new GradleResourcesTargetType("gradle-resources-test", true); + + private final boolean myIsTests; + + private GradleResourcesTargetType(final String typeId, boolean isTests) { + super(typeId); + myIsTests = isTests; + } + + public boolean isTests() { + return myIsTests; + } + + @NotNull + @Override + public List computeAllTargets(@NotNull JpsModel model) { + final List targets = new ArrayList(); + for (JpsModule module : model.getProject().getModules()) { + if (JpsGradleExtensionService.getInstance().getExtension(module) != null) { + targets.add(new GradleResourcesTarget(this, module)); + } + } + return targets; + } + + @NotNull + @Override + public BuildTargetLoader createLoader(@NotNull JpsModel model) { + final Map modules = new HashMap(); + for (JpsModule module : model.getProject().getModules()) { + modules.put(module.getName(), module); + } + return new BuildTargetLoader() { + @Nullable + @Override + public GradleResourcesTarget createTarget(@NotNull String targetId) { + final JpsModule module = modules.get(targetId); + return module != null ? new GradleResourcesTarget(GradleResourcesTargetType.this, module) : null; + } + }; + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleDependenciesEnumerationHandler.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleDependenciesEnumerationHandler.java new file mode 100644 index 000000000000..af0fd08fb6cf --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleDependenciesEnumerationHandler.java @@ -0,0 +1,57 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.gradle.model.JpsGradleExtensionService; +import org.jetbrains.jps.model.java.impl.JpsJavaDependenciesEnumerationHandler; +import org.jetbrains.jps.model.module.JpsDependencyElement; +import org.jetbrains.jps.model.module.JpsModule; + +import java.util.Collection; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class JpsGradleDependenciesEnumerationHandler extends JpsJavaDependenciesEnumerationHandler { + private static final JpsGradleDependenciesEnumerationHandler INSTANCE = new JpsGradleDependenciesEnumerationHandler(); + + @Override + public boolean shouldAddRuntimeDependenciesToTestCompilationClasspath() { + return true; + } + + @Override + public boolean isProductionOnTestsDependency(JpsDependencyElement element) { + return JpsGradleExtensionService.getInstance().isProductionOnTestDependency(element); + } + + public static class GradleFactory extends Factory { + @Nullable + @Override + public JpsJavaDependenciesEnumerationHandler createHandler(@NotNull Collection modules) { + JpsGradleExtensionService service = JpsGradleExtensionService.getInstance(); + for (JpsModule module : modules) { + if (service.getExtension(module) != null) { + return INSTANCE; + } + } + return null; + } + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleExtensionServiceImpl.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleExtensionServiceImpl.java new file mode 100644 index 000000000000..81ed9076f13e --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleExtensionServiceImpl.java @@ -0,0 +1,136 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.containers.ConcurrentFactoryMap; +import com.intellij.util.containers.FactoryMap; +import com.intellij.util.xmlb.XmlSerializer; +import gnu.trove.THashMap; +import org.jdom.Document; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.builders.storage.BuildDataPaths; +import org.jetbrains.jps.gradle.model.JpsGradleExtensionService; +import org.jetbrains.jps.gradle.model.JpsGradleModuleExtension; +import org.jetbrains.jps.incremental.resources.ResourcesBuilder; +import org.jetbrains.jps.incremental.resources.StandardResourceBuilderEnabler; +import org.jetbrains.jps.model.JpsElementChildRole; +import org.jetbrains.jps.model.JpsElementFactory; +import org.jetbrains.jps.model.JpsSimpleElement; +import org.jetbrains.jps.model.ex.JpsElementChildRoleBase; +import org.jetbrains.jps.model.module.JpsDependencyElement; +import org.jetbrains.jps.model.module.JpsModule; + +import java.io.File; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class JpsGradleExtensionServiceImpl extends JpsGradleExtensionService { + private static final Logger LOG = Logger.getInstance(JpsGradleExtensionServiceImpl.class); + private static final JpsElementChildRole> PRODUCTION_ON_TEST_ROLE = JpsElementChildRoleBase.create("production on test"); + private final Map myLoadedConfigs = + new THashMap(FileUtil.FILE_HASHING_STRATEGY); + private final FactoryMap myConfigFileExists = new ConcurrentFactoryMap() { + @Nullable + @Override + protected Boolean create(File key) { + return key.exists(); + } + }; + + public JpsGradleExtensionServiceImpl() { + ResourcesBuilder.registerEnabler(new StandardResourceBuilderEnabler() { + @Override + public boolean isResourceProcessingEnabled(JpsModule module) { + // enable standard resource processing only if this is not a gradle module + // for gradle modules use gradle-aware resource builder + return getExtension(module) == null; + } + }); + } + + @Nullable + @Override + public JpsGradleModuleExtension getExtension(@NotNull JpsModule module) { + return module.getContainer().getChild(JpsGradleModuleExtensionImpl.ROLE); + } + + @NotNull + @Override + public JpsGradleModuleExtension getOrCreateExtension(@NotNull JpsModule module) { + JpsGradleModuleExtension extension = module.getContainer().getChild(JpsGradleModuleExtensionImpl.ROLE); + if (extension == null) { + extension = new JpsGradleModuleExtensionImpl(); + module.getContainer().setChild(JpsGradleModuleExtensionImpl.ROLE, extension); + } + return extension; + } + + @Override + public void setProductionOnTestDependency(@NotNull JpsDependencyElement dependency, boolean value) { + if (value) { + dependency.getContainer().setChild(PRODUCTION_ON_TEST_ROLE, JpsElementFactory.getInstance().createSimpleElement(true)); + } + else { + dependency.getContainer().removeChild(PRODUCTION_ON_TEST_ROLE); + } + } + + @Override + public boolean isProductionOnTestDependency(@NotNull JpsDependencyElement dependency) { + JpsSimpleElement child = dependency.getContainer().getChild(PRODUCTION_ON_TEST_ROLE); + return child != null && child.getData(); + } + + @Override + public boolean hasGradleProjectConfiguration(@NotNull BuildDataPaths paths) { + return myConfigFileExists.get(new File(paths.getDataStorageRoot(), GradleProjectConfiguration.CONFIGURATION_FILE_RELATIVE_PATH)); + } + + @NotNull + @Override + public GradleProjectConfiguration getGradleProjectConfiguration(BuildDataPaths paths) { + final File dataStorageRoot = paths.getDataStorageRoot(); + return getGradleProjectConfiguration(dataStorageRoot); + } + + @NotNull + public GradleProjectConfiguration getGradleProjectConfiguration(@NotNull File dataStorageRoot) { + final File configFile = new File(dataStorageRoot, GradleProjectConfiguration.CONFIGURATION_FILE_RELATIVE_PATH); + GradleProjectConfiguration config; + synchronized (myLoadedConfigs) { + config = myLoadedConfigs.get(configFile); + if (config == null) { + config = new GradleProjectConfiguration(); + try { + final Document document = JDOMUtil.loadDocument(configFile); + XmlSerializer.deserializeInto(config, document.getRootElement()); + } + catch (Exception e) { + LOG.info(e); + } + myLoadedConfigs.put(configFile, config); + } + } + return config; + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleModelSerializationExtension.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleModelSerializationExtension.java new file mode 100644 index 000000000000..cebd00878b19 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleModelSerializationExtension.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.gradle.model.JpsGradleExtensionService; +import org.jetbrains.jps.model.module.JpsDependencyElement; +import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class JpsGradleModelSerializationExtension extends JpsModelSerializerExtension { + private static final String PRODUCTION_ON_TEST_ATTRIBUTE = "production-on-test"; + + @Override + public void loadModuleOptions(@NotNull JpsModule module, @NotNull Element rootElement) { + if ("GRADLE".equals(rootElement.getAttributeValue("external.system.id"))) { + JpsGradleExtensionService.getInstance().getOrCreateExtension(module); + } + } + + @Override + public void saveModuleOptions(@NotNull JpsModule module, @NotNull Element rootElement) { + if (JpsGradleExtensionService.getInstance().getExtension(module) != null) { + rootElement.setAttribute("external.system.id", "GRADLE"); + } + } + + @Override + public void loadModuleDependencyProperties(JpsDependencyElement dependency, Element orderEntry) { + if (orderEntry.getAttributeValue(PRODUCTION_ON_TEST_ATTRIBUTE) != null) { + JpsGradleExtensionService.getInstance().setProductionOnTestDependency(dependency, true); + } + } + + @Override + public void saveModuleDependencyProperties(JpsDependencyElement dependency, Element orderEntry) { + if (JpsGradleExtensionService.getInstance().isProductionOnTestDependency(dependency)) { + orderEntry.setAttribute(PRODUCTION_ON_TEST_ATTRIBUTE, ""); + } + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleModuleExtensionImpl.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleModuleExtensionImpl.java new file mode 100644 index 000000000000..ecdd04fce10e --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/JpsGradleModuleExtensionImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.gradle.model.JpsGradleModuleExtension; +import org.jetbrains.jps.model.JpsElementChildRole; +import org.jetbrains.jps.model.ex.JpsElementBase; +import org.jetbrains.jps.model.ex.JpsElementChildRoleBase; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class JpsGradleModuleExtensionImpl extends JpsElementBase implements JpsGradleModuleExtension { + public static final JpsElementChildRole ROLE = JpsElementChildRoleBase.create("gradle"); + + public JpsGradleModuleExtensionImpl() { + } + + @NotNull + @Override + public JpsGradleModuleExtensionImpl createCopy() { + return new JpsGradleModuleExtensionImpl(); + } + + @Override + public void applyChanges(@NotNull JpsGradleModuleExtensionImpl modified) { + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ModuleVersion.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ModuleVersion.java new file mode 100644 index 000000000000..dbb696d8c33c --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ModuleVersion.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.util.xmlb.annotations.Tag; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class ModuleVersion { + @Tag("groupId") + public String groupId; + + @Tag("artifactId") + public String artifactId; + + @Tag("version") + public String version; + + public ModuleVersion() { + } + + public ModuleVersion(String groupId, String artifactId, String version) { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ModuleVersion bean = (ModuleVersion)o; + + if (artifactId != null ? !artifactId.equals(bean.artifactId) : bean.artifactId != null) return false; + if (groupId != null ? !groupId.equals(bean.groupId) : bean.groupId != null) return false; + if (version != null ? !version.equals(bean.version) : bean.version != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = groupId != null ? groupId.hashCode() : 0; + result = 31 * result + (artifactId != null ? artifactId.hashCode() : 0); + result = 31 * result + (version != null ? version.hashCode() : 0); + return result; + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ResourceRootConfiguration.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ResourceRootConfiguration.java new file mode 100644 index 000000000000..efd419175dc3 --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ResourceRootConfiguration.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.Attribute; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +@Tag("resource") +public class ResourceRootConfiguration extends FilePattern { + @Tag("directory") + @NotNull + public String directory; + + @Tag("targetPath") + @Nullable + public String targetPath; + + @Attribute("filtered") + public boolean isFiltered; + + @Tag("filters") + @AbstractCollection(surroundWithTag = false, elementTag = "filter") + public List filters = new ArrayList(); + + public int computeConfigurationHash() { + int result = directory.hashCode(); + result = 31 * result + (targetPath != null ? targetPath.hashCode() : 0); + result = 31 * result + (isFiltered ? 1 : 0); + result = 31 * result + includes.hashCode(); + result = 31 * result + excludes.hashCode(); + for (ResourceRootFilter filter : filters) { + result = 31 * result + filter.computeConfigurationHash(); + } + return result; + } +} diff --git a/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ResourceRootFilter.java b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ResourceRootFilter.java new file mode 100644 index 000000000000..e8c39e32417e --- /dev/null +++ b/plugins/gradle/jps-plugin/src/org/jetbrains/jps/gradle/model/impl/ResourceRootFilter.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.jps.gradle.model.impl; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import com.intellij.util.xmlb.annotations.Tag; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Vladislav.Soroka + * @since 7/22/2014 + */ +@Tag("filter") +public class ResourceRootFilter { + @Tag("filterType") + @NotNull + public String filterType; + @Tag("properties") + @NotNull + public String properties; + + private transient Map propertiesMap; + + public int computeConfigurationHash() { + int result = filterType.hashCode(); + result = 31 * result + properties.hashCode(); + return result; + } + + @NotNull + public Map getProperties() { + if (propertiesMap == null) { + try { + Gson gson = new GsonBuilder().create(); + propertiesMap = gson.fromJson( + properties, + new TypeToken>() { + }.getType()); + + if("RenamingCopyFilter".equals(filterType)) { + final Object pattern = propertiesMap.get("pattern"); + final Matcher matcher = Pattern.compile(pattern instanceof String ? (String)pattern : "").matcher(""); + propertiesMap.put("matcher", matcher); + } + } + catch (JsonSyntaxException e) { + throw new RuntimeException("Unsupported filter: " + properties , e); + } catch (JsonParseException e) { + throw new RuntimeException("Unsupported filter: " + properties , e); + } + } + return propertiesMap; + } +} diff --git a/plugins/gradle/src/META-INF/gradle-maven-integration.xml b/plugins/gradle/src/META-INF/gradle-maven-integration.xml index 72f46b785ea7..b8fad2f5c3be 100644 --- a/plugins/gradle/src/META-INF/gradle-maven-integration.xml +++ b/plugins/gradle/src/META-INF/gradle-maven-integration.xml @@ -21,7 +21,6 @@ - diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index de6937c18c6c..f15f424a10b8 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -67,6 +67,11 @@ + + + + + @@ -95,6 +100,7 @@ + diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java new file mode 100644 index 000000000000..1b0386e47be7 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.gradle.compiler; + +import com.google.gson.Gson; +import com.intellij.compiler.server.BuildProcessParametersProvider; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.PathUtil; +import com.intellij.util.containers.ContainerUtil; +import groovy.lang.GroovyObject; +import org.apache.tools.ant.taskdefs.Ant; +import org.gradle.tooling.ProjectConnection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.util.GradleConstants; + +import java.io.File; +import java.util.List; + +/** + * Adds Gradle build dependencies to the project build process' classpath if it has at least one gradle module. + * + * @author Vladislav.Soroka + * @since 7/22/2014 + */ +public class GradleBuildProcessParametersProvider extends BuildProcessParametersProvider { + @NotNull private final Project myProject; + + private List myClasspath; + + public GradleBuildProcessParametersProvider(@NotNull Project project) { + myProject = project; + } + + @Override + @NotNull + public List getClassPath() { + if (myClasspath == null) { + final ModuleManager moduleManager = ModuleManager.getInstance(myProject); + for (Module module : moduleManager.getModules()) { + if (ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) { + myClasspath = ContainerUtil.newArrayList(); + addGradleClassPath(myClasspath); + addOtherClassPath(myClasspath); + break; + } + } + } + return myClasspath; + } + + private static void addGradleClassPath(@NotNull final List classpath) { + String gradleLibDirPath = null; + String gradleToolingApiJarPath = PathUtil.getJarPathForClass(ProjectConnection.class); + if (!StringUtil.isEmpty(gradleToolingApiJarPath)) { + gradleLibDirPath = PathUtil.getParentPath(gradleToolingApiJarPath); + } + if (gradleLibDirPath == null || gradleLibDirPath.isEmpty()) return; + + File gradleLibDir = new File(gradleLibDirPath); + if (!gradleLibDir.isDirectory()) return; + + File[] children = FileUtil.notNullize(gradleLibDir.listFiles()); + for (File child : children) { + if (child.isFile() && child.getName().endsWith(".jar")) { + classpath.add(child.getAbsolutePath()); + } + } + } + + private static void addOtherClassPath(@NotNull final List classpath) { + classpath.add(PathUtil.getJarPathForClass(Ant.class)); + classpath.add(PathUtil.getJarPathForClass(GroovyObject.class)); + classpath.add(PathUtil.getJarPathForClass(Gson.class)); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java new file mode 100644 index 000000000000..01c5275f3828 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/config/GradleResourceCompilerConfigurationGenerator.java @@ -0,0 +1,186 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.gradle.config; + +import com.intellij.compiler.server.BuildManager; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompilerMessageCategory; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.ExternalFilter; +import com.intellij.openapi.externalSystem.model.ExternalProject; +import com.intellij.openapi.externalSystem.model.ExternalSourceDirectorySet; +import com.intellij.openapi.externalSystem.model.ExternalSourceSet; +import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.CompilerModuleExtension; +import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.util.containers.FactoryMap; +import com.intellij.util.xmlb.XmlSerializer; +import org.jdom.Document; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.gradle.model.impl.*; +import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService; +import org.jetbrains.plugins.gradle.util.GradleConstants; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/10/2014 + */ +public class GradleResourceCompilerConfigurationGenerator { + + private static Logger LOG = Logger.getInstance(GradleResourceCompilerConfigurationGenerator.class); + + @NotNull private final Project myProject; + @NotNull private final CompileContext myContext; + @NotNull private final Map myExternalProjectMap; + private final ExternalProjectDataService myExternalProjectDataService; + + public GradleResourceCompilerConfigurationGenerator(@NotNull final Project project, @NotNull final CompileContext context) { + myProject = project; + myContext = context; + myExternalProjectDataService = + (ExternalProjectDataService)ServiceManager.getService(ProjectDataManager.class).getDataService(ExternalProjectDataService.KEY); + assert myExternalProjectDataService != null; + + myExternalProjectMap = new FactoryMap() { + @Nullable + @Override + protected ExternalProject create(String gradleProjectPath) { + return myExternalProjectDataService.getOrImportRootExternalProject(project, GradleConstants.SYSTEM_ID, new File(gradleProjectPath)); + } + }; + } + + public void generateBuildConfiguration() { + if (!hasGradleModules()) return; + + final BuildManager buildManager = BuildManager.getInstance(); + final File projectSystemDir = buildManager.getProjectSystemDirectory(myProject); + if (projectSystemDir == null) return; + + final File gradleConfigFile = new File(projectSystemDir, GradleProjectConfiguration.CONFIGURATION_FILE_RELATIVE_PATH); + + GradleProjectConfiguration projectConfig = new GradleProjectConfiguration(); + for (Module module : myContext.getCompileScope().getAffectedModules()) { + if (!ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) continue; + + final String gradleProjectPath = module.getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY); + assert gradleProjectPath != null; + final ExternalProject externalRootProject = myExternalProjectMap.get(gradleProjectPath); + if (externalRootProject == null) { + myContext.addMessage(CompilerMessageCategory.ERROR, + String.format("Unable to make the module: %s, related gradle module configuration was not imported", + module.getName()), + VfsUtilCore.pathToUrl(gradleProjectPath), -1, -1); + continue; + } + + ExternalProject externalProject = myExternalProjectDataService.findExternalProject(externalRootProject, module); + if (externalProject == null) { + LOG.warn("Unable to find config for module: " + module.getName()); + continue; + } + + final CompilerModuleExtension compilerModuleExtension = CompilerModuleExtension.getInstance(module); + assert compilerModuleExtension != null; + + GradleModuleResourceConfiguration resourceConfig = new GradleModuleResourceConfiguration(); + resourceConfig.id = new ModuleVersion(externalProject.getGroup(), externalProject.getName(), externalProject.getVersion()); + resourceConfig.directory = FileUtil.toSystemIndependentName(externalProject.getProjectDir().getPath()); + + final ExternalSourceSet mainSourcesSet = externalProject.getSourceSets().get("main"); + addResources(resourceConfig.resources, mainSourcesSet, ExternalSystemSourceType.RESOURCE); + + final ExternalSourceSet testSourcesSet = externalProject.getSourceSets().get("test"); + addResources(resourceConfig.testResources, testSourcesSet, ExternalSystemSourceType.TEST_RESOURCE); + + projectConfig.moduleConfigurations.put(module.getName(), resourceConfig); + } + + final Document document = new Document(new Element("gradle-project-configuration")); + XmlSerializer.serializeInto(projectConfig, document.getRootElement()); + buildManager.runCommand(new Runnable() { + @Override + public void run() { + buildManager.clearState(myProject); + FileUtil.createIfDoesntExist(gradleConfigFile); + try { + JDOMUtil.writeDocument(document, gradleConfigFile, "\n"); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + + private boolean hasGradleModules() { + for (Module module : myContext.getCompileScope().getAffectedModules()) { + if (ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) return true; + } + return false; + } + + private static void addResources(@NotNull List container, + @Nullable ExternalSourceSet externalSourceSet, + @NotNull ExternalSystemSourceType sourceType) { + if (externalSourceSet == null) return; + final ExternalSourceDirectorySet directorySet = externalSourceSet.getSources().get(sourceType); + if (directorySet == null) return; + + for (File file : directorySet.getSrcDirs()) { + final String dir = file.getPath(); + final ResourceRootConfiguration rootConfiguration = new ResourceRootConfiguration(); + rootConfiguration.directory = FileUtil.toSystemIndependentName(dir); + final String target = directorySet.getOutputDir().getPath(); + rootConfiguration.targetPath = FileUtil.toSystemIndependentName(target); + + rootConfiguration.includes.clear(); + for (String include : directorySet.getIncludes()) { + rootConfiguration.includes.add(include.trim()); + } + rootConfiguration.excludes.clear(); + for (String exclude : directorySet.getExcludes()) { + rootConfiguration.excludes.add(exclude.trim()); + } + + rootConfiguration.isFiltered = !directorySet.getFilters().isEmpty(); + rootConfiguration.filters.clear(); + for (ExternalFilter filter : directorySet.getFilters()) { + final ResourceRootFilter resourceRootFilter = new ResourceRootFilter(); + resourceRootFilter.filterType = filter.getFilterType(); + resourceRootFilter.properties = filter.getPropertiesAsJsonMap(); + rootConfiguration.filters.add(resourceRootFilter); + } + + container.add(rootConfiguration); + } + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java new file mode 100644 index 000000000000..0dc670dbefa8 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java @@ -0,0 +1,114 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.gradle.execution; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.ExternalProject; +import com.intellij.openapi.externalSystem.model.ExternalSourceDirectorySet; +import com.intellij.openapi.externalSystem.model.ExternalSourceSet; +import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleRootModel; +import com.intellij.openapi.roots.OrderEnumerationHandler; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.vfs.VfsUtilCore; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService; +import org.jetbrains.plugins.gradle.util.GradleConstants; + +import java.io.File; +import java.util.Collection; + +public class GradleOrderEnumeratorHandler extends OrderEnumerationHandler { + private static final Logger LOG = Logger.getInstance(GradleOrderEnumeratorHandler.class); + + public static class FactoryImpl extends Factory { + @Override + public boolean isApplicable(@NotNull Project project) { + return true; + } + + @Override + public boolean isApplicable(@NotNull Module module) { + return ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module); + } + + @Override + public OrderEnumerationHandler createHandler(@Nullable Module module) { + return INSTANCE; + } + } + + private static final GradleOrderEnumeratorHandler INSTANCE = new GradleOrderEnumeratorHandler(); + + @Override + public boolean addCustomModuleRoots(@NotNull OrderRootType type, + @NotNull ModuleRootModel rootModel, + @NotNull Collection result, + boolean includeProduction, + boolean includeTests) { + if (!type.equals(OrderRootType.CLASSES)) return false; + if (!ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, rootModel.getModule())) return false; + + final String gradleProjectPath = rootModel.getModule().getOptionValue(ExternalSystemConstants.ROOT_PROJECT_PATH_KEY); + if (gradleProjectPath == null) { + LOG.error("Root project path of the Gradle project not found for " + rootModel.getModule()); + return false; + } + + final ExternalProjectDataService externalProjectDataService = + (ExternalProjectDataService)ServiceManager.getService(ProjectDataManager.class).getDataService(ExternalProjectDataService.KEY); + + assert externalProjectDataService != null; + final ExternalProject externalRootProject = + externalProjectDataService.getRootExternalProject(GradleConstants.SYSTEM_ID, new File(gradleProjectPath)); + if (externalRootProject == null) { + LOG.debug("Root external project was not yep imported for the project path: " + gradleProjectPath); + return false; + } + + ExternalProject externalProject = externalProjectDataService.findExternalProject(externalRootProject, rootModel.getModule()); + if (externalProject == null) return false; + + if (includeProduction) { + addSourceRoots(externalProject.getSourceSets().get("main"), ExternalSystemSourceType.RESOURCE, result); + } + + if (includeTests) { + addSourceRoots(externalProject.getSourceSets().get("test"), ExternalSystemSourceType.TEST_RESOURCE, result); + } + + return true; + } + + private static void addSourceRoots(@Nullable ExternalSourceSet externalSourceSet, + @NotNull ExternalSystemSourceType sourceType, + @NotNull Collection result) { + if (externalSourceSet == null) return; + final ExternalSourceDirectorySet directorySet = externalSourceSet.getSources().get(sourceType); + if (directorySet == null) return; + + for (File file : directorySet.getSrcDirs()) { + result.add(VfsUtilCore.pathToUrl(file.getAbsolutePath())); + } + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java index 1d8addd31369..9603470a0878 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/AbstractProjectResolverExtension.java @@ -28,6 +28,7 @@ import com.intellij.openapi.externalSystem.util.Order; import com.intellij.openapi.util.KeyValue; import com.intellij.util.Consumer; import org.gradle.tooling.model.idea.IdeaModule; +import org.gradle.tooling.model.idea.IdeaProject; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -80,6 +81,11 @@ public abstract class AbstractProjectResolverExtension implements GradleProjectR return nextResolver.createJavaProjectData(); } + @Override + public void populateProjectExtraModels(@NotNull IdeaProject gradleProject, @NotNull DataNode ideProject) { + nextResolver.populateProjectExtraModels(gradleProject, ideProject); + } + @NotNull @Override public ModuleData createModule(@NotNull IdeaModule gradleModule, @NotNull ProjectData projectData) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index 6f43559f7a11..877432457336 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -15,14 +15,13 @@ */ package org.jetbrains.plugins.gradle.service.project; +import com.google.gson.GsonBuilder; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.SimpleJavaParameters; import com.intellij.externalSystem.JavaProjectData; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.externalSystem.model.DataNode; -import com.intellij.openapi.externalSystem.model.ExternalSystemException; -import com.intellij.openapi.externalSystem.model.ProjectKeys; +import com.intellij.openapi.externalSystem.model.*; import com.intellij.openapi.externalSystem.model.project.*; import com.intellij.openapi.externalSystem.model.task.TaskData; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; @@ -56,6 +55,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.model.*; import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData; +import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataService; import org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl; import org.jetbrains.plugins.gradle.tooling.internal.init.Init; import org.jetbrains.plugins.gradle.util.GradleBundle; @@ -79,6 +79,8 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver private static final Logger LOG = Logger.getInstance("#" + BaseGradleProjectResolverExtension.class.getName()); @NotNull @NonNls private static final String UNRESOLVED_DEPENDENCY_PREFIX = "unresolved dependency - "; + private static final String MAIN_SOURCE_SET = "main"; + private static final String TEST_SOURCE_SET = "test"; @NotNull private ProjectResolverContext resolverCtx; @NotNull private final BaseProjectImportErrorHandler myErrorHandler = new BaseProjectImportErrorHandler(); @@ -120,6 +122,14 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver return javaProjectData; } + @Override + public void populateProjectExtraModels(@NotNull IdeaProject gradleProject, @NotNull DataNode ideProject) { + final ExternalProject externalProject = resolverCtx.getExtraProject(ExternalProject.class); + if (externalProject != null) { + ideProject.createChild(ExternalProjectDataService.KEY, externalProject); + } + } + @NotNull @Override public ModuleData createModule(@NotNull IdeaModule gradleModule, @NotNull ProjectData projectData) { @@ -220,6 +230,8 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver File sourceCompileOutputPath = null; File testCompileOutputPath = null; + File resourceCompileOutputPath; + File testResourceCompileOutputPath; boolean inheritOutputDirs = false; ModuleData moduleData = ideModule.getData(); @@ -229,30 +241,71 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver inheritOutputDirs = moduleCompilerOutput.getInheritOutputDirs(); } + ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class); + if (externalProject != null) { + externalProject = new DefaultExternalProject(externalProject); + } + if (!inheritOutputDirs && (sourceCompileOutputPath == null || testCompileOutputPath == null)) { - ModuleExtendedModel moduleExtendedModel = resolverCtx.getExtraProject(gradleModule, ModuleExtendedModel.class); - if (moduleExtendedModel != null) { - ExtIdeaCompilerOutput output = moduleExtendedModel.getCompilerOutput(); - if (output != null) { - if (sourceCompileOutputPath == null) { - sourceCompileOutputPath = output.getMainClassesDir(); - } - if (testCompileOutputPath == null) { - testCompileOutputPath = output.getTestClassesDir(); + sourceCompileOutputPath = getCompileOutputPath(externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.SOURCE); + resourceCompileOutputPath = getCompileOutputPath(externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.RESOURCE); + testCompileOutputPath = getCompileOutputPath(externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST); + testResourceCompileOutputPath = getCompileOutputPath(externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST_RESOURCE); + } + else { + resourceCompileOutputPath = sourceCompileOutputPath; + testResourceCompileOutputPath = testCompileOutputPath; + + if (externalProject != null) { + final ExternalSourceSet mainSourceSet = externalProject.getSourceSets().get(MAIN_SOURCE_SET); + if (mainSourceSet != null) { + final ExternalSourceDirectorySet resourceDirectories = mainSourceSet.getSources().get(ExternalSystemSourceType.RESOURCE); + if (resourceDirectories instanceof DefaultExternalSourceDirectorySet) { + ((DefaultExternalSourceDirectorySet)resourceDirectories).setOutputDir(sourceCompileOutputPath); } } + final ExternalSourceSet testSourceSet = externalProject.getSourceSets().get(TEST_SOURCE_SET); + if (testSourceSet != null) { + final ExternalSourceDirectorySet testResourceDirectories = testSourceSet.getSources().get(ExternalSystemSourceType.TEST_RESOURCE); + if (testResourceDirectories instanceof DefaultExternalSourceDirectorySet) { + ((DefaultExternalSourceDirectorySet)testResourceDirectories).setOutputDir(testCompileOutputPath); + } + } + + final DataNode projectDataNode = ExternalSystemApiUtil.findParent(ideModule, ProjectKeys.PROJECT); + assert projectDataNode != null; + projectDataNode.createOrReplaceChild(ExternalProjectDataService.KEY, externalProject); } } if (sourceCompileOutputPath != null) { moduleData.setCompileOutputPath(ExternalSystemSourceType.SOURCE, sourceCompileOutputPath.getAbsolutePath()); } + if (resourceCompileOutputPath != null) { + moduleData.setCompileOutputPath(ExternalSystemSourceType.RESOURCE, resourceCompileOutputPath.getAbsolutePath()); + } if (testCompileOutputPath != null) { moduleData.setCompileOutputPath(ExternalSystemSourceType.TEST, testCompileOutputPath.getAbsolutePath()); } + if (testResourceCompileOutputPath != null) { + moduleData.setCompileOutputPath(ExternalSystemSourceType.TEST_RESOURCE, testResourceCompileOutputPath.getAbsolutePath()); + } + moduleData.setInheritProjectCompileOutputPath(inheritOutputDirs || sourceCompileOutputPath == null); } + @Nullable + private static File getCompileOutputPath(@Nullable ExternalProject externalProject, + @NotNull String sourceSetName, + @NotNull ExternalSystemSourceType sourceType) { + if (externalProject == null) return null; + final ExternalSourceSet sourceSet = externalProject.getSourceSets().get(sourceSetName); + if(sourceSet == null) return null; + + final ExternalSourceDirectorySet directorySet = sourceSet.getSources().get(sourceType); + return directorySet != null ? directorySet.getOutputDir() : null; + } + @Override public void populateModuleDependencies(@NotNull IdeaModule gradleModule, @NotNull DataNode ideModule, @@ -319,17 +372,19 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver @Override public Set getExtraProjectModelClasses() { return ContainerUtil.set( - GradleBuild.class, ModuleExtendedModel.class, BuildScriptClasspathModel.class); + GradleBuild.class, ExternalProject.class, ModuleExtendedModel.class, BuildScriptClasspathModel.class); } @NotNull @Override public Set getToolingExtensionsClasses() { return ContainerUtil.set( + ExternalProject.class, // gradle-tooling-extension-api jar ProjectImportAction.class, // gradle-tooling-extension-impl jar - ModelBuildScriptClasspathBuilderImpl.class + ModelBuildScriptClasspathBuilderImpl.class, + GsonBuilder.class ); } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java index 8a2ffaae36c9..24c8e126ed3f 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java @@ -199,6 +199,9 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver gradleModules = ideaProject.getModules(); if (gradleModules == null || gradleModules.isEmpty()) { throw new IllegalStateException("No modules found for the target project: " + ideaProject); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java index b727005db845..6dd201b839fb 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolverExtension.java @@ -27,6 +27,7 @@ import com.intellij.openapi.externalSystem.service.ParametersEnhancer; import com.intellij.openapi.util.KeyValue; import com.intellij.util.Consumer; import org.gradle.tooling.model.idea.IdeaModule; +import org.gradle.tooling.model.idea.IdeaProject; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.GradleManager; @@ -62,6 +63,8 @@ public interface GradleProjectResolverExtension extends ParametersEnhancer { @NotNull JavaProjectData createJavaProjectData(); + void populateProjectExtraModels(@NotNull IdeaProject gradleProject, @NotNull DataNode ideProject); + @NotNull ModuleData createModule(@NotNull IdeaModule gradleModule, @NotNull ProjectData projectData); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleStartupActivity.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleStartupActivity.java index 81ceaa3177e2..0e5fcc12f3dc 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleStartupActivity.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleStartupActivity.java @@ -21,6 +21,11 @@ import com.intellij.ide.util.newProjectWizard.AddModuleWizard; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompileTask; +import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys; import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager; @@ -30,6 +35,8 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VfsUtilCore; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.maven.project.MavenResourceCompilerConfigurationGenerator; +import org.jetbrains.plugins.gradle.config.GradleResourceCompilerConfigurationGenerator; import org.jetbrains.plugins.gradle.service.GradleBuildClasspathManager; import org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportBuilder; import org.jetbrains.plugins.gradle.service.project.wizard.GradleProjectImportProvider; @@ -52,9 +59,22 @@ public class GradleStartupActivity implements StartupActivity { private static final String DO_NOT_SHOW_EVENT_DESCRIPTION = "do.not.show"; @Override - public void runActivity(@NotNull Project project) { + public void runActivity(@NotNull final Project project) { configureBuildClasspath(project); showNotificationForUnlinkedGradleProject(project); + CompilerManager.getInstance(project).addBeforeTask(new CompileTask() { + @Override + public boolean execute(CompileContext context) { + AccessToken token = ReadAction.start(); + try { + new GradleResourceCompilerConfigurationGenerator(project, context).generateBuildConfiguration(); + } + finally { + token.finish(); + } + return true; + } + }); } private static void configureBuildClasspath(@NotNull final Project project) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/ProjectResolverContext.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/ProjectResolverContext.java index b7f94dd57122..3aa0027185bf 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/ProjectResolverContext.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/ProjectResolverContext.java @@ -19,6 +19,7 @@ import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener; import org.gradle.tooling.ProjectConnection; import org.gradle.tooling.model.idea.IdeaModule; +import org.gradle.tooling.model.idea.IdeaProject; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.model.ProjectImportAction; @@ -93,7 +94,12 @@ public class ProjectResolverContext { } @Nullable - public T getExtraProject(@NotNull IdeaModule module, Class modelClazz) { + public T getExtraProject(Class modelClazz) { + return myModels.getExtraProject(null, modelClazz); + } + + @Nullable + public T getExtraProject(@Nullable IdeaModule module, Class modelClazz) { return myModels.getExtraProject(module, modelClazz); } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataService.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataService.java new file mode 100644 index 000000000000..fae9ee0d88c1 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectDataService.java @@ -0,0 +1,233 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.gradle.service.project.data; + +import com.intellij.CommonBundle; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.model.*; +import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener; +import com.intellij.openapi.externalSystem.service.internal.ExternalSystemResolveProjectTask; +import com.intellij.openapi.externalSystem.service.notification.ExternalSystemNotificationManager; +import com.intellij.openapi.externalSystem.service.notification.NotificationSource; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataService; +import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; +import com.intellij.openapi.externalSystem.util.*; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ex.ProjectRootManagerEx; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ConcurrentFactoryMap; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.Collection; +import java.util.Map; + +/** + * @author Vladislav.Soroka + * @since 7/17/2014 + */ +@Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER) +public class ExternalProjectDataService implements ProjectDataService { + private static final Logger LOG = Logger.getInstance(ExternalProjectDataService.class); + + @NotNull public static final Key KEY = Key.create(ExternalProject.class, ProjectKeys.TASK.getProcessingWeight() + 1); + + @NotNull private final Map, ExternalProject> myExternalRootProjects; + + @NotNull private ProjectDataManager myProjectDataManager; + + public ExternalProjectDataService(@NotNull ProjectDataManager projectDataManager) { + myProjectDataManager = projectDataManager; + myExternalRootProjects = new ConcurrentFactoryMap, ExternalProject>() { + @Nullable + @Override + protected ExternalProject create(Pair key) { + return new ExternalProjectSerializer().load(key.first, key.second); + } + + @Override + public ExternalProject put(Pair key, ExternalProject value) { + new ExternalProjectSerializer().save(value); + return super.put(key, value); + } + }; + } + + @NotNull + @Override + public Key getTargetDataKey() { + return KEY; + } + + public void importData(@NotNull final Collection> toImport, + @NotNull final Project project, + final boolean synchronous) { + if (toImport.size() != 1) { + throw new IllegalArgumentException( + String.format("Expected to get a single external project but got %d: %s", toImport.size(), toImport)); + } + saveExternalProject(toImport.iterator().next().getData()); + } + + @Override + public void removeData(@NotNull final Collection modules, @NotNull Project project, boolean synchronous) { + } + + @Nullable + public ExternalProject getOrImportRootExternalProject(@NotNull Project project, + @NotNull ProjectSystemId systemId, + @NotNull File projectRootDir) { + final ExternalProject externalProject = getRootExternalProject(systemId, projectRootDir); + return externalProject != null ? externalProject : importExternalProject(project, systemId, projectRootDir); + } + + @Nullable + private ExternalProject importExternalProject(@NotNull final Project project, + @NotNull final ProjectSystemId projectSystemId, + @NotNull final File projectRootDir) { + final Boolean result = UIUtil.invokeAndWaitIfNeeded(new Computable() { + @Override + public Boolean compute() { + final Ref result = new Ref(false); + if (project.isDisposed()) return false; + + final String linkedProjectPath = FileUtil.toCanonicalPath(projectRootDir.getPath()); + final ExternalProjectSettings projectSettings = + ExternalSystemApiUtil.getSettings(project, projectSystemId).getLinkedProjectSettings(linkedProjectPath); + if (projectSettings == null) { + LOG.warn("Unable to get project settings for project path: " + linkedProjectPath); + if (LOG.isDebugEnabled()) { + LOG.debug("Available projects paths: " + ContainerUtil.map( + ExternalSystemApiUtil.getSettings(project, projectSystemId).getLinkedProjectsSettings(), + new Function() { + @Override + public String fun(ExternalProjectSettings settings) { + return settings.getExternalProjectPath(); + } + })); + } + return false; + } + + final File projectFile = new File(linkedProjectPath); + final String projectName; + if (projectFile.isFile()) { + projectName = projectFile.getParentFile().getName(); + } + else { + projectName = projectFile.getName(); + } + + // ask a user for the project import if auto-import is disabled + if (!projectSettings.isUseAutoImport()) { + String message = String.format("Project '%s' require synchronization with %s configuration. \nImport the project?", + projectName, projectSystemId.getReadableName()); + int returnValue = Messages.showOkCancelDialog( + message, "Import Project", CommonBundle.getOkButtonText(), CommonBundle.getCancelButtonText(), Messages.getQuestionIcon() + ); + if (returnValue != Messages.OK) return false; + } + + final String title = ExternalSystemBundle.message("progress.import.text", linkedProjectPath, projectSystemId.getReadableName()); + new Task.Modal(project, title, false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + if (project.isDisposed()) return; + + ExternalSystemNotificationManager.getInstance(project) + .clearNotifications(null, NotificationSource.PROJECT_SYNC, projectSystemId); + ExternalSystemResolveProjectTask task = + new ExternalSystemResolveProjectTask(projectSystemId, project, linkedProjectPath, false); + task.execute(indicator, ExternalSystemTaskNotificationListener.EP_NAME.getExtensions()); + if (project.isDisposed()) return; + + final Throwable error = task.getError(); + if (error != null) { + ExternalSystemNotificationManager.getInstance(project) + .processExternalProjectRefreshError(error, projectName, projectSystemId); + return; + } + final DataNode projectDataDataNode = task.getExternalProject(); + if (projectDataDataNode == null) return; + + final Collection> nodes = ExternalSystemApiUtil.findAll(projectDataDataNode, KEY); + if (nodes.size() != 1) { + throw new IllegalArgumentException( + String.format("Expected to get a single external project but got %d: %s", nodes.size(), nodes)); + } + + ProjectRootManagerEx.getInstanceEx(myProject).mergeRootsChangesDuring(new Runnable() { + @Override + public void run() { + myProjectDataManager.importData(KEY, nodes, project, true); + } + }); + + result.set(true); + } + }.queue(); + + return result.get(); + } + }); + + return result ? getRootExternalProject(projectSystemId, projectRootDir) : null; + } + + @Nullable + public ExternalProject getRootExternalProject(@NotNull ProjectSystemId systemId, @NotNull File projectRootDir) { + return myExternalRootProjects.get(Pair.create(systemId, projectRootDir)); + } + + public void saveExternalProject(@NotNull ExternalProject externalProject) { + myExternalRootProjects.put( + Pair.create(new ProjectSystemId(externalProject.getExternalSystemId()), externalProject.getProjectDir()), + new DefaultExternalProject(externalProject) + ); + } + + @Nullable + public ExternalProject findExternalProject(@NotNull ExternalProject parentProject, @NotNull Module module) { + String externalProjectId = ExternalSystemApiUtil.getExternalProjectId(module); + return externalProjectId != null ? findExternalProject(parentProject, externalProjectId) : null; + } + + @Nullable + private static ExternalProject findExternalProject(@NotNull ExternalProject parentProject, @NotNull String externalProjectId) { + if (parentProject.getQName().equals(externalProjectId)) return parentProject; + if (parentProject.getChildProjects().containsKey(externalProjectId)) { + return parentProject.getChildProjects().get(externalProjectId); + } + for (ExternalProject externalProject : parentProject.getChildProjects().values()) { + final ExternalProject project = findExternalProject(externalProject, externalProjectId); + if (project != null) return project; + } + return null; + } +} diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleCompilingTestCase.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleCompilingTestCase.java new file mode 100644 index 000000000000..3b508c8cf289 --- /dev/null +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleCompilingTestCase.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.gradle.compiler; + +import com.intellij.compiler.artifacts.ArtifactsTestUtil; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompileTask; +import com.intellij.openapi.compiler.CompilerManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.packaging.impl.artifacts.ArtifactUtil; +import com.intellij.util.io.TestFileSystemItem; +import org.jetbrains.plugins.gradle.config.GradleResourceCompilerConfigurationGenerator; +import org.jetbrains.plugins.gradle.importing.GradleImportingTestCase; + +import java.io.File; + +/** + * @author Vladislav.Soroka + * @since 7/21/2014 + */ +public abstract class GradleCompilingTestCase extends GradleImportingTestCase { + @Override + protected void setUpInWriteAction() throws Exception { + super.setUpInWriteAction(); + CompilerManager.getInstance(myProject).addBeforeTask(new CompileTask() { + @Override + public boolean execute(CompileContext context) { + AccessToken token = ReadAction.start(); + try { + new GradleResourceCompilerConfigurationGenerator(myProject, context).generateBuildConfiguration(); + } + finally { + token.finish(); + } + return true; + } + }); + } + + protected void assertCopied(String path) { + assertTrue(new File(myProjectConfig.getParent().getPath(), path).exists()); + } + + protected void assertCopied(String path, String content) { + assertCopied(path); + assertSameLinesWithFile(new File(myProjectConfig.getParent().getPath(), path).getPath(), content); + } + + protected void assertNotCopied(String path) { + assertFalse(new File(myProjectConfig.getParent().getPath(), path).exists()); + } + + @Override + protected void assertArtifactOutputPath(String artifactName, String expected) { + final String defaultArtifactOutputPath = ArtifactUtil.getDefaultArtifactOutputPath(artifactName, myProject); + assert defaultArtifactOutputPath != null; + final String basePath = FileUtil.toSystemIndependentName(new File(defaultArtifactOutputPath).getParent()); + super.assertArtifactOutputPath(artifactName, basePath + expected); + } + + protected void assertArtifactOutputFile(String artifactName, String path, String content) { + final String defaultArtifactOutputPath = ArtifactUtil.getDefaultArtifactOutputPath(artifactName, myProject); + assert defaultArtifactOutputPath != null; + final String basePath = FileUtil.toSystemIndependentName(new File(defaultArtifactOutputPath).getParent()); + assertSameLinesWithFile(basePath + path, content); + } + + protected void assertArtifactOutputFile(String artifactName, String path) { + final String defaultArtifactOutputPath = ArtifactUtil.getDefaultArtifactOutputPath(artifactName, myProject); + assert defaultArtifactOutputPath != null; + final String basePath = FileUtil.toSystemIndependentName(new File(defaultArtifactOutputPath).getParent()); + assertExists(new File(basePath + path)); + } +} diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceFilteringTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceFilteringTest.java new file mode 100644 index 000000000000..df9c6a879ac7 --- /dev/null +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceFilteringTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.gradle.compiler; + +import org.junit.Test; + +/** + * @author Vladislav.Soroka + * @since 7/21/2014 + */ +@SuppressWarnings("JUnit4AnnotatedMethodInJUnit3TestCase") +public class GradleResourceFilteringTest extends GradleCompilingTestCase { + + @Test + public void testHeadFilter() throws Exception { + createProjectSubFile( + "src/main/resources/dir/file.txt", "1 Header\n" + + "2\n" + + "3 another text\n" + + "4\n" + + "5 another text \n" + + "6 another text @token@ another text\n" + + "7\n" + + "8 Footer"); + importProject( + "apply plugin: 'java'\n" + + "\n" + + "import org.apache.tools.ant.filters.*\n" + + "processResources {\n" + + " filter(HeadFilter, lines:3, skip:2)\n" + + "}" + ); + assertModules("project"); + compileModules("project"); + + assertCopied("build/resources/main/dir/file.txt", "3 another text\n" + + "4\n" + + "5 another text \n"); + } + + @Test + public void testReplaceTokensFilter() throws Exception { + createProjectSubFile( + "src/main/resources/dir/file.txt", "1 Header\n" + + "2\n" + + "3 #token1#another text\n" + + "4\n" + + "5 another text \n" + + "6 another text #token2# another text\n" + + "7\n" + + "8 Footer"); + importProject( + "apply plugin: 'java'\n" + + "\n" + + "import org.apache.tools.ant.filters.*\n" + + "processResources {\n" + + " filter(ReplaceTokens, tokens:[token1:'<11111>', token2:'<2222>'], beginToken: '#', endToken: '#')\n" + + "}" + ); + assertModules("project"); + compileModules("project"); + + assertCopied("build/resources/main/dir/file.txt", "1 Header\n" + + "2\n" + + "3 <11111>another text\n" + + "4\n" + + "5 another text \n" + + "6 another text <2222> another text\n" + + "7\n" + + "8 Footer"); + } + + @Test + public void testRenameFilter() throws Exception { + createProjectSubFile("src/main/resources/dir/file.txt"); + importProject( + "apply plugin: 'java'\n" + + "\n" + + "import org.apache.tools.ant.filters.*\n" + + "processResources {\n" + + " rename 'file.txt', 'file001.txt'\n" + + "}" + ); + assertModules("project"); + compileModules("project"); + + assertCopied("build/resources/main/dir/file001.txt"); + } + + @Test + public void testFiltersChain() throws Exception { + createProjectSubFile( + "src/main/resources/dir/file.txt", "1 Header\n" + + "2\n" + + "3 another text@token1@\n" + + "4\n" + + "5 another text \n" + + "6 another text @token2@ another text\n" + + "7\n" + + "8 Footer"); + importProject( + "apply plugin: 'java'\n" + + "\n" + + "import org.apache.tools.ant.filters.*\n" + + "processResources {\n" + + " filter(HeadFilter, lines:4, skip:2)\n" + + " filter(ReplaceTokens, tokens:[token1:'<11111>', token2:'<2222>'])\n" + + " rename 'file.txt', 'file001.txt'\n" + + "}" + ); + assertModules("project"); + compileModules("project"); + + assertCopied("build/resources/main/dir/file001.txt", "3 another text<11111>\n" + + "4\n" + + "5 another text \n" + + "6 another text <2222> another text"); + } +} \ No newline at end of file diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceProcessingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceProcessingTest.java new file mode 100644 index 000000000000..873234425e30 --- /dev/null +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/compiler/GradleResourceProcessingTest.java @@ -0,0 +1,176 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.gradle.compiler; + +import org.junit.Test; + +import java.io.IOException; + +/** + * @author Vladislav.Soroka + * @since 7/21/2014 + */ +@SuppressWarnings("JUnit4AnnotatedMethodInJUnit3TestCase") +public class GradleResourceProcessingTest extends GradleCompilingTestCase { + + @Test + public void testBasicResourceCopying() throws Exception { + createProjectSubFile("src/main/resources/dir/file.properties"); + createProjectSubFile("src/test/resources/dir/file-test.properties"); + importProject( + "apply plugin: 'java'" + ); + assertModules("project"); + compileModules("project"); + + assertCopied("build/resources/main/dir/file.properties"); + assertCopied("build/resources/test/dir/file-test.properties"); + } + + @Test + public void testResourceProcessingWithIdeaGradlePluginCustomization() throws Exception { + createProjectSubFile("src/main/resources/dir/file.properties"); + createProjectSubFile("src/test/resources/dir/file-test.properties"); + importProject( + "apply plugin: 'java'\n" + + "apply plugin: 'idea'\n" + + "idea {\n" + + " module {\n" + + " inheritOutputDirs = false\n" + + " outputDir = file('muchBetterOutputDir')\n" + + " testOutputDir = file('muchBetterTestOutputDir')\n" + + " }\n" + + "}" + ); + assertModules("project"); + compileModules("project"); + + assertCopied("muchBetterOutputDir/dir/file.properties"); + assertCopied("muchBetterTestOutputDir/dir/file-test.properties"); + } + + @Test + public void testIncludesAndExcludesInSourceSets() throws Exception { + createFilesForIncludesAndExcludesTest(); + + importProject( + "apply plugin: 'java'\n" + + "\n" + + "sourceSets {\n" + + " main {\n" + + " resources {\n" + + " include '**/*.yyy'\n" + + " include '**/*.xxx'\n" + + " exclude 'dir/*.yyy'\n" + + " exclude '*.xxx'\n" + + " }\n" + + " }\n" + + " test {\n" + + " resources {\n" + + " include '**/*.yyy'\n" + + " include '**/*.xxx'\n" + + " exclude 'dir/*.yyy'\n" + + " exclude '*.xxx'\n" + + " }\n" + + " }\n" + + "}" + ); + assertModules("project"); + compileModules("project"); + + assertCopiedResources(); + } + + @Test + public void testIncludesAndExcludesInAllSourceSets() throws Exception { + createFilesForIncludesAndExcludesTest(); + + importProject( + "apply plugin: 'java'\n" + + "\n" + + "sourceSets.all {\n" + + " resources {\n" + + " include '**/*.yyy'\n" + + " include '**/*.xxx'\n" + + " exclude 'dir/*.yyy'\n" + + " exclude '*.xxx'\n" + + " }\n" + + "}" + ); + assertModules("project"); + compileModules("project"); + + assertCopiedResources(); + } + + + @Test + public void testIncludesAndExcludesInResourcesTask() throws Exception { + createFilesForIncludesAndExcludesTest(); + + importProject( + "apply plugin: 'java'\n" + + "\n" + + "processResources {\n" + + " include '**/*.yyy'\n" + + " include '**/*.xxx'\n" + + " exclude 'dir/*.yyy'\n" + + " exclude '*.xxx'\n" + + "}\n" + + "\n" + + "processTestResources {\n" + + " include '**/*.yyy'\n" + + " include '**/*.xxx'\n" + + " exclude 'dir/*.yyy'\n" + + " exclude '*.xxx'\n" + + "}\n" + ); + assertModules("project"); + compileModules("project"); + + assertCopiedResources(); + } + + private void createFilesForIncludesAndExcludesTest() throws IOException { + createProjectSubFile("src/main/resources/dir/file.xxx"); + createProjectSubFile("src/main/resources/dir/file.yyy"); + createProjectSubFile("src/main/resources/file.xxx"); + createProjectSubFile("src/main/resources/file.yyy"); + createProjectSubFile("src/main/resources/file.zzz"); + + createProjectSubFile("src/test/resources/dir/file.xxx"); + createProjectSubFile("src/test/resources/dir/file.yyy"); + createProjectSubFile("src/test/resources/file.xxx"); + createProjectSubFile("src/test/resources/file.yyy"); + createProjectSubFile("src/test/resources/file.zzz"); + } + + private void assertCopiedResources() { + // assert production resources + assertCopied("build/resources/main/dir/file.xxx"); + assertCopied("build/resources/main/file.yyy"); + assertNotCopied("build/resources/main/dir/file.yyy"); + assertNotCopied("build/resources/main/file.xxx"); + assertNotCopied("build/resources/main/file.zzz"); + + // assert test resources + assertCopied("build/resources/test/dir/file.xxx"); + assertCopied("build/resources/test/file.yyy"); + assertNotCopied("build/resources/test/dir/file.yyy"); + assertNotCopied("build/resources/test/file.xxx"); + assertNotCopied("build/resources/test/file.zzz"); + } +} diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ModuleExtendedModel.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ModuleExtendedModel.java index 97dfd2547b73..91889d85fa9f 100644 --- a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ModuleExtendedModel.java +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ModuleExtendedModel.java @@ -22,9 +22,12 @@ import java.io.Serializable; import java.util.List; /** + * @deprecated Use {@link com.intellij.openapi.externalSystem.model.ExternalProject} model instead. + * * @author Vladislav.Soroka * @since 11/5/13 */ +@Deprecated() public interface ModuleExtendedModel extends Serializable { /** * The group of the module. diff --git a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java index af6f4fed60f4..c27a9a216c36 100644 --- a/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java +++ b/plugins/gradle/tooling-extension-api/src/org/jetbrains/plugins/gradle/model/ProjectImportAction.java @@ -18,6 +18,7 @@ package org.jetbrains.plugins.gradle.model; import com.intellij.openapi.externalSystem.model.ExternalSystemException; import org.gradle.tooling.BuildAction; import org.gradle.tooling.BuildController; +import org.gradle.tooling.model.Model; import org.gradle.tooling.model.build.BuildEnvironment; import org.gradle.tooling.model.idea.BasicIdeaProject; import org.gradle.tooling.model.idea.IdeaModule; @@ -48,8 +49,7 @@ public class ProjectImportAction implements BuildAction aClass1 = myIsPreviewMode ? BasicIdeaProject.class : IdeaProject.class; - final IdeaProject ideaProject = controller.getModel(aClass1); + final IdeaProject ideaProject = controller.getModel(myIsPreviewMode ? BasicIdeaProject.class : IdeaProject.class); if (ideaProject == null || ideaProject.getModules().isEmpty()) { return null; } @@ -59,25 +59,30 @@ public class ProjectImportAction implements BuildAction projectsByPath = new HashMap(); @NotNull private final IdeaProject myIdeaProject; @@ -101,9 +106,13 @@ public class ProjectImportAction implements BuildAction T getExtraProject(Class modelClazz) { + return getExtraProject(null, modelClazz); + } @Nullable - public T getExtraProject(@NotNull IdeaModule module, Class modelClazz) { + public T getExtraProject(@Nullable IdeaModule module, Class modelClazz) { Object extraProject = projectsByPath.get(extractMapKey(modelClazz, module)); if (modelClazz.isInstance(extraProject)) { //noinspection unchecked @@ -129,13 +138,17 @@ public class ProjectImportAction implements BuildAction" + "" + myProject + ((myDescription != null) ? ": " + myDescription : "") + "" + - "\nDetails: " + getErrorMessage(myException) + + (myException != null ? "\nDetails: " + getErrorMessage(myException) : "") + "" ).replaceAll("\r\n|\n\r|\n|\r", EOL_TAG) ); diff --git a/plugins/gradle/tooling-extension-impl/gradle-tooling-extension-impl.iml b/plugins/gradle/tooling-extension-impl/gradle-tooling-extension-impl.iml index 1bcde5e35b79..ea579287b3e8 100644 --- a/plugins/gradle/tooling-extension-impl/gradle-tooling-extension-impl.iml +++ b/plugins/gradle/tooling-extension-impl/gradle-tooling-extension-impl.iml @@ -67,6 +67,7 @@ + diff --git a/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService b/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService index 244e58d2128e..816ef05c1e4d 100644 --- a/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService +++ b/plugins/gradle/tooling-extension-impl/src/META-INF/services/org.jetbrains.plugins.gradle.tooling.ModelBuilderService @@ -17,3 +17,4 @@ org.jetbrains.plugins.gradle.tooling.builder.WarModelBuilderImpl org.jetbrains.plugins.gradle.tooling.builder.ModuleExtendedModelBuilderImpl org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl org.jetbrains.plugins.gradle.tooling.builder.ScalaModelBuilderImpl +org.jetbrains.plugins.gradle.tooling.builder.ExternalProjectBuilderImpl diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy new file mode 100644 index 000000000000..3bb6b471b755 --- /dev/null +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy @@ -0,0 +1,231 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.plugins.gradle.tooling.builder + +import com.google.gson.GsonBuilder +import com.intellij.openapi.externalSystem.model.* +import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType +import org.gradle.api.Action +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.ContentFilterable +import org.gradle.api.file.FileCopyDetails +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.util.PatternFilterable +import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.Nullable +import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder +import org.jetbrains.plugins.gradle.tooling.ModelBuilderService + +import java.util.concurrent.ConcurrentHashMap + +/** + * @author Vladislav.Soroka + * @since 12/20/13 + */ +class ExternalProjectBuilderImpl implements ModelBuilderService { + + private final cache = new ConcurrentHashMap() + + @Override + public boolean canBuild(String modelName) { + return ExternalProject.name.equals(modelName) + } + + @Nullable + @Override + public Object buildAll(final String modelName, final Project project) { + ExternalProject externalProject = cache[project.path] + if (externalProject != null) return externalProject + + DefaultExternalProject defaultExternalProject = new DefaultExternalProject() + defaultExternalProject.externalSystemId = "GRADLE" + defaultExternalProject.name = project.name + defaultExternalProject.QName = ":".equals(project.path) ? project.name : project.path + defaultExternalProject.version = wrap(project.version) + defaultExternalProject.description = project.description + defaultExternalProject.buildDir = project.buildDir + defaultExternalProject.buildFile = project.buildFile + defaultExternalProject.group = wrap(project.group) + defaultExternalProject.projectDir = project.projectDir + defaultExternalProject.sourceSets = getSourceSets(project) + defaultExternalProject.tasks = getTasks(project) + + defaultExternalProject.plugins = getPlugins(project) + //defaultExternalProject.setProperties(project.getProperties()) + + + final Map childProjects = new HashMap(project.getChildProjects().size()) + for (Map.Entry projectEntry : project.getChildProjects().entrySet()) { + final Object externalProjectChild = buildAll(modelName, projectEntry.getValue()) + if (externalProjectChild instanceof ExternalProject) { + childProjects.put(projectEntry.getKey(), (ExternalProject)externalProjectChild) + } + } + defaultExternalProject.setChildProjects(childProjects) + cache.put(project.getPath(), defaultExternalProject) + + defaultExternalProject + } + + static Map getPlugins(Project project) { + def result = [:] as Map + project.convention.plugins.each { key, value -> + ExternalPlugin externalPlugin = new DefaultExternalPlugin() + externalPlugin.id = key + result.put(key, externalPlugin) + } + + result + } + + static Map getTasks(Project project) { + def result = [:] as Map + + project.tasks.all { Task task -> + ExternalTask externalTask = new DefaultExternalTask() + externalTask.name = task.name + externalTask.description = task.description + externalTask.group = task.group + externalTask.QName = task.path + result.put(externalTask.QName, externalTask) + } + + result + } + + static Map getSourceSets(Project project) { + def result = [:] as Map + if (!project.hasProperty("sourceSets") || !(project.sourceSets instanceof SourceSetContainer)) { + return result + } + def sourceSets = project.sourceSets as SourceSetContainer + + def (resourcesIncludes, resourcesExcludes, filterReaders) = getFilters(project, 'processResources') + def (testResourcesIncludes, testResourcesExcludes, testFilterReaders) = getFilters(project, 'processTestResources') + //def (javaIncludes,javaExcludes) = getFilters(project,'compileJava') + + sourceSets.all { SourceSet sourceSet -> + ExternalSourceSet externalSourceSet = new DefaultExternalSourceSet() + externalSourceSet.name = sourceSet.name + + def sources = [:] as Map + ExternalSourceDirectorySet resourcesDirectorySet = new DefaultExternalSourceDirectorySet() + resourcesDirectorySet.name = sourceSet.resources.name + resourcesDirectorySet.srcDirs = sourceSet.resources.srcDirs + resourcesDirectorySet.outputDir = sourceSet.output.resourcesDir + + ExternalSourceDirectorySet javaDirectorySet = new DefaultExternalSourceDirectorySet() + javaDirectorySet.name = sourceSet.allJava.name + javaDirectorySet.srcDirs = sourceSet.allJava.srcDirs + javaDirectorySet.outputDir = sourceSet.output.classesDir +// javaDirectorySet.excludes = javaExcludes + sourceSet.java.excludes; +// javaDirectorySet.includes = javaIncludes + sourceSet.java.includes; + + if (SourceSet.TEST_SOURCE_SET_NAME.equals(sourceSet.name)) { + resourcesDirectorySet.excludes = testResourcesExcludes + sourceSet.resources.excludes; + resourcesDirectorySet.includes = testResourcesIncludes + sourceSet.resources.includes; + resourcesDirectorySet.filters = testFilterReaders + sources.put(ExternalSystemSourceType.TEST, javaDirectorySet) + sources.put(ExternalSystemSourceType.TEST_RESOURCE, resourcesDirectorySet) + } + else { + resourcesDirectorySet.excludes = resourcesExcludes + sourceSet.resources.excludes; + resourcesDirectorySet.includes = resourcesIncludes + sourceSet.resources.includes; + resourcesDirectorySet.filters = filterReaders + sources.put(ExternalSystemSourceType.SOURCE, javaDirectorySet) + sources.put(ExternalSystemSourceType.RESOURCE, resourcesDirectorySet) + } + + externalSourceSet.sources = sources + result[sourceSet.name] = externalSourceSet + } + result + } + + static getFilters(Project project, String taskName) { + def includes = [] + def excludes = [] + def filterReaders = [] as List + def filterableTask = project.tasks.findByName(taskName) + if (filterableTask instanceof PatternFilterable) { + includes += filterableTask.includes + excludes += filterableTask.excludes + } + + if(System.getProperty('idea.disable.gradle.resource.filtering', 'false').toBoolean()) { + return [includes, excludes, filterReaders] + } + + try { + if (filterableTask instanceof ContentFilterable && filterableTask.metaClass.respondsTo(filterableTask, "getMainSpec")) { + def properties = filterableTask.getMainSpec().properties + def copyActions = properties?.allCopyActions ?: properties?.copyActions + + if(copyActions) { + copyActions.each { Action action -> + if (action.hasProperty('val$filterType') && action.hasProperty('val$properties')) { + def filterType = (action?.val$filterType as Class).name + def filter = [filterType: filterType] as DefaultExternalFilter + def props = action?.val$properties + if (props) { + filter.propertiesAsJsonMap = new GsonBuilder().create().toJson(props); + } + filterReaders << filter + } + else if (action.class.simpleName.equals('RenamingCopyAction') && action.hasProperty('transformer')) { + if (action.transformer.hasProperty('matcher') && action?.transformer.hasProperty('replacement')) { + String pattern = action?.transformer?.matcher.pattern().pattern + String replacement = action?.transformer?.replacement + def filter = [filterType: 'RenamingCopyFilter'] as DefaultExternalFilter + if(pattern && replacement){ + filter.propertiesAsJsonMap = new GsonBuilder().create().toJson([pattern: pattern, replacement: replacement]); + filterReaders << filter + } + } + } +// else { +// project.logger.error( +// ErrorMessageBuilder.create(project, "Resource configuration errors") +// .withDescription("Unsupported copy action found: " + action.class.name).build()) +// } + } + } + } + } + catch (Exception ignore) { +// project.logger.error( +// ErrorMessageBuilder.create(project, e, "Resource configuration errors") +// .withDescription("Unable to resolve resources filtering configuration").build()) + } + + return [includes, excludes, filterReaders] + } + + + private static String wrap(Object o) { + return o instanceof CharSequence ? o.toString() : "" + } + + @NotNull + @Override + public ErrorMessageBuilder getErrorMessageBuilder(@NotNull Project project, @NotNull Exception e) { + return ErrorMessageBuilder.create( + project, e, "Project resolve errors" + ).withDescription("Unable to resolve additional project configuration.") + } +} From d5273fd2a3dbe9a8ecbd30be21dc9b24ec74eb18 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 31 Jul 2014 16:47:32 +0400 Subject: [PATCH 25/85] IDEA-127797 Support opening Gradle projects in headless mode --- ...stractImportFromExternalSystemControl.java | 5 + .../wizard/GradleProjectOpenProcessor.java | 97 ++++++++++++++++--- .../settings/GradleSystemSettingsControl.java | 5 + .../gradle/util/GradleEnvironment.java | 12 +++ 4 files changed, 108 insertions(+), 11 deletions(-) diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractImportFromExternalSystemControl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractImportFromExternalSystemControl.java index 5dcb5ef71e3b..deb9a9044ea6 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractImportFromExternalSystemControl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractImportFromExternalSystemControl.java @@ -193,6 +193,11 @@ public abstract class AbstractImportFromExternalSystemControl< return myProjectSettingsControl; } + @Nullable + public ExternalSystemSettingsControl getSystemSettingsControl() { + return mySystemSettingsControl; + } + public void setLinkedProjectPath(@NotNull String path) { myProjectSettings.setExternalProjectPath(path); myLinkedProjectPathField.setText(path); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleProjectOpenProcessor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleProjectOpenProcessor.java index 213ba84271ef..ebd9d3392879 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleProjectOpenProcessor.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleProjectOpenProcessor.java @@ -16,17 +16,26 @@ package org.jetbrains.plugins.gradle.service.project.wizard; import com.intellij.ide.util.newProjectWizard.AddModuleWizard; -import com.intellij.ide.util.projectWizard.ProjectBuilder; +import com.intellij.ide.util.projectWizard.ModuleWizardStep; import com.intellij.ide.util.projectWizard.WizardContext; import com.intellij.ide.wizard.Step; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.externalSystem.service.project.wizard.SelectExternalProjectStep; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.projectImport.ProjectOpenProcessorBase; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.service.settings.GradleProjectSettingsControl; +import org.jetbrains.plugins.gradle.service.settings.GradleSystemSettingsControl; +import org.jetbrains.plugins.gradle.service.settings.ImportFromGradleControl; +import org.jetbrains.plugins.gradle.settings.DistributionType; +import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; +import org.jetbrains.plugins.gradle.settings.GradleSettings; import org.jetbrains.plugins.gradle.util.GradleConstants; +import static org.jetbrains.plugins.gradle.util.GradleEnvironment.Headless.*; /** * @author Denis Zhdanov @@ -61,24 +70,90 @@ public class GradleProjectOpenProcessor extends ProjectOpenProcessorBase() { - @Override - public Boolean fun(Step step) { - return step instanceof SelectExternalProjectStep; - } - }); - boolean result = dialog.showAndGet(); + final boolean result; + if (ApplicationManager.getApplication().isHeadlessEnvironment()) { + result = setupGradleProjectSettingsInHeadlessMode(file, projectImportProvider, wizardContext); + } + else { + AddModuleWizard dialog = new AddModuleWizard(null, file.getPath(), projectImportProvider); + dialog.getWizardContext().setProjectBuilder(getBuilder()); + dialog.navigateToStep(new Function() { + @Override + public Boolean fun(Step step) { + return step instanceof SelectExternalProjectStep; + } + }); + result = dialog.showAndGet(); + } if (result && getBuilder().getExternalProjectNode() != null) { wizardContext.setProjectName(getBuilder().getExternalProjectNode().getData().getInternalName()); } return result; } + private boolean setupGradleProjectSettingsInHeadlessMode(VirtualFile file, + GradleProjectImportProvider projectImportProvider, + WizardContext wizardContext) { + getBuilder().setFileToImport(file.getPath()); + final ModuleWizardStep[] wizardSteps = projectImportProvider.createSteps(wizardContext); + if (wizardSteps.length > 0 && wizardSteps[0] instanceof SelectExternalProjectStep) { + SelectExternalProjectStep selectExternalProjectStep = (SelectExternalProjectStep)wizardSteps[0]; + wizardContext.setProjectBuilder(getBuilder()); + try { + selectExternalProjectStep.updateStep(); + final ImportFromGradleControl importFromGradleControl = getBuilder().getControl(wizardContext.getProject()); + + GradleProjectSettingsControl gradleProjectSettingsControl = + (GradleProjectSettingsControl)importFromGradleControl.getProjectSettingsControl(); + + final GradleProjectSettings projectSettings = gradleProjectSettingsControl.getInitialSettings(); + + if (GRADLE_DISTRIBUTION_TYPE != null) { + for (DistributionType type : DistributionType.values()) { + if (type.name().equals(GRADLE_DISTRIBUTION_TYPE)) { + projectSettings.setDistributionType(type); + break; + } + } + } + if (GRADLE_HOME != null) { + projectSettings.setGradleHome(GRADLE_HOME); + } + gradleProjectSettingsControl.reset(); + + final GradleSystemSettingsControl systemSettingsControl = + (GradleSystemSettingsControl)importFromGradleControl.getSystemSettingsControl(); + assert systemSettingsControl != null; + final GradleSettings gradleSettings = systemSettingsControl.getInitialSettings(); + if (GRADLE_VM_OPTIONS != null) { + gradleSettings.setGradleVmOptions(GRADLE_VM_OPTIONS); + } + if (GRADLE_OFFLINE != null) { + gradleSettings.setOfflineWork(Boolean.parseBoolean(GRADLE_OFFLINE)); + } + String serviceDirectory = GRADLE_SERVICE_DIRECTORY; + if (GRADLE_SERVICE_DIRECTORY != null) { + gradleSettings.setServiceDirectoryPath(serviceDirectory); + } + systemSettingsControl.reset(); + + if (!selectExternalProjectStep.validate()) { + return false; + } + } + catch (ConfigurationException e) { + Messages.showErrorDialog(wizardContext.getProject(), e.getMessage(), e.getTitle()); + return false; + } + selectExternalProjectStep.updateDataModel(); + } + return true; + } + @Override public boolean lookForProjectsInDirectory() { return false; diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleSystemSettingsControl.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleSystemSettingsControl.java index ffdf37c74b87..ab6454c46c99 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleSystemSettingsControl.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleSystemSettingsControl.java @@ -173,4 +173,9 @@ public class GradleSystemSettingsControl implements ExternalSystemSettingsContro public void disposeUIResources() { ExternalSystemUiUtil.disposeUi(this); } + + @NotNull + public GradleSettings getInitialSettings() { + return myInitialSettings; + } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java index 2c61ac6c10eb..eb780ced4d5a 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEnvironment.java @@ -13,6 +13,18 @@ public class GradleEnvironment { @NonNls public static final boolean DISABLE_ENHANCED_TOOLING_API = Boolean.getBoolean("gradle.disable.enhanced.tooling.api"); @NonNls public static final boolean ADJUST_USER_DIR = Boolean.getBoolean("gradle.adjust.userdir"); + public static class Headless { + @NonNls public static final String GRADLE_DISTRIBUTION_TYPE = System.getProperty("idea.gradle.distributionType"); + @NonNls public static final String GRADLE_HOME = System.getProperty("idea.gradle.home"); + @NonNls public static final String GRADLE_VM_OPTIONS = System.getProperty("idea.gradle.vmOptions"); + @NonNls public static final String GRADLE_OFFLINE = System.getProperty("idea.gradle.offline"); + @NonNls public static final String GRADLE_SERVICE_DIRECTORY = System.getProperty("idea.gradle.serviceDirectory"); + + private Headless() { + } + } + + private GradleEnvironment() { } } From 5277806f7c8c47866bfa8112e5db66fce1da32cb Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 31 Jul 2014 16:57:49 +0400 Subject: [PATCH 26/85] IDEA-127046 Gradle: processing resources --- .../gradle/compiler/GradleBuildProcessParametersProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java index 1b0386e47be7..eaedad361241 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java @@ -53,10 +53,10 @@ public class GradleBuildProcessParametersProvider extends BuildProcessParameters @NotNull public List getClassPath() { if (myClasspath == null) { + myClasspath = ContainerUtil.newArrayList(); final ModuleManager moduleManager = ModuleManager.getInstance(myProject); for (Module module : moduleManager.getModules()) { if (ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) { - myClasspath = ContainerUtil.newArrayList(); addGradleClassPath(myClasspath); addOtherClassPath(myClasspath); break; From 85f854ca89c846f4521b9ed53899a654cf5c2b20 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 31 Jul 2014 17:21:40 +0400 Subject: [PATCH 27/85] IDEA-127046 Gradle: processing resources --- .idea/modules.xml | 1 + platform/external-system-impl/external-system-impl.iml | 1 - plugins/gradle/gradle.iml | 1 + .../gradle/service/project/data}/ExternalProjectSerializer.java | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) rename {platform/external-system-impl/src/com/intellij/openapi/externalSystem/util => plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data}/ExternalProjectSerializer.java (99%) diff --git a/.idea/modules.xml b/.idea/modules.xml index ca7b52d58ab5..2805b881df85 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -65,6 +65,7 @@ + diff --git a/platform/external-system-impl/external-system-impl.iml b/platform/external-system-impl/external-system-impl.iml index a59eae8eea21..1cbf68f907f9 100644 --- a/platform/external-system-impl/external-system-impl.iml +++ b/platform/external-system-impl/external-system-impl.iml @@ -24,7 +24,6 @@ - diff --git a/plugins/gradle/gradle.iml b/plugins/gradle/gradle.iml index e7873ca13ead..9e9fb73cdd72 100644 --- a/plugins/gradle/gradle.iml +++ b/plugins/gradle/gradle.iml @@ -106,6 +106,7 @@ + diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalProjectSerializer.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectSerializer.java similarity index 99% rename from platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalProjectSerializer.java rename to plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectSerializer.java index 0a863ed08852..8828b9ef5880 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalProjectSerializer.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/data/ExternalProjectSerializer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.openapi.externalSystem.util; +package org.jetbrains.plugins.gradle.service.project.data; import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.Serializer; From e1a1d3724fc1d46de47818f7b196de1198a6791b Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Thu, 31 Jul 2014 17:33:18 +0400 Subject: [PATCH 28/85] try to reinvalidate JEditorPane only --- .../packaging/PackagesNotificationPanel.java | 58 ++++++++----------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index d33bf51e9002..8b913f00baec 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -8,7 +8,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.HyperlinkAdapter; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBLabel; -import com.intellij.util.ObjectUtils; +import com.intellij.util.ui.SwingHelper; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,19 +23,17 @@ import java.util.Map; * @author yole */ public class PackagesNotificationPanel { - private final JEditorPane myEditorPane = new MyNotificationPane(); private final Project myProject; + private final JEditorPane myHtmlViewer; private final Map myLinkHandlers = new HashMap(); private String myErrorTitle; private String myErrorDescription; - public PackagesNotificationPanel(Project project) { + public PackagesNotificationPanel(@NotNull Project project) { myProject = project; - myEditorPane.setBackground(UIManager.getColor("ArrowButton.background")); - myEditorPane.setContentType("text/html"); - myEditorPane.setEditable(false); - myEditorPane.setVisible(false); - myEditorPane.addHyperlinkListener(new HyperlinkAdapter() { + myHtmlViewer = SwingHelper.createHtmlViewer(true, null, null, null); + myHtmlViewer.setVisible(false); + myHtmlViewer.addHyperlinkListener(new HyperlinkAdapter() { @Override protected void hyperlinkActivated(HyperlinkEvent e) { final Runnable handler = myLinkHandlers.get(e.getDescription()); @@ -78,11 +76,18 @@ public class PackagesNotificationPanel { public void showResult(String packageName, @Nullable String errorDescription) { if (StringUtil.isEmpty(errorDescription)) { - showSuccess("Package successfully installed."); + String message = "Package installed successfully"; + if (packageName != null) { + message = "Package '" + packageName + "' installed successfully"; + } + showSuccess(message); } else { - String title = "Install packages failed"; - final String firstLine = title + ": Error occurred when installing package " + packageName + ". "; + String title = "Failed to install packages"; + if (packageName != null) { + title = "Failed to install package '" + packageName + "'"; + } + String firstLine = title + ": Error occurred when installing package " + packageName + ". "; showError(firstLine + "Details...", title, firstLine + errorDescription); @@ -98,17 +103,17 @@ public class PackagesNotificationPanel { } public JComponent getComponent() { - return myEditorPane; + return myHtmlViewer; } public void showSuccess(String text) { showContent(text, MessageType.INFO.getPopupBackground()); } - private void showContent(String text, final Color background) { + private void showContent(@NotNull String text, @NotNull Color background) { String htmlText = text.startsWith("") ? text : UIUtil.toHtml(text); - myEditorPane.setText(htmlText); - myEditorPane.setBackground(background); + myHtmlViewer.setText(htmlText); + myHtmlViewer.setBackground(background); setVisibleEditorPane(true); myErrorTitle = null; myErrorDescription = null; @@ -129,15 +134,11 @@ public class PackagesNotificationPanel { } private void setVisibleEditorPane(boolean visible) { - boolean oldVisible = myEditorPane.isVisible(); - myEditorPane.setVisible(visible); + boolean oldVisible = myHtmlViewer.isVisible(); + myHtmlViewer.setVisible(visible); if (oldVisible != visible) { - JComponent comp = ObjectUtils.tryCast(myEditorPane.getParent(), JComponent.class); - if (comp == null) { - comp = myEditorPane; - } - comp.revalidate(); - comp.repaint(); + myHtmlViewer.revalidate(); + myHtmlViewer.repaint(); } } @@ -145,17 +146,6 @@ public class PackagesNotificationPanel { return myLinkHandlers.containsKey(key); } - private static class MyNotificationPane extends JEditorPane { - @Override - public Dimension getPreferredSize() { - // This trick makes text component to carry text over to the next line - // iff the text line width exceeds parent's width - Dimension dimension = super.getPreferredSize(); - dimension.width = 0; - return dimension; - } - } - public void removeLinkHandler(String key) { myLinkHandlers.remove(key); } From 120a5e8db8345d1ebaf97feb507930933fe8e641 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 31 Jul 2014 15:41:30 +0200 Subject: [PATCH 29/85] Fixed python-intellilang dependencies, moved configs to better place. --- python/IntelliLang-python/IntelliLang-python.iml | 1 + .../{src => resources}/META-INF/intellilang-python-support.xml | 2 +- .../IntelliLang-python/{src => resources}/META-INF/plugin.xml | 1 + python/IntelliLang-python/src/{ => resources}/pyInjections.xml | 0 4 files changed, 3 insertions(+), 1 deletion(-) rename python/IntelliLang-python/{src => resources}/META-INF/intellilang-python-support.xml (91%) rename python/IntelliLang-python/{src => resources}/META-INF/plugin.xml (92%) rename python/IntelliLang-python/src/{ => resources}/pyInjections.xml (100%) diff --git a/python/IntelliLang-python/IntelliLang-python.iml b/python/IntelliLang-python/IntelliLang-python.iml index a877c73afd91..49ab883c711d 100644 --- a/python/IntelliLang-python/IntelliLang-python.iml +++ b/python/IntelliLang-python/IntelliLang-python.iml @@ -4,6 +4,7 @@ + diff --git a/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml b/python/IntelliLang-python/resources/META-INF/intellilang-python-support.xml similarity index 91% rename from python/IntelliLang-python/src/META-INF/intellilang-python-support.xml rename to python/IntelliLang-python/resources/META-INF/intellilang-python-support.xml index 44bab2c91b05..c96f0b7f2b3a 100644 --- a/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml +++ b/python/IntelliLang-python/resources/META-INF/intellilang-python-support.xml @@ -3,7 +3,7 @@ - + diff --git a/python/IntelliLang-python/src/META-INF/plugin.xml b/python/IntelliLang-python/resources/META-INF/plugin.xml similarity index 92% rename from python/IntelliLang-python/src/META-INF/plugin.xml rename to python/IntelliLang-python/resources/META-INF/plugin.xml index 3009f34c2b9c..6b2717a6e7b1 100644 --- a/python/IntelliLang-python/src/META-INF/plugin.xml +++ b/python/IntelliLang-python/resources/META-INF/plugin.xml @@ -5,6 +5,7 @@ This plugin enables language injections JetBrains + com.intellij.modules.python org.intellij.intelliLang diff --git a/python/IntelliLang-python/src/pyInjections.xml b/python/IntelliLang-python/src/resources/pyInjections.xml similarity index 100% rename from python/IntelliLang-python/src/pyInjections.xml rename to python/IntelliLang-python/src/resources/pyInjections.xml From 9fa7020b6d5a64810c5b001301d7c9921b94b7fc Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 31 Jul 2014 17:42:05 +0400 Subject: [PATCH 30/85] IDEA-84858 Edit breakpoints popup: inspection tooltip for the condition is shown under the popup --- platform/platform-impl/src/com/intellij/ui/BalloonImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index b73b8ed522ea..513d5cedb2ab 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -613,7 +613,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { myComp.setBorder(new EmptyBorder(borderSize, borderSize, borderSize, borderSize)); myLayeredPane.add(myComp); - myLayeredPane.setLayer(myComp, getLayer()); + myLayeredPane.setLayer(myComp, getLayer(), 0); // the second balloon must be over the first one myPosition.updateBounds(this); if (myBlockClicks) { myComp.addMouseListener(new MouseAdapter() { From 9c37afacdd9e99d85b6712e859b193320cc6347b Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Thu, 31 Jul 2014 17:45:36 +0400 Subject: [PATCH 31/85] IDEA-109167 Provide ability to manually edit root url used for browsing when configuring branch locations --- .../dialogs/BranchConfigurationDialog.java | 25 ++++--- .../dialogs/RepositoryBrowserComponent.java | 16 +++++ .../svn/dialogs/RepositoryBrowserDialog.java | 27 +++++--- .../svn/dialogs/SelectLocationDialog.java | 69 ++++++++++++++++--- 4 files changed, 107 insertions(+), 30 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java index 6bd08c1089cb..50ddd9615d41 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/BranchConfigurationDialog.java @@ -22,10 +22,12 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.MultiLineLabelUI; import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.*; import com.intellij.ui.components.JBList; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -100,16 +102,23 @@ public class BranchConfigurationDialog extends DialogWrapper { myListPanel.add( ToolbarDecorator.createDecorator(myLocationList) .setAddAction(new AnActionButtonRunnable() { + + @Nullable private SVNURL usedRootUrl; + @Override public void run(AnActionButton button) { - final String selectedUrl = SelectLocationDialog.selectLocation(project, rootUrl.toDecodedString()); - if (selectedUrl != null) { - if (!configuration.getBranchUrls().contains(selectedUrl)) { - configuration - .addBranches(selectedUrl, new InfoStorage>(new ArrayList(), InfoReliability.empty)); - mySvnBranchConfigManager.reloadBranches(myRoot, selectedUrl, null); - listModel.fireItemAdded(); - myLocationList.setSelectedIndex(listModel.getSize() - 1); + Pair result = SelectLocationDialog.selectLocation(project, ObjectUtils.notNull(usedRootUrl, rootUrl)); + if (result != null) { + String selectedUrl = result.getFirst(); + usedRootUrl = result.getSecond(); + if (selectedUrl != null) { + if (!configuration.getBranchUrls().contains(selectedUrl)) { + configuration + .addBranches(selectedUrl, new InfoStorage>(new ArrayList(), InfoReliability.empty)); + mySvnBranchConfigManager.reloadBranches(myRoot, selectedUrl, null); + listModel.fireItemAdded(); + myLocationList.setSelectedIndex(listModel.getSize() - 1); + } } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserComponent.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserComponent.java index 0fd1f06bf7cb..163be4922224 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserComponent.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserComponent.java @@ -76,10 +76,26 @@ public class RepositoryBrowserComponent extends JPanel implements Disposable, Da } public void setRepositoryURLs(SVNURL[] urls, final boolean showFiles) { + setRepositoryURLs(urls, showFiles, null, false); + } + + public void setRepositoryURLs(SVNURL[] urls, + final boolean showFiles, + @Nullable NotNullFunction defaultExpanderFactory, + boolean expandFirst) { RepositoryTreeModel model = new RepositoryTreeModel(myVCS, showFiles, this); + + if (defaultExpanderFactory != null) { + model.setDefaultExpanderFactory(defaultExpanderFactory); + } + model.setRoots(urls); Disposer.register(this, model); myRepositoryTree.setModel(model); + + if (expandFirst) { + myRepositoryTree.expandRow(0); + } } public void setRepositoryURL(SVNURL url, boolean showFiles, final NotNullFunction defaultExpanderFactory) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java index 191acf387b86..83d895b93ffc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java @@ -93,7 +93,7 @@ public class RepositoryBrowserDialog extends DialogWrapper { private final boolean myShowFiles; - @NonNls private static final String PLACE_TOOLBAR = "RepositoryBrowser.Toolbar"; + @NonNls public static final String PLACE_TOOLBAR = "RepositoryBrowser.Toolbar"; @NonNls private static final String PLACE_MENU = "RepositoryBrowser.Menu"; private final String myRepositoriesLabelText; protected JLabel myRepositoriesLabel; @@ -138,7 +138,7 @@ public class RepositoryBrowserDialog extends DialogWrapper { DefaultActionGroup group = new DefaultActionGroup(); final RepositoryBrowserComponent browser = getRepositoryBrowser(); group.add(new AddLocationAction(browser)); - group.add(new EditLocationAction()); + group.add(new EditLocationAction(browser)); group.add(new DiscardLocationAction(browser)); group.add(new DetailsAction()); group.addSeparator(); @@ -216,7 +216,7 @@ public class RepositoryBrowserDialog extends DialogWrapper { group.add(copyUrlAction); group.addSeparator(); group.add(new RefreshAction(browser)); - group.add(new EditLocationAction()); + group.add(new EditLocationAction(browser)); group.add(new DiscardLocationAction(browser)); ActionPopupMenu menu = ActionManager.getInstance().createActionPopupMenu(PLACE_MENU, group); return menu.getComponent(); @@ -413,13 +413,17 @@ public class RepositoryBrowserDialog extends DialogWrapper { } } - protected class EditLocationAction extends AnAction { - public EditLocationAction() { + protected static class EditLocationAction extends AnAction { + + @NotNull private final RepositoryBrowserComponent myBrowserComponent; + + public EditLocationAction(@NotNull RepositoryBrowserComponent browserComponent) { super(SvnBundle.message("repository.browser.edit.location.menu.item")); + myBrowserComponent = browserComponent; } public void update(AnActionEvent e) { - RepositoryTreeNode node = getRepositoryBrowser().getSelectedNode(); + RepositoryTreeNode node = myBrowserComponent.getSelectedNode(); if (e.getPlace().equals(PLACE_TOOLBAR)) { e.getPresentation().setDescription(SvnBundle.message("repository.browser.edit.location.menu.item")); e.getPresentation().setText(SvnBundle.message("repository.browser.edit.location.menu.item")); @@ -429,13 +433,14 @@ public class RepositoryBrowserDialog extends DialogWrapper { } public void actionPerformed(AnActionEvent e) { - RepositoryTreeNode node = getRepositoryBrowser().getSelectedNode(); + RepositoryTreeNode node = myBrowserComponent.getSelectedNode(); if (node == null || (! (node.getParent() instanceof RepositoryTreeRootNode))) { return; } final String oldUrl = node.getURL().toString(); final SvnApplicationSettings settings = SvnApplicationSettings.getInstance(); - final AddRepositoryLocationDialog dialog = new AddRepositoryLocationDialog(myProject, settings.getTypedUrlsListCopy()) { + final AddRepositoryLocationDialog dialog = + new AddRepositoryLocationDialog(myBrowserComponent.getProject(), settings.getTypedUrlsListCopy()) { @Override protected String initText() { return oldUrl; @@ -453,9 +458,9 @@ public class RepositoryBrowserDialog extends DialogWrapper { settings.addTypedUrl(url); settings.removeCheckoutURL(oldUrl); settings.addCheckoutURL(url); - final RepositoryBrowserComponent browser = getRepositoryBrowser(); - browser.removeURL(oldUrl); - browser.addURL(url); + + myBrowserComponent.removeURL(oldUrl); + myBrowserComponent.addURL(url); } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java index a6ca6ad54f11..bcbbe79c1b45 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SelectLocationDialog.java @@ -15,12 +15,15 @@ */ package org.jetbrains.idea.svn.dialogs; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -52,21 +55,30 @@ public class SelectLocationDialog extends DialogWrapper { private final String myDstLabel; private JTextField myDstText; private final boolean myIsShowFiles; + private final boolean myAllowActions; @NonNls private static final String HELP_ID = "vcs.subversion.common"; // todo check that works when authenticated @Nullable public static String selectLocation(Project project, String url) { - SelectLocationDialog dialog = openDialog(project, url, null, null, true, null); + SelectLocationDialog dialog = openDialog(project, url, null, null, true, false, null); return dialog == null || !dialog.isOK() ? null : dialog.getSelectedURL(); } + @Nullable + public static Pair selectLocation(Project project, @NotNull SVNURL url) { + SelectLocationDialog dialog = new SelectLocationDialog(project, url, null, null, true, true); + dialog.show(); + + return dialog.isOK() ? Pair.create(dialog.getSelectedURL(), dialog.getRootUrl()) : null; + } + @Nullable public static String selectCopyDestination(Project project, String url, String dstLabel, String dstName, boolean showFiles) { SelectLocationDialog dialog = - openDialog(project, url, dstLabel, dstName, showFiles, SvnBundle.message("select.location.invalid.url.message", url)); + openDialog(project, url, dstLabel, dstName, showFiles, false, SvnBundle.message("select.location.invalid.url.message", url)); return dialog == null || !dialog.isOK() ? null : SVNPathUtil.append(dialog.getSelectedURL(), dialog.getDestinationName()); } @@ -77,6 +89,7 @@ public class SelectLocationDialog extends DialogWrapper { String dstLabel, String dstName, boolean showFiles, + boolean allowActions, String errorMessage) { try { SVNURL svnUrl = SvnUtil.createUrl(url); @@ -86,8 +99,7 @@ public class SelectLocationDialog extends DialogWrapper { SvnBundle.message("dialog.title.select.repository.location")); return null; } - - SelectLocationDialog dialog = new SelectLocationDialog(project, repositoryUrl, dstLabel, dstName, showFiles); + SelectLocationDialog dialog = new SelectLocationDialog(project, repositoryUrl, dstLabel, dstName, showFiles, allowActions); dialog.show(); return dialog; } @@ -98,13 +110,14 @@ public class SelectLocationDialog extends DialogWrapper { } } - private SelectLocationDialog(Project project, SVNURL url, String dstLabel, String dstName, boolean showFiles) { + private SelectLocationDialog(Project project, SVNURL url, String dstLabel, String dstName, boolean showFiles, boolean allowActions) { super(project, true); myProject = project; myDstLabel = dstLabel; myDstName = dstName; myURL = url; myIsShowFiles = showFiles; + myAllowActions = allowActions; setTitle(SvnBundle.message("dialog.title.select.repository.location")); getHelpAction().setEnabled(true); init(); @@ -146,7 +159,13 @@ public class SelectLocationDialog extends DialogWrapper { protected void init() { super.init(); final String urlString = myURL.toString(); - myRepositoryBrowser.setRepositoryURL(myURL, myIsShowFiles, new UrlOpeningExpander.Factory(urlString, urlString)); + if (myAllowActions) { + // initialize repo browser this way - to make actions work correctly + myRepositoryBrowser.setRepositoryURLs(new SVNURL[]{myURL}, myIsShowFiles, new UrlOpeningExpander.Factory(urlString, urlString), true); + } + else { + myRepositoryBrowser.setRepositoryURL(myURL, myIsShowFiles, new UrlOpeningExpander.Factory(urlString, urlString)); + } myRepositoryBrowser.addChangeListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { getOKAction().setEnabled(isOKActionEnabled()); @@ -162,7 +181,10 @@ public class SelectLocationDialog extends DialogWrapper { protected JComponent createCenterPanel() { JPanel panel = new JPanel(); - panel.setLayout(new GridBagLayout()); + panel.setLayout(new BorderLayout()); + + JPanel browserPanel = new JPanel(); + browserPanel.setLayout(new GridBagLayout()); GridBagConstraints gc = new GridBagConstraints(); gc.insets = new Insets(2, 2, 2, 2); @@ -177,7 +199,7 @@ public class SelectLocationDialog extends DialogWrapper { myRepositoryBrowser = new RepositoryBrowserComponent(SvnVcs.getInstance(myProject)); - panel.add(myRepositoryBrowser, gc); + browserPanel.add(myRepositoryBrowser, gc); if (myDstName != null) { gc.gridy += 1; gc.gridwidth = 1; @@ -187,7 +209,7 @@ public class SelectLocationDialog extends DialogWrapper { gc.weighty = 0; JLabel dstLabel = new JLabel(myDstLabel); - panel.add(dstLabel, gc); + browserPanel.add(dstLabel, gc); gc.gridx += 1; gc.weightx = 1; @@ -196,7 +218,7 @@ public class SelectLocationDialog extends DialogWrapper { myDstText = new JTextField(); myDstText.setText(myDstName); myDstText.selectAll(); - panel.add(myDstText, gc); + browserPanel.add(myDstText, gc); myDstText.getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { @@ -217,12 +239,25 @@ public class SelectLocationDialog extends DialogWrapper { gc.gridy += 1; gc.gridwidth = 2; - panel.add(new JSeparator(), gc); + browserPanel.add(new JSeparator(), gc); } + if (myAllowActions) { + panel.add(createToolbar(), BorderLayout.NORTH); + } + panel.add(browserPanel, BorderLayout.CENTER); + return panel; } + @NotNull + private JComponent createToolbar() { + DefaultActionGroup group = new DefaultActionGroup(); + group.add(new RepositoryBrowserDialog.EditLocationAction(myRepositoryBrowser)); + + return ActionManager.getInstance().createActionToolbar(RepositoryBrowserDialog.PLACE_TOOLBAR, group, true).getComponent(); + } + public JComponent getPreferredFocusedComponent() { return (JComponent)myRepositoryBrowser.getPreferredFocusedComponent(); } @@ -246,4 +281,16 @@ public class SelectLocationDialog extends DialogWrapper { public String getSelectedURL() { return myRepositoryBrowser.getSelectedURL(); } + + @Nullable + public SVNURL getRootUrl() { + RepositoryTreeNode node = myRepositoryBrowser.getSelectedNode(); + + // find the most top parent of type RepositoryTreeNode + while (node != null && node.getParent() instanceof RepositoryTreeNode) { + node = (RepositoryTreeNode)node.getParent(); + } + + return node != null ? node.getURL() : null; + } } From 71208052706b90e6108b534d371b5b04bb08deee Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 31 Jul 2014 15:55:33 +0200 Subject: [PATCH 32/85] IG: drop StringBuilder parameter [CR-IC-5971] --- .../ig/style/SimplifiableAnnotationInspection.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SimplifiableAnnotationInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SimplifiableAnnotationInspection.java index 007280f6da74..e45f06cac284 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SimplifiableAnnotationInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/SimplifiableAnnotationInspection.java @@ -84,20 +84,20 @@ public class SimplifiableAnnotationInspection extends BaseInspection { return; } final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - final String annotationText = buildAnnotationText(annotation, new StringBuilder()).toString(); + final String annotationText = buildAnnotationText(annotation); final PsiAnnotation newAnnotation = factory.createAnnotationFromText(annotationText, element); annotation.replace(newAnnotation); } - private static StringBuilder buildAnnotationText(PsiAnnotation annotation, StringBuilder out) { - out.append('@'); + private static String buildAnnotationText(PsiAnnotation annotation) { + final StringBuilder out = new StringBuilder("@"); final PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement(); assert nameReferenceElement != null; out.append(nameReferenceElement.getText()); final PsiAnnotationParameterList parameterList = annotation.getParameterList(); final PsiNameValuePair[] attributes = parameterList.getAttributes(); if (attributes.length == 0) { - return out; + return out.toString(); } out.append('('); if (attributes.length == 1) { @@ -119,7 +119,7 @@ public class SimplifiableAnnotationInspection extends BaseInspection { } } out.append(')'); - return out; + return out.toString(); } private static StringBuilder buildAttributeValueText(PsiAnnotationMemberValue value, StringBuilder out) { @@ -131,7 +131,7 @@ public class SimplifiableAnnotationInspection extends BaseInspection { } } else if (value instanceof PsiAnnotation) { - return buildAnnotationText((PsiAnnotation)value, out); + return out.append(buildAnnotationText((PsiAnnotation)value)); } return out.append(value.getText()); } From eb273d2d217f159c084c75b68e54fe12b8392d78 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 31 Jul 2014 16:19:08 +0200 Subject: [PATCH 33/85] proper lexer in usage view for custom file editor highlighters (e.g. jsp, php) --- .../impl/search/JspIndexPatternBuilder.java | 3 +- .../search/LexerEditorHighlighterLexer.java | 36 ------- .../intellij/find/impl/FindManagerImpl.java | 53 ++------- .../editor/impl/EditorHighlighterCache.java | 33 ++++++ .../com/intellij/usages/ChunkExtractor.java | 31 +++--- ...yntaxHighlighterOverEditorHighlighter.java | 101 ++++++++++++++++++ platform/usageView/usageView.iml | 1 + 7 files changed, 164 insertions(+), 94 deletions(-) rename platform/{lang-impl => editor-ui-ex}/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java (61%) create mode 100644 platform/usageView/src/com/intellij/usages/impl/SyntaxHighlighterOverEditorHighlighter.java diff --git a/java/java-impl/src/com/intellij/psi/impl/search/JspIndexPatternBuilder.java b/java/java-impl/src/com/intellij/psi/impl/search/JspIndexPatternBuilder.java index a8316f8dc0ee..eaa93875ba15 100644 --- a/java/java-impl/src/com/intellij/psi/impl/search/JspIndexPatternBuilder.java +++ b/java/java-impl/src/com/intellij/psi/impl/search/JspIndexPatternBuilder.java @@ -18,6 +18,7 @@ package com.intellij.psi.impl.search; import com.intellij.lang.LanguageParserDefinitions; import com.intellij.lang.ParserDefinition; import com.intellij.lexer.Lexer; +import com.intellij.openapi.editor.impl.EditorHighlighterCache; import com.intellij.psi.JspPsiUtil; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.tree.StdTokenSets; @@ -34,7 +35,7 @@ public class JspIndexPatternBuilder implements IndexPatternBuilder { @Override public Lexer getIndexingLexer(@NotNull final PsiFile file) { if (JspPsiUtil.isInJspFile(file)) { - return LexerEditorHighlighterLexer.getLexerBasedOnLexerHighlighter(file.getText(), file.getVirtualFile(), file.getProject()); + return EditorHighlighterCache.getLexerBasedOnLexerHighlighter(file.getText(), file.getVirtualFile(), file.getProject()); } return null; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java b/platform/editor-ui-ex/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java similarity index 61% rename from platform/lang-impl/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java rename to platform/editor-ui-ex/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java index 2add3ed1b3c5..a966d831b71a 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java +++ b/platform/editor-ui-ex/src/com/intellij/psi/impl/search/LexerEditorHighlighterLexer.java @@ -15,23 +15,12 @@ */ package com.intellij.psi.impl.search; -import com.intellij.lexer.Lexer; import com.intellij.lexer.LexerBase; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; import com.intellij.openapi.editor.highlighter.HighlighterIterator; -import com.intellij.openapi.editor.impl.EditorHighlighterCache; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.psi.impl.cache.impl.id.PlatformIdTableBuilding; import com.intellij.psi.tree.IElementType; import com.intellij.util.text.CharSequenceSubSequence; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * @author Sergey Evdokimov @@ -49,31 +38,6 @@ public class LexerEditorHighlighterLexer extends LexerBase { myAlreadyInitializedHighlighter = alreadyInitializedHighlighter; } - @Nullable - public static Lexer getLexerBasedOnLexerHighlighter(CharSequence text, VirtualFile virtualFile, Project project) { - EditorHighlighter highlighter = null; - - PsiFile psiFile = virtualFile != null ? PsiManager.getInstance(project).findFile(virtualFile) : null; - final Document document = psiFile != null ? PsiDocumentManager.getInstance(project).getDocument(psiFile) : null; - final EditorHighlighter cachedEditorHighlighter; - boolean alreadyInitializedHighlighter = false; - - if (document != null && - (cachedEditorHighlighter = EditorHighlighterCache.getEditorHighlighterForCachesBuilding(document)) != null && - PlatformIdTableBuilding.checkCanUseCachedEditorHighlighter(text, cachedEditorHighlighter)) { - highlighter = cachedEditorHighlighter; - alreadyInitializedHighlighter = true; - } - else if (virtualFile != null) { - highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, virtualFile); - } - - if (highlighter != null) { - return new LexerEditorHighlighterLexer(highlighter, alreadyInitializedHighlighter); - } - return null; - } - @Override public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int state) { if (myAlreadyInitializedHighlighter) { diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index bc99477ac08f..05a40ab19ec5 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -27,7 +27,6 @@ import com.intellij.find.impl.livePreview.SearchResults; import com.intellij.lang.Language; import com.intellij.lang.LanguageParserDefinitions; import com.intellij.lang.ParserDefinition; -import com.intellij.lexer.LayeredLexer; import com.intellij.lexer.Lexer; import com.intellij.navigation.NavigationItem; import com.intellij.openapi.actionSystem.ActionManager; @@ -42,11 +41,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.ex.FoldingModelEx; -import com.intellij.openapi.editor.ex.util.LayeredHighlighterIterator; -import com.intellij.openapi.editor.ex.util.LayeredLexerEditorHighlighter; -import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; -import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.TextEditor; @@ -58,7 +52,6 @@ import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; -import com.intellij.psi.impl.search.LexerEditorHighlighterLexer; import com.intellij.psi.search.SearchScope; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; @@ -66,6 +59,7 @@ import com.intellij.ui.LightweightHint; import com.intellij.ui.ReplacePromptDialog; import com.intellij.usages.ChunkExtractor; import com.intellij.usages.UsageViewManager; +import com.intellij.usages.impl.SyntaxHighlighterOverEditorHighlighter; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.MessageBus; @@ -424,8 +418,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo private static class CommentsLiteralsSearchData { final VirtualFile lastFile; int startOffset = 0; - final SyntaxHighlighter highlighter; - final Lexer highlightingLexer; + final SyntaxHighlighterOverEditorHighlighter highlighter; TokenSet tokensOfInterest; final StringSearcher searcher; @@ -433,8 +426,8 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo final Set relevantLanguages; final FindModel model; - public CommentsLiteralsSearchData(VirtualFile lastFile, Set relevantLanguages, SyntaxHighlighter highlighter, - Lexer lexer, TokenSet tokensOfInterest, + public CommentsLiteralsSearchData(VirtualFile lastFile, Set relevantLanguages, + SyntaxHighlighterOverEditorHighlighter highlighter, TokenSet tokensOfInterest, StringSearcher searcher, Matcher matcher, FindModel model) { this.lastFile = lastFile; this.highlighter = highlighter; @@ -442,7 +435,6 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo this.searcher = searcher; this.matcher = matcher; this.relevantLanguages = relevantLanguages; - highlightingLexer = lexer; this.model = model; } } @@ -525,38 +517,15 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo Matcher matcher = model.isRegularExpressions() ? compileRegExp(model, ""):null; StringSearcher searcher = matcher != null ? null: new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), true); - LayeredLexer.ourDisableLayersFlag.set(Boolean.TRUE); - EditorHighlighter editorHighlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, file); - Lexer lexer; - - try { - if (editorHighlighter instanceof LayeredLexerEditorHighlighter) { - lexer = new LexerEditorHighlighterLexer(editorHighlighter, false); - } else { - lexer = highlighter.getHighlightingLexer(); - } - } - finally { - LayeredLexer.ourDisableLayersFlag.set(null); - } - - data = new CommentsLiteralsSearchData(file, relevantLanguages, highlighter, lexer, tokensOfInterest, searcher, matcher, (FindModel)model.clone()); - lexer.start(text, 0, text.length(), 0); + SyntaxHighlighterOverEditorHighlighter highlighterAdapter = new SyntaxHighlighterOverEditorHighlighter(highlighter, file, myProject); + data = new CommentsLiteralsSearchData(file, relevantLanguages, highlighterAdapter, tokensOfInterest, searcher, matcher, model.clone()); + data.highlighter.restart(text); model.putUserData(ourCommentsLiteralsSearchDataKey, data); } int initialStartOffset = model.isForward() && data.startOffset < offset ? data.startOffset : 0; - final Lexer lexer = data.highlightingLexer; - LayeredHighlighterIterator layeredHighlighterIterator = null; - if (lexer instanceof LexerEditorHighlighterLexer) { - ((LexerEditorHighlighterLexer)lexer).resetPosition(initialStartOffset); - HighlighterIterator iterator = ((LexerEditorHighlighterLexer)lexer).getHighlighterIterator(); - if (iterator instanceof LayeredHighlighterIterator) { - layeredHighlighterIterator = (LayeredHighlighterIterator)iterator; - } - } else { - lexer.start(text, initialStartOffset, text.length(), 0); - } + data.highlighter.resetPosition(initialStartOffset); + final Lexer lexer = data.highlighter.getHighlightingLexer(); IElementType tokenType; TokenSet tokens = data.tokensOfInterest; @@ -568,9 +537,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo while((tokenType = lexer.getTokenType()) != null) { if (lexer.getState() == 0) lastGoodOffset = lexer.getTokenStart(); - final SyntaxHighlighter activeSyntaxHighlighter = - layeredHighlighterIterator != null ? layeredHighlighterIterator.getActiveSyntaxHighlighter() : data.highlighter; - final TextAttributesKey[] keys = activeSyntaxHighlighter.getTokenHighlights(tokenType); + final TextAttributesKey[] keys = data.highlighter.getTokenHighlights(tokenType); if (tokens.contains(tokenType) || (model.isInStringLiteralsOnly() && ChunkExtractor.isHighlightedAsString(keys)) || diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java index c1499a71a71b..8be2284e8360 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java @@ -15,10 +15,19 @@ */ package com.intellij.openapi.editor.impl; +import com.intellij.lexer.Lexer; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighter; +import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.cache.impl.id.PlatformIdTableBuilding; +import com.intellij.psi.impl.search.LexerEditorHighlighterLexer; import com.intellij.reference.SoftReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -55,4 +64,28 @@ public class EditorHighlighterCache { return null; } + @Nullable + public static Lexer getLexerBasedOnLexerHighlighter(CharSequence text, VirtualFile virtualFile, Project project) { + EditorHighlighter highlighter = null; + + PsiFile psiFile = virtualFile != null ? PsiManager.getInstance(project).findFile(virtualFile) : null; + final Document document = psiFile != null ? PsiDocumentManager.getInstance(project).getDocument(psiFile) : null; + final EditorHighlighter cachedEditorHighlighter; + boolean alreadyInitializedHighlighter = false; + + if (document != null && + (cachedEditorHighlighter = getEditorHighlighterForCachesBuilding(document)) != null && + PlatformIdTableBuilding.checkCanUseCachedEditorHighlighter(text, cachedEditorHighlighter)) { + highlighter = cachedEditorHighlighter; + alreadyInitializedHighlighter = true; + } + else if (virtualFile != null) { + highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, virtualFile); + } + + if (highlighter != null) { + return new LexerEditorHighlighterLexer(highlighter, alreadyInitializedHighlighter); + } + return null; + } } diff --git a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java index 71a1f50f1cd5..eea15c93605d 100644 --- a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java +++ b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java @@ -37,6 +37,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.reference.SoftReference; import com.intellij.usageView.UsageTreeColors; import com.intellij.usageView.UsageTreeColorsScheme; +import com.intellij.usages.impl.SyntaxHighlighterOverEditorHighlighter; import com.intellij.usages.impl.rules.UsageType; import com.intellij.util.Processor; import com.intellij.util.containers.FactoryMap; @@ -64,9 +65,7 @@ public class ChunkExtractor { private final Document myDocument; private long myDocumentStamp; - private final SyntaxHighlighter myHighlighter; - - private final Lexer myLexer; + private final SyntaxHighlighterOverEditorHighlighter myHighlighter; private abstract static class WeakFactory { private WeakReference myRef; @@ -119,11 +118,10 @@ public class ChunkExtractor { myDocument = PsiDocumentManager.getInstance(project).getDocument(file); LOG.assertTrue(myDocument != null); final FileType fileType = file.getFileType(); - final SyntaxHighlighter highlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(fileType, project, file.getVirtualFile()); - myHighlighter = highlighter == null ? new PlainSyntaxHighlighter() : highlighter; - myLexer = myHighlighter.getHighlightingLexer(); - myLexer.start(myDocument.getCharsSequence()); - myDocumentStamp = myDocument.getModificationStamp(); + SyntaxHighlighter highlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(fileType, project, file.getVirtualFile()); + highlighter = highlighter == null ? new PlainSyntaxHighlighter() : highlighter; + myHighlighter = new SyntaxHighlighterOverEditorHighlighter(highlighter, file.getVirtualFile(), project); + myDocumentStamp = -1; } public static int getStartOffset(final List rangeMarkers) { @@ -194,18 +192,21 @@ public class ChunkExtractor { int end, boolean selectUsageWithBold, @NotNull List result) { - final Lexer lexer = myLexer; - final SyntaxHighlighter highlighter = myHighlighter; + final Lexer lexer = myHighlighter.getHighlightingLexer(); + final SyntaxHighlighterOverEditorHighlighter highlighter = myHighlighter; LOG.assertTrue(start <= end); int i = StringUtil.indexOf(chars, '\n', start, end); if (i != -1) end = i; - if (lexer.getTokenStart() > start || myDocumentStamp != myDocument.getModificationStamp()) { - lexer.start(chars); + if (myDocumentStamp != myDocument.getModificationStamp()) { + highlighter.restart(chars); myDocumentStamp = myDocument.getModificationStamp(); + } else if(lexer.getTokenStart() > start) { + highlighter.resetPosition(0); } + boolean isBeginning = true; for(;lexer.getTokenType() != null; lexer.advance()) { @@ -218,8 +219,10 @@ public class ChunkExtractor { hiEnd = Math.min(hiEnd, end); if (hiStart >= hiEnd) { continue; } - String text = chars.subSequence(hiStart, hiEnd).toString(); - if (isBeginning && text.trim().isEmpty()) continue; + if (isBeginning) { + String text = chars.subSequence(hiStart, hiEnd).toString(); + if(text.trim().isEmpty()) continue; + } isBeginning = false; IElementType tokenType = lexer.getTokenType(); TextAttributesKey[] tokenHighlights = highlighter.getTokenHighlights(tokenType); diff --git a/platform/usageView/src/com/intellij/usages/impl/SyntaxHighlighterOverEditorHighlighter.java b/platform/usageView/src/com/intellij/usages/impl/SyntaxHighlighterOverEditorHighlighter.java new file mode 100644 index 000000000000..94162fcb0715 --- /dev/null +++ b/platform/usageView/src/com/intellij/usages/impl/SyntaxHighlighterOverEditorHighlighter.java @@ -0,0 +1,101 @@ +/* + * Copyright 2000-2014 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.usages.impl; + +import com.intellij.lexer.LayeredLexer; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.editor.ex.util.LayeredHighlighterIterator; +import com.intellij.openapi.editor.ex.util.LayeredLexerEditorHighlighter; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; +import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; +import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.openapi.fileTypes.SyntaxHighlighter; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.impl.search.LexerEditorHighlighterLexer; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +/** +* Created by Maxim.Mossienko on 7/31/2014. +*/ +public class SyntaxHighlighterOverEditorHighlighter implements SyntaxHighlighter { + private final Lexer lexer; + private LayeredHighlighterIterator layeredHighlighterIterator = null; + private boolean lexerEditorHighlighterInitialized; + private final SyntaxHighlighter highlighter; + + public SyntaxHighlighterOverEditorHighlighter(SyntaxHighlighter _highlighter, VirtualFile file, Project project) { + highlighter = _highlighter; + LayeredLexer.ourDisableLayersFlag.set(Boolean.TRUE); + EditorHighlighter editorHighlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, file); + + try { + if (editorHighlighter instanceof LayeredLexerEditorHighlighter) { + lexer = new LexerEditorHighlighterLexer(editorHighlighter, false); + } else { + lexer = highlighter.getHighlightingLexer(); + } + } + finally { + LayeredLexer.ourDisableLayersFlag.set(null); + } + } + + @NotNull + @Override + public Lexer getHighlightingLexer() { + return lexer; + } + + @NotNull + @Override + public TextAttributesKey[] getTokenHighlights(IElementType tokenType) { + final SyntaxHighlighter activeSyntaxHighlighter = + layeredHighlighterIterator != null ? layeredHighlighterIterator.getActiveSyntaxHighlighter() : highlighter; + return activeSyntaxHighlighter.getTokenHighlights(tokenType); + } + + public void restart(@NotNull CharSequence text) { + lexer.start(text); + + if (lexer instanceof LexerEditorHighlighterLexer) { + HighlighterIterator iterator = ((LexerEditorHighlighterLexer)lexer).getHighlighterIterator(); + if (iterator instanceof LayeredHighlighterIterator) { + layeredHighlighterIterator = (LayeredHighlighterIterator)iterator; + } else { + layeredHighlighterIterator = null; + } + } + } + + public void resetPosition(int startOffset) { + if (lexer instanceof LexerEditorHighlighterLexer) { + ((LexerEditorHighlighterLexer)lexer).resetPosition(startOffset); + + HighlighterIterator iterator = ((LexerEditorHighlighterLexer)lexer).getHighlighterIterator(); + if (iterator instanceof LayeredHighlighterIterator) { + layeredHighlighterIterator = (LayeredHighlighterIterator)iterator; + } else { + layeredHighlighterIterator = null; + } + } else { + CharSequence text = lexer.getBufferSequence(); + lexer.start(text, startOffset, text.length()); + } + } +} diff --git a/platform/usageView/usageView.iml b/platform/usageView/usageView.iml index e3943e442779..d69cf73e8a0c 100644 --- a/platform/usageView/usageView.iml +++ b/platform/usageView/usageView.iml @@ -9,6 +9,7 @@ + From 6ef79a223ac7c4ef880b2545bc3c6b153f06ba49 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 31 Jul 2014 18:18:08 +0400 Subject: [PATCH 34/85] IDEA-127797 Support opening Gradle projects in headless mode --- .../service/project/wizard/GradleProjectOpenProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleProjectOpenProcessor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleProjectOpenProcessor.java index ebd9d3392879..10f766cc5e34 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleProjectOpenProcessor.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/wizard/GradleProjectOpenProcessor.java @@ -71,6 +71,7 @@ public class GradleProjectOpenProcessor extends ProjectOpenProcessorBase 0 && wizardSteps[0] instanceof SelectExternalProjectStep) { SelectExternalProjectStep selectExternalProjectStep = (SelectExternalProjectStep)wizardSteps[0]; From 54b1086b1ada68fc8d44398b04c24e042d51f878 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Thu, 31 Jul 2014 18:39:22 +0400 Subject: [PATCH 35/85] IDEA-128017 IDE got stuck on Breakpoint edit window during debug --- .../debugger/ui/breakpoints/JavaBreakpointTypeBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointTypeBase.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointTypeBase.java index a537db14b2e8..4e1d3e2adb3f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointTypeBase.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/JavaBreakpointTypeBase.java @@ -63,7 +63,7 @@ public abstract class JavaBreakpointTypeBase Breakpoint javaBreakpoint = BreakpointManager.getJavaBreakpoint(breakpoint); if (javaBreakpoint != null) { PsiClass aClass = javaBreakpoint.getPsiClass(); - if (aClass != null && aClass.getContainingFile() != null) { + if (aClass != null && aClass.getContainingFile() != null && aClass.getTextOffset() >= 0) { return XDebuggerUtil.getInstance().createPositionByOffset(aClass.getContainingFile().getVirtualFile(), aClass.getTextOffset()); } } From 2be97c81b87a287d5e28c20133ad5c2f42ffa42a Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 31 Jul 2014 18:41:59 +0400 Subject: [PATCH 36/85] Kryo project level lib added to community addition --- .idea/libraries/Kryo.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .idea/libraries/Kryo.xml diff --git a/.idea/libraries/Kryo.xml b/.idea/libraries/Kryo.xml new file mode 100644 index 000000000000..b11f03b83ef2 --- /dev/null +++ b/.idea/libraries/Kryo.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file From 0514e09cb4a0cd30d6475402fec685c143d6504f Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Thu, 31 Jul 2014 16:42:17 +0400 Subject: [PATCH 37/85] smart backspace: better handing of uncommitted documents --- .../IndentingBackspaceHandler.java | 15 ++++---- ...ckspaceHandlerUncommittedDocumentTest.java | 34 +++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/IndentingBackspaceHandlerUncommittedDocumentTest.java diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java index 501ea66fd767..cd49d111950b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/IndentingBackspaceHandler.java @@ -27,11 +27,8 @@ import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Couple; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; @@ -46,12 +43,10 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { private static final Logger LOG = Logger.getInstance(IndentingBackspaceHandler.class); private boolean caretWasAtLineStart; - private int initialCaretOffset; @Override public void beforeCharDeleted(char c, PsiFile file, Editor editor) { caretWasAtLineStart = editor.getCaretModel().getLogicalPosition().column == 0; - initialCaretOffset = editor.getCaretModel().getOffset(); } @Override @@ -67,11 +62,10 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { Project project = file.getProject(); Document document = editor.getDocument(); CaretModel caretModel = editor.getCaretModel(); - boolean isUncommitted = PsiDocumentManager.getInstance(project).isUncommited(document); int caretOffset = caretModel.getOffset(); int offset = CharArrayUtil.shiftForward(document.getCharsSequence(), caretOffset, " \t"); - int offsetInPsi = offset + (isUncommitted ? 1 : 0); + int offsetInPsi = getOffsetInPsi(document, file, offset); int beforeWhitespaceOffset = CharArrayUtil.shiftBackward(document.getCharsSequence(), offset - 1, " \t") + 1; LogicalPosition logicalPosition = caretOffset < offset ? editor.offsetToLogicalPosition(offset) : caretModel.getLogicalPosition(); int lineStartOffset = document.getLineStartOffset(logicalPosition.line); @@ -89,7 +83,7 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { CodeStyleFacade codeStyleFacade = CodeStyleFacade.getInstance(project); // We should calculate indent at line containing the text after caret, cause that text might affect the result (e.g. closing brace in Java) - String indent = codeStyleFacade.getLineIndent(document, caretWasAtLineStart && isUncommitted ? initialCaretOffset : lineStartOffset); + String indent = codeStyleFacade.getLineIndent(document, offsetInPsi); if (indent == null) { return false; } @@ -131,6 +125,11 @@ public class IndentingBackspaceHandler extends BackspaceHandlerDelegate { return true; } + private static int getOffsetInPsi(Document document, PsiFile file, int offset) { + int psiLength = file.getTextLength(); + return Math.max(0, Math.min(psiLength, offset + psiLength - document.getTextLength())); + } + private static int getTabSize(@NotNull CodeStyleFacade codeStyleFacade, @NotNull Document document) { VirtualFile file = FileDocumentManager.getInstance().getFile(document); FileType fileType = file == null ? null : file.getFileType(); diff --git a/platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/IndentingBackspaceHandlerUncommittedDocumentTest.java b/platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/IndentingBackspaceHandlerUncommittedDocumentTest.java new file mode 100644 index 000000000000..c4913f888f1b --- /dev/null +++ b/platform/lang-impl/testSources/com/intellij/codeInsight/editorActions/IndentingBackspaceHandlerUncommittedDocumentTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 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.codeInsight.editorActions; + +import com.intellij.testFramework.LightPlatformCodeInsightTestCase; + +import java.io.IOException; + +public class IndentingBackspaceHandlerUncommittedDocumentTest extends LightPlatformCodeInsightTestCase { + public void testSequentialBackspaceInvocation() throws IOException { + configureFromFileText(getTestName(false) + ".java", + "class Foo {\n" + + "\n" + + "\n" + + "}"); + backspace(); + backspace(); + checkResultByText("class Foo {\n" + + "}"); + } +} \ No newline at end of file From 5ee1d5585c6453afd1960d3df6f7a91dc2327802 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Thu, 31 Jul 2014 19:09:17 +0400 Subject: [PATCH 38/85] smart backspace: fix failures of JspxSanityTest --- .../lang-impl/src/com/intellij/formatting/FormatterImpl.java | 2 +- .../psi/impl/source/codeStyle/CodeStyleManagerRunnable.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java index d28a68570e4d..7b40b786ff7a 100644 --- a/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java +++ b/platform/lang-impl/src/com/intellij/formatting/FormatterImpl.java @@ -128,7 +128,7 @@ public class FormatterImpl extends FormatterEx if (spacing == null) { return 0; } - return spacing.getMinSpaces(); + return Math.max(spacing.getMinSpaces(), 0); } private static Couple getBlockAtOffset(Block parent, Block block, int offset) { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerRunnable.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerRunnable.java index 1b17faf21905..42dcb46c7ac0 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerRunnable.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleManagerRunnable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -149,7 +149,7 @@ abstract class CodeStyleManagerRunnable { SourceTreeToPsiMap.psiElementToTree(CodeStyleManagerImpl.findElementInTreeWithFormatterEnabled(file, offset)); if (elementAtOffset == null) { int significantRangeStart = CharArrayUtil.shiftBackward(file.getText(), offset - 1, "\r\t "); - return new TextRange(significantRangeStart, offset); + return new TextRange(Math.max(significantRangeStart, 0), offset); } final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file); From 05252025fa9eb33a642379b5cb4af89d918ef5f8 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Thu, 31 Jul 2014 17:12:00 +0200 Subject: [PATCH 39/85] Possible fix for lost caret --- .../wm/impl/commands/RequestFocusInEditorComponentCmd.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInEditorComponentCmd.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInEditorComponentCmd.java index 7a772e7aed34..554d2dd6ba78 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInEditorComponentCmd.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/commands/RequestFocusInEditorComponentCmd.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -94,7 +94,8 @@ public final class RequestFocusInEditorComponentCmd extends FinalizableCommand{ } if(myComponent != null){ - myFocusManager.requestFocus(myComponent, myForced).notifyWhenDone(myDoneCallback).doWhenDone(new Runnable() { + final boolean forced = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == null; + myFocusManager.requestFocus(myComponent, myForced || forced).notifyWhenDone(myDoneCallback).doWhenDone(new Runnable() { public void run() { // if owner is active window or it has active child window which isn't floating decorator then // don't bring owner window to font. If we will make toFront every time then it's possible From b37bb31ad4c9b4e43f625747dafe7ad7f080e5f8 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 31 Jul 2014 19:11:37 +0400 Subject: [PATCH 40/85] Gradle: compilation fix --- build/scripts/layouts.gant | 1 + 1 file changed, 1 insertion(+) diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index ed22dfed5f99..1aec74e2aa43 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -348,6 +348,7 @@ public def layoutCommunityPlugins(String home) { module("gradle-tooling-extension-impl") } fileset(dir: "$home/plugins/gradle/lib") { include(name: "*.jar") } + fileset(dir: "$home/lib/") { include(name: "kryo-*.jar") } } layoutPlugin("git4idea") { From a366ed77c5f94a56ecc72931f9e6839bd0e43b78 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Thu, 31 Jul 2014 19:57:01 +0400 Subject: [PATCH 41/85] Introduce APPLICATION_CONFIG_DIR macro (IDEA-122001) --- .../model/serialization/PathMacroUtil.java | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/PathMacroUtil.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/PathMacroUtil.java index 6214412c4a52..cf87eb5744b3 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/PathMacroUtil.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/PathMacroUtil.java @@ -16,16 +16,19 @@ package org.jetbrains.jps.model.serialization; import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ObjectUtils; import com.intellij.util.SystemProperties; -import com.intellij.util.containers.HashMap; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Map; +import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName; + /** * @author nik */ @@ -34,9 +37,16 @@ public class PathMacroUtil { @NonNls public static final String MODULE_DIR_MACRO_NAME = "MODULE_DIR"; @NonNls public static final String DIRECTORY_STORE_NAME = ".idea"; @NonNls public static final String APPLICATION_HOME_DIR = "APPLICATION_HOME_DIR"; + @NonNls public static final String APPLICATION_CONFIG_DIR = "APPLICATION_CONFIG_DIR"; @NonNls public static final String APPLICATION_PLUGINS_DIR = "APPLICATION_PLUGINS_DIR"; @NonNls public static final String USER_HOME_NAME = "USER_HOME"; + private static final Map ourGlobalMacros = ContainerUtil.immutableMapBuilder() + .put(APPLICATION_HOME_DIR, toSystemIndependentName(PathManager.getHomePath())) + .put(APPLICATION_CONFIG_DIR, toSystemIndependentName(PathManager.getConfigPath())) + .put(APPLICATION_PLUGINS_DIR, toSystemIndependentName(PathManager.getPluginsPath())) + .put(USER_HOME_NAME, StringUtil.trimEnd(toSystemIndependentName(SystemProperties.getUserHome()), "/")).build(); + @Nullable public static String getModuleDir(String moduleFilePath) { File moduleDirFile = new File(moduleFilePath).getParentFile(); @@ -57,31 +67,18 @@ public class PathMacroUtil { return moduleDir; } + @NotNull public static String getUserHomePath() { - return StringUtil.trimEnd(FileUtil.toSystemIndependentName(SystemProperties.getUserHome()), "/"); + return ObjectUtils.assertNotNull(getGlobalSystemMacroValue(USER_HOME_NAME)); } + @NotNull public static Map getGlobalSystemMacros() { - final Map map = new HashMap(); - map.put(APPLICATION_HOME_DIR, getApplicationHomeDirPath()); - map.put(APPLICATION_PLUGINS_DIR, getApplicationPluginsDirPath()); - map.put(USER_HOME_NAME, getUserHomePath()); - return map; - } - - private static String getApplicationHomeDirPath() { - return FileUtil.toSystemIndependentName(PathManager.getHomePath()); - } - - private static String getApplicationPluginsDirPath() { - return FileUtil.toSystemIndependentName(PathManager.getPluginsPath()); + return ourGlobalMacros; } @Nullable public static String getGlobalSystemMacroValue(String name) { - if (APPLICATION_HOME_DIR.equals(name)) return getApplicationHomeDirPath(); - if (APPLICATION_PLUGINS_DIR.equals(name)) return getApplicationPluginsDirPath(); - if (USER_HOME_NAME.equals(name)) return getUserHomePath(); - return null; + return ourGlobalMacros.get(name); } } From b4ff3fb3db0f06bc84bff45dc4eb51de3d46b12f Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 31 Jul 2014 17:59:50 +0200 Subject: [PATCH 42/85] group text occurrences in comments literals in different groups (IDEA-54427 IDEA-114331 --- .../com/intellij/find/FindManagerTest.java | 43 +++++++ .../intellij/find/impl/FindManagerImpl.java | 106 ++++++++++++++++-- .../find/impl/FindResultUsageInfo.java | 2 +- 3 files changed, 139 insertions(+), 12 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java index 33f43ef93fa7..2f6c4216e4ed 100644 --- a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java +++ b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java @@ -27,6 +27,7 @@ import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.projectRoots.Sdk; @@ -38,6 +39,7 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.GlobalSearchScopes; import com.intellij.psi.search.LocalSearchScope; import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.LightVirtualFile; @@ -301,6 +303,9 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { findModel.setGlobal(true); findModel.setMultipleFiles(true); findModel.setCustomScope(true); + OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, nonProjectFile); + descriptor.navigate(true); + GlobalSearchScopes.openFilesScope(myProject); findModel.setCustomScope(new GlobalSearchScope.FilesScope(myProject, ContainerUtil.list(nonProjectFile))); assertSize(1, findUsages(findModel)); @@ -650,4 +655,42 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { findModel.setWholeWordsOnly(true); assertSize(1, findUsages(findModel)); } + + public void testFindExceptComments() { + FindModel findModel = FindManagerTestUtils.configureFindModel("done"); + + String prefix = "/*"; + String text = prefix + "done*/done"; + + findModel.setSearchContext(FindModel.SearchContext.EXCEPT_COMMENTS); + LightVirtualFile file = new LightVirtualFile("A.java", text); + + FindResult findResult = myFindManager.findString(text, prefix.length(), findModel, file); + assertTrue(findResult.isStringFound()); + assertTrue(findResult.getStartOffset() > prefix.length()); + + findModel.setRegularExpressions(true); + findResult = myFindManager.findString(text, prefix.length(), findModel, file); + assertTrue(findResult.isStringFound()); + assertTrue(findResult.getStartOffset() > prefix.length()); + } + + public void testFindExceptLiterals() { + FindModel findModel = FindManagerTestUtils.configureFindModel("done"); + + String prefix = "\""; + String text = prefix + "done\"done"; + + findModel.setSearchContext(FindModel.SearchContext.EXCEPT_STRINGS); + LightVirtualFile file = new LightVirtualFile("A.java", text); + + FindResult findResult = myFindManager.findString(text, prefix.length(), findModel, file); + assertTrue(findResult.isStringFound()); + assertTrue(findResult.getStartOffset() > prefix.length()); + + findModel.setRegularExpressions(true); + findResult = myFindManager.findString(text, prefix.length(), findModel, file); + assertTrue(findResult.isStringFound()); + assertTrue(findResult.getStartOffset() > prefix.length()); + } } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index 05a40ab19ec5..c1c5185f3d7a 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -62,6 +62,7 @@ import com.intellij.usages.UsageViewManager; import com.intellij.usages.impl.SyntaxHighlighterOverEditorHighlighter; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.Predicate; import com.intellij.util.messages.MessageBus; import com.intellij.util.text.CharArrayUtil; import com.intellij.util.text.StringSearcher; @@ -312,19 +313,23 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo LOG.debug(model.toString()); } - final char[] textArray = CharArrayUtil.fromSequenceWithoutCopying(text); + return findStringLoop(text, offset, model, file, getFindContextPredicate(model, file, text)); + } + private FindResult findStringLoop(CharSequence text, int offset, FindModel model, VirtualFile file, @Nullable Predicate filter) { + final char[] textArray = CharArrayUtil.fromSequenceWithoutCopying(text); while(true) { FindResult result = doFindString(text, textArray, offset, model, file); - - if (!model.isWholeWordsOnly()) { - return result; - } - if (!result.isStringFound()){ - return result; - } - if (isWholeWord(text, result.getStartOffset(), result.getEndOffset())){ - return result; + if (filter == null || filter.apply(result)) { + if (!model.isWholeWordsOnly()) { + return result; + } + if (!result.isStringFound()) { + return result; + } + if (isWholeWord(text, result.getStartOffset(), result.getEndOffset())) { + return result; + } } offset = model.isForward() ? result.getStartOffset() + 1 : result.getEndOffset() - 1; @@ -332,6 +337,80 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo } } + private class FindExceptCommentsOrLiteralsData implements Predicate { + private final VirtualFile myFile; + private final FindModel myFindModel; + private final TreeMap mySkipRangesSet; + + private FindExceptCommentsOrLiteralsData(VirtualFile file, FindModel model, CharSequence text) { + myFile = file; + myFindModel = model.clone(); + + TreeMap result = new TreeMap(); + + if (model.isExceptComments() || model.isExceptCommentsAndStringLiterals()) { + addRanges(file, model, text, result, FindModel.SearchContext.IN_COMMENTS); + } + + if (model.isExceptStringLiterals() || model.isExceptCommentsAndStringLiterals()) { + addRanges(file, model, text, result, FindModel.SearchContext.IN_STRINGS); + } + + mySkipRangesSet = result; + } + + private void addRanges(VirtualFile file, + FindModel model, + CharSequence text, + TreeMap result, + FindModel.SearchContext searchContext) { + FindModel clonedModel = model.clone(); + clonedModel.setSearchContext(searchContext); + clonedModel.setForward(true); + int offset = 0; + + while(true) { + FindResult customResult = findStringLoop(text, offset, clonedModel, file, null); + if (!customResult.isStringFound()) break; + result.put(customResult.getStartOffset(), customResult.getEndOffset()); + offset = Math.max(customResult.getEndOffset(), offset + 1); // avoid loop for zero size reg exps matches + if (offset >= text.length()) break; + } + } + + boolean isAcceptableFor(FindModel model, VirtualFile file) { + return Comparing.equal(myFile, file) && myFindModel.equals(model); + } + + @Override + public boolean apply(@Nullable FindResult input) { + if (input == null || !input.isStringFound()) return true; + NavigableMap map = mySkipRangesSet.headMap(input.getStartOffset(), true); + for(Map.Entry e:map.descendingMap().entrySet()) { + if (e.getKey() <= input.getStartOffset() && e.getValue() >= input.getEndOffset()) return false; + if (e.getValue() <= input.getStartOffset()) break; + } + return true; + } + } + private static Key ourExceptCommentsOrLiteralsDataKey = Key.create("except.comments.literals.search.data"); + + private Predicate getFindContextPredicate(@NotNull FindModel model, VirtualFile file, CharSequence text) { + if (file == null) return null; + FindModel.SearchContext context = model.getSearchContext(); + if( context == FindModel.SearchContext.ANY || context == FindModel.SearchContext.IN_COMMENTS || + context == FindModel.SearchContext.IN_STRINGS) { + return null; + } + + FindExceptCommentsOrLiteralsData data = model.getUserData(ourExceptCommentsOrLiteralsDataKey); + if (data == null || !data.isAcceptableFor(model, file)) { + model.putUserData(ourExceptCommentsOrLiteralsDataKey, data = new FindExceptCommentsOrLiteralsData(file, model, text)); + } + + return data; + } + @Override public int showMalformedReplacementPrompt(@NotNull FindModel model, String title, MalformedReplacementStringException exception) { return showPromptDialogImpl(model, title, exception); @@ -415,6 +494,11 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo return new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), model.isForward()); } + public static void clearPreviousFindData(FindModel model) { + model.putUserData(ourCommentsLiteralsSearchDataKey, null); + model.putUserData(ourExceptCommentsOrLiteralsDataKey, null); + } + private static class CommentsLiteralsSearchData { final VirtualFile lastFile; int startOffset = 0; @@ -439,7 +523,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo } } - public static final Key ourCommentsLiteralsSearchDataKey = Key.create("comments.literals.search.data"); + private static final Key ourCommentsLiteralsSearchDataKey = Key.create("comments.literals.search.data"); @NotNull private FindResult findInCommentsAndLiterals(@NotNull CharSequence text, diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindResultUsageInfo.java b/platform/lang-impl/src/com/intellij/find/impl/FindResultUsageInfo.java index 6baf8cc4fca8..ee8d98422250 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindResultUsageInfo.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindResultUsageInfo.java @@ -80,7 +80,7 @@ public class FindResultUsageInfo extends UsageInfo { Long data = myFindModel.getUserData(ourDocumentTimestampKey); if (data == null || data != myTimestamp) { data = myTimestamp; - myFindModel.putUserData(FindManagerImpl.ourCommentsLiteralsSearchDataKey, null); + FindManagerImpl.clearPreviousFindData(myFindModel); } myFindModel.putUserData(ourDocumentTimestampKey, data); FindResult result; From e6c943ef0a3dfd9df0247cfde3c3c85ca9e6ebb2 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Thu, 31 Jul 2014 20:22:08 +0400 Subject: [PATCH 43/85] fix make process classpath --- .../GradleBuildProcessParametersProvider.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java index eaedad361241..4a9dc92e022b 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java @@ -35,33 +35,21 @@ import java.io.File; import java.util.List; /** - * Adds Gradle build dependencies to the project build process' classpath if it has at least one gradle module. + * Adds Gradle build dependencies to the project build process' classpath. * * @author Vladislav.Soroka * @since 7/22/2014 */ public class GradleBuildProcessParametersProvider extends BuildProcessParametersProvider { - @NotNull private final Project myProject; - private List myClasspath; - public GradleBuildProcessParametersProvider(@NotNull Project project) { - myProject = project; - } - @Override @NotNull public List getClassPath() { if (myClasspath == null) { myClasspath = ContainerUtil.newArrayList(); - final ModuleManager moduleManager = ModuleManager.getInstance(myProject); - for (Module module : moduleManager.getModules()) { - if (ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) { - addGradleClassPath(myClasspath); - addOtherClassPath(myClasspath); - break; - } - } + addGradleClassPath(myClasspath); + addOtherClassPath(myClasspath); } return myClasspath; } From 826dc6eae40ec46c933cb75c8158be15261c8146 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 31 Jul 2014 20:40:51 +0400 Subject: [PATCH 44/85] Renaming and documentation --- .../ide/actions/SplitterActionBase.java | 18 ++++++++++++++---- .../intellij/ide/actions/UnsplitAllAction.java | 4 ++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/actions/SplitterActionBase.java b/platform/platform-impl/src/com/intellij/ide/actions/SplitterActionBase.java index 14216a6a29bd..dde5a5c898dd 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/SplitterActionBase.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/SplitterActionBase.java @@ -27,9 +27,9 @@ public abstract class SplitterActionBase extends AnAction implements DumbAware { public void update(final AnActionEvent event) { final Project project = CommonDataKeys.PROJECT.getData(event.getDataContext()); final Presentation presentation = event.getPresentation(); - boolean context = ActionPlaces.isPopupPlace(event.getPlace()); - boolean enabled = project != null && isActionEnabled(project, context); - if (context) { + boolean inContextMenu = ActionPlaces.isPopupPlace(event.getPlace()); + boolean enabled = project != null && isActionEnabled(project, inContextMenu); + if (inContextMenu) { presentation.setVisible(enabled); } else { @@ -37,7 +37,17 @@ public abstract class SplitterActionBase extends AnAction implements DumbAware { } } - protected boolean isActionEnabled(Project project, boolean context) { + /** + * This method determines whether the action is enabled for {@code project} + * if {@code inContextMenu} is set to {@code false}. Otherwise, + * it determines whether the action is visible in the context menu. + * + * @param project the specified project + * @param inContextMenu whether the action is used in context menu + * @return {@code true} if the action is enabled, + * {@code false} otherwise + */ + protected boolean isActionEnabled(Project project, boolean inContextMenu) { final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project); return fileEditorManager.isInSplitter(); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/UnsplitAllAction.java b/platform/platform-impl/src/com/intellij/ide/actions/UnsplitAllAction.java index 32a7f05388c1..ec724884ef2d 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/UnsplitAllAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/UnsplitAllAction.java @@ -32,8 +32,8 @@ public final class UnsplitAllAction extends SplitterActionBase { } @Override - protected boolean isActionEnabled(Project project, boolean context) { + protected boolean isActionEnabled(Project project, boolean inContextMenu) { final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project); - return context ? fileEditorManager.getWindowSplitCount() > 2 : fileEditorManager.isInSplitter(); + return inContextMenu ? fileEditorManager.getWindowSplitCount() > 2 : fileEditorManager.isInSplitter(); } } From 09d77f7e8c5dfe13d439f9e4681d6b8bb99cb487 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 31 Jul 2014 18:39:19 +0200 Subject: [PATCH 45/85] optimization for syntax highlighter of text file --- ...yntaxHighlighterOverEditorHighlighter.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/platform/usageView/src/com/intellij/usages/impl/SyntaxHighlighterOverEditorHighlighter.java b/platform/usageView/src/com/intellij/usages/impl/SyntaxHighlighterOverEditorHighlighter.java index 94162fcb0715..04690cc6fa20 100644 --- a/platform/usageView/src/com/intellij/usages/impl/SyntaxHighlighterOverEditorHighlighter.java +++ b/platform/usageView/src/com/intellij/usages/impl/SyntaxHighlighterOverEditorHighlighter.java @@ -23,6 +23,8 @@ import com.intellij.openapi.editor.ex.util.LayeredLexerEditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory; import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter; +import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.fileTypes.SyntaxHighlighter; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -36,23 +38,28 @@ import org.jetbrains.annotations.NotNull; public class SyntaxHighlighterOverEditorHighlighter implements SyntaxHighlighter { private final Lexer lexer; private LayeredHighlighterIterator layeredHighlighterIterator = null; - private boolean lexerEditorHighlighterInitialized; private final SyntaxHighlighter highlighter; public SyntaxHighlighterOverEditorHighlighter(SyntaxHighlighter _highlighter, VirtualFile file, Project project) { - highlighter = _highlighter; - LayeredLexer.ourDisableLayersFlag.set(Boolean.TRUE); - EditorHighlighter editorHighlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, file); + if (file.getFileType() == PlainTextFileType.INSTANCE) { // optimization for large files, PlainTextSyntaxHighlighterFactory is slow + highlighter = new PlainSyntaxHighlighter(); + lexer = highlighter.getHighlightingLexer(); + } else { + highlighter = _highlighter; + LayeredLexer.ourDisableLayersFlag.set(Boolean.TRUE); + EditorHighlighter editorHighlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, file); - try { - if (editorHighlighter instanceof LayeredLexerEditorHighlighter) { - lexer = new LexerEditorHighlighterLexer(editorHighlighter, false); - } else { - lexer = highlighter.getHighlightingLexer(); + try { + if (editorHighlighter instanceof LayeredLexerEditorHighlighter) { + lexer = new LexerEditorHighlighterLexer(editorHighlighter, false); + } + else { + lexer = highlighter.getHighlightingLexer(); + } + } + finally { + LayeredLexer.ourDisableLayersFlag.set(null); } - } - finally { - LayeredLexer.ourDisableLayersFlag.set(null); } } From 91256433323028dcc3f7d59ee6c2c3d58324c372 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 31 Jul 2014 19:14:43 +0200 Subject: [PATCH 46/85] move built-in server to platform (xml module) --- java/compiler/impl/compiler-impl.iml | 1 + .../ArtifactProjectRootsProvider.java | 40 +++ .../src/META-INF/XmlPlugin.xml | 15 + resources/src/idea/RichPlatformPlugin.xml | 2 + .../BuiltInServerConfigurableUi.form | 40 +++ .../BuiltInServerConfigurableUi.java | 47 +++ .../BuiltInServerOptions.java | 116 +++++++ .../BuiltInWebBrowserUrlProvider.java | 78 +++++ .../builtInWebServer/BuiltInWebServer.java | 285 ++++++++++++++++++ .../builtInWebServer/FileHandler.java | 20 ++ .../builtInWebServer/PathToFileManager.java | 146 +++++++++ .../ProjectRootsProvider.java | 37 +++ .../ProjectRootsProviderImpl.java | 135 +++++++++ .../io/fastCgi/FastCgiChannelHandler.java | 108 +++++++ .../io/fastCgi/FastCgiConstants.java | 5 + .../jetbrains/io/fastCgi/FastCgiDecoder.java | 149 +++++++++ .../jetbrains/io/fastCgi/FastCgiRequest.java | 147 +++++++++ .../jetbrains/io/fastCgi/FastCgiResponse.java | 21 ++ .../jetbrains/io/fastCgi/FastCgiService.java | 249 +++++++++++++++ .../SingletonNotificationManager.java | 86 ++++++ xml/impl/xml.iml | 2 + 21 files changed, 1729 insertions(+) create mode 100644 java/compiler/impl/src/org/jetbrains/builtInWebServer/ArtifactProjectRootsProvider.java create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.form create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.java create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/BuiltInWebBrowserUrlProvider.java create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/BuiltInWebServer.java create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/FileHandler.java create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/PathToFileManager.java create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/ProjectRootsProvider.java create mode 100644 xml/impl/src/org/jetbrains/builtInWebServer/ProjectRootsProviderImpl.java create mode 100644 xml/impl/src/org/jetbrains/io/fastCgi/FastCgiChannelHandler.java create mode 100644 xml/impl/src/org/jetbrains/io/fastCgi/FastCgiConstants.java create mode 100644 xml/impl/src/org/jetbrains/io/fastCgi/FastCgiDecoder.java create mode 100644 xml/impl/src/org/jetbrains/io/fastCgi/FastCgiRequest.java create mode 100644 xml/impl/src/org/jetbrains/io/fastCgi/FastCgiResponse.java create mode 100644 xml/impl/src/org/jetbrains/io/fastCgi/FastCgiService.java create mode 100644 xml/impl/src/org/jetbrains/notification/SingletonNotificationManager.java diff --git a/java/compiler/impl/compiler-impl.iml b/java/compiler/impl/compiler-impl.iml index 08d3ca179fc0..c6e3e4f81eab 100644 --- a/java/compiler/impl/compiler-impl.iml +++ b/java/compiler/impl/compiler-impl.iml @@ -28,6 +28,7 @@ + diff --git a/java/compiler/impl/src/org/jetbrains/builtInWebServer/ArtifactProjectRootsProvider.java b/java/compiler/impl/src/org/jetbrains/builtInWebServer/ArtifactProjectRootsProvider.java new file mode 100644 index 000000000000..c8d253141180 --- /dev/null +++ b/java/compiler/impl/src/org/jetbrains/builtInWebServer/ArtifactProjectRootsProvider.java @@ -0,0 +1,40 @@ +package org.jetbrains.builtInWebServer; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.packaging.artifacts.ArtifactManager; +import com.intellij.util.PairFunction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class ArtifactProjectRootsProvider extends ProjectRootsProvider.PrefixlessProjectRootsProvider { + @Nullable + @Override + public Pair> resolve(@NotNull String path, @NotNull Project project, @NotNull PairFunction resolver) { + for (Artifact artifact : ArtifactManager.getInstance(project).getArtifacts()) { + VirtualFile root = artifact.getOutputFile(); + if (root != null) { + VirtualFile file = root.findFileByRelativePath(path); + if (file != null) { + return Pair.create(file, new Pair(root, null)); + } + } + } + return null; + } + + @Nullable + @Override + public Pair getRoot(@NotNull VirtualFile file, @NotNull Project project) { + for (Artifact artifact : ArtifactManager.getInstance(project).getArtifacts()) { + VirtualFile root = artifact.getOutputFile(); + if (root != null && VfsUtilCore.isAncestor(root, file, true)) { + return Pair.create(root, null); + } + } + return null; + } +} \ No newline at end of file diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 9a737df77ef6..5622c96a0b2e 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -89,6 +89,9 @@ + + + @@ -530,8 +533,20 @@ + + + + + + + + + + + + diff --git a/resources/src/idea/RichPlatformPlugin.xml b/resources/src/idea/RichPlatformPlugin.xml index 92ae7046006e..56c14fb3f9de 100644 --- a/resources/src/idea/RichPlatformPlugin.xml +++ b/resources/src/idea/RichPlatformPlugin.xml @@ -372,6 +372,8 @@ + + diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.form b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.form new file mode 100644 index 000000000000..d42b3e3d8dfe --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.form @@ -0,0 +1,40 @@ + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.java b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.java new file mode 100644 index 000000000000..edbef65543af --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.java @@ -0,0 +1,47 @@ +package org.jetbrains.builtInWebServer; + +import com.intellij.openapi.options.ConfigurableUi; +import com.intellij.ui.PortField; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +class BuiltInServerConfigurableUi implements ConfigurableUi { + private JPanel mainPanel; + + private PortField builtInServerPort; + private JCheckBox builtInServerAvailableExternallyCheckBox; + + public BuiltInServerConfigurableUi() { + builtInServerPort.setMin(1024); + } + + @Override + @NotNull + public JComponent getComponent() { + return mainPanel; + } + + @Override + public boolean isModified(@NotNull BuiltInServerOptions settings) { + return builtInServerPort.getNumber() != settings.builtInServerPort || + builtInServerAvailableExternallyCheckBox.isSelected() != settings.builtInServerAvailableExternally; + } + + @Override + public void apply(@NotNull BuiltInServerOptions settings) { + boolean builtInServerPortChanged = settings.builtInServerPort != builtInServerPort.getNumber() || settings.builtInServerAvailableExternally != builtInServerAvailableExternallyCheckBox.isSelected(); + if (builtInServerPortChanged) { + settings.builtInServerPort = builtInServerPort.getNumber(); + settings.builtInServerAvailableExternally = builtInServerAvailableExternallyCheckBox.isSelected(); + + BuiltInServerOptions.onBuiltInServerPortChanged(); + } + } + + @Override + public void reset(@NotNull BuiltInServerOptions settings) { + builtInServerPort.setNumber(settings.builtInServerPort); + builtInServerAvailableExternallyCheckBox.setSelected(settings.builtInServerAvailableExternally); + } +} diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java new file mode 100644 index 000000000000..5eb65c1f9cbc --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerOptions.java @@ -0,0 +1,116 @@ +package org.jetbrains.builtInWebServer; + +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationDisplayType; +import com.intellij.notification.NotificationType; +import com.intellij.notification.Notifications; +import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.components.*; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.SimpleConfigurable; +import com.intellij.openapi.util.Getter; +import com.intellij.util.xmlb.XmlSerializerUtil; +import com.intellij.util.xmlb.annotations.Attribute; +import com.intellij.xdebugger.settings.DebuggerConfigurableProvider; +import com.intellij.xdebugger.settings.DebuggerSettingsCategory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.ide.BuiltInServerManager; +import org.jetbrains.ide.CustomPortServerManager; +import org.jetbrains.io.CustomPortServerManagerBase; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; + +@State( + name = "BuiltInServerOptions", + storages = { + @Storage( + file = StoragePathMacros.APP_CONFIG + "/other.xml" + )} +) +public class BuiltInServerOptions implements PersistentStateComponent, ExportableComponent, Getter { + @Attribute + public int builtInServerPort = 63342; + @Attribute + public boolean builtInServerAvailableExternally = false; + + public static BuiltInServerOptions getInstance() { + return ServiceManager.getService(BuiltInServerOptions.class); + } + + @Override + public BuiltInServerOptions get() { + return this; + } + + static final class BuiltInServerDebuggerConfigurableProvider extends DebuggerConfigurableProvider { + @NotNull + @Override + public Collection getConfigurables(@NotNull DebuggerSettingsCategory category) { + if (category == DebuggerSettingsCategory.GENERAL) { + return Collections.singletonList(SimpleConfigurable.create("builtInServer", "", BuiltInServerConfigurableUi.class, getInstance())); + } + return Collections.emptyList(); + } + } + + @NotNull + @Override + public File[] getExportFiles() { + return new File[]{PathManager.getOptionsFile("other")}; + } + + @NotNull + @Override + public String getPresentableName() { + return "Built-in server"; + } + + @Nullable + @Override + public BuiltInServerOptions getState() { + return this; + } + + @Override + public void loadState(BuiltInServerOptions state) { + XmlSerializerUtil.copyBean(state, this); + } + + public int getEffectiveBuiltInServerPort() { + MyCustomPortServerManager portServerManager = CustomPortServerManager.EP_NAME.findExtension(MyCustomPortServerManager.class); + if (!portServerManager.isBound()) { + return BuiltInServerManager.getInstance().getPort(); + } + return builtInServerPort; + } + + public static final class MyCustomPortServerManager extends CustomPortServerManagerBase { + @Override + public void cannotBind(Exception e, int port) { + String groupDisplayId = "Built-in Web Server"; + Notifications.Bus.register(groupDisplayId, NotificationDisplayType.STICKY_BALLOON); + new Notification(groupDisplayId, "Built-in HTTP server on custom port " + port + " disabled", + "Cannot start built-in HTTP server on custom port " + port + ". " + + "Please ensure that port is free (or check your firewall settings) and restart " + ApplicationNamesInfo.getInstance().getFullProductName(), + NotificationType.ERROR).notify(null); + } + + @Override + public int getPort() { + return getInstance().builtInServerPort; + } + + @Override + public boolean isAvailableExternally() { + return getInstance().builtInServerAvailableExternally; + } + } + + public static void onBuiltInServerPortChanged() { + CustomPortServerManager.EP_NAME.findExtension(MyCustomPortServerManager.class).portChanged(); + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInWebBrowserUrlProvider.java b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInWebBrowserUrlProvider.java new file mode 100644 index 000000000000..bb170d38b96c --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInWebBrowserUrlProvider.java @@ -0,0 +1,78 @@ +package org.jetbrains.builtInWebServer; + +import com.intellij.ide.browsers.OpenInBrowserRequest; +import com.intellij.ide.browsers.WebBrowserUrlProvider; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.Url; +import com.intellij.util.Urls; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.xml.util.HtmlUtil; +import org.jetbrains.ide.BuiltInServerManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class BuiltInWebBrowserUrlProvider extends WebBrowserUrlProvider implements DumbAware { + @NotNull + public static List getUrls(@NotNull VirtualFile file, @NotNull Project project, @Nullable String currentAuthority) { + if (currentAuthority != null && !compareAuthority(currentAuthority)) { + return Collections.emptyList(); + } + + String path = PathToFileManager.getInstance(project).getPath(file); + if (path == null) { + return Collections.emptyList(); + } + + int effectiveBuiltInServerPort = BuiltInServerOptions.getInstance().getEffectiveBuiltInServerPort(); + Url url = Urls.newHttpUrl(currentAuthority == null ? "localhost:" + effectiveBuiltInServerPort : currentAuthority, '/' + project.getName() + '/' + path); + int defaultPort = BuiltInServerManager.getInstance().getPort(); + if (currentAuthority != null || defaultPort == effectiveBuiltInServerPort) { + return Collections.singletonList(url); + } + return Arrays.asList(url, Urls.newHttpUrl("localhost:" + defaultPort, '/' + project.getName() + '/' + path)); + } + + public static boolean compareAuthority(@Nullable String currentAuthority) { + if (currentAuthority == null) { + return false; + } + + int portIndex = currentAuthority.indexOf(':'); + if (portIndex < 0) { + return false; + } + + String host = currentAuthority.substring(0, portIndex); + if (!BuiltInWebServer.isOwnHostName(host)) { + return false; + } + + int port = StringUtil.parseInt(currentAuthority.substring(portIndex + 1), -1); + return port == BuiltInServerOptions.getInstance().getEffectiveBuiltInServerPort() || + port == BuiltInServerManager.getInstance().getPort(); + } + + @Override + public boolean canHandleElement(@NotNull OpenInBrowserRequest request) { + return request.getFile().getViewProvider().isPhysical() && !(request.getVirtualFile() instanceof LightVirtualFile) && isMyLanguage(request.getFile()); + } + + protected boolean isMyLanguage(PsiFile psiFile) { + return HtmlUtil.isHtmlFile(psiFile); + } + + @Nullable + @Override + protected Url getUrl(@NotNull OpenInBrowserRequest request, @NotNull VirtualFile virtualFile) throws BrowserException { + return ContainerUtil.getFirstItem(getUrls(virtualFile, request.getProject(), null)); + } +} diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInWebServer.java b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInWebServer.java new file mode 100644 index 000000000000..c87c3096dca3 --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInWebServer.java @@ -0,0 +1,285 @@ +package org.jetbrains.builtInWebServer; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.util.SystemInfoRt; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.UriUtil; +import com.intellij.util.io.URLUtil; +import com.intellij.util.net.NetUtils; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.ide.HttpRequestHandler; +import org.jetbrains.io.FileResponses; +import org.jetbrains.io.Responses; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; + +import static org.jetbrains.io.Responses.sendOptionsResponse; +import static org.jetbrains.io.Responses.sendStatus; + +public class BuiltInWebServer extends HttpRequestHandler { + private static final Logger LOG = Logger.getInstance(BuiltInWebServer.class); + + @Nullable + public static VirtualFile findIndexFile(@NotNull VirtualFile basedir) { + VirtualFile[] children = basedir.getChildren(); + if (children == null || children.length == 0) { + return null; + } + + for (String indexNamePrefix : new String[]{"index.", "default."}) { + VirtualFile index = null; + String preferredName = indexNamePrefix + "html"; + for (VirtualFile child : children) { + if (!child.isDirectory()) { + String name = child.getName(); + if (name.equals(preferredName)) { + return child; + } + else if (index == null && name.startsWith(indexNamePrefix)) { + index = child; + } + } + } + if (index != null) { + return index; + } + } + return null; + } + + @Override + public boolean isSupported(@NotNull FullHttpRequest request) { + return super.isSupported(request) || request.method() == HttpMethod.POST || request.method() == HttpMethod.OPTIONS; + } + + @Override + public boolean process(@NotNull QueryStringDecoder urlDecoder, @NotNull FullHttpRequest request, @NotNull ChannelHandlerContext context) throws IOException { + if (request.method() == HttpMethod.OPTIONS) { + sendOptionsResponse("GET, POST, HEAD, OPTIONS", request, context); + return true; + } + + String host = HttpHeaders.getHost(request); + if (StringUtil.isEmpty(host)) { + return false; + } + + int portIndex = host.indexOf(':'); + if (portIndex > 0) { + host = host.substring(0, portIndex); + } + + String projectName; + boolean isIpv6 = host.charAt(0) == '[' && host.length() > 2 && host.charAt(host.length() - 1) == ']'; + if (isIpv6) { + host = host.substring(1, host.length() - 1); + } + + if (isIpv6 || Character.digit(host.charAt(0), 10) != -1 || host.charAt(0) == ':' || isOwnHostName(host)) { + if (urlDecoder.path().length() < 2) { + return false; + } + projectName = null; + } + else { + projectName = host; + } + return doProcess(request, context.channel(), projectName); + } + + public static boolean isOwnHostName(String host) { + if (NetUtils.isLocalhost(host)) { + return true; + } + + try { + InetAddress address = InetAddress.getByName(host); + if (host.equals(address.getHostAddress()) || host.equalsIgnoreCase(address.getCanonicalHostName())) { + return true; + } + + String localHostName = InetAddress.getLocalHost().getHostName(); + // WEB-8889 + // develar.local is own host name: develar. equals to "develar.labs.intellij.net" (canonical host name) + return localHostName.equalsIgnoreCase(host) || + (host.endsWith(".local") && localHostName.regionMatches(true, 0, host, 0, host.length() - ".local".length())); + } + catch (UnknownHostException ignored) { + return false; + } + } + + private static boolean doProcess(FullHttpRequest request, Channel channel, @Nullable String projectName) { + final String decodedPath = URLUtil.unescapePercentSequences(UriUtil.trimParameters(request.uri())); + int offset; + boolean emptyPath; + boolean isCustomHost = projectName != null; + if (isCustomHost) { + // host mapped to us + offset = 0; + emptyPath = decodedPath.isEmpty(); + } + else { + offset = decodedPath.indexOf('/', 1); + projectName = decodedPath.substring(1, offset == -1 ? decodedPath.length() : offset); + emptyPath = offset == -1; + } + + Project project = findProject(projectName, isCustomHost); + if (project == null) { + return false; + } + + if (emptyPath) { + if (!SystemInfoRt.isFileSystemCaseSensitive) { + // may be passed path is not correct + projectName = project.getName(); + } + + // we must redirect "jsdebug" to "jsdebug/" as nginx does, otherwise browser will treat it as file instead of directory, so, relative path will not work + redirectToDirectory(request, channel, projectName); + return true; + } + + + final String path = FileUtil.toCanonicalPath(decodedPath.substring(offset + 1), '/'); + LOG.assertTrue(path != null); + PathToFileManager pathToFileManager = PathToFileManager.getInstance(project); + VirtualFile result = pathToFileManager.pathToFileCache.getIfPresent(path); + boolean indexUsed = false; + if (result == null || !result.isValid()) { + result = pathToFileManager.findByRelativePath(project, path); + if (result == null) { + if (path.isEmpty()) { + sendStatus(HttpResponseStatus.NOT_FOUND, channel, "Index file doesn't exist.", request); + return true; + } + else { + return false; + } + } + else if (result.isDirectory()) { + if (!endsWithSlash(decodedPath)) { + redirectToDirectory(request, channel, isCustomHost ? path : (projectName + '/' + path)); + return true; + } + + result = findIndexFile(result); + if (result == null) { + sendStatus(HttpResponseStatus.NOT_FOUND, channel, "Index file doesn't exist.", request); + return true; + } + indexUsed = true; + } + + pathToFileManager.pathToFileCache.put(path, result); + } + else if (!path.endsWith(result.getName())) { + if (endsWithSlash(decodedPath)) { + indexUsed = true; + } + else { + // FallbackResource feature in action, /login requested, /index.php retrieved, we must not redirect /login to /login/ + if (path.endsWith(result.getParent().getName())) { + redirectToDirectory(request, channel, isCustomHost ? path : (projectName + '/' + path)); + return true; + } + } + } + + StringBuilder canonicalRequestPath = new StringBuilder(); + canonicalRequestPath.append('/'); + if (!isCustomHost) { + canonicalRequestPath.append(projectName).append('/'); + } + canonicalRequestPath.append(path); + if (indexUsed) { + canonicalRequestPath.append('/').append(result.getName()); + } + + for (FileHandler fileHandler : FileHandler.EP_NAME.getExtensions()) { + try { + if (fileHandler.process(result, canonicalRequestPath.toString(), project, request, channel)) { + return true; + } + } + catch (Throwable e) { + LOG.error(e); + } + } + + return false; + } + + private static boolean endsWithSlash(String path) { + return path.charAt(path.length() - 1) == '/'; + } + + static final class StaticFileHandler extends FileHandler { + @Override + public boolean process(@NotNull VirtualFile file, + @NotNull String canonicalRequestPath, + @NotNull Project project, + @NotNull FullHttpRequest request, + @NotNull Channel channel) throws IOException { + File ioFile = VfsUtilCore.virtualToIoFile(file); + if (hasAccess(ioFile)) { + FileResponses.sendFile(request, channel, ioFile); + } + else { + sendStatus(HttpResponseStatus.FORBIDDEN, channel, request); + } + return true; + } + + private static boolean hasAccess(File result) { + // deny access to .htaccess files + return !result.isDirectory() && result.canRead() && !(result.isHidden() || result.getName().startsWith(".ht")); + } + } + + private static void redirectToDirectory(HttpRequest request, Channel channel, String path) { + FullHttpResponse response = Responses.response(HttpResponseStatus.MOVED_PERMANENTLY); + URI url = VfsUtil.toUri("http://" + HttpHeaders.getHost(request) + "/" + path + "/"); + LOG.assertTrue(url != null); + response.headers().add(HttpHeaders.Names.LOCATION, url.toASCIIString()); + Responses.send(response, channel, request); + } + + @Nullable + private static Project findProject(String projectName, boolean isCustomHost) { + // user can rename project directory, so, we should support this case - find project by base directory name + Project candidateByDirectoryName = null; + for (Project project : ProjectManager.getInstance().getOpenProjects()) { + String name = project.getName(); + // domain name is case-insensitive + if (!project.isDisposed() && ((isCustomHost || !SystemInfoRt.isFileSystemCaseSensitive) ? projectName.equalsIgnoreCase(name) : projectName.equals(name))) { + return project; + } + + if (candidateByDirectoryName == null && compareNameAndProjectBasePath(projectName, project)) { + candidateByDirectoryName = project; + } + } + return candidateByDirectoryName; + } + + public static boolean compareNameAndProjectBasePath(String projectName, Project project) { + String basePath = project.getBasePath(); + return basePath != null && basePath.length() > projectName.length() && basePath.endsWith(projectName) && basePath.charAt(basePath.length() - projectName.length() - 1) == '/'; + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/FileHandler.java b/xml/impl/src/org/jetbrains/builtInWebServer/FileHandler.java new file mode 100644 index 000000000000..a122bf35719a --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/FileHandler.java @@ -0,0 +1,20 @@ +package org.jetbrains.builtInWebServer; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public abstract class FileHandler { + static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.jetbrains.webServerFileHandler"); + + public abstract boolean process(@NotNull VirtualFile file, + @NotNull String canonicalRequestPath, + @NotNull Project project, + @NotNull FullHttpRequest request, + @NotNull Channel channel) throws IOException; +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/PathToFileManager.java b/xml/impl/src/org/jetbrains/builtInWebServer/PathToFileManager.java new file mode 100644 index 000000000000..4cf1d3d6d2d2 --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/PathToFileManager.java @@ -0,0 +1,146 @@ +package org.jetbrains.builtInWebServer; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.intellij.ProjectTopics; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleRootAdapter; +import com.intellij.openapi.roots.ModuleRootEvent; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.newvfs.BulkFileListener; +import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent; +import com.intellij.openapi.vfs.newvfs.events.VFileEvent; +import com.intellij.util.PairFunction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class PathToFileManager { + private static final PairFunction RELATIVE_PATH_RESOLVER = new PairFunction() { + @Nullable + @Override + public VirtualFile fun(String path, VirtualFile parent) { + return parent.findFileByRelativePath(path); + } + }; + + private static final PairFunction EMPTY_PATH_RESOLVER = new PairFunction() { + @Nullable + @Override + public VirtualFile fun(String path, VirtualFile parent) { + return BuiltInWebServer.findIndexFile(parent); + } + }; + + private final Project project; + + final Cache pathToFileCache = CacheBuilder.newBuilder().maximumSize(512).expireAfterAccess(10, TimeUnit.MINUTES).build(); + // time to expire should be greater than pathToFileCache + private final Cache> fileToRoot = CacheBuilder.newBuilder().maximumSize(512).expireAfterAccess(11, TimeUnit.MINUTES).build(); + + public static PathToFileManager getInstance(@NotNull Project project) { + return ServiceManager.getService(project, PathToFileManager.class); + } + + public PathToFileManager(@NotNull Application application, @NotNull Project project) { + this.project = project; + application.getMessageBus().connect(project).subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() { + @Override + public void after(@NotNull List events) { + for (VFileEvent event : events) { + if (event instanceof VFileContentChangeEvent) { + VirtualFile file = ((VFileContentChangeEvent)event).getFile(); + for (ProjectRootsProvider rootsProvider : ProjectRootsProvider.EP_NAME.getExtensions()) { + if (rootsProvider.isClearCacheOnFileContentChanged(file)) { + clearCache(); + break; + } + } + } + else { + clearCache(); + break; + } + } + } + }); + project.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() { + @Override + public void rootsChanged(ModuleRootEvent event) { + clearCache(); + } + }); + } + + private void clearCache() { + pathToFileCache.invalidateAll(); + fileToRoot.invalidateAll(); + } + + @Nullable + public VirtualFile get(@NotNull String path) { + return get(path, true); + } + + @Nullable + public VirtualFile get(@NotNull String path, boolean cacheResult) { + VirtualFile result = pathToFileCache.getIfPresent(path); + if (result == null || !result.isValid()) { + result = findByRelativePath(project, path); + if (cacheResult && result != null && result.isValid()) { + pathToFileCache.put(path, result); + } + } + return result; + } + + @Nullable + public String getPath(@NotNull VirtualFile file) { + Pair root = getRoot(file); + if (root == null) { + return null; + } + else { + return (root.second == null ? "" : root.second + '/') + VfsUtilCore.getRelativePath(file, root.first, '/'); + } + } + + @Nullable + public Pair getRoot(@NotNull VirtualFile child) { + Pair result = fileToRoot.getIfPresent(child); + if (result == null) { + for (ProjectRootsProvider rootsProvider : ProjectRootsProvider.EP_NAME.getExtensions()) { + result = rootsProvider.getRoot(child, project); + if (result != null) { + fileToRoot.put(child, result); + break; + } + } + } + return result; + } + + @Nullable + VirtualFile findByRelativePath(@NotNull Project project, @NotNull String path) { + for (ProjectRootsProvider rootsProvider : ProjectRootsProvider.EP_NAME.getExtensions()) { + Pair> result = rootsProvider.resolve(path, project); + if (result != null) { + fileToRoot.put(result.first, result.second); + return result.first; + } + } + return null; + } + + @NotNull + public PairFunction getResolver(@NotNull String path) { + return path.isEmpty() ? EMPTY_PATH_RESOLVER : RELATIVE_PATH_RESOLVER; + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/ProjectRootsProvider.java b/xml/impl/src/org/jetbrains/builtInWebServer/ProjectRootsProvider.java new file mode 100644 index 000000000000..ae69616b4174 --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/ProjectRootsProvider.java @@ -0,0 +1,37 @@ +package org.jetbrains.builtInWebServer; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.PairFunction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class ProjectRootsProvider { + static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.jetbrains.webServerProjectRootsProvider"); + + @Nullable + /** + * @return pair of child and root or null if cannot resolve + */ + public abstract Pair> resolve(@NotNull String path, @NotNull Project project); + + @Nullable + public abstract Pair getRoot(@NotNull VirtualFile file, @NotNull Project project); + + public boolean isClearCacheOnFileContentChanged(@NotNull VirtualFile file) { + return false; + } + + public abstract static class PrefixlessProjectRootsProvider extends ProjectRootsProvider { + @Nullable + @Override + public final Pair> resolve(@NotNull String path, @NotNull Project project) { + return resolve(path, project, PathToFileManager.getInstance(project).getResolver(path)); + } + + @Nullable + public abstract Pair> resolve(@NotNull String path, @NotNull Project project, @NotNull PairFunction resolver); + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/ProjectRootsProviderImpl.java b/xml/impl/src/org/jetbrains/builtInWebServer/ProjectRootsProviderImpl.java new file mode 100644 index 000000000000..811aaf1b85a4 --- /dev/null +++ b/xml/impl/src/org/jetbrains/builtInWebServer/ProjectRootsProviderImpl.java @@ -0,0 +1,135 @@ +package org.jetbrains.builtInWebServer; + +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.PairFunction; +import com.intellij.util.PlatformUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +final class ProjectRootsProviderImpl extends ProjectRootsProvider { + @Nullable + @Override + public Pair> resolve(@NotNull String path, @NotNull Project project) { + PairFunction resolver; + if (PlatformUtils.isIntelliJ()) { + int index = path.indexOf('/'); + if (index > 0 && !path.regionMatches(!SystemInfo.isFileSystemCaseSensitive, 0, project.getName(), 0, index)) { + String moduleName = path.substring(0, index); + AccessToken token = ReadAction.start(); + Module module; + try { + module = ModuleManager.getInstance(project).findModuleByName(moduleName); + } + finally { + token.finish(); + } + + if (module != null && !module.isDisposed()) { + path = path.substring(index + 1); + resolver = PathToFileManager.getInstance(project).getResolver(path); + + ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); + Couple result = resolve(path, moduleRootManager.getSourceRoots(), resolver); + if (result == null) { + result = resolve(path, moduleRootManager.getContentRoots(), resolver); + } + if (result != null) { + return Pair.create(result.first, Pair.create(result.second, module.getName())); + } + } + } + } + + Module[] modules; + AccessToken token = ReadAction.start(); + try { + modules = ModuleManager.getInstance(project).getModules(); + } + finally { + token.finish(); + } + + resolver = PathToFileManager.getInstance(project).getResolver(path); + Pair> result = findByRelativePath(project, path, modules, true, resolver); + if (result == null) { + // let's find in content roots + return findByRelativePath(project, path, modules, false, resolver); + } + else { + return result; + } + } + + @Nullable + @Override + public Pair getRoot(@NotNull final VirtualFile file, @NotNull final Project project) { + return new ReadAction>() { + protected void run(@NotNull final Result> result) { + VirtualFile root = null; + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + if (fileIndex.isInSourceContent(file)) { + root = fileIndex.getSourceRootForFile(file); + } + else if (fileIndex.isInContent(file)) { + root = fileIndex.getContentRootForFile(file); + } + else if (fileIndex.isInLibraryClasses(file)) { + root = fileIndex.getClassRootForFile(file); + } + assert root != null : file.getPresentableUrl(); + result.setResult(Pair.create(root, getModuleNameQualifier(project, fileIndex.getModuleForFile(file)))); + } + }.execute().getResultObject(); + } + + @Nullable + private static String getModuleNameQualifier(@NotNull Project project, @Nullable Module module) { + if (module != null && + PlatformUtils.isIntelliJ() && + !(module.getName().equalsIgnoreCase(project.getName()) || BuiltInWebServer.compareNameAndProjectBasePath(module.getName(), project))) { + return module.getName(); + } + return null; + } + + @Nullable + private static Couple resolve(@NotNull String path, @NotNull VirtualFile[] roots, @NotNull PairFunction resolver) { + for (VirtualFile root : roots) { + VirtualFile file = resolver.fun(path, root); + if (file != null) { + return Couple.of(file, root); + } + } + return null; + } + + @Nullable + private static Pair> findByRelativePath(@NotNull Project project, + @NotNull String path, + @NotNull Module[] modules, + boolean inSourceRoot, + @NotNull PairFunction resolver) { + for (Module module : modules) { + if (!module.isDisposed()) { + ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module); + Couple result = resolve(path, inSourceRoot ? moduleRootManager.getSourceRoots() : moduleRootManager.getContentRoots(), resolver); + if (result != null) { + return Pair.create(result.first, Pair.create(result.second, getModuleNameQualifier(project, module))); + } + } + } + return null; + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiChannelHandler.java b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiChannelHandler.java new file mode 100644 index 000000000000..d24078221b30 --- /dev/null +++ b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiChannelHandler.java @@ -0,0 +1,108 @@ +package org.jetbrains.io.fastCgi; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.text.StringUtilRt; +import com.intellij.util.containers.ConcurrentIntObjectMap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.*; +import org.jetbrains.io.Responses; +import org.jetbrains.io.SimpleChannelInboundHandlerAdapter; + +import static org.jetbrains.io.fastCgi.FastCgiService.LOG; + +@ChannelHandler.Sharable +public class FastCgiChannelHandler extends SimpleChannelInboundHandlerAdapter { + private final ConcurrentIntObjectMap requestToChannel; + + public FastCgiChannelHandler(ConcurrentIntObjectMap channel) { + requestToChannel = channel; + } + + @Override + protected void messageReceived(ChannelHandlerContext context, FastCgiResponse response) throws Exception { + ByteBuf buffer = response.getData(); + Channel channel = requestToChannel.remove(response.getId()); + if (channel == null || !channel.isActive()) { + if (buffer != null) { + buffer.release(); + } + return; + } + + if (buffer == null) { + Responses.sendStatus(HttpResponseStatus.BAD_GATEWAY, channel); + return; + } + + HttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buffer); + try { + parseHeaders(httpResponse, buffer); + Responses.addServer(httpResponse); + if (!HttpHeaders.isContentLengthSet(httpResponse)) { + HttpHeaders.setContentLength(httpResponse, buffer.readableBytes()); + } + } + catch (Throwable e) { + buffer.release(); + Responses.sendStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR, channel); + LOG.error(e); + } + channel.writeAndFlush(httpResponse); + } + + private static void parseHeaders(HttpResponse response, ByteBuf buffer) { + StringBuilder builder = new StringBuilder(); + while (buffer.isReadable()) { + builder.setLength(0); + + String key = null; + boolean valueExpected = true; + while (true) { + int b = buffer.readByte(); + if (b < 0 || b == '\n') { + break; + } + + if (b != '\r') { + if (valueExpected && b == ':') { + valueExpected = false; + + key = builder.toString(); + builder.setLength(0); + skipWhitespace(buffer); + } + else { + builder.append((char)b); + } + } + } + + if (builder.length() == 0) { + // end of headers + return; + } + + // skip standard headers + if (StringUtil.isEmpty(key) || StringUtilRt.startsWithIgnoreCase(key, "http") || StringUtilRt.startsWithIgnoreCase(key, "X-Accel-")) { + continue; + } + + String value = builder.toString(); + if (key.equalsIgnoreCase("status")) { + response.setStatus(HttpResponseStatus.valueOf(Integer.parseInt(value.substring(0, value.indexOf(' '))))); + } + else if (!(key.startsWith("http") || key.startsWith("HTTP"))) { + response.headers().add(key, value); + } + } + } + + private static void skipWhitespace(ByteBuf buffer) { + while (buffer.isReadable() && buffer.getByte(buffer.readerIndex()) == ' ') { + buffer.skipBytes(1); + } + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiConstants.java b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiConstants.java new file mode 100644 index 000000000000..23a85dead1f9 --- /dev/null +++ b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiConstants.java @@ -0,0 +1,5 @@ +package org.jetbrains.io.fastCgi; + +public final class FastCgiConstants { + public static final int HEADER_LENGTH = 8; +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiDecoder.java b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiDecoder.java new file mode 100644 index 000000000000..1cd7adbef5d4 --- /dev/null +++ b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiDecoder.java @@ -0,0 +1,149 @@ +package org.jetbrains.io.fastCgi; + +import com.intellij.util.Consumer; +import gnu.trove.TIntObjectHashMap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.CharsetUtil; +import org.jetbrains.io.Decoder; + +import static org.jetbrains.io.fastCgi.FastCgiService.LOG; + +public class FastCgiDecoder extends Decoder { + private enum State { + HEADER, CONTENT + } + + private State state = State.HEADER; + + private enum ProtocolStatus { + REQUEST_COMPLETE, CANT_MPX_CONN, OVERLOADED, UNKNOWN_ROLE + } + + public static final class RecordType { + public static final int END_REQUEST = 3; + public static final int STDOUT = 6; + public static final int STDERR = 7; + } + + private int type; + private int id; + private int contentLength; + private int paddingLength; + + private final TIntObjectHashMap dataBuffers = new TIntObjectHashMap(); + + private final Consumer errorOutputConsumer; + + public FastCgiDecoder(Consumer errorOutputConsumer) { + this.errorOutputConsumer = errorOutputConsumer; + } + + @Override + protected void messageReceived(ChannelHandlerContext context, ByteBuf input) throws Exception { + while (true) { + switch (state) { + case HEADER: { + if (paddingLength > 0) { + if (input.readableBytes() >= paddingLength) { + input.skipBytes(paddingLength); + paddingLength = 0; + } + else { + paddingLength -= input.readableBytes(); + input.skipBytes(input.readableBytes()); + input.release(); + return; + } + } + + ByteBuf buffer = getBufferIfSufficient(input, FastCgiConstants.HEADER_LENGTH, context); + if (buffer == null) { + input.release(); + return; + } + + decodeHeader(buffer); + state = State.CONTENT; + } + + case CONTENT: { + if (contentLength > 0) { + ByteBuf buffer = getBufferIfSufficient(input, contentLength, context); + if (buffer == null) { + input.release(); + return; + } + + FastCgiResponse response = readContent(buffer); + if (response != null) { + context.fireChannelRead(response); + } + } + state = State.HEADER; + } + } + } + } + + private void decodeHeader(ByteBuf buffer) { + buffer.skipBytes(1); + type = buffer.readUnsignedByte(); + id = buffer.readUnsignedShort(); + contentLength = buffer.readUnsignedShort(); + paddingLength = buffer.readUnsignedByte(); + buffer.skipBytes(1); + } + + private FastCgiResponse readContent(ByteBuf buffer) { + switch (type) { + case RecordType.END_REQUEST: + int appStatus = buffer.readInt(); + int protocolStatus = buffer.readUnsignedByte(); + buffer.skipBytes(3); + if (appStatus != 0 || protocolStatus != ProtocolStatus.REQUEST_COMPLETE.ordinal()) { + LOG.warn("Protocol status " + protocolStatus); + dataBuffers.remove(id); + return new FastCgiResponse(id, null); + } + else if (protocolStatus == ProtocolStatus.REQUEST_COMPLETE.ordinal()) { + return new FastCgiResponse(id, dataBuffers.remove(id)); + } + break; + + case RecordType.STDOUT: + ByteBuf data = dataBuffers.get(id); + ByteBuf sliced = buffer.slice(buffer.readerIndex(), contentLength); + if (data == null) { + dataBuffers.put(id, sliced); + } + else if (data instanceof CompositeByteBuf) { + ((CompositeByteBuf)data).addComponent(sliced); + data.writerIndex(data.writerIndex() + sliced.readableBytes()); + } + else { + dataBuffers.put(id, Unpooled.wrappedBuffer(data, sliced)); + } + sliced.retain(); + buffer.skipBytes(contentLength); + break; + + case RecordType.STDERR: + try { + errorOutputConsumer.consume(buffer.toString(buffer.readerIndex(), contentLength, CharsetUtil.UTF_8)); + } + catch (Throwable e) { + LOG.error(e); + } + buffer.skipBytes(contentLength); + break; + + default: + LOG.error("Unknown type " + type); + break; + } + return null; + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiRequest.java b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiRequest.java new file mode 100644 index 000000000000..73dbcbf12b3a --- /dev/null +++ b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiRequest.java @@ -0,0 +1,147 @@ +package org.jetbrains.io.fastCgi; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.builtInWebServer.PathToFileManager; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpHeaders; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.io.Responses; + +import java.net.InetSocketAddress; +import java.util.Locale; +import java.util.Map; + +public class FastCgiRequest { + private static final int PARAMS = 4; + private static final int BEGIN_REQUEST = 1; + private static final int RESPONDER = 1; + private static final int FCGI_KEEP_CONNECTION = 1; + private static final int STDIN = 5; + private static final int VERSION = 1; + + private final ByteBuf buffer; + final int requestId; + + public FastCgiRequest(int requestId, ByteBufAllocator allocator) { + this.requestId = requestId; + + buffer = allocator.buffer(); + writeHeader(buffer, BEGIN_REQUEST, FastCgiConstants.HEADER_LENGTH); + buffer.writeShort(RESPONDER); + buffer.writeByte(FCGI_KEEP_CONNECTION); + buffer.writeZero(5); + } + + public void writeFileHeaders(VirtualFile file, Project project, String canonicalRequestPath) { + Pair root = PathToFileManager.getInstance(project).getRoot(file); + FastCgiService.LOG.assertTrue(root != null); + addHeader("DOCUMENT_ROOT", root.first.getPath()); + addHeader("SCRIPT_FILENAME", file.getPath()); + addHeader("SCRIPT_NAME", canonicalRequestPath); + } + + public final void addHeader(@NotNull String key, @Nullable String value) { + if (value == null) { + return; + } + + int keyLength = key.length(); + int valLength = value.length(); + writeHeader(buffer, PARAMS, keyLength + valLength + (keyLength < 0x80 ? 1 : 4) + (valLength < 0x80 ? 1 : 4)); + + if (keyLength < 0x80) { + buffer.writeByte(keyLength); + } + else { + buffer.writeByte(0x80 | (keyLength >> 24)); + buffer.writeByte(keyLength >> 16); + buffer.writeByte(keyLength >> 8); + buffer.writeByte(keyLength); + } + + if (valLength < 0x80) { + buffer.writeByte(valLength); + } + else { + buffer.writeByte(0x80 | (valLength >> 24)); + buffer.writeByte(valLength >> 16); + buffer.writeByte(valLength >> 8); + buffer.writeByte(valLength); + } + + buffer.writeBytes(key.getBytes()); + buffer.writeBytes(value.getBytes()); + } + + public void writeHeaders(FullHttpRequest request, Channel clientChannel) { + addHeader("REQUEST_URI", request.uri()); + addHeader("REQUEST_METHOD", request.method().name()); + + InetSocketAddress remote = (InetSocketAddress)clientChannel.remoteAddress(); + addHeader("REMOTE_ADDR", remote.getAddress().getHostAddress()); + addHeader("REMOTE_PORT", Integer.toString(remote.getPort())); + + InetSocketAddress local = (InetSocketAddress)clientChannel.localAddress(); + addHeader("SERVER_SOFTWARE", Responses.getServerHeaderValue()); + addHeader("SERVER_NAME", Responses.getServerHeaderValue()); + + addHeader("SERVER_ADDR", local.getAddress().getHostAddress()); + addHeader("SERVER_PORT", Integer.toString(local.getPort())); + + addHeader("GATEWAY_INTERFACE", "CGI/1.1"); + addHeader("SERVER_PROTOCOL", request.protocolVersion().text()); + addHeader("CONTENT_TYPE", request.headers().get(HttpHeaders.Names.CONTENT_TYPE)); + + // PHP only, required if PHP was built with --enable-force-cgi-redirect + addHeader("REDIRECT_STATUS", "200"); + + String queryString = ""; + int queryIndex = request.uri().indexOf('?'); + if (queryIndex != -1) { + queryString = request.uri().substring(queryIndex + 1); + } + addHeader("QUERY_STRING", queryString); + + addHeader("CONTENT_LENGTH", String.valueOf(request.content().readableBytes())); + + for (Map.Entry entry : request.headers()) { + addHeader("HTTP_" + entry.getKey().replace('-', '_').toUpperCase(Locale.ENGLISH), entry.getValue()); + } + } + + final void writeToServerChannel(ByteBuf content, Channel fastCgiChannel) { + writeHeader(buffer, PARAMS, 0); + fastCgiChannel.write(buffer); + + if (content.isReadable()) { + ByteBuf headerBuffer = fastCgiChannel.alloc().buffer(FastCgiConstants.HEADER_LENGTH, FastCgiConstants.HEADER_LENGTH); + writeHeader(headerBuffer, STDIN, content.readableBytes()); + fastCgiChannel.write(headerBuffer); + + fastCgiChannel.write(content); + + headerBuffer = fastCgiChannel.alloc().buffer(FastCgiConstants.HEADER_LENGTH, FastCgiConstants.HEADER_LENGTH); + writeHeader(headerBuffer, STDIN, 0); + fastCgiChannel.write(headerBuffer); + } + else { + content.release(); + } + + fastCgiChannel.flush(); + } + + private void writeHeader(ByteBuf buffer, int type, int length) { + buffer.writeByte(VERSION); + buffer.writeByte(type); + buffer.writeShort(requestId); + buffer.writeShort(length); + buffer.writeZero(2); + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiResponse.java b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiResponse.java new file mode 100644 index 000000000000..e249f7152c7c --- /dev/null +++ b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiResponse.java @@ -0,0 +1,21 @@ +package org.jetbrains.io.fastCgi; + +import io.netty.buffer.ByteBuf; + +public class FastCgiResponse { + private final int id; + private final ByteBuf data; + + public FastCgiResponse(int id, ByteBuf data) { + this.id = id; + this.data = data; + } + + public ByteBuf getData() { + return data; + } + + public int getId() { + return id; + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiService.java b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiService.java new file mode 100644 index 000000000000..54f13c6c8ea1 --- /dev/null +++ b/xml/impl/src/org/jetbrains/io/fastCgi/FastCgiService.java @@ -0,0 +1,249 @@ +package org.jetbrains.io.fastCgi; + +import com.intellij.concurrency.JobScheduler; +import com.intellij.execution.filters.TextConsoleBuilder; +import com.intellij.execution.filters.TextConsoleBuilderFactory; +import com.intellij.execution.process.OSProcessHandler; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.ui.ConsoleView; +import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.AsyncResult; +import com.intellij.openapi.util.AsyncValueLoader; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowAnchor; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.content.ContentFactory; +import com.intellij.util.Consumer; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.StripedLockIntObjectConcurrentHashMap; +import com.intellij.util.net.NetUtils; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.io.ChannelExceptionHandler; +import org.jetbrains.io.NettyUtil; +import org.jetbrains.io.Responses; + +import javax.swing.*; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +// todo send FCGI_ABORT_REQUEST if client channel disconnected +public abstract class FastCgiService implements Disposable { + static final Logger LOG = Logger.getInstance(FastCgiService.class); + + protected final Project project; + + private final AtomicInteger requestIdCounter = new AtomicInteger(); + private final StripedLockIntObjectConcurrentHashMap requests = new StripedLockIntObjectConcurrentHashMap(); + + private volatile Channel fastCgiChannel; + + protected final AsyncValueLoader processHandler = new AsyncValueLoader() { + @Override + protected boolean isCancelOnReject() { + return true; + } + + @Override + protected void load(@NotNull final AsyncResult result) throws IOException { + final int port = NetUtils.findAvailableSocketPort(); + final OSProcessHandler processHandler = createProcessHandler(project, port); + if (processHandler == null) { + result.setRejected(); + return; + } + + result.doWhenRejected(new Runnable() { + @Override + public void run() { + processHandler.destroyProcess(); + } + }); + + final MyProcessAdapter processListener = new MyProcessAdapter(); + processHandler.addProcessListener(processListener); + processHandler.startNotify(); + + if (result.isRejected()) { + return; + } + + JobScheduler.getScheduler().schedule(new Runnable() { + @Override + public void run() { + if (result.isRejected()) { + return; + } + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + if (!result.isRejected()) { + try { + connectToProcess(result, port, processHandler, processListener); + } + catch (Throwable e) { + result.setRejected(); + LOG.error(e); + } + } + } + }); + } + }, NettyUtil.MIN_START_TIME, TimeUnit.MILLISECONDS); + } + + @Override + protected void disposeResult(@NotNull OSProcessHandler processHandler) { + try { + Channel currentFastCgiChannel = fastCgiChannel; + if (currentFastCgiChannel != null) { + fastCgiChannel = null; + NettyUtil.closeAndReleaseFactory(currentFastCgiChannel); + } + processHandler.destroyProcess(); + } + finally { + requestIdCounter.set(0); + if (!requests.isEmpty()) { + List waitingClients = ContainerUtil.toList(requests.elements()); + requests.clear(); + for (Channel channel : waitingClients) { + try { + if (channel.isActive()) { + Responses.sendStatus(HttpResponseStatus.BAD_GATEWAY, channel); + } + } + catch (Throwable e) { + NettyUtil.log(e, LOG); + } + } + } + } + } + }; + + private ConsoleView console; + + protected FastCgiService(Project project) { + this.project = project; + } + + protected abstract OSProcessHandler createProcessHandler(Project project, int port); + + private void connectToProcess(final AsyncResult asyncResult, final int port, final OSProcessHandler processHandler, final Consumer errorOutputConsumer) { + Bootstrap bootstrap = NettyUtil.oioClientBootstrap(); + final FastCgiChannelHandler fastCgiChannelHandler = new FastCgiChannelHandler(requests); + bootstrap.handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(new FastCgiDecoder(errorOutputConsumer), fastCgiChannelHandler, ChannelExceptionHandler.getInstance()); + } + }); + fastCgiChannel = NettyUtil.connectClient(bootstrap, new InetSocketAddress(NetUtils.getLoopbackAddress(), port), asyncResult); + if (fastCgiChannel != null) { + asyncResult.setDone(processHandler); + } + } + + public void send(final FastCgiRequest fastCgiRequest, final ByteBuf content) { + content.retain(); + + if (processHandler.has()) { + fastCgiRequest.writeToServerChannel(content, fastCgiChannel); + } + else { + processHandler.get().doWhenDone(new Runnable() { + @Override + public void run() { + fastCgiRequest.writeToServerChannel(content, fastCgiChannel); + } + }).doWhenRejected(new Runnable() { + @Override + public void run() { + content.release(); + Channel channel = requests.get(fastCgiRequest.requestId); + if (channel != null && channel.isActive()) { + Responses.sendStatus(HttpResponseStatus.BAD_GATEWAY, channel); + } + } + }); + } + } + + public int allocateRequestId(Channel channel) { + int requestId = requestIdCounter.getAndIncrement(); + if (requestId >= Short.MAX_VALUE) { + requestIdCounter.set(0); + requestId = requestIdCounter.getAndDecrement(); + } + requests.put(requestId, channel); + return requestId; + } + + @Override + public void dispose() { + processHandler.reset(); + } + + protected abstract void buildConsole(@NotNull TextConsoleBuilder consoleBuilder); + + @NotNull + protected abstract String getConsoleToolWindowId(); + + @NotNull + protected abstract Icon getConsoleToolWindowIcon(); + + private final class MyProcessAdapter extends ProcessAdapter implements Consumer { + private void createConsole() { + TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project); + buildConsole(consoleBuilder); + console = consoleBuilder.getConsole(); + + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + ToolWindow toolWindow = ToolWindowManager.getInstance(project).registerToolWindow(getConsoleToolWindowId(), false, ToolWindowAnchor.BOTTOM, project, true); + toolWindow.setIcon(getConsoleToolWindowIcon()); + toolWindow.getContentManager().addContent(ContentFactory.SERVICE.getInstance().createContent(console.getComponent(), "", false)); + } + }, project.getDisposed()); + } + + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + print(event.getText(), ConsoleViewContentType.getConsoleViewType(outputType)); + } + + private void print(String text, ConsoleViewContentType contentType) { + if (console == null) { + createConsole(); + } + console.print(text, contentType); + } + + @Override + public void processTerminated(ProcessEvent event) { + processHandler.reset(); + print(getConsoleToolWindowId() + " terminated\n", ConsoleViewContentType.SYSTEM_OUTPUT); + } + + @Override + public void consume(String message) { + print(message, ConsoleViewContentType.ERROR_OUTPUT); + } + } +} \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/notification/SingletonNotificationManager.java b/xml/impl/src/org/jetbrains/notification/SingletonNotificationManager.java new file mode 100644 index 000000000000..4ea4a10dcf85 --- /dev/null +++ b/xml/impl/src/org/jetbrains/notification/SingletonNotificationManager.java @@ -0,0 +1,86 @@ +package org.jetbrains.notification; + +import com.intellij.notification.*; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindowManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.atomic.AtomicReference; + +public final class SingletonNotificationManager { + private final AtomicReference notification = new AtomicReference(); + + private final NotificationGroup group; + private final NotificationType type; + @Nullable + private final NotificationListener listener; + + private Runnable expiredListener; + + public SingletonNotificationManager(@NotNull String groupId, @NotNull NotificationType type, @Nullable NotificationListener listener) { + this(new NotificationGroup(groupId, NotificationDisplayType.STICKY_BALLOON, true), type, listener); + } + + public SingletonNotificationManager(@NotNull NotificationGroup group, @NotNull NotificationType type, @Nullable NotificationListener listener) { + this.group = group; + this.type = type; + this.listener = listener; + } + + public boolean notify(@NotNull String title, @NotNull String content) { + return notify(title, content, null); + } + + public boolean notify(@NotNull String title, @NotNull String content, @Nullable Project project) { + return notify(title, content, listener, project); + } + + public boolean notify(@NotNull String content, @Nullable Project project) { + return notify("", content, listener, project); + } + + public boolean notify(@NotNull String title, + @NotNull String content, + @Nullable NotificationListener listener, + @Nullable Project project) { + Notification oldNotification = notification.get(); + // !oldNotification.isExpired() is not enough - notification could be closed, but not expired + if (oldNotification != null) { + if (!oldNotification.isExpired() && (oldNotification.getBalloon() != null || + (project != null && + group.getDisplayType() == NotificationDisplayType.TOOL_WINDOW && + ToolWindowManager.getInstance(project).getToolWindowBalloon(group.getToolWindowId()) != null))) { + return false; + } + oldNotification.whenExpired(null); + oldNotification.expire(); + } + + if (expiredListener == null) { + expiredListener = new Runnable() { + @Override + public void run() { + Notification currentNotification = notification.get(); + if (currentNotification != null && currentNotification.isExpired()) { + notification.compareAndSet(currentNotification, null); + } + } + }; + } + + Notification newNotification = group.createNotification(title, content, type, listener); + newNotification.whenExpired(expiredListener); + notification.set(newNotification); + newNotification.notify(project); + return true; + } + + public void clear() { + Notification oldNotification = notification.getAndSet(null); + if (oldNotification != null) { + oldNotification.whenExpired(null); + oldNotification.expire(); + } + } +} \ No newline at end of file diff --git a/xml/impl/xml.iml b/xml/impl/xml.iml index 60e210829091..2fb3ee7ea262 100644 --- a/xml/impl/xml.iml +++ b/xml/impl/xml.iml @@ -21,6 +21,8 @@ + + From eb4e0bb7ab484b3cc0ffd86bc77eb3bdd6de281f Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 31 Jul 2014 19:18:17 +0200 Subject: [PATCH 47/85] Clickable links for remote files in console (PY-10224). --- .../rest/run/RestCommandLineState.java | 4 +- .../python/run/PyRemoteTracebackFilter.java | 52 +++++++++++++++++++ .../python/run/PythonCommandLineState.java | 26 +++++++--- .../python/run/PythonRunConfiguration.java | 8 +-- .../run/PythonScriptCommandLineState.java | 7 +-- .../python/run/PythonTracebackFilter.java | 14 +++-- .../PythonTestCommandLineStateBase.java | 8 +-- .../pytest/PyTestCommandLineState.java | 2 +- 8 files changed, 87 insertions(+), 34 deletions(-) create mode 100644 python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java diff --git a/python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java b/python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java index e421df82d4f7..e5b52b5dc3a7 100644 --- a/python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java +++ b/python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java @@ -19,7 +19,6 @@ import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.ParametersList; import com.intellij.execution.configurations.ParamsGroup; -import com.intellij.execution.filters.Filter; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; @@ -33,7 +32,6 @@ import com.jetbrains.python.run.PythonProcessRunner; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.Collections; /** * User : catherine @@ -43,7 +41,7 @@ public abstract class RestCommandLineState extends PythonCommandLineState { public RestCommandLineState(RestRunConfiguration configuration, ExecutionEnvironment env) { - super(configuration, env, Collections.emptyList()); + super(configuration, env); myConfiguration = configuration; } diff --git a/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java b/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java new file mode 100644 index 000000000000..c8d7850a4112 --- /dev/null +++ b/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2014 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.jetbrains.python.run; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.remote.RemoteProcessHandlerBase; +import com.intellij.util.PathMappingSettings; +import org.jetbrains.annotations.Nullable; + +/** + * @author traff + */ +public class PyRemoteTracebackFilter extends PythonTracebackFilter { + private final RemoteProcessHandlerBase myHandler; + + public PyRemoteTracebackFilter(Project project, RemoteProcessHandlerBase remoteProcessHandler) { + super(project); + + + myHandler = remoteProcessHandler; + } + + @Override + @Nullable + protected VirtualFile findFileByName(String fileName) { + for (PathMappingSettings.PathMapping m : myHandler.getMappingSettings().getPathMappings()) { + if (m.canReplaceRemote(fileName)) { + VirtualFile file = LocalFileSystem.getInstance().findFileByPath(m.mapToLocal(fileName)); + if (file != null && file.exists()) { + return file; + } + } + } + + return null; + } +} diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java index 216e68c58e1d..f326c05f3aae 100644 --- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java @@ -47,6 +47,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.remote.RemoteProcessHandlerBase; import com.intellij.util.PlatformUtils; import com.intellij.util.containers.HashMap; import com.jetbrains.python.PythonHelpersLocator; @@ -80,7 +81,9 @@ public abstract class PythonCommandLineState extends CommandLineState { public static final String GROUP_DEBUGGER = "Debugger"; public static final String GROUP_SCRIPT = "Script"; private final AbstractPythonRunConfiguration myConfig; - private final List myFilters; + + private final List myFilters = Lists.newArrayList(new UrlFilter()); + private Boolean myMultiprocessDebug = null; public boolean isDebug() { @@ -99,15 +102,9 @@ public abstract class PythonCommandLineState extends CommandLineState { return serverSocket; } - public PythonCommandLineState(AbstractPythonRunConfiguration runConfiguration, ExecutionEnvironment env, List filters) { + public PythonCommandLineState(AbstractPythonRunConfiguration runConfiguration, ExecutionEnvironment env) { super(env); myConfig = runConfiguration; - myFilters = Lists.newArrayList(filters); - addDefaultFilters(); - } - - protected void addDefaultFilters() { - myFilters.add(new UrlFilter()); } @Nullable @@ -134,10 +131,23 @@ public abstract class PythonCommandLineState extends CommandLineState { protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor) throws ExecutionException { final ConsoleView consoleView = createConsoleBuilder(project).filters(myFilters).getConsole(); + + addTracebackFilter(project, consoleView, processHandler); + consoleView.attachToProcess(processHandler); return consoleView; } + protected void addTracebackFilter(Project project, ConsoleView consoleView, ProcessHandler processHandler) { + if (PySdkUtil.isRemote(myConfig.getSdk())) { + assert processHandler instanceof RemoteProcessHandlerBase; + consoleView.addMessageFilter(new PyRemoteTracebackFilter(project, (RemoteProcessHandlerBase) processHandler)); + } + else { + consoleView.addMessageFilter(new PythonTracebackFilter(project, myConfig.getWorkingDirectory())); + } + } + private TextConsoleBuilder createConsoleBuilder(Project project) { if (isDebug()) { return new PyDebugConsoleBuilder(project, PythonSdkType.findSdkByPath(myConfig.getInterpreterPath())); diff --git a/python/src/com/jetbrains/python/run/PythonRunConfiguration.java b/python/src/com/jetbrains/python/run/PythonRunConfiguration.java index bbba44441227..118c27beb548 100644 --- a/python/src/com/jetbrains/python/run/PythonRunConfiguration.java +++ b/python/src/com/jetbrains/python/run/PythonRunConfiguration.java @@ -15,11 +15,9 @@ */ package com.jetbrains.python.run; -import com.google.common.collect.Lists; import com.intellij.execution.ExecutionException; import com.intellij.execution.Executor; import com.intellij.execution.configurations.*; -import com.intellij.execution.filters.Filter; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.options.SettingsEditor; @@ -40,7 +38,6 @@ import org.jdom.Element; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.util.List; /** * @author yole @@ -64,10 +61,7 @@ public class PythonRunConfiguration extends AbstractPythonRunConfiguration } public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException { - List filters = Lists.newArrayList(); - filters.add(new PythonTracebackFilter(getProject(), getWorkingDirectory())); - - return new PythonScriptCommandLineState(this, env, filters); + return new PythonScriptCommandLineState(this, env); } public void checkConfiguration() throws RuntimeConfigurationException { diff --git a/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java b/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java index 53ef554838ae..81d2daaee3d0 100644 --- a/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java @@ -18,20 +18,17 @@ package com.jetbrains.python.run; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.ParametersList; import com.intellij.execution.configurations.ParamsGroup; -import com.intellij.execution.filters.Filter; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.openapi.util.text.StringUtil; -import java.util.List; - /** * @author yole */ public class PythonScriptCommandLineState extends PythonCommandLineState { private final PythonRunConfiguration myConfig; - public PythonScriptCommandLineState(PythonRunConfiguration runConfiguration, ExecutionEnvironment env, List filters) { - super(runConfiguration, env, filters); + public PythonScriptCommandLineState(PythonRunConfiguration runConfiguration, ExecutionEnvironment env) { + super(runConfiguration, env); myConfig = runConfiguration; } diff --git a/python/src/com/jetbrains/python/run/PythonTracebackFilter.java b/python/src/com/jetbrains/python/run/PythonTracebackFilter.java index e3bd879cfe81..73a3abd222b1 100644 --- a/python/src/com/jetbrains/python/run/PythonTracebackFilter.java +++ b/python/src/com/jetbrains/python/run/PythonTracebackFilter.java @@ -51,10 +51,7 @@ public class PythonTracebackFilter implements Filter { if (matcher.find()) { String fileName = matcher.group(1).replace('\\', '/'); int lineNumber = Integer.parseInt(matcher.group(2)); - VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(fileName); - if (vFile == null && !StringUtil.isEmptyOrSpaces(myWorkingDirectory)) { - vFile = LocalFileSystem.getInstance().findFileByIoFile(new File(myWorkingDirectory, fileName)); - } + VirtualFile vFile = findFileByName(fileName); if (vFile != null) { OpenFileHyperlinkInfo hyperlink = new OpenFileHyperlinkInfo(myProject, vFile, lineNumber - 1); @@ -66,4 +63,13 @@ public class PythonTracebackFilter implements Filter { } return null; } + + @Nullable + protected VirtualFile findFileByName(String fileName) { + VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(fileName); + if (vFile == null && !StringUtil.isEmptyOrSpaces(myWorkingDirectory)) { + vFile = LocalFileSystem.getInstance().findFileByIoFile(new File(myWorkingDirectory, fileName)); + } + return vFile; + } } diff --git a/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java b/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java index 39e4e96b836b..1a5aafe59051 100644 --- a/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java +++ b/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java @@ -22,7 +22,6 @@ import com.intellij.execution.ExecutionResult; import com.intellij.execution.Executor; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.ParamsGroup; -import com.intellij.execution.filters.Filter; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.testframework.TestFrameworkRunningModel; @@ -42,12 +41,10 @@ import com.jetbrains.python.console.PythonDebugLanguageConsoleView; import com.jetbrains.python.run.AbstractPythonRunConfiguration; import com.jetbrains.python.run.CommandLinePatcher; import com.jetbrains.python.run.PythonCommandLineState; -import com.jetbrains.python.run.PythonTracebackFilter; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -62,7 +59,7 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt } public PythonTestCommandLineStateBase(AbstractPythonRunConfiguration configuration, ExecutionEnvironment env) { - super(configuration, env, Collections.emptyList()); + super(configuration, env); myConfiguration = configuration; } @@ -77,7 +74,6 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt consoleProperties, getEnvironment()); final ConsoleView consoleView = new PythonDebugLanguageConsoleView(project, PythonSdkType.findSdkByPath(myConfiguration.getInterpreterPath()), testsOutputConsoleView); - consoleView.addMessageFilter(new PythonTracebackFilter(project, myConfiguration.getWorkingDirectory())); consoleView.attachToProcess(processHandler); return consoleView; } @@ -85,7 +81,7 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt processHandler, consoleProperties, getEnvironment()); - consoleView.addMessageFilter(new PythonTracebackFilter(project, myConfiguration.getWorkingDirectory())); + addTracebackFilter(project, consoleView, processHandler); return consoleView; } diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java b/python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java index 3637f37ca91c..581e688de1d5 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java @@ -81,7 +81,7 @@ public class PyTestCommandLineState extends PythonTestCommandLineStateBase { protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor) throws ExecutionException { final ConsoleView consoleView = super.createAndAttachConsole(project, processHandler, executor); - consoleView.addMessageFilter(new PyTestTracebackFilter(project, myConfiguration.getWorkingDirectory())); + addTracebackFilter(project, consoleView, processHandler); return consoleView; } } From 7507f8e98c22b47b712028e9374d2c52a8fa8060 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 31 Jul 2014 19:19:39 +0200 Subject: [PATCH 48/85] removed accidental change --- .../testSrc/com/intellij/find/FindManagerTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java index 2f6c4216e4ed..fded8bdc12c9 100644 --- a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java +++ b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java @@ -27,7 +27,6 @@ import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.projectRoots.Sdk; @@ -39,7 +38,6 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.GlobalSearchScopes; import com.intellij.psi.search.LocalSearchScope; import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.LightVirtualFile; @@ -303,9 +301,6 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { findModel.setGlobal(true); findModel.setMultipleFiles(true); findModel.setCustomScope(true); - OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, nonProjectFile); - descriptor.navigate(true); - GlobalSearchScopes.openFilesScope(myProject); findModel.setCustomScope(new GlobalSearchScope.FilesScope(myProject, ContainerUtil.list(nonProjectFile))); assertSize(1, findUsages(findModel)); From 6ea333f80d358f799e752f9c2511a9a9b14a0bfa Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 31 Jul 2014 19:22:18 +0200 Subject: [PATCH 49/85] Try also local files in remote traceback filter. --- .../jetbrains/python/run/PyRemoteTracebackFilter.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java b/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java index c8d7850a4112..2af1a641de83 100644 --- a/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java +++ b/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java @@ -28,9 +28,8 @@ import org.jetbrains.annotations.Nullable; public class PyRemoteTracebackFilter extends PythonTracebackFilter { private final RemoteProcessHandlerBase myHandler; - public PyRemoteTracebackFilter(Project project, RemoteProcessHandlerBase remoteProcessHandler) { - super(project); - + public PyRemoteTracebackFilter(Project project, String workingDirectory, RemoteProcessHandlerBase remoteProcessHandler) { + super(project, workingDirectory); myHandler = remoteProcessHandler; } @@ -38,6 +37,10 @@ public class PyRemoteTracebackFilter extends PythonTracebackFilter { @Override @Nullable protected VirtualFile findFileByName(String fileName) { + VirtualFile vFile = super.findFileByName(fileName); + if (vFile != null) { + return vFile; + } for (PathMappingSettings.PathMapping m : myHandler.getMappingSettings().getPathMappings()) { if (m.canReplaceRemote(fileName)) { VirtualFile file = LocalFileSystem.getInstance().findFileByPath(m.mapToLocal(fileName)); @@ -47,6 +50,8 @@ public class PyRemoteTracebackFilter extends PythonTracebackFilter { } } + + return null; } } From 806c7c2f9bc5702a5b7f44f2dfacb575b9d2d9d8 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 31 Jul 2014 19:44:52 +0200 Subject: [PATCH 50/85] fix mem leak (avoid ugly disposeUIResources()) --- .../CompoundRendererConfigurable.java | 103 ++++++------------ .../settings/UserRenderersConfigurable.java | 24 +--- 2 files changed, 38 insertions(+), 89 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/CompoundRendererConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/CompoundRendererConfigurable.java index 58b1be02b18f..a4484cccc1fe 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/CompoundRendererConfigurable.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/CompoundRendererConfigurable.java @@ -25,13 +25,10 @@ import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.ui.DebuggerExpressionTextField; import com.intellij.debugger.ui.JavaDebuggerSupport; import com.intellij.debugger.ui.tree.render.*; -import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; @@ -51,54 +48,33 @@ import java.awt.event.*; import java.util.ArrayList; import java.util.List; -/** - * @author Eugene Zhuravlev - * Date: Feb 24, 2005 - */ -public class CompoundRendererConfigurable implements UnnamedConfigurable { +class CompoundRendererConfigurable extends JPanel { private CompoundReferenceRenderer myRenderer; private CompoundReferenceRenderer myOriginalRenderer; private Project myProject; - private ClassNameEditorWithBrowseButton myClassNameField; - private JRadioButton myRbDefaultLabel; - private JRadioButton myRbExpressionLabel; - private JRadioButton myRbDefaultChildrenRenderer; - private JRadioButton myRbExpressionChildrenRenderer; - private JRadioButton myRbListChildrenRenderer; - private DebuggerExpressionTextField myLabelEditor; - private DebuggerExpressionTextField myChildrenEditor; - private DebuggerExpressionTextField myChildrenExpandedEditor; + private final ClassNameEditorWithBrowseButton myClassNameField; + private final JRadioButton myRbDefaultLabel; + private final JRadioButton myRbExpressionLabel; + private final JRadioButton myRbDefaultChildrenRenderer; + private final JRadioButton myRbExpressionChildrenRenderer; + private final JRadioButton myRbListChildrenRenderer; + private final DebuggerExpressionTextField myLabelEditor; + private final DebuggerExpressionTextField myChildrenEditor; + private final DebuggerExpressionTextField myChildrenExpandedEditor; private DebuggerExpressionTextField myListChildrenEditor; - private JLabel myExpandedLabel; - private JPanel myMainPanel; + private final JLabel myExpandedLabel; private JBTable myTable; @NonNls private static final String EMPTY_PANEL_ID = "EMPTY"; @NonNls private static final String DATA_PANEL_ID = "DATA"; private static final int NAME_TABLE_COLUMN = 0; private static final int EXPRESSION_TABLE_COLUMN = 1; - public void setRenderer(NodeRenderer renderer) { - if (renderer instanceof CompoundReferenceRenderer) { - myRenderer = (CompoundReferenceRenderer)renderer; - myOriginalRenderer = (CompoundReferenceRenderer)renderer.clone(); - } - else { - myRenderer = myOriginalRenderer = null; - } - reset(); - } + public CompoundRendererConfigurable() { + super(new CardLayout()); - public CompoundReferenceRenderer getRenderer() { - return myRenderer; - } - - @Override - @NotNull - public JComponent createComponent() { if (myProject == null) { myProject = JavaDebuggerSupport.getContextProjectForEditorFieldsInDebuggerConfigurables(); } - final JPanel panel = new JPanel(new GridBagLayout()); myRbDefaultLabel = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.default.renderer")); myRbExpressionLabel = new JRadioButton(DebuggerBundle.message("label.compound.renderer.configurable.use.expression")); @@ -141,21 +117,14 @@ public class CompoundRendererConfigurable implements UnnamedConfigurable { } } }, myProject); - final EditorTextField textField = myClassNameField.getEditorTextField(); - final FocusAdapter updateContextListener = new FocusAdapter() { + myClassNameField.getEditorTextField().addFocusListener(new FocusAdapter() { @Override public void focusLost(@NotNull FocusEvent e) { updateContext(myClassNameField.getText()); } - }; - textField.addFocusListener(updateContextListener); - Disposer.register(myClassNameField, new Disposable() { - @Override - public void dispose() { - textField.removeFocusListener(updateContextListener); - } }); + JPanel panel = new JPanel(new GridBagLayout()); panel.add(new JLabel(DebuggerBundle.message("label.compound.renderer.configurable.apply.to")), new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); @@ -196,11 +165,23 @@ public class CompoundRendererConfigurable implements UnnamedConfigurable { panel.add(myChildrenListEditor, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(4, 30, 0, 0), 0, 0)); + add(new JPanel(), EMPTY_PANEL_ID); + add(panel, DATA_PANEL_ID); + } - myMainPanel = new JPanel(new CardLayout()); - myMainPanel.add(new JPanel(), EMPTY_PANEL_ID); - myMainPanel.add(panel, DATA_PANEL_ID); - return myMainPanel; + public void setRenderer(NodeRenderer renderer) { + if (renderer instanceof CompoundReferenceRenderer) { + myRenderer = (CompoundReferenceRenderer)renderer; + myOriginalRenderer = (CompoundReferenceRenderer)renderer.clone(); + } + else { + myRenderer = myOriginalRenderer = null; + } + reset(); + } + + public CompoundReferenceRenderer getRenderer() { + return myRenderer; } private void updateContext(final String qName) { @@ -305,7 +286,6 @@ public class CompoundRendererConfigurable implements UnnamedConfigurable { }).createPanel(); } - @Override public boolean isModified() { if (myRenderer == null) { return false; @@ -315,7 +295,6 @@ public class CompoundRendererConfigurable implements UnnamedConfigurable { return !DebuggerUtilsEx.externalizableEqual(cloned, myOriginalRenderer); } - @Override public void apply() { if (myRenderer == null) { return; @@ -347,10 +326,9 @@ public class CompoundRendererConfigurable implements UnnamedConfigurable { renderer.setClassName(myClassNameField.getText()); } - @Override public void reset() { final TextWithImports emptyExpressionFragment = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, ""); - ((CardLayout)myMainPanel.getLayout()).show(myMainPanel, myRenderer == null ? EMPTY_PANEL_ID : DATA_PANEL_ID); + ((CardLayout)getLayout()).show(this, myRenderer == null ? EMPTY_PANEL_ID : DATA_PANEL_ID); if (myRenderer == null) { return; } @@ -398,23 +376,6 @@ public class CompoundRendererConfigurable implements UnnamedConfigurable { updateContext(className); } - @Override - public void disposeUIResources() { - myRenderer = null; - myOriginalRenderer = null; - myLabelEditor.dispose(); - myChildrenEditor.dispose(); - myChildrenExpandedEditor.dispose(); - myListChildrenEditor.dispose(); - Disposer.dispose(myClassNameField); - myLabelEditor = null; - myChildrenEditor = null; - myChildrenExpandedEditor = null; - myListChildrenEditor = null; - myClassNameField = null; - myProject = null; - } - private MyTableModel getTableModel() { return (MyTableModel)myTable.getModel(); } diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/UserRenderersConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/UserRenderersConfigurable.java index a9197da82568..3967d2239455 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/UserRenderersConfigurable.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/UserRenderersConfigurable.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -35,10 +35,6 @@ import java.awt.*; import java.util.ArrayList; import java.util.List; -/** - * @author Eugene Zhuravlev - * Date: Feb 19, 2005 - */ public class UserRenderersConfigurable implements ConfigurableUi { private static final Icon ADD_ICON = IconUtil.getAddIcon(); private static final Icon REMOVE_ICON = IconUtil.getRemoveIcon(); @@ -55,16 +51,9 @@ public class UserRenderersConfigurable implements ConfigurableUi Date: Thu, 31 Jul 2014 20:27:17 +0400 Subject: [PATCH 51/85] better EOF handling for EA-55590 - EOFE: MXParser.fillBuf --- .../execution/console/ConsoleHistoryController.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java index 6c0bfffd450e..c40c0a95a277 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java @@ -45,6 +45,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.ExceptionUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.io.SafeFileOutputStream; import com.thoughtworks.xstream.io.HierarchicalStreamReader; @@ -394,7 +395,15 @@ public class ConsoleHistoryController { } } catch (Exception ex) { - LOG.error(ex); + //noinspection ThrowableResultOfMethodCallIgnored + Throwable cause = ExceptionUtil.getRootCause(ex); + if (cause instanceof EOFException) { + LOG.warn(ex); + return false; + } + else { + LOG.error(ex); + } } finally { if (xmlReader != null) { From 07a7e93895d0eb4f5a6b154a19686fd6bc4e37cc Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Thu, 31 Jul 2014 20:49:31 +0400 Subject: [PATCH 52/85] do not report bad pattern EA-58884 - PSE: FindModel.compileRegExp --- .../lang-api/src/com/intellij/find/FindModel.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/platform/lang-api/src/com/intellij/find/FindModel.java b/platform/lang-api/src/com/intellij/find/FindModel.java index 7275bde8d3fe..8c34697ac0fa 100644 --- a/platform/lang-api/src/com/intellij/find/FindModel.java +++ b/platform/lang-api/src/com/intellij/find/FindModel.java @@ -19,6 +19,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.search.SearchScope; +import com.intellij.util.PatternUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -274,7 +275,7 @@ public class FindModel extends UserDataHolderBase implements Cloneable { public void setStringToFind(@NotNull String s) { boolean changed = !StringUtil.equals(s, myStringToFind); myStringToFind = s; - myPattern = NO_PATTERN; + myPattern = PatternUtil.NOTHING; if (changed) { notifyObservers(); } @@ -409,7 +410,7 @@ public class FindModel extends UserDataHolderBase implements Cloneable { boolean changed = val != isCaseSensitive; isCaseSensitive = val; if (changed) { - myPattern = NO_PATTERN; + myPattern = PatternUtil.NOTHING; notifyObservers(); } } @@ -901,21 +902,18 @@ public class FindModel extends UserDataHolderBase implements Cloneable { } } - private static final Pattern NO_PATTERN = Pattern.compile(""); - private Pattern myPattern = NO_PATTERN; + private Pattern myPattern = PatternUtil.NOTHING; public Pattern compileRegExp() { String toFind = getStringToFind(); Pattern pattern = myPattern; - if (pattern == NO_PATTERN) { + if (pattern == PatternUtil.NOTHING) { try { myPattern = pattern = Pattern.compile(toFind, isCaseSensitive() ? Pattern.MULTILINE : Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); } catch (PatternSyntaxException e) { - LOG.error("Regexp:'" + toFind + "'", e); - myPattern = null; - return null; + myPattern = pattern = null; } } From 9acc44ae18d09899f4ea213cc6d938dbd60fea89 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Thu, 31 Jul 2014 21:50:42 +0400 Subject: [PATCH 53/85] EA-56975 - ISE: MXSerializer.writeElementContent --- .../console/ConsoleHistoryController.java | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java index c40c0a95a277..9aab415d5e70 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java @@ -47,6 +47,7 @@ import com.intellij.psi.PsiFileFactory; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.ExceptionUtil; import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.SafeFileOutputStream; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.xml.XppReader; @@ -57,7 +58,6 @@ import org.xmlpull.v1.XmlSerializer; import java.awt.event.KeyEvent; import java.io.*; -import java.util.ArrayList; import java.util.List; import java.util.ListIterator; @@ -66,6 +66,8 @@ import java.util.ListIterator; */ public class ConsoleHistoryController { + private static final int VERSION = 1; + private static final Logger LOG = Logger.getInstance("com.intellij.execution.console.ConsoleHistoryController"); private final LanguageConsoleImpl myConsole; @@ -398,7 +400,7 @@ public class ConsoleHistoryController { //noinspection ThrowableResultOfMethodCallIgnored Throwable cause = ExceptionUtil.getRootCause(ex); if (cause instanceof EOFException) { - LOG.warn(ex); + LOG.warn("Failed to load " + myType + " console history from: " + file.getPath(), ex); return false; } else { @@ -432,6 +434,7 @@ public class ConsoleHistoryController { } serializer.setOutput(os = new SafeFileOutputStream(file), CharsetToolkit.UTF8); saveHistory(serializer); + serializer.flush(); } catch (Exception ex) { LOG.error(ex); @@ -448,47 +451,58 @@ public class ConsoleHistoryController { } @Nullable - private String loadHistory(final HierarchicalStreamReader in, final String expectedId) { + private String loadHistory(HierarchicalStreamReader in, String expectedId) { if (!in.getNodeName().equals("console-history")) return null; - final String id = in.getAttribute("id"); + int version = StringUtil.parseInt(in.getAttribute("version"), 0); + String id = in.getAttribute("id"); if (!expectedId.equals(id)) return null; - final ArrayList entries = new ArrayList(); + List entries = ContainerUtil.newArrayList(); String consoleContent = null; while (in.hasMoreChildren()) { in.moveDown(); if ("history-entry".equals(in.getNodeName())) { - entries.add(in.getValue()); + entries.add(StringUtil.notNullize(in.getValue())); } else if ("console-content".equals(in.getNodeName())) { - consoleContent = in.getValue(); + consoleContent = StringUtil.notNullize(in.getValue()); } in.moveUp(); } for (ListIterator iterator = entries.listIterator(entries.size()); iterator.hasPrevious(); ) { - final String entry = iterator.previous(); + String entry = iterator.previous(); getModel().addToHistory(entry); } return consoleContent; } - private void saveHistory(final XmlSerializer out) throws IOException { + private void saveHistory(XmlSerializer out) throws IOException { out.startDocument(CharsetToolkit.UTF8, null); out.startTag(null, "console-history"); + out.attribute(null, "version", String.valueOf(VERSION)); out.attribute(null, "id", myId); - for (String s : getModel().getHistory()) { - out.startTag(null, "history-entry"); - out.text(s); - out.endTag(null, "history-entry"); + try { + for (String s : getModel().getHistory()) { + textTag(out, "history-entry", s); + } + String current = myContent; + if (StringUtil.isNotEmpty(current)) { + textTag(out, "console-content", current); + } } - String current = myContent; - if (StringUtil.isNotEmpty(current)) { - out.startTag(null, "console-content"); - out.text(current); - out.endTag(null, "console-content"); + finally { + out.endTag(null, "console-history"); + out.endDocument(); } - out.endTag(null, "console-history"); - out.endDocument(); } } + private static void textTag(@NotNull XmlSerializer out, @NotNull String tag, @NotNull String text) throws IOException { + out.startTag(null, tag); + try { + out.cdsect(text); + } + finally { + out.endTag(null, tag); + } + } } From a3b9fbf66dec30dcc5e8014cd326c7055572231d Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Thu, 31 Jul 2014 21:58:46 +0400 Subject: [PATCH 54/85] PY-13566 Create step definition doesn't create step --- python/helpers/pycharm/_bdd_utils.py | 17 +++++++++++------ python/helpers/pycharm/behave_runner.py | 15 ++++----------- python/helpers/pycharm/lettuce_runner.py | 19 ++++++++++++++----- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/python/helpers/pycharm/_bdd_utils.py b/python/helpers/pycharm/_bdd_utils.py index 26bd107c5288..0c92532b516c 100644 --- a/python/helpers/pycharm/_bdd_utils.py +++ b/python/helpers/pycharm/_bdd_utils.py @@ -19,12 +19,16 @@ def get_path_by_args(arguments): """ :type arguments list :param arguments: arguments (sys.argv) - :return: Folder (current or first argument from argv) checking it exists - :rtype str + :return: tuple (base_dir, what_to_run) where dir is current or first argument from argv, checking it exists + :rtype tuple of str """ - path = arguments[1] if len(arguments) > 1 else "." - assert os.path.exists(path), "{} does not exist".format(path) - return path + what_to_run = arguments[1] if len(arguments) > 1 else "." + base_dir = what_to_run + assert os.path.exists(what_to_run), "{} does not exist".format(what_to_run) + + if os.path.isfile(what_to_run): + base_dir = os.path.dirname(what_to_run) # User may point to the file directly + return base_dir, what_to_run class BddRunner(object): @@ -68,7 +72,8 @@ class BddRunner(object): :param location object with "file" (relative to base_dir) and "line" fields. :return: location in format file:line (as supported in tcmessages) """ - return "file:///{}/{}:{}".format(self.__base_dir, location.file, location.line) + my_file = str(location.file).lstrip("/\\") + return "file:///{}:{}".format(os.path.normpath(os.path.join(self.__base_dir, my_file)), location.line) def _test_undefined(self, test_name, location): """ diff --git a/python/helpers/pycharm/behave_runner.py b/python/helpers/pycharm/behave_runner.py index b11abfe7cc6b..933588aa444a 100644 --- a/python/helpers/pycharm/behave_runner.py +++ b/python/helpers/pycharm/behave_runner.py @@ -219,22 +219,15 @@ if __name__ == "__main__": Null formater to prevent stdout output """ pass - - tags = [] - if len(sys.argv) > 1: - for arg in filter(None, sys.argv[2::]): - if str(arg).startswith("--tags"): - tags.append(str(arg)) - else: - raise Exception("Not a tag expression (should be --tags=..):{}".format(str(arg))) - my_config = configuration.Configuration(command_args=tags) + my_config = configuration.Configuration(command_args=filter(None, sys.argv[1:])) formatters.register_as(_Null, "com.intellij.python.null") my_config.format = ["com.intellij.python.null"] # To prevent output to stdout my_config.reporters = [] # To prevent summary to stdout my_config.stdout_capture = False # For test output my_config.stderr_capture = False # For test output - base_dir = _bdd_utils.get_path_by_args(sys.argv) - my_config.paths = _get_dirs_to_run(base_dir) + (base_dir, what_to_run) = _bdd_utils.get_path_by_args(sys.argv) + if not my_config.paths: # No path provided, trying to load dir + my_config.paths = _get_dirs_to_run(base_dir) _BehaveRunner(my_config, base_dir).run() diff --git a/python/helpers/pycharm/lettuce_runner.py b/python/helpers/pycharm/lettuce_runner.py index 6a3558bc8f44..3cd112540e5f 100644 --- a/python/helpers/pycharm/lettuce_runner.py +++ b/python/helpers/pycharm/lettuce_runner.py @@ -18,17 +18,23 @@ class _LettuceRunner(_bdd_utils.BddRunner): Lettuce runner (BddRunner for lettuce) """ - def __init__(self, base_dir): + def __init__(self, base_dir, what_to_run): """ - :param base_dir base directory to run tests in - :type base_dir: str + :param base_dir base directory to run tests in + :type base_dir: str + :param what_to_run folder or file to run + :type what_to_run str """ super(_LettuceRunner, self).__init__(base_dir) - self.__runner = lettuce.Runner(base_dir) + self.__runner = lettuce.Runner(what_to_run) def _get_features_to_run(self): super(_LettuceRunner, self)._get_features_to_run() + if self.__runner.single_feature: # We need to run one and only one feature + return [core.Feature.from_file(self.__runner.single_feature)] + + # Find all features in dir features = [] for feature_file in self.__runner.loader.find_feature_files(): feature = core.Feature.from_file(feature_file) @@ -59,6 +65,8 @@ class _LettuceRunner(_bdd_utils.BddRunner): reason = step.why assert isinstance(reason, ReasonToFail), reason self._test_failed(test_name, message=reason.exception, details=reason.traceback) + elif step.has_definition: + self._test_skipped(test_name, "In lettuce, we do know the reason", step.described_at) else: self._test_undefined(test_name, step.described_at) @@ -100,4 +108,5 @@ class _LettuceRunner(_bdd_utils.BddRunner): if __name__ == "__main__": - _LettuceRunner(_bdd_utils.get_path_by_args(sys.argv)).run() \ No newline at end of file + (base_dir, what_to_run) = _bdd_utils.get_path_by_args(sys.argv) + _LettuceRunner(base_dir, what_to_run).run() \ No newline at end of file From 823c1122da34f4c5072f80f0fbdac99d222f882a Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 31 Jul 2014 22:09:35 +0400 Subject: [PATCH 55/85] Debug life cycle of the AbstractPopup objects --- .../com/intellij/ui/popup/AbstractPopup.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java index 33fd4fef996f..6daf7e6057c0 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java @@ -162,6 +162,25 @@ public class AbstractPopup implements JBPopup { private UiActivity myActivityKey; private Disposable myProjectDisposable; + private volatile State myState = State.NEW; + + private enum State {NEW, INIT, SHOWING, SHOWN, CANCEL, DISPOSE} + + private void debugState(String message, State... states) { + if (LOG.isDebugEnabled()) { + LOG.debug(hashCode() + " - " + message); + if (!ApplicationManager.getApplication().isDispatchThread()) { + LOG.debug("unexpected thread"); + } + for (State state : states) { + if (state == myState) { + return; + } + } + LOG.debug(new IllegalStateException("myState=" + myState)); + } + } + AbstractPopup() { } AbstractPopup init(Project project, @@ -302,6 +321,8 @@ public class AbstractPopup implements JBPopup { } myKeyEventHandler = keyEventHandler; + debugState("popup initialized", State.NEW); + myState = State.INIT; return this; } @@ -582,6 +603,12 @@ public class AbstractPopup implements JBPopup { @Override public void cancel(InputEvent e) { + if (myState == State.CANCEL || myState == State.DISPOSE) { + return; + } + debugState("cancel popup", State.SHOWN); + myState = State.CANCEL; + if (isDisposed()) return; if (myPopup != null) { @@ -669,6 +696,9 @@ public class AbstractPopup implements JBPopup { assert ApplicationManager.getApplication().isDispatchThread(); + debugState("show popup", State.INIT); + myState = State.SHOWING; + installWindowHook(this); installProjectDisposer(); addActivity(); @@ -678,6 +708,8 @@ public class AbstractPopup implements JBPopup { final boolean shouldShow = beforeShow(); if (!shouldShow) { removeActivity(); + debugState("rejected to show popup", State.SHOWING); + myState = State.INIT; return; } @@ -963,6 +995,8 @@ public class AbstractPopup implements JBPopup { } }); } + debugState("popup shown", State.SHOWING); + myState = State.SHOWN; } public void focusPreferredComponent() { @@ -1238,6 +1272,16 @@ public class AbstractPopup implements JBPopup { @Override public void dispose() { + if (myState == State.SHOWN) { + LOG.debug("shown popup must be cancelled"); + cancel(); + } + if (myState == State.DISPOSE) { + return; + } + debugState("dispose popup", State.INIT, State.CANCEL); + myState = State.DISPOSE; + if (myDisposed) { return; } From ededbcf242f58d69c6253162cf522972e683890e Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 31 Jul 2014 20:19:32 +0200 Subject: [PATCH 56/85] move built-in server to platform (xml module) --- .../platform-resources-en/src/messages/XmlBundle.properties | 3 +++ .../builtInWebServer/BuiltInServerConfigurableUi.form | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/platform-resources-en/src/messages/XmlBundle.properties b/platform/platform-resources-en/src/messages/XmlBundle.properties index 70e26de6ffeb..7343727c5b72 100644 --- a/platform/platform-resources-en/src/messages/XmlBundle.properties +++ b/platform/platform-resources-en/src/messages/XmlBundle.properties @@ -233,3 +233,6 @@ start.browser.with.js.debugger=with &JavaScript debugger javascript.debugger.settings.choose.file.title=Select javascript.debugger.settings.choose.file.subtitle=Select file to debug JavaScript in + +setting.value.builtin.server.port.label=Built-in server &port: +setting.value.can.accept.external.connections=Can accept &external connections \ No newline at end of file diff --git a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.form b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.form index d42b3e3d8dfe..3aa78b3be81c 100644 --- a/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.form +++ b/xml/impl/src/org/jetbrains/builtInWebServer/BuiltInServerConfigurableUi.form @@ -3,7 +3,7 @@ - + @@ -13,7 +13,7 @@ - + @@ -27,7 +27,7 @@ - + From 114392919e7648aa3d570838241c8a27660ad494 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 31 Jul 2014 20:19:22 +0200 Subject: [PATCH 57/85] Highlight "Unnecessary interface modifier" inspection like unused symbol --- .../ig/style/UnnecessaryInterfaceModifierInspection.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryInterfaceModifierInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryInterfaceModifierInspection.java index 310cc20c30b6..a0718c07fa8f 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryInterfaceModifierInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryInterfaceModifierInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.CleanupLocalInspectionTool; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; @@ -214,7 +215,7 @@ public class UnnecessaryInterfaceModifierInspection extends BaseInspection imple } for (PsiElement child : children) { if (modifiers.contains(child.getText())) { - registerError(child, redundantModifiers.toString(), list); + registerError(child, ProblemHighlightType.LIKE_UNUSED_SYMBOL, redundantModifiers.toString(), list); } } } From aadea700a718079842f1489a51af757ada1a0084 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 31 Jul 2014 21:03:02 +0200 Subject: [PATCH 58/85] Highlight "Unnecessary semicolon" inspection like unused symbol --- .../ig/style/UnnecessarySemicolonInspection.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessarySemicolonInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessarySemicolonInspection.java index c1a75887c6aa..36f5af9408df 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessarySemicolonInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessarySemicolonInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.CleanupLocalInspectionTool; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; @@ -111,7 +112,7 @@ public class UnnecessarySemicolonInspection extends BaseInspection implements Cl private void findTopLevelSemicolons(PsiElement element) { for (PsiElement sibling = element.getFirstChild(); sibling != null; sibling = skipForwardWhiteSpacesAndComments(sibling)) { if (PsiUtil.isJavaToken(sibling, JavaTokenType.SEMICOLON)) { - registerError(sibling); + registerError(sibling, ProblemHighlightType.LIKE_UNUSED_SYMBOL); } } } @@ -149,7 +150,7 @@ public class UnnecessarySemicolonInspection extends BaseInspection implements Cl if (next == null || !next.equals(aClass.getRBrace())) { return; } - registerError(element); + registerError(element, ProblemHighlightType.LIKE_UNUSED_SYMBOL); } private void findUnnecessarySemicolonsAfterEnumConstants( @@ -171,11 +172,11 @@ public class UnnecessarySemicolonInspection extends BaseInspection implements Cl if (!JavaTokenType.COMMA.equals(prevTokenType) && !JavaTokenType.LBRACE.equals( prevTokenType)) { - registerError(child); + registerError(child, ProblemHighlightType.LIKE_UNUSED_SYMBOL); } } else { - registerError(child); + registerError(child, ProblemHighlightType.LIKE_UNUSED_SYMBOL); } } } @@ -207,7 +208,7 @@ public class UnnecessarySemicolonInspection extends BaseInspection implements Cl if (semicolon == null) { return; } - registerError(semicolon); + registerError(semicolon, ProblemHighlightType.LIKE_UNUSED_SYMBOL); } } @@ -218,7 +219,7 @@ public class UnnecessarySemicolonInspection extends BaseInspection implements Cl if (last instanceof PsiJavaToken && ((PsiJavaToken)last).getTokenType() == JavaTokenType.RPARENTH) { final PsiElement prev = skipBackwardWhiteSpacesAndComments(last); if (prev instanceof PsiJavaToken && ((PsiJavaToken)prev).getTokenType() == JavaTokenType.SEMICOLON) { - registerError(prev); + registerError(prev, ProblemHighlightType.LIKE_UNUSED_SYMBOL); } } } From 998bfd56d33e34b65b62224714d3a71176292fdb Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 31 Jul 2014 21:07:19 +0200 Subject: [PATCH 59/85] Highlight "Unnecessary enum modifier" inspection like unused symbol --- .../siyeh/ig/style/UnnecessaryEnumModifierInspection.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryEnumModifierInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryEnumModifierInspection.java index 2aa72fae7bbe..7d8d9558ef53 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryEnumModifierInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/UnnecessaryEnumModifierInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.CleanupLocalInspectionTool; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.util.IncorrectOperationException; @@ -112,7 +113,7 @@ public class UnnecessaryEnumModifierInspection extends BaseInspection implements for (final PsiElement child : children) { final String text = child.getText(); if (PsiModifier.STATIC.equals(text)) { - registerError(child, child, aClass); + registerError(child, ProblemHighlightType.LIKE_UNUSED_SYMBOL, child, aClass); } } } @@ -131,7 +132,7 @@ public class UnnecessaryEnumModifierInspection extends BaseInspection implements for (final PsiElement child : children) { final String text = child.getText(); if (PsiModifier.PRIVATE.equals(text)) { - registerError(child, child, method); + registerError(child, ProblemHighlightType.LIKE_UNUSED_SYMBOL, child, method); } } } From d4df4b427ad5590c25e8f04dddece9494555fe66 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 31 Jul 2014 22:00:43 +0200 Subject: [PATCH 60/85] fix compilation (python probably will be broken, but it is ok :) --- python/src/com/jetbrains/python/run/PythonCommandLineState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java index f326c05f3aae..17fda94437d0 100644 --- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java @@ -141,7 +141,7 @@ public abstract class PythonCommandLineState extends CommandLineState { protected void addTracebackFilter(Project project, ConsoleView consoleView, ProcessHandler processHandler) { if (PySdkUtil.isRemote(myConfig.getSdk())) { assert processHandler instanceof RemoteProcessHandlerBase; - consoleView.addMessageFilter(new PyRemoteTracebackFilter(project, (RemoteProcessHandlerBase) processHandler)); + consoleView.addMessageFilter(new PyRemoteTracebackFilter(project, null, (RemoteProcessHandlerBase) processHandler)); } else { consoleView.addMessageFilter(new PythonTracebackFilter(project, myConfig.getWorkingDirectory())); From a93caa7bd996f4edc06f19fdf07dad6c4eb69aed Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Fri, 1 Aug 2014 00:53:07 +0400 Subject: [PATCH 61/85] PY-13577 Behave support doesn't work with Chinese --- python/helpers/pycharm/behave_runner.py | 11 +++++--- .../python/psi/PyElementGenerator.java | 25 ++++++++++++------- .../psi/impl/PyElementGeneratorImpl.java | 11 +++++--- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/python/helpers/pycharm/behave_runner.py b/python/helpers/pycharm/behave_runner.py index 933588aa444a..e66805cfafb2 100644 --- a/python/helpers/pycharm/behave_runner.py +++ b/python/helpers/pycharm/behave_runner.py @@ -219,15 +219,20 @@ if __name__ == "__main__": Null formater to prevent stdout output """ pass - my_config = configuration.Configuration(command_args=filter(None, sys.argv[1:])) + + command_args = list(filter(None, sys.argv[1:])) + my_config = configuration.Configuration(command_args=command_args) formatters.register_as(_Null, "com.intellij.python.null") my_config.format = ["com.intellij.python.null"] # To prevent output to stdout my_config.reporters = [] # To prevent summary to stdout my_config.stdout_capture = False # For test output my_config.stderr_capture = False # For test output (base_dir, what_to_run) = _bdd_utils.get_path_by_args(sys.argv) - if not my_config.paths: # No path provided, trying to load dir - my_config.paths = _get_dirs_to_run(base_dir) + if not my_config.paths: # No path provided, trying to load dit manually + if os.path.isfile(what_to_run): # File is provided, load it + my_config.paths = [what_to_run] + else: # Dir is provided, find subdirs ro run + my_config.paths = _get_dirs_to_run(base_dir) _BehaveRunner(my_config, base_dir).run() diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java index 197f7536e121..daa8b3063014 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java @@ -37,15 +37,17 @@ public abstract class PyElementGenerator { public abstract PyStringLiteralExpression createStringLiteralAlreadyEscaped(String str); - - /** * Creates a string literal, adding appropriate quotes, properly escaping characters inside. + * * @param destination where the literal is destined to; used to determine the encoding. * @param unescaped the string - * @return a newly created literal + * @param preferUTF8 try to use UTF8 (would use ascii if false) + * @return a newly created literal */ - public abstract PyStringLiteralExpression createStringLiteralFromString(@Nullable PsiFile destination, String unescaped); + public abstract PyStringLiteralExpression createStringLiteralFromString(@Nullable PsiFile destination, String unescaped, + boolean preferUTF8); + public abstract PyStringLiteralExpression createStringLiteralFromString(@NotNull String unescaped); public abstract PyStringLiteralExpression createStringLiteral(@NotNull PyStringLiteralExpression oldElement, @NotNull String unescaped); @@ -59,11 +61,11 @@ public abstract class PyElementGenerator { public abstract PyBinaryExpression createBinaryExpression(String s, PyExpression expr, PyExpression listLiteral); /** - * @deprecated use the overload with language level specified * @param text the text to create an expression from * @return the expression + * @deprecated use the overload with language level specified */ - public abstract PyExpression createExpressionFromText(String text); + public abstract PyExpression createExpressionFromText(String text); public abstract PyExpression createExpressionFromText(final LanguageLevel languageLevel, String text); @@ -71,9 +73,9 @@ public abstract class PyElementGenerator { * Adds elements to list inserting required commas. * Method is like {@link #insertItemIntoList(PyElement, PyExpression, PyExpression)} but does not add unneeded commas. * - * @param list where to add + * @param list where to add * @param afterThis after which element it should be added (null for add to the head) - * @param toInsert what to insert + * @param toInsert what to insert * @return newly inserted element */ @NotNull @@ -92,7 +94,10 @@ public abstract class PyElementGenerator { public abstract PyImportElement createImportElement(final LanguageLevel languageLevel, String name); - public abstract PyFunction createProperty(final LanguageLevel languageLevel, String propertyName, String fieldName, AccessDirection accessDirection); + public abstract PyFunction createProperty(final LanguageLevel languageLevel, + String propertyName, + String fieldName, + AccessDirection accessDirection); @NotNull public abstract T createFromText(LanguageLevel langLevel, Class aClass, final String text); @@ -103,6 +108,7 @@ public abstract class PyElementGenerator { /** * Creates an arbitrary PSI element from text, by creating a bigger construction and then cutting the proper subelement. * Will produce all kinds of exceptions if the path or class would not match the PSI tree. + * * @param langLevel the language level to use for parsing the text * @param aClass class of the PSI element; may be an interface not descending from PsiElement, as long as target node can be cast to it * @param text text to parse @@ -122,6 +128,7 @@ public abstract class PyElementGenerator { public abstract PsiFile createDummyFile(LanguageLevel langLevel, String contents); public abstract PyExpressionStatement createDocstring(String content); + public abstract PyPassStatement createPassStatement(); @NotNull diff --git a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java index 0fa3a4dff6b9..bb0e29f800a7 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java @@ -87,8 +87,9 @@ public class PyElementGeneratorImpl extends PyElementGenerator { } + @Override public PyStringLiteralExpression createStringLiteralFromString(@NotNull String unescaped) { - return createStringLiteralFromString(null, unescaped); + return createStringLiteralFromString(null, unescaped, true); } public PyStringLiteralExpression createStringLiteral(@NotNull PyStringLiteralExpression oldElement, @NotNull String unescaped) { @@ -101,7 +102,11 @@ public class PyElementGeneratorImpl extends PyElementGenerator { } } - public PyStringLiteralExpression createStringLiteralFromString(@Nullable PsiFile destination, @NotNull String unescaped) { + + @Override + public PyStringLiteralExpression createStringLiteralFromString(@Nullable PsiFile destination, + @NotNull String unescaped, + final boolean preferUTF8) { boolean useDouble = !unescaped.contains("\""); boolean useMulti = unescaped.matches(".*(\r|\n).*"); String quotes; @@ -116,7 +121,7 @@ public class PyElementGeneratorImpl extends PyElementGenerator { VirtualFile vfile = destination == null ? null : destination.getVirtualFile(); Charset charset; if (vfile == null) { - charset = Charset.forName("US-ASCII"); + charset = (preferUTF8 ? Charset.forName("UTF-8") : Charset.forName("US-ASCII")); } else { charset = vfile.getCharset(); From d178ff9935e6b5cbeaa318bc0f82d18c4422551c Mon Sep 17 00:00:00 2001 From: Sergey Simonchik Date: Fri, 1 Aug 2014 01:02:04 +0400 Subject: [PATCH 62/85] description text simplified --- .../intellij/webcore/packaging/PackagesNotificationPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index 8b913f00baec..1513bb451aa2 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -87,7 +87,7 @@ public class PackagesNotificationPanel { if (packageName != null) { title = "Failed to install package '" + packageName + "'"; } - String firstLine = title + ": Error occurred when installing package " + packageName + ". "; + String firstLine = "Error occurred when installing package '" + packageName + "'. "; showError(firstLine + "Details...", title, firstLine + errorDescription); From 6109285882ffbaf90a0a6f6818a413de26aea363 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 1 Aug 2014 12:02:49 +0400 Subject: [PATCH 63/85] IDEA-126930 Schemas & DTDs - External & Ignored Changes Not Saved --- .../com/intellij/javaee/ExternalResourceManagerExImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java b/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java index 2eee2882613e..d030ccf9f189 100644 --- a/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java +++ b/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java @@ -47,7 +47,7 @@ import java.util.*; @State(name = "ExternalResourceManagerImpl", storages = {@Storage( file = StoragePathMacros.APP_CONFIG + "/other.xml")}) -public class ExternalResourceManagerExImpl extends ExternalResourceManagerEx { +public class ExternalResourceManagerExImpl extends ExternalResourceManagerEx implements JDOMExternalizable { static final Logger LOG = Logger.getInstance("#com.intellij.j2ee.openapi.impl.ExternalResourceManagerImpl"); @NonNls public static final String J2EE_1_3 = "http://java.sun.com/dtd/"; @@ -374,6 +374,7 @@ public class ExternalResourceManagerExImpl extends ExternalResourceManagerEx { return getProjectResources(project).getModificationCount(); } + @Override public void readExternal(Element element) { final ExpandMacroToPathMap macroExpands = new ExpandMacroToPathMap(); myPathMacros.addMacroExpands(macroExpands); @@ -404,6 +405,7 @@ public class ExternalResourceManagerExImpl extends ExternalResourceManagerEx { } } + @Override public void writeExternal(Element element) { final String[] urls = getAvailableUrls(); for (String url : urls) { From 0f67de265f4a1f6529001784b356ef4158a90b62 Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Thu, 31 Jul 2014 16:31:37 +0400 Subject: [PATCH 64/85] editor scrollbar: use ButtonlessScrollBarUI.isMacOverlayScrollbarSupported instead of SystemInfo.isMac --- .../openapi/editor/impl/EditorMarkupModelImpl.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index f89d92ffd026..3bd4b270f31d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -46,7 +46,6 @@ import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.ProperTextRange; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; @@ -439,7 +438,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark errorIconBounds.y = bounds.height / 2 - errorIconBounds.height / 2; try { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) { + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) { g.setColor(getEditor().getColorsScheme().getDefaultBackground()); g.fillRect(0, 0, bounds.width, bounds.height); @@ -530,7 +529,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark @Override protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) { + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) { super.paintThumb(g, c, thumbBounds); return; } @@ -567,13 +566,13 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark @Override protected int getThickness() { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) return super.getThickness(); + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) return super.getThickness(); return super.getThickness() + (isMacOverlayScrollbar() ? 2 : 7); } @Override protected void doPaintTrack(Graphics g, JComponent c, Rectangle bounds) { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) { + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) { g.setColor(getEditor().getColorsScheme().getDefaultBackground()); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); //return; @@ -611,7 +610,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } private void paintTrackBasement(Graphics g, Rectangle bounds) { - if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) { + if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) { g.setColor(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground()); g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); return; From 3bad0ee57d3fa073bb37dbea2dc8be855fe209e2 Mon Sep 17 00:00:00 2001 From: Sergey Ignatov Date: Thu, 31 Jul 2014 16:31:37 +0400 Subject: [PATCH 65/85] notnull --- .../editor/impl/EditorMarkupModelImpl.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index 3bd4b270f31d..e276fe2cd4da 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -429,7 +429,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void paint(Graphics g) { + public void paint(@NotNull Graphics g) { ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart(); final Rectangle bounds = getBounds(); @@ -494,7 +494,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void uninstallUI(JComponent c) { + public void uninstallUI(@NotNull JComponent c) { super.uninstallUI(c); myCachedTrack = null; } @@ -650,13 +650,13 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark private void drawMarkup(final Graphics g, final int width, int startOffset, int endOffset, MarkupModelEx markup) { final Queue thinEnds = new PriorityQueue(5, new Comparator() { @Override - public int compare(PositionedStripe o1, PositionedStripe o2) { + public int compare(@NotNull PositionedStripe o1, @NotNull PositionedStripe o2) { return o1.yEnd - o2.yEnd; } }); final Queue wideEnds = new PriorityQueue(5, new Comparator() { @Override - public int compare(PositionedStripe o1, PositionedStripe o2) { + public int compare(@NotNull PositionedStripe o1, @NotNull PositionedStripe o2) { return o1.yEnd - o2.yEnd; } }); @@ -798,7 +798,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark // mouse events @Override - public void mouseClicked(final MouseEvent e) { + public void mouseClicked(@NotNull final MouseEvent e) { CommandProcessor.getInstance().executeCommand(myEditor.getProject(), new Runnable() { @Override public void run() { @@ -812,11 +812,11 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void mousePressed(MouseEvent e) { + public void mousePressed(@NotNull MouseEvent e) { } @Override - public void mouseReleased(MouseEvent e) { + public void mouseReleased(@NotNull MouseEvent e) { } private int getWidth() { @@ -835,7 +835,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void mouseMoved(MouseEvent e) { + public void mouseMoved(@NotNull MouseEvent e) { EditorImpl.MyScrollBar scrollBar = myEditor.getVerticalScrollBar(); int buttonHeight = scrollBar.getDecScrollButtonHeight(); int lineCount = getDocument().getLineCount() + myEditor.getSettings().getAdditionalLinesCount(); @@ -908,16 +908,16 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - public void mouseEntered(MouseEvent e) { + public void mouseEntered(@NotNull MouseEvent e) { } @Override - public void mouseExited(MouseEvent e) { + public void mouseExited(@NotNull MouseEvent e) { cancelMyToolTips(e, true); } @Override - public void mouseDragged(MouseEvent e) { + public void mouseDragged(@NotNull MouseEvent e) { cancelMyToolTips(e, true); } @@ -1148,7 +1148,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark myHighlighters.add(rangeHighlighter); } Collections.sort(myHighlighters, new Comparator() { - public int compare(RangeHighlighterEx ex1, RangeHighlighterEx ex2) { + public int compare(@NotNull RangeHighlighterEx ex1, @NotNull RangeHighlighterEx ex2) { LogicalPosition startPos1 = myEditor.offsetToLogicalPosition(ex1.getAffectedAreaStartOffset()); LogicalPosition startPos2 = myEditor.offsetToLogicalPosition(ex2.getAffectedAreaStartOffset()); if (startPos1.line != startPos2.line) return 0; @@ -1178,7 +1178,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark } @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { if (myVisualLine ==-1) return; Dimension size = getPreferredSize(); EditorGutterComponentEx gutterComponentEx = myEditor.getGutterComponentEx(); From a4d54fa1d5125904af2b5805f54209aeb68cbeaa Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 1 Aug 2014 12:59:53 +0400 Subject: [PATCH 66/85] Use debugger variable value as a default text in search in path --- .../find/findInProject/FindInProjectManager.java | 11 +++++++++-- .../xdebugger/impl/ui/tree/XDebuggerTree.java | 11 +++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java b/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java index 7bc0e0c595f8..d7f7665a9299 100644 --- a/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java +++ b/platform/lang-impl/src/com/intellij/find/findInProject/FindInProjectManager.java @@ -25,6 +25,7 @@ import com.intellij.find.impl.FindManagerImpl; import com.intellij.find.replaceInProject.ReplaceInProjectManager; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -71,8 +72,14 @@ public class FindInProjectManager { findModel.setOpenInNewTab(toOpenInNewTab[0]); FindInProjectUtil.setDirectoryName(findModel, dataContext); - Editor editor = CommonDataKeys.EDITOR.getData(dataContext); - FindUtil.initStringToFindWithSelection(findModel, editor); + String text = PlatformDataKeys.PREDEFINED_TEXT.getData(dataContext); + if (text != null) { + FindModel.initStringToFindNoMultiline(findModel, text); + } + else { + Editor editor = CommonDataKeys.EDITOR.getData(dataContext); + FindUtil.initStringToFindWithSelection(findModel, editor); + } findManager.showFindDialog(findModel, new Runnable() { @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java index d097b09ac3ac..fe32920a7ac6 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTree.java @@ -35,10 +35,7 @@ import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; import com.intellij.xdebugger.impl.actions.XDebuggerActions; import com.intellij.xdebugger.impl.frame.XValueMarkers; -import com.intellij.xdebugger.impl.ui.tree.nodes.MessageTreeNode; -import com.intellij.xdebugger.impl.ui.tree.nodes.RestorableStateNode; -import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; -import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode; +import com.intellij.xdebugger.impl.ui.tree.nodes.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -254,6 +251,12 @@ public class XDebuggerTree extends DnDAwareTree implements DataProvider, Disposa if (XDEBUGGER_TREE_KEY.is(dataId)) { return this; } + if (PlatformDataKeys.PREDEFINED_TEXT.is(dataId)) { + XValueNodeImpl[] selectedNodes = getSelectedNodes(XValueNodeImpl.class, null); + if (selectedNodes.length == 1 && selectedNodes[0].getFullValueEvaluator() == null) { + return selectedNodes[0].getRawValue(); + } + } return null; } From facd767c24a11273986e50fe3da45aa4672412ad Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Fri, 1 Aug 2014 13:12:01 +0400 Subject: [PATCH 67/85] IDEA-118403 Fixed immediate children processing (notify change list builder) while requesting svn status with "immediates" depth for unversioned directory (currently when file is marked dirty svn4idea requests status for its parent directory with "immediates" depth - so for unversioned directories we need to manually process immediate children as "svn status" does not provide then in result) --- .../idea/svn/SvnRecursiveStatusWalker.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java index 1b1cd00589d0..dae523569c51 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnRecursiveStatusWalker.java @@ -184,15 +184,20 @@ public class SvnRecursiveStatusWalker { } }; if (Depth.EMPTY.equals(newDepth)) { - directoryFilter = Processor.TRUE; + // just process immediate children - so only root directory itself should satisfy filter + directoryFilter = new Processor() { + @Override + public boolean process(File file) { + return FileUtil.filesEqual(ioFile, file); + } + }; processor = new Processor() { @Override public boolean process(File file) { - // here we deal only with immediate children - so ignored on IDEA level for children is not important - we nevertheless do not go into - // other levels - if (! FileUtil.filesEqual(ioFile, file)) return true; - if (! FileUtil.filesEqual(ioFile, file.getParentFile())) return false; - return checkDirProcessor.process(file); + // TODO: check if we should still call checkDirProcessor() here - or we really could not check ignore settings but just call + // TODO: myReceiver.processUnversioned() for all immediate children + // here we deal only with immediate children - so ignored on IDEA level for children is not important + return FileUtil.filesEqual(ioFile, file) || checkDirProcessor.process(file); } }; } else { From 94cefafd3c309dec3ccd4d4e0cac7298cc2ee495 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 1 Aug 2014 13:03:35 +0400 Subject: [PATCH 68/85] diff: rollback changed lines - add shortcut --- .../src/com/intellij/openapi/actionSystem/IdeActions.java | 2 +- .../src/messages/ActionsBundle.properties | 4 ++-- platform/platform-resources/src/idea/Keymap_Default.xml | 3 +++ platform/platform-resources/src/idea/VcsActions.xml | 2 +- .../intellij/openapi/vcs/ex/RollbackLineStatusAction.java | 5 ++++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java index 6ade9b77afc0..049bde52f582 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/IdeActions.java @@ -271,7 +271,7 @@ public interface IdeActions { String ACTION_UNDO = "$Undo"; String ACTION_REDO = "$Redo"; String GROUP_REFACTOR = "RefactoringMenu"; - String SELECTED_CHANGES_ROLLBACK = "RollbackLineStatusChanges"; + String SELECTED_CHANGES_ROLLBACK = "Vcs.RollbackChangedLines"; String CHANGES_VIEW_ROLLBACK = "ChangesView.Rollback"; String CONSOLE_CLEAR_ALL = "ConsoleView.ClearAll"; diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 01179dca9e7b..47c4cef92207 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1016,8 +1016,8 @@ action.Vcs.ShowDiffAction.text=Show Changes action.Vcs.ShowDiffAction.description=Show changes action.Vcs.RollbackChanges.text=Rollback Changes action.Vcs.RollbackChanges.description=Rollback changes -action.RollbackLineStatusChanges.text=Rollback -action.RollbackLineStatusChanges.description=Rollback selected local changes +action.Vcs.RollbackChangedLines.text=Rollback +action.Vcs.RollbackChangedLines.description=Rollback changes in selected lines action.Vcs.EditSourceAction.text=Edit Source action.Vcs.EditSourceAction.description=Edit source action.Vcs.ExcludeAction.text=Exclude from Commit diff --git a/platform/platform-resources/src/idea/Keymap_Default.xml b/platform/platform-resources/src/idea/Keymap_Default.xml index 58d6f3a76d27..3508b1d0112e 100644 --- a/platform/platform-resources/src/idea/Keymap_Default.xml +++ b/platform/platform-resources/src/idea/Keymap_Default.xml @@ -506,6 +506,9 @@ + + + diff --git a/platform/platform-resources/src/idea/VcsActions.xml b/platform/platform-resources/src/idea/VcsActions.xml index dc15f19282bf..5496ab6bf3e7 100644 --- a/platform/platform-resources/src/idea/VcsActions.xml +++ b/platform/platform-resources/src/idea/VcsActions.xml @@ -265,7 +265,7 @@ - + Date: Fri, 1 Aug 2014 13:17:56 +0400 Subject: [PATCH 69/85] Gradle: do not cache compiled gradle init-scripts, fixed classpath for out-of-process invocation mode, fixed runtime classpath of the gradle plugin --- build/scripts/layouts.gant | 8 +++++++- .../project/BaseGradleProjectResolverExtension.java | 2 ++ .../gradle/tooling/builder/AbstractModelBuilderTest.java | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index 1aec74e2aa43..f91ac85d7449 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -348,7 +348,13 @@ public def layoutCommunityPlugins(String home) { module("gradle-tooling-extension-impl") } fileset(dir: "$home/plugins/gradle/lib") { include(name: "*.jar") } - fileset(dir: "$home/lib/") { include(name: "kryo-*.jar") } + // add kryo lib + fileset(dir: "$home/lib/") { + include(name: "kryo-*.jar") + include(name: "reflectasm-*.jar") + include(name: "objenesis-*.jar") + include(name: "minlog-*.jar") + } } layoutPlugin("git4idea") { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index 877432457336..ad7ade6a3c58 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -471,6 +471,8 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver List additionalEntries = ContainerUtilRt.newArrayList(); ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(GroovyObject.class)); + ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(GsonBuilder.class)); + ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(ExternalProject.class)); ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(JavaProjectData.class)); ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(LanguageLevel.class)); ContainerUtilRt.addIfNotNull(additionalEntries, PathUtil.getJarPathForClass(StdModuleTypes.class)); diff --git a/plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java b/plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java index d09a9b7c5e3c..faf3a98bc3a4 100644 --- a/plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java +++ b/plugins/gradle/tooling-extension-impl/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.gradle.tooling.builder; +import com.intellij.openapi.externalSystem.model.ExternalProject; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.testFramework.UsefulTestCase; @@ -127,7 +128,7 @@ public abstract class AbstractModelBuilderTest { BuildActionExecuter buildActionExecutor = connection.action(projectImportAction); File initScript = GradleExecutionHelper.generateInitScript(false, getToolingExtensionClasses()); assertNotNull(initScript); - buildActionExecutor.withArguments(GradleConstants.INIT_SCRIPT_CMD_OPTION, initScript.getAbsolutePath()); + buildActionExecutor.withArguments("--recompile-scripts", GradleConstants.INIT_SCRIPT_CMD_OPTION, initScript.getAbsolutePath()); allModels = buildActionExecutor.run(); assertNotNull(allModels); } @@ -135,6 +136,7 @@ public abstract class AbstractModelBuilderTest { @NotNull private Set getToolingExtensionClasses() { final Set classes = ContainerUtil.set( + ExternalProject.class, // gradle-tooling-extension-api jar ProjectImportAction.class, // gradle-tooling-extension-impl jar From 987230f1ed10662ac1ad3912e6728c36bac9c581 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 29 Jul 2014 17:12:28 +0400 Subject: [PATCH 70/85] [vcs] Make VcsLogSingleCommitAction support one-commit-per-repo multiselection mode Keep existing actions to be single commit for now. --- .../dvcs/ui/VcsLogSingleCommitAction.java | 74 +++++++++++++++---- .../actions/GitLogSingleCommitAction.java | 20 +++++ .../action/HgLogSingleCommitAction.java | 21 ++++++ 3 files changed, 102 insertions(+), 13 deletions(-) diff --git a/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogSingleCommitAction.java b/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogSingleCommitAction.java index 1e20f2ceac11..14ccc7652cf0 100644 --- a/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogSingleCommitAction.java +++ b/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogSingleCommitAction.java @@ -23,27 +23,34 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import com.intellij.vcs.log.VcsFullCommitDetails; import com.intellij.vcs.log.VcsLog; import com.intellij.vcs.log.VcsLogDataKeys; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.List; +import java.util.Map; public abstract class VcsLogSingleCommitAction extends DumbAwareAction { + protected enum Mode { + SINGLE_COMMIT, + SINGLE_PER_REPO + } + @Override public void actionPerformed(AnActionEvent e) { Project project = e.getRequiredData(CommonDataKeys.PROJECT); VcsLog log = e.getRequiredData(VcsLogDataKeys.VSC_LOG); List details = log.getSelectedDetails(); - assert details.size() == 1; - VcsFullCommitDetails commit = details.get(0); - Repo repository = getRepositoryForRoot(project, commit.getRoot()); - assert repository != null; - - actionPerformed(repository, commit); + MultiMap grouped = groupByRoot(project, details); + assert grouped != null; + Map singleElementMap = convertToSingleElementMap(grouped); + assert singleElementMap != null; + actionPerformed(singleElementMap); } @Override @@ -56,27 +63,68 @@ public abstract class VcsLogSingleCommitAction extends } List details = log.getSelectedDetails(); - if (containsCommitFromOtherVcs(project, details)) { + MultiMap grouped = groupByRoot(project, details); + if (grouped == null) { e.getPresentation().setEnabledAndVisible(false); } else { e.getPresentation().setVisible(true); - e.getPresentation().setEnabled(details.size() == 1); + e.getPresentation().setEnabled(isEnabled(grouped)); } } + private boolean isEnabled(@NotNull MultiMap grouped) { + Mode mode = getMode(); + if (mode == Mode.SINGLE_COMMIT) { + return grouped.size() == 1; + } + if (mode == Mode.SINGLE_PER_REPO) { + return allValuesAreSingletons(grouped); + } + return false; + } + @Nullable protected abstract Repo getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root); - protected abstract void actionPerformed(@NotNull Repo repository, @NotNull VcsFullCommitDetails commit); + protected abstract void actionPerformed(@NotNull Map commits); - private boolean containsCommitFromOtherVcs(@NotNull final Project project, @NotNull List details) { - return ContainerUtil.exists(details, new Condition() { + @NotNull + protected abstract Mode getMode(); + + @Nullable + private MultiMap groupByRoot(@NotNull Project project, @NotNull List commits) { + MultiMap map = MultiMap.create(); + for (VcsFullCommitDetails commit : commits) { + Repo root = getRepositoryForRoot(project, commit.getRoot()); + if (root == null) { // commit from some other VCS + return null; + } + map.putValue(root, commit); + } + return map; + } + + private boolean allValuesAreSingletons(@NotNull MultiMap grouped) { + return !ContainerUtil.exists(grouped.entrySet(), new Condition>>() { @Override - public boolean value(VcsFullCommitDetails details) { - return getRepositoryForRoot(project, details.getRoot()) == null; + public boolean value(Map.Entry> entry) { + return entry.getValue().size() != 1; } }); } + @Nullable + private Map convertToSingleElementMap(@NotNull MultiMap groupedCommits) { + Map map = ContainerUtil.newHashMap(); + for (Map.Entry> entry : groupedCommits.entrySet()) { + Collection commits = entry.getValue(); + if (commits.size() != 1) { + return null; + } + map.put(entry.getKey(), commits.iterator().next()); + } + return map; + } + } diff --git a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java index 83512de71d1f..f946748f21d1 100644 --- a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java @@ -19,11 +19,14 @@ import com.intellij.dvcs.ui.VcsLogSingleCommitAction; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.vcs.log.VcsFullCommitDetails; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; + public abstract class GitLogSingleCommitAction extends VcsLogSingleCommitAction { @Override @@ -32,4 +35,21 @@ public abstract class GitLogSingleCommitAction extends VcsLogSingleCommitAction< return ServiceManager.getService(project, GitRepositoryManager.class).getRepositoryForRoot(root); } + @NotNull + @Override + protected Mode getMode() { + return Mode.SINGLE_COMMIT; + } + + @Override + protected void actionPerformed(@NotNull Map commits) { + assert commits.size() == 1; + Map.Entry entry = commits.entrySet().iterator().next(); + GitRepository repository = entry.getKey(); + VcsFullCommitDetails commit = entry.getValue(); + actionPerformed(repository, commit); + } + + protected abstract void actionPerformed(@NotNull GitRepository repository, @NotNull VcsFullCommitDetails commit); + } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java index 415e79be5a3b..87a0e5e1b296 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java @@ -20,11 +20,14 @@ import com.intellij.dvcs.ui.VcsLogSingleCommitAction; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.vcs.log.VcsFullCommitDetails; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.repo.HgRepositoryManager; +import java.util.Map; + public abstract class HgLogSingleCommitAction extends VcsLogSingleCommitAction { @Nullable @@ -32,4 +35,22 @@ public abstract class HgLogSingleCommitAction extends VcsLogSingleCommitAction commits) { + assert commits.size() == 1; + Map.Entry entry = commits.entrySet().iterator().next(); + HgRepository repository = entry.getKey(); + VcsFullCommitDetails commit = entry.getValue(); + actionPerformed(repository, commit); + } + + protected abstract void actionPerformed(@NotNull HgRepository repository, @NotNull VcsFullCommitDetails commit); + } From f25d98f78d684bed8e94a2576923c30446ce592a Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 29 Jul 2014 17:13:29 +0400 Subject: [PATCH 71/85] [vcs] Rename to more generic name --- .../ui/{VcsLogSingleCommitAction.java => VcsLogAction.java} | 2 +- .../src/git4idea/actions/GitLogSingleCommitAction.java | 4 ++-- .../src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename platform/dvcs/src/com/intellij/dvcs/ui/{VcsLogSingleCommitAction.java => VcsLogAction.java} (97%) diff --git a/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogSingleCommitAction.java b/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogAction.java similarity index 97% rename from platform/dvcs/src/com/intellij/dvcs/ui/VcsLogSingleCommitAction.java rename to platform/dvcs/src/com/intellij/dvcs/ui/VcsLogAction.java index 14ccc7652cf0..c04e30fcc774 100644 --- a/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogSingleCommitAction.java +++ b/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogAction.java @@ -34,7 +34,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; -public abstract class VcsLogSingleCommitAction extends DumbAwareAction { +public abstract class VcsLogAction extends DumbAwareAction { protected enum Mode { SINGLE_COMMIT, diff --git a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java index f946748f21d1..605ea86759d0 100644 --- a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java @@ -15,7 +15,7 @@ */ package git4idea.actions; -import com.intellij.dvcs.ui.VcsLogSingleCommitAction; +import com.intellij.dvcs.ui.VcsLogAction; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -27,7 +27,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Map; -public abstract class GitLogSingleCommitAction extends VcsLogSingleCommitAction { +public abstract class GitLogSingleCommitAction extends VcsLogAction { @Override @Nullable diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java index 87a0e5e1b296..dd7c7dc14e5c 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java @@ -16,7 +16,7 @@ package org.zmlx.hg4idea.action; -import com.intellij.dvcs.ui.VcsLogSingleCommitAction; +import com.intellij.dvcs.ui.VcsLogAction; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; @@ -28,7 +28,7 @@ import org.zmlx.hg4idea.repo.HgRepositoryManager; import java.util.Map; -public abstract class HgLogSingleCommitAction extends VcsLogSingleCommitAction { +public abstract class HgLogSingleCommitAction extends VcsLogAction { @Nullable @Override From 3aa8c01c7b19619e2ffb231ef880f8d7c8767320 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Tue, 29 Jul 2014 21:06:35 +0400 Subject: [PATCH 72/85] [git] IDEA-80300 Implement Git reset from log * Introduce new action & new dialog. Later it will be unified with the existing action & dialog. * Generalize Git#resetHard to accept reset mode, but keep resetMerge separate to avoid extra constant in GitResetMode, that shouldn't be exposed to the user. * Implement GitResetOperation on a basic level: just execute the command for each root & collect results. Better support (handling some special situations) will or maybe implemented further. --- .../com/intellij/dvcs/ui/VcsLogAction.java | 4 +- plugins/git4idea/src/META-INF/plugin.xml | 3 + .../actions/GitLogSingleCommitAction.java | 2 +- .../git4idea/branch/GitMergeOperation.java | 3 +- .../git4idea/src/git4idea/commands/Git.java | 3 +- .../src/git4idea/commands/GitImpl.java | 18 ++- .../src/git4idea/config/GitVcsSettings.java | 11 ++ .../src/git4idea/reset/GitNewResetDialog.java | 142 ++++++++++++++++++ .../src/git4idea/reset/GitResetAction.java | 71 +++++++++ .../src/git4idea/reset/GitResetMode.java | 59 ++++++++ .../src/git4idea/reset/GitResetOperation.java | 141 +++++++++++++++++ .../action/HgLogSingleCommitAction.java | 2 +- 12 files changed, 446 insertions(+), 13 deletions(-) create mode 100644 plugins/git4idea/src/git4idea/reset/GitNewResetDialog.java create mode 100644 plugins/git4idea/src/git4idea/reset/GitResetAction.java create mode 100644 plugins/git4idea/src/git4idea/reset/GitResetMode.java create mode 100644 plugins/git4idea/src/git4idea/reset/GitResetOperation.java diff --git a/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogAction.java b/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogAction.java index c04e30fcc774..d8f4012f1b0e 100644 --- a/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogAction.java +++ b/platform/dvcs/src/com/intellij/dvcs/ui/VcsLogAction.java @@ -50,7 +50,7 @@ public abstract class VcsLogAction extends DumbAwareAct assert grouped != null; Map singleElementMap = convertToSingleElementMap(grouped); assert singleElementMap != null; - actionPerformed(singleElementMap); + actionPerformed(project, singleElementMap); } @Override @@ -87,7 +87,7 @@ public abstract class VcsLogAction extends DumbAwareAct @Nullable protected abstract Repo getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root); - protected abstract void actionPerformed(@NotNull Map commits); + protected abstract void actionPerformed(@NotNull Project project, @NotNull Map commits); @NotNull protected abstract Mode getMode(); diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index d0a467a709c6..b8fd645a12fa 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -98,6 +98,7 @@ description="Create new branch starting from the selected commit"/> + @@ -105,6 +106,8 @@ + + diff --git a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java index 605ea86759d0..2ddbed7e5478 100644 --- a/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitLogSingleCommitAction.java @@ -42,7 +42,7 @@ public abstract class GitLogSingleCommitAction extends VcsLogAction commits) { + protected void actionPerformed(@NotNull Project project, @NotNull Map commits) { assert commits.size() == 1; Map.Entry entry = commits.entrySet().iterator().next(); GitRepository repository = entry.getKey(); diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java index bf14a6050cac..cc7065575d61 100644 --- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java @@ -34,6 +34,7 @@ import git4idea.commands.*; import git4idea.merge.GitMergeCommittingConflictResolver; import git4idea.merge.GitMerger; import git4idea.repo.GitRepository; +import git4idea.reset.GitResetMode; import git4idea.util.GitPreservingProcess; import org.jetbrains.annotations.NotNull; @@ -323,7 +324,7 @@ class GitMergeOperation extends GitBranchOperation { @NotNull private GitCommandResult rollback(@NotNull GitRepository repository) { - return myGit.resetHard(repository, myCurrentRevisionsBeforeMerge.get(repository)); + return myGit.reset(repository, GitResetMode.HARD, myCurrentRevisionsBeforeMerge.get(repository)); } @NotNull diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java index 284ccc1e20c5..bc0678c41728 100644 --- a/plugins/git4idea/src/git4idea/commands/Git.java +++ b/plugins/git4idea/src/git4idea/commands/Git.java @@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitCommit; import git4idea.push.GitPushSpec; import git4idea.repo.GitRepository; +import git4idea.reset.GitResetMode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -93,7 +94,7 @@ public interface Git { GitCommandResult branchCreate(@NotNull GitRepository repository, @NotNull String branchName); @NotNull - GitCommandResult resetHard(@NotNull GitRepository repository, @NotNull String revision); + GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target); @NotNull GitCommandResult resetMerge(@NotNull GitRepository repository, @Nullable String revision); diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java index c873e71ff94b..9f91bbc4f0e2 100644 --- a/plugins/git4idea/src/git4idea/commands/GitImpl.java +++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java @@ -32,6 +32,7 @@ import git4idea.history.GitHistoryUtils; import git4idea.push.GitPushSpec; import git4idea.repo.GitRemote; import git4idea.repo.GitRepository; +import git4idea.reset.GitResetMode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -338,19 +339,22 @@ public class GitImpl implements Git { @Override @NotNull - public GitCommandResult resetHard(@NotNull GitRepository repository, @NotNull String revision) { - final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.RESET); - handler.addParameters("--hard", revision); - return run(handler); + public GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target) { + return reset(repository, mode.getArgument(), target); } @Override @NotNull public GitCommandResult resetMerge(@NotNull GitRepository repository, @Nullable String revision) { + return reset(repository, "--merge", revision); + } + + @NotNull + private static GitCommandResult reset(@NotNull GitRepository repository, @NotNull String argument, @Nullable String target) { final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.RESET); - handler.addParameters("--merge"); - if (revision != null) { - handler.addParameters(revision); + handler.addParameters(argument); + if (target != null) { + handler.addParameters(target); } return run(handler); } diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java index 4f74f7ca94f3..1b5860631e59 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java @@ -19,6 +19,7 @@ import com.intellij.lifecycle.PeriodicalTasksCloser; import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.ArrayUtil; +import git4idea.reset.GitResetMode; import git4idea.ui.branch.GitBranchSyncSetting; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -61,6 +62,7 @@ public class GitVcsSettings implements PersistentStateComponent myCommits; + @NotNull private final GitResetMode myDefaultMode; + @NotNull private final ButtonGroup myButtonGroup; + + private RadioButtonEnumModel myEnumModel; + + protected GitNewResetDialog(@NotNull Project project, @NotNull Map commits, + @NotNull GitResetMode defaultMode) { + super(project); + myProject = project; + myCommits = commits; + myDefaultMode = defaultMode; + myButtonGroup = new ButtonGroup(); + + init(); + setTitle("Git Reset"); + setOKButtonText("Reset"); + setOKButtonMnemonic('R'); + setResizable(false); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + JPanel panel = new JPanel(new GridBagLayout()); + GridBag gb = new GridBag(). + setDefaultAnchor(GridBagConstraints.LINE_START). + setDefaultInsets(0, UIUtil.DEFAULT_HGAP, UIUtil.LARGE_VGAP, 0); + + String description = prepareDescription(myProject, myCommits); + panel.add(new JBLabel(XmlStringUtil.wrapInHtml(description)), gb.nextLine().next().coverLine()); + + String explanation = "This will reset the current branch head to the selected commit,
" + + "and update the working tree and the index according to the selected mode:"; + panel.add(new JBLabel(XmlStringUtil.wrapInHtml(explanation), UIUtil.ComponentStyle.SMALL), gb.nextLine().next().coverLine()); + + for (GitResetMode mode : GitResetMode.values()) { + JBRadioButton button = new JBRadioButton(mode.getName()); + button.setMnemonic(mode.getName().charAt(0)); + myButtonGroup.add(button); + panel.add(button, gb.nextLine().next()); + panel.add(new JBLabel(XmlStringUtil.wrapInHtml(mode.getDescription()), UIUtil.ComponentStyle.SMALL), gb.next()); + } + + myEnumModel = RadioButtonEnumModel.bindEnum(GitResetMode.class, myButtonGroup); + myEnumModel.setSelected(myDefaultMode); + return panel; + } + + @Nullable + @Override + protected String getHelpId() { + return DIALOG_ID; + } + + @NotNull + private static String prepareDescription(@NotNull Project project, @NotNull Map commits) { + if (commits.size() == 1 && !isMultiRepo(project)) { + Map.Entry entry = commits.entrySet().iterator().next(); + return "Reset " + getSourceText(entry.getKey()) + " to " + getTargetText(entry.getValue()); + } + + StringBuilder desc = new StringBuilder("Reset "); + for (Map.Entry entry : commits.entrySet()) { + GitRepository repository = entry.getKey(); + VcsFullCommitDetails commit = entry.getValue(); + desc.append(String.format("%s in %s to %s
", getSourceText(repository), + getShortRepositoryName(repository), getTargetText(commit))); + } + return desc.toString(); + } + + @NotNull + private static String getTargetText(@NotNull VcsFullCommitDetails commit) { + String commitMessage = StringUtil.shortenTextWithEllipsis(commit.getSubject(), 20, 0); + return "" + commit.getId().toShortString() + " \"" + commitMessage + "\" by " + commit.getAuthor().getName() + ""; + } + + @NotNull + private static String getSourceText(@NotNull GitRepository repository) { + String currentRevision = repository.getCurrentRevision(); + assert currentRevision != null; + return repository.getCurrentBranch() == null ? + "HEAD (" + GitUtil.getShortHash(currentRevision) + ")" : + repository.getCurrentBranch().getName(); + } + + private static boolean isMultiRepo(@NotNull Project project) { + return ServiceManager.getService(project, GitRepositoryManager.class).moreThanOneRoot(); + } + + @NotNull + public GitResetMode getResetMode() { + return myEnumModel.getSelected(); + } + +} diff --git a/plugins/git4idea/src/git4idea/reset/GitResetAction.java b/plugins/git4idea/src/git4idea/reset/GitResetAction.java new file mode 100644 index 000000000000..c6013474a81f --- /dev/null +++ b/plugins/git4idea/src/git4idea/reset/GitResetAction.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2014 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.reset; + +import com.intellij.dvcs.ui.VcsLogAction; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ObjectUtils; +import com.intellij.vcs.log.VcsFullCommitDetails; +import git4idea.config.GitVcsSettings; +import git4idea.repo.GitRepository; +import git4idea.repo.GitRepositoryManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public class GitResetAction extends VcsLogAction { + + @Nullable + @Override + protected GitRepository getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root) { + return getRepoManager(project).getRepositoryForRoot(root); + } + + @NotNull + @Override + protected Mode getMode() { + return Mode.SINGLE_PER_REPO; + } + + @Override + protected void actionPerformed(@NotNull final Project project, @NotNull final Map commits) { + GitVcsSettings settings = GitVcsSettings.getInstance(project); + GitResetMode defaultMode = ObjectUtils.notNull(settings.getResetMode(), GitResetMode.getDefault()); + GitNewResetDialog dialog = new GitNewResetDialog(project, commits, defaultMode); + dialog.show(); + if (dialog.isOK()) { + final GitResetMode selectedMode = dialog.getResetMode(); + settings.setResetMode(selectedMode); + new Task.Backgroundable(project, "Git reset", false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + new GitResetOperation(project, commits, selectedMode).execute(); + } + }.queue(); + } + } + + @NotNull + private static GitRepositoryManager getRepoManager(@NotNull Project project) { + return ServiceManager.getService(project, GitRepositoryManager.class); + } + +} diff --git a/plugins/git4idea/src/git4idea/reset/GitResetMode.java b/plugins/git4idea/src/git4idea/reset/GitResetMode.java new file mode 100644 index 000000000000..c10d95707073 --- /dev/null +++ b/plugins/git4idea/src/git4idea/reset/GitResetMode.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2014 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.reset; + +import org.jetbrains.annotations.NotNull; + +public enum GitResetMode { + + SOFT("Soft", "--soft", "Files won't change, differences will be staged for commit."), + MIXED("Mixed", "--mixed", "Files won't change, differences won't be staged."), + HARD("Hard", "--hard", "Files will be reverted to the state of the selected commit.
" + + "Warning: any local changes will be lost."), + KEEP("Keep", "--keep", "Files will be reverted to the state of the selected commit,
" + + "but local changes will be kept intact."); + + @NotNull private final String myName; + @NotNull private final String myArgument; + @NotNull private final String myDescription; + + GitResetMode(@NotNull String name, @NotNull String argument, @NotNull String description) { + myName = name; + myArgument = argument; + myDescription = description; + } + + @NotNull + public static GitResetMode getDefault() { + return MIXED; + } + + @NotNull + public String getName() { + return myName; + } + + @NotNull + public String getArgument() { + return myArgument; + } + + @NotNull + public String getDescription() { + return myDescription; + } + +} diff --git a/plugins/git4idea/src/git4idea/reset/GitResetOperation.java b/plugins/git4idea/src/git4idea/reset/GitResetOperation.java new file mode 100644 index 000000000000..c0c644027526 --- /dev/null +++ b/plugins/git4idea/src/git4idea/reset/GitResetOperation.java @@ -0,0 +1,141 @@ +/* + * Copyright 2000-2014 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.reset; + +import com.intellij.dvcs.repo.RepositoryUtil; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsNotifier; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; +import com.intellij.util.ui.UIUtil; +import com.intellij.vcs.log.VcsFullCommitDetails; +import git4idea.GitUtil; +import git4idea.commands.Git; +import git4idea.commands.GitCommandResult; +import git4idea.repo.GitRepository; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Map; + +public class GitResetOperation { + + @NotNull private final Project myProject; + @NotNull private final Map myCommits; + @NotNull private final GitResetMode myMode; + @NotNull private final Git myGit; + @NotNull private final VcsNotifier myNotifier; + + public GitResetOperation(@NotNull Project project, @NotNull Map targetCommits, + @NotNull GitResetMode mode) { + myProject = project; + myCommits = targetCommits; + myMode = mode; + myGit = ServiceManager.getService(Git.class); + myNotifier = VcsNotifier.getInstance(project); + } + + public void execute() { + saveAllDocuments(); + GitUtil.workingTreeChangeStarted(myProject); + Map results = ContainerUtil.newHashMap(); + try { + for (Map.Entry entry : myCommits.entrySet()) { + GitRepository repository = entry.getKey(); + VcsFullCommitDetails target = entry.getValue(); + results.put(repository, myGit.reset(repository, myMode, target.getId().asString())); + repository.update(); + VfsUtil.markDirtyAndRefresh(true, true, false, repository.getRoot()); + } + } + finally { + GitUtil.workingTreeChangeFinished(myProject); + } + notifyResult(results); + } + + private void notifyResult(@NotNull Map results) { + Map successes = ContainerUtil.newHashMap(); + Map errors = ContainerUtil.newHashMap(); + for (Map.Entry entry : results.entrySet()) { + GitCommandResult result = entry.getValue(); + GitRepository repository = entry.getKey(); + if (result.success()) { + successes.put(repository, result); + } + else { + errors.put(repository, result); + } + } + + if (errors.isEmpty()) { + myNotifier.notifySuccess("", "Reset successful"); + } + else if (!successes.isEmpty()) { + myNotifier.notifyImportantWarning("Reset partially failed", + "Reset was successful for " + joinRepos(successes.keySet()) + + "
but failed for " + joinRepos(errors.keySet()) + ":
" + formErrorReport(errors)); + } + else { + myNotifier.notifyError("Reset Failed", formErrorReport(errors)); + } + } + + @NotNull + private static String formErrorReport(@NotNull Map errorResults) { + MultiMap grouped = groupByResult(errorResults); + if (grouped.size() == 1) { + return "" + grouped.keySet().iterator().next() + ""; + } + return StringUtil.join(grouped.entrySet(), new Function>, String>() { + @NotNull + @Override + public String fun(@NotNull Map.Entry> entry) { + return joinRepos(entry.getValue()) + ":
" + entry.getKey() + ""; + } + }, "
"); + } + + // to avoid duplicate error reports if they are the same for different repositories + @NotNull + private static MultiMap groupByResult(@NotNull Map results) { + MultiMap grouped = MultiMap.create(); + for (Map.Entry entry : results.entrySet()) { + grouped.putValue(entry.getValue().getErrorOutputAsHtmlString(), entry.getKey()); + } + return grouped; + } + + @NotNull + private static String joinRepos(@NotNull Collection repositories) { + return StringUtil.join(RepositoryUtil.sortRepositories(repositories), ", "); + } + + private static void saveAllDocuments() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + FileDocumentManager.getInstance().saveAllDocuments(); + } + }); + } + +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java index dd7c7dc14e5c..e0bde5df924a 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgLogSingleCommitAction.java @@ -43,7 +43,7 @@ public abstract class HgLogSingleCommitAction extends VcsLogAction } @Override - protected void actionPerformed(@NotNull Map commits) { + protected void actionPerformed(@NotNull Project project, @NotNull Map commits) { assert commits.size() == 1; Map.Entry entry = commits.entrySet().iterator().next(); HgRepository repository = entry.getKey(); From 01bb25ff98d1d71aa984b27322f4f2d66acb0956 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 31 Jul 2014 15:13:45 +0400 Subject: [PATCH 73/85] [git] Support multiple patterns in local changes overwritten detector + add a pattern for reset --keep --- ...ocalChangesWouldBeOverwrittenDetector.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java b/plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java index b62f12806761..e9d8df89614a 100644 --- a/plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java +++ b/plugins/git4idea/src/git4idea/commands/GitLocalChangesWouldBeOverwrittenDetector.java @@ -40,6 +40,13 @@ public class GitLocalChangesWouldBeOverwrittenDetector extends GitMessageWithFil ".*Your local changes to '(.*)' would be overwritten by merge.*" ); + private static final Pattern[] RESET_PATTERNS = new Pattern[]{Pattern.compile( + ".*Entry '(.*)' not uptodate. Cannot merge.*" + ), + Pattern.compile( + ".*Entry '(.*)' would be overwritten by merge.*" + )}; + // common for checkout and merge public static final Event NEW_PATTERN = new Event( "Your local changes to the following files would be overwritten by", @@ -49,17 +56,18 @@ public class GitLocalChangesWouldBeOverwrittenDetector extends GitMessageWithFil public enum Operation { CHECKOUT(OLD_CHECKOUT_PATTERN), - MERGE(OLD_MERGE_PATTERN); + MERGE(OLD_MERGE_PATTERN), + RESET(RESET_PATTERNS); - @NotNull private final Pattern myPattern; + @NotNull private final Pattern[] myPatterns; - Operation(@NotNull Pattern pattern) { - myPattern = pattern; + Operation(@NotNull Pattern... patterns) { + myPatterns = patterns; } @NotNull - public Pattern getPattern() { - return myPattern; + Pattern[] getPatterns() { + return myPatterns; } } @@ -71,10 +79,13 @@ public class GitLocalChangesWouldBeOverwrittenDetector extends GitMessageWithFil @Override public void onLineAvailable(@NotNull String line, @NotNull Key outputType) { super.onLineAvailable(line, outputType); - Matcher m = myOperation.getPattern().matcher(line); - if (m.matches()) { - myMessageDetected = true; - myAffectedFiles.add(m.group(1)); + for (Pattern pattern : myOperation.getPatterns()) { + Matcher m = pattern.matcher(line); + if (m.matches()) { + myMessageDetected = true; + myAffectedFiles.add(m.group(1)); + break; + } } } } From 648d78304fcdd02b14863c20f9be4cd252ea035f Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Thu, 31 Jul 2014 15:28:34 +0400 Subject: [PATCH 74/85] [git] reset: support local changes would be overwritten error for --keep --- .../git4idea/branch/GitBranchUiHandler.java | 12 ++-- .../branch/GitBranchUiHandlerImpl.java | 4 +- .../git4idea/branch/GitCheckoutOperation.java | 3 +- .../git4idea/branch/GitMergeOperation.java | 2 +- .../branch/GitSmartOperationDialog.java | 19 +++--- .../git4idea/src/git4idea/commands/Git.java | 3 +- .../src/git4idea/commands/GitImpl.java | 9 ++- .../src/git4idea/reset/GitResetAction.java | 2 +- .../src/git4idea/reset/GitResetOperation.java | 58 +++++++++++++++++-- .../branch/GitBranchWorkerTest.groovy | 12 ++-- 10 files changed, 92 insertions(+), 32 deletions(-) diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java index 3b694c455c7b..95546f0547e8 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandler.java @@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitCommit; import git4idea.repo.GitRepository; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -68,14 +69,15 @@ public interface GitBranchUiHandler { * Shows the dialog proposing to execute the operation (checkout or merge) smartly, i.e. stash-execute-unstash. * * @param project - * @param changes local changes that would be overwritten by checkout or merge. - * @param paths paths reported by Git (in most cases this is covered by {@code changes}. - * @param operation operation name: checkout or merge - * @param isForcePossible can the operation be executed force (force checkout is possible, force merge - not). + * @param changes local changes that would be overwritten by checkout or merge. + * @param paths paths reported by Git (in most cases this is covered by {@code changes}. + * @param operation operation name: checkout or merge + * @param forceButtonTitle if the operation can be executed force (force checkout is possible), + * specify the title of the force button; otherwise (force merge is not possible) pass null. * @return the code of the decision. */ int showSmartOperationDialog(@NotNull Project project, @NotNull List changes, @NotNull Collection paths, - @NotNull String operation, boolean isForcePossible); + @NotNull String operation, @Nullable String forceButtonTitle); boolean showBranchIsNotFullyMergedDialog(@NotNull Project project, @NotNull Map> history, @NotNull String unmergedBranch, @NotNull List mergedToBranches, diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java index cea2b55596e0..20e207d14ba1 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchUiHandlerImpl.java @@ -172,7 +172,7 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler { @Override public int showSmartOperationDialog(@NotNull Project project, @NotNull List changes, @NotNull Collection paths, - @NotNull String operation, boolean isForcePossible) { + @NotNull String operation, @Nullable String forceButtonTitle) { JComponent fileBrowser; if (!changes.isEmpty()) { fileBrowser = new ChangesBrowserWithRollback(project, changes); @@ -180,7 +180,7 @@ public class GitBranchUiHandlerImpl implements GitBranchUiHandler { else { fileBrowser = new GitSimplePathsBrowser(project, paths); } - return GitSmartOperationDialog.showAndGetAnswer(myProject, fileBrowser, operation, isForcePossible); + return GitSmartOperationDialog.showAndGetAnswer(myProject, fileBrowser, operation, forceButtonTitle); } @Override diff --git a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java index e69f5ecfe8fc..b772434959e8 100644 --- a/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitCheckoutOperation.java @@ -121,7 +121,8 @@ class GitCheckoutOperation extends GitBranchOperation { List affectedChanges = conflictingRepositoriesAndAffectedChanges.getSecond(); Collection absolutePaths = GitUtil.toAbsolute(repository.getRoot(), localChangesOverwrittenByCheckout.getRelativeFilePaths()); - int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "checkout", true); + int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "checkout", + "&Force Checkout"); if (smartCheckoutDecision == GitSmartOperationDialog.SMART_EXIT_CODE) { boolean smartCheckedOutSuccessfully = smartCheckout(allConflictingRepositories, myStartPointReference, myNewBranch, getIndicator()); if (smartCheckedOutSuccessfully) { diff --git a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java index cc7065575d61..1b8f382d1dd8 100644 --- a/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java +++ b/plugins/git4idea/src/git4idea/branch/GitMergeOperation.java @@ -196,7 +196,7 @@ class GitMergeOperation extends GitBranchOperation { List affectedChanges = conflictingRepositoriesAndAffectedChanges.getSecond(); Collection absolutePaths = GitUtil.toAbsolute(repository.getRoot(), localChangesOverwrittenByMerge.getRelativeFilePaths()); - int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "merge", false); + int smartCheckoutDecision = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "merge", null); if (smartCheckoutDecision == GitSmartOperationDialog.SMART_EXIT_CODE) { return doSmartMerge(allConflictingRepositories); } diff --git a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java index 9c2ef4a8d107..4f5073027624 100644 --- a/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java +++ b/plugins/git4idea/src/git4idea/branch/GitSmartOperationDialog.java @@ -24,6 +24,7 @@ import com.intellij.ui.components.JBLabel; import com.intellij.util.ui.UIUtil; import git4idea.GitPlatformFacade; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.ActionEvent; @@ -44,18 +45,18 @@ public class GitSmartOperationDialog extends DialogWrapper { @NotNull private final JComponent myFileBrowser; @NotNull private final String myOperationTitle; - private final boolean myShowForceButton; + @Nullable private final String myForceButton; /** * Shows the dialog with the list of local changes preventing merge/checkout and returns the dialog exit code. */ static int showAndGetAnswer(@NotNull final Project project, @NotNull final JComponent fileBrowser, - @NotNull final String operationTitle, final boolean showForceButton) { + @NotNull final String operationTitle, @Nullable final String forceButtonTitle) { final AtomicInteger exitCode = new AtomicInteger(); UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { - GitSmartOperationDialog dialog = new GitSmartOperationDialog(project, fileBrowser, operationTitle, showForceButton); + GitSmartOperationDialog dialog = new GitSmartOperationDialog(project, fileBrowser, operationTitle, forceButtonTitle); ServiceManager.getService(project, GitPlatformFacade.class).showDialog(dialog); exitCode.set(dialog.getExitCode()); } @@ -64,11 +65,11 @@ public class GitSmartOperationDialog extends DialogWrapper { } private GitSmartOperationDialog(@NotNull Project project, @NotNull JComponent fileBrowser, @NotNull String operationTitle, - boolean showForceButton) { + @Nullable String forceButton) { super(project); myFileBrowser = fileBrowser; myOperationTitle = operationTitle; - myShowForceButton = showForceButton; + myForceButton = forceButton; String capitalizedOperation = capitalize(myOperationTitle); setTitle("Git " + capitalizedOperation + " Problem"); @@ -82,8 +83,8 @@ public class GitSmartOperationDialog extends DialogWrapper { @NotNull @Override protected Action[] createLeftSideActions() { - if (myShowForceButton) { - return new Action[] {new ForceCheckoutAction(myOperationTitle) }; + if (myForceButton != null) { + return new Action[]{new ForceCheckoutAction(myForceButton, myOperationTitle)}; } return new Action[0]; } @@ -110,8 +111,8 @@ public class GitSmartOperationDialog extends DialogWrapper { private class ForceCheckoutAction extends AbstractAction { - ForceCheckoutAction(@NotNull String operationTitle) { - super("&Force " + capitalize(operationTitle)); + ForceCheckoutAction(@NotNull String buttonTitle, @NotNull String operationTitle) { + super(buttonTitle); putValue(Action.SHORT_DESCRIPTION, capitalize(operationTitle) + " and overwrite local changes"); } diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java index bc0678c41728..830f73b4508f 100644 --- a/plugins/git4idea/src/git4idea/commands/Git.java +++ b/plugins/git4idea/src/git4idea/commands/Git.java @@ -94,7 +94,8 @@ public interface Git { GitCommandResult branchCreate(@NotNull GitRepository repository, @NotNull String branchName); @NotNull - GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target); + GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target, + @NotNull GitLineHandlerListener... listeners); @NotNull GitCommandResult resetMerge(@NotNull GitRepository repository, @Nullable String revision); diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java index 9f91bbc4f0e2..09e254f5392b 100644 --- a/plugins/git4idea/src/git4idea/commands/GitImpl.java +++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java @@ -339,8 +339,9 @@ public class GitImpl implements Git { @Override @NotNull - public GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target) { - return reset(repository, mode.getArgument(), target); + public GitCommandResult reset(@NotNull GitRepository repository, @NotNull GitResetMode mode, @NotNull String target, + @NotNull GitLineHandlerListener... listeners) { + return reset(repository, mode.getArgument(), target, listeners); } @Override @@ -350,12 +351,14 @@ public class GitImpl implements Git { } @NotNull - private static GitCommandResult reset(@NotNull GitRepository repository, @NotNull String argument, @Nullable String target) { + private static GitCommandResult reset(@NotNull GitRepository repository, @NotNull String argument, @Nullable String target, + @NotNull GitLineHandlerListener... listeners) { final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.RESET); handler.addParameters(argument); if (target != null) { handler.addParameters(target); } + addListeners(handler, listeners); return run(handler); } diff --git a/plugins/git4idea/src/git4idea/reset/GitResetAction.java b/plugins/git4idea/src/git4idea/reset/GitResetAction.java index c6013474a81f..5870c2fc4993 100644 --- a/plugins/git4idea/src/git4idea/reset/GitResetAction.java +++ b/plugins/git4idea/src/git4idea/reset/GitResetAction.java @@ -57,7 +57,7 @@ public class GitResetAction extends VcsLogAction { new Task.Backgroundable(project, "Git reset", false) { @Override public void run(@NotNull ProgressIndicator indicator) { - new GitResetOperation(project, commits, selectedMode).execute(); + new GitResetOperation(project, commits, selectedMode, indicator).execute(); } }.queue(); } diff --git a/plugins/git4idea/src/git4idea/reset/GitResetOperation.java b/plugins/git4idea/src/git4idea/reset/GitResetOperation.java index c0c644027526..9c66a5225a52 100644 --- a/plugins/git4idea/src/git4idea/reset/GitResetOperation.java +++ b/plugins/git4idea/src/git4idea/reset/GitResetOperation.java @@ -18,39 +18,58 @@ package git4idea.reset; import com.intellij.dvcs.repo.RepositoryUtil; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsNotifier; +import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.VcsFullCommitDetails; +import git4idea.GitPlatformFacade; import git4idea.GitUtil; +import git4idea.branch.GitBranchUiHandlerImpl; +import git4idea.branch.GitSmartOperationDialog; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; +import git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector; import git4idea.repo.GitRepository; +import git4idea.util.GitPreservingProcess; import org.jetbrains.annotations.NotNull; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Map; +import static git4idea.commands.GitLocalChangesWouldBeOverwrittenDetector.Operation.RESET; + public class GitResetOperation { @NotNull private final Project myProject; @NotNull private final Map myCommits; @NotNull private final GitResetMode myMode; + @NotNull private final ProgressIndicator myIndicator; @NotNull private final Git myGit; @NotNull private final VcsNotifier myNotifier; + @NotNull private final GitPlatformFacade myFacade; + @NotNull private final GitBranchUiHandlerImpl myUiHandler; public GitResetOperation(@NotNull Project project, @NotNull Map targetCommits, - @NotNull GitResetMode mode) { + @NotNull GitResetMode mode, @NotNull ProgressIndicator indicator) { myProject = project; myCommits = targetCommits; myMode = mode; + myIndicator = indicator; myGit = ServiceManager.getService(Git.class); myNotifier = VcsNotifier.getInstance(project); + myFacade = ServiceManager.getService(GitPlatformFacade.class); + myUiHandler = new GitBranchUiHandlerImpl(myProject, myFacade, myGit, indicator); } public void execute() { @@ -60,10 +79,20 @@ public class GitResetOperation { try { for (Map.Entry entry : myCommits.entrySet()) { GitRepository repository = entry.getKey(); - VcsFullCommitDetails target = entry.getValue(); - results.put(repository, myGit.reset(repository, myMode, target.getId().asString())); + VirtualFile root = repository.getRoot(); + String target = entry.getValue().getId().asString(); + GitLocalChangesWouldBeOverwrittenDetector detector = new GitLocalChangesWouldBeOverwrittenDetector(root, RESET); + + GitCommandResult result = myGit.reset(repository, myMode, target, detector); + if (!result.success() && detector.wasMessageDetected()) { + GitCommandResult smartResult = proposeSmartReset(detector, repository, target); + if (smartResult != null) { + result = smartResult; + } + } + results.put(repository, result); repository.update(); - VfsUtil.markDirtyAndRefresh(true, true, false, repository.getRoot()); + VfsUtil.markDirtyAndRefresh(true, true, false, root); } } finally { @@ -72,6 +101,27 @@ public class GitResetOperation { notifyResult(results); } + private GitCommandResult proposeSmartReset(@NotNull GitLocalChangesWouldBeOverwrittenDetector detector, + @NotNull final GitRepository repository, @NotNull final String target) { + Collection absolutePaths = GitUtil.toAbsolute(repository.getRoot(), detector.getRelativeFilePaths()); + List affectedChanges = GitUtil.findLocalChangesForPaths(myProject, repository.getRoot(), absolutePaths, false); + int choice = myUiHandler.showSmartOperationDialog(myProject, affectedChanges, absolutePaths, "reset", "&Hard Reset"); + if (choice == GitSmartOperationDialog.SMART_EXIT_CODE) { + final Ref result = Ref.create(); + new GitPreservingProcess(myProject, myFacade, myGit, Collections.singleton(repository), "reset", target, myIndicator, new Runnable() { + @Override + public void run() { + result.set(myGit.reset(repository, myMode, target)); + } + }).execute(); + return result.get(); + } + if (choice == GitSmartOperationDialog.FORCE_EXIT_CODE) { + return myGit.reset(repository, GitResetMode.HARD, target); + } + return null; + } + private void notifyResult(@NotNull Map results) { Map successes = ContainerUtil.newHashMap(); Map errors = ContainerUtil.newHashMap(); diff --git a/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy b/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy index d838310c41c5..9a2bad2ad663 100644 --- a/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy +++ b/plugins/git4idea/tests/git4idea/branch/GitBranchWorkerTest.groovy @@ -14,6 +14,7 @@ * limitations under the License. */ package git4idea.branch + import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.util.ProgressIndicatorBase import com.intellij.openapi.project.Project @@ -32,6 +33,7 @@ import git4idea.config.GitVersionSpecialty import git4idea.repo.GitRepository import git4idea.test.GitPlatformTest import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.Nullable import java.util.regex.Matcher @@ -243,7 +245,7 @@ class GitBranchWorkerTest extends GitPlatformTest { List changes = null; checkoutOrMerge(operation, "feature", [ - showSmartOperationDialog: { Project p, List cs, Collection paths, String op, boolean force -> + showSmartOperationDialog: { Project p, List cs, Collection paths, String op, String force -> changes = cs DialogWrapper.CANCEL_EXIT_CODE } @@ -329,7 +331,7 @@ class GitBranchWorkerTest extends GitPlatformTest { prepareLocalChangesOverwrittenBy(myUltimate) checkoutOrMerge(operation, "feature", [ - showSmartOperationDialog: { Project p, List cs, Collection paths, String op, boolean f + showSmartOperationDialog: { Project p, List cs, Collection paths, String op, String force -> GitSmartOperationDialog.CANCEL_EXIT_CODE }, ] as GitBranchUiHandler ) @@ -355,7 +357,7 @@ class GitBranchWorkerTest extends GitPlatformTest { def rollbackMsg = null checkoutOrMerge(operation, "feature", [ showSmartOperationDialog : { - Project p, List cs, Collection paths, String op, boolean f -> GitSmartOperationDialog.CANCEL_EXIT_CODE + Project p, List cs, Collection paths, String op, String f -> GitSmartOperationDialog.CANCEL_EXIT_CODE }, notifyErrorWithRollbackProposal: { String t, String m, String rp -> rollbackMsg = m; false } ] as GitBranchUiHandler ) @@ -368,7 +370,7 @@ class GitBranchWorkerTest extends GitPlatformTest { prepareLocalChangesOverwrittenBy(myUltimate) def uiHandler = [ - showSmartOperationDialog: { Project p, List cs, Collection paths, String op, boolean force -> + showSmartOperationDialog: { Project p, List cs, Collection paths, String op, String force -> GitSmartOperationDialog.FORCE_EXIT_CODE; }, ] as GitBranchUiHandler @@ -733,7 +735,7 @@ class GitBranchWorkerTest extends GitPlatformTest { @NotNull List changes, @NotNull Collection paths, @NotNull String operation, - boolean isForcePossible) { + @Nullable String forceButton) { GitSmartOperationDialog.SMART_EXIT_CODE } From 857b2a1e6712dfe66daf44faa3784f998ebcd939 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 1 Aug 2014 12:40:48 +0400 Subject: [PATCH 75/85] [git] cleanup error prefixes when reporting git command result --- plugins/git4idea/src/git4idea/GitUtil.java | 10 ++++++++++ .../checkin/GitCheckinEnvironment.java | 5 +---- .../git4idea/commands/GitCommandResult.java | 19 +++++++++++++++++-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index ed3554eab9b3..4ff61fb4a68b 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -1025,4 +1025,14 @@ public class GitUtil { ApplicationManager.getApplication().getMessageBus().syncPublisher(BatchFileChangeListener.TOPIC).batchChangeCompleted(project); } + @NotNull + public static String cleanupErrorPrefixes(@NotNull String msg) { + final String[] PREFIXES = { "fatal:", "error:" }; + for (String prefix : PREFIXES) { + if (msg.startsWith(prefix)) { + return msg.substring(prefix.length()).trim(); + } + } + return msg; + } } diff --git a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java index 3bb008a2f95f..448dd25ef7ab 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java +++ b/plugins/git4idea/src/git4idea/checkin/GitCheckinEnvironment.java @@ -226,10 +226,7 @@ public class GitCheckinEnvironment implements CheckinEnvironment { @NotNull private static VcsException cleanupExceptionText(VcsException original) { String msg = original.getMessage(); - final String FATAL_PREFIX = "fatal:"; - if (msg.startsWith(FATAL_PREFIX)) { - msg = msg.substring(FATAL_PREFIX.length()); - } + msg = GitUtil.cleanupErrorPrefixes(msg); final String DURING_EXECUTING_SUFFIX = GitSimpleHandler.DURING_EXECUTING_ERROR_MESSAGE; int suffix = msg.indexOf(DURING_EXECUTING_SUFFIX); if (suffix > 0) { diff --git a/plugins/git4idea/src/git4idea/commands/GitCommandResult.java b/plugins/git4idea/src/git4idea/commands/GitCommandResult.java index f89a4890a76b..0683d738a9bf 100644 --- a/plugins/git4idea/src/git4idea/commands/GitCommandResult.java +++ b/plugins/git4idea/src/git4idea/commands/GitCommandResult.java @@ -16,10 +16,14 @@ package git4idea.commands; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import git4idea.GitUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -64,9 +68,10 @@ public class GitCommandResult { @NotNull public String getErrorOutputAsHtmlString() { - return StringUtil.join(myErrorOutput, "
"); + return StringUtil.join(cleanup(myErrorOutput), "
"); } - + + @NotNull public String getErrorOutputAsJoinedString() { return StringUtil.join(myErrorOutput, "\n"); } @@ -90,4 +95,14 @@ public class GitCommandResult { return false; // will be implemented later } + @NotNull + private static Collection cleanup(@NotNull Collection errorOutput) { + return ContainerUtil.map(errorOutput, new Function() { + @Override + public String fun(String errorMessage) { + return GitUtil.cleanupErrorPrefixes(errorMessage); + } + }); + } + } From de3fcada81adf102029d427bcdc06bc15ede96d2 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 1 Aug 2014 13:04:37 +0400 Subject: [PATCH 76/85] [git] Add ellipsis symbol to some actions which produce a dialog Specially support Cherry-Pick, since it shows dialog depending on the settings => move name to update() & check the settings. Also fix "disabled & invisible" condition for cherry-pick. Also don't make description equal to the name to avoid trivial tooltips. --- plugins/git4idea/src/META-INF/plugin.xml | 6 +++--- .../cherrypick/GitCherryPickAction.java | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index b8fd645a12fa..dc12d0e03ad0 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -92,11 +92,11 @@ - + - - diff --git a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java index aa3e1e440277..b4250a0d142b 100644 --- a/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java +++ b/plugins/git4idea/src/git4idea/cherrypick/GitCherryPickAction.java @@ -49,6 +49,8 @@ import java.util.*; * @author Kirill Likhodedov */ public class GitCherryPickAction extends DumbAwareAction { + + private static final String NAME = "Cherry-Pick"; private static final Logger LOG = Logger.getInstance(GitCherryPickAction.class); @NotNull private final GitPlatformFacade myPlatformFacade; @@ -56,7 +58,7 @@ public class GitCherryPickAction extends DumbAwareAction { @NotNull private final Set myIdsInProgress; public GitCherryPickAction() { - super("Cherry-pick", "Cherry-pick", Git4ideaIcons.CherryPick); + super(NAME, null, Git4ideaIcons.CherryPick); myGit = ServiceManager.getService(Git.class); myPlatformFacade = ServiceManager.getService(GitPlatformFacade.class); myIdsInProgress = ContainerUtil.newHashSet(); @@ -81,9 +83,8 @@ public class GitCherryPickAction extends DumbAwareAction { new Task.Backgroundable(project, "Cherry-picking", false) { public void run(@NotNull ProgressIndicator indicator) { try { - boolean autoCommit = GitVcsSettings.getInstance(myProject).isAutoCommitOnCherryPick(); Map> commitsInRoots = sortCommits(groupCommitsByRoots(project, commits)); - new GitCherryPicker(myProject, myGit, myPlatformFacade, autoCommit).cherryPick(commitsInRoots); + new GitCherryPicker(project, myGit, myPlatformFacade, isAutoCommit(project)).cherryPick(commitsInRoots); } finally { ApplicationManager.getApplication().invokeLater(new Runnable() { @@ -130,15 +131,21 @@ public class GitCherryPickAction extends DumbAwareAction { return groupedCommits; } + private static boolean isAutoCommit(@NotNull Project project) { + return GitVcsSettings.getInstance(project).isAutoCommitOnCherryPick(); + } + @Override public void update(AnActionEvent e) { super.update(e); - final VcsLog log = getVcsLog(e); - if (log != null && !DvcsUtil.logHasRootForVcs(log, GitVcs.getKey())) { + VcsLog log = getVcsLog(e); + Project project = getEventProject(e); + if (project == null || log == null || !DvcsUtil.logHasRootForVcs(log, GitVcs.getKey())) { e.getPresentation().setEnabledAndVisible(false); } else { e.getPresentation().setEnabled(enabled(e)); + e.getPresentation().setText(isAutoCommit(project) ? NAME : NAME + "..."); } } From 5ade74a141cb3d5656d21d61a0bfa07eaf451ecf Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 1 Aug 2014 12:03:38 +0200 Subject: [PATCH 77/85] disable useTargetedProductPolicyIfSeveralChildren If Ruby general settings will be without titled border in RubyMine, user could think that all other debugger categories also about Ruby. --- .../impl/settings/MergedCompositeConfigurable.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/MergedCompositeConfigurable.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/MergedCompositeConfigurable.java index 459dda57d259..ccfc2708cb9e 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/MergedCompositeConfigurable.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/MergedCompositeConfigurable.java @@ -58,6 +58,15 @@ class MergedCompositeConfigurable implements SearchableConfigurable { return children.length == 1 ? children[0].getHelpTopic() : null; } + /** + * false by default. + * + * If Ruby general settings will be without titled border in RubyMine, user could think that all other debugger categories also about Ruby. + */ + protected boolean isUseTargetedProductPolicyIfSeveralChildren() { + return false; + } + @Nullable @Override public JComponent createComponent() { @@ -82,7 +91,7 @@ class MergedCompositeConfigurable implements SearchableConfigurable { } else { boolean addBorder = true; - if (isFirstNamed) { + if (isUseTargetedProductPolicyIfSeveralChildren() && isFirstNamed) { isFirstNamed = false; if (isTargetedToProduct(configurable)) { addBorder = false; From 3868d67bcf5583325f1484b8c6852570b6acbb46 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Fri, 1 Aug 2014 12:28:54 +0200 Subject: [PATCH 78/85] fix tests --- .../testData/find/findInEditor/BasicFind.gold | 3 +-- .../find/findInEditor/EmacsLikeFallback.gold | 9 +++------ .../findInEditor/ReplacementWithEmptyString.gold | 12 ++++-------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/java/java-tests/testData/find/findInEditor/BasicFind.gold b/java/java-tests/testData/find/findInEditor/BasicFind.gold index f1ea3fa66d4c..46486765486d 100644 --- a/java/java-tests/testData/find/findInEditor/BasicFind.gold +++ b/java/java-tests/testData/find/findInEditor/BasicFind.gold @@ -3,8 +3,7 @@ myStringToFind =a myStringToReplace = isReplaceState =false isWholeWordsOnly =false -isInStringLiterals =false -isInComments =false +searchContext =ANY isFromCursor =true isForward =true isGlobal =true diff --git a/java/java-tests/testData/find/findInEditor/EmacsLikeFallback.gold b/java/java-tests/testData/find/findInEditor/EmacsLikeFallback.gold index c7676944b8da..bfda616a3ac1 100644 --- a/java/java-tests/testData/find/findInEditor/EmacsLikeFallback.gold +++ b/java/java-tests/testData/find/findInEditor/EmacsLikeFallback.gold @@ -3,8 +3,7 @@ myStringToFind =a myStringToReplace = isReplaceState =false isWholeWordsOnly =false -isInStringLiterals =false -isInComments =false +searchContext =ANY isFromCursor =true isForward =true isGlobal =true @@ -38,8 +37,7 @@ myStringToFind =ab myStringToReplace = isReplaceState =false isWholeWordsOnly =false -isInStringLiterals =false -isInComments =false +searchContext =ANY isFromCursor =true isForward =true isGlobal =true @@ -72,8 +70,7 @@ myStringToFind =a myStringToReplace = isReplaceState =false isWholeWordsOnly =false -isInStringLiterals =false -isInComments =false +searchContext =ANY isFromCursor =true isForward =true isGlobal =true diff --git a/java/java-tests/testData/find/findInEditor/ReplacementWithEmptyString.gold b/java/java-tests/testData/find/findInEditor/ReplacementWithEmptyString.gold index eb2cdb2c1240..a4d44cbfa445 100644 --- a/java/java-tests/testData/find/findInEditor/ReplacementWithEmptyString.gold +++ b/java/java-tests/testData/find/findInEditor/ReplacementWithEmptyString.gold @@ -3,8 +3,7 @@ myStringToFind = myStringToReplace = isReplaceState =false isWholeWordsOnly =false -isInStringLiterals =false -isInComments =false +searchContext =ANY isFromCursor =true isForward =true isGlobal =true @@ -34,8 +33,7 @@ myStringToFind =a myStringToReplace = isReplaceState =false isWholeWordsOnly =false -isInStringLiterals =false -isInComments =false +searchContext =ANY isFromCursor =true isForward =true isGlobal =true @@ -67,8 +65,7 @@ myStringToFind =a myStringToReplace = isReplaceState =true isWholeWordsOnly =false -isInStringLiterals =false -isInComments =false +searchContext =ANY isFromCursor =true isForward =true isGlobal =true @@ -102,8 +99,7 @@ myStringToFind =a myStringToReplace = isReplaceState =true isWholeWordsOnly =false -isInStringLiterals =false -isInComments =false +searchContext =ANY isFromCursor =true isForward =true isGlobal =true From 4ab11eb1eed4f818aa48b41794fb742fc568c972 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Fri, 1 Aug 2014 12:33:25 +0200 Subject: [PATCH 79/85] restore open api + more consistency in naming --- .../com/intellij/find/FindManagerTest.java | 4 +-- .../intellij/find/FindManagerTestUtils.java | 2 +- .../src/com/intellij/find/FindModel.java | 33 ++++++++++++++++--- ...ToggleExceptCommentsAndLiteralsAction.java | 2 +- .../ToggleExceptLiteralsAction.java | 2 +- .../ToggleInLiteralsOnlyAction.java | 2 +- .../com/intellij/find/impl/FindDialog.java | 6 ++-- .../intellij/find/impl/FindManagerImpl.java | 4 +-- .../intellij/find/impl/FindSettingsImpl.java | 18 +++++----- .../plugins/groovy/GrFindManagerTest.java | 5 +-- 10 files changed, 52 insertions(+), 26 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java index fded8bdc12c9..f83d836bb55a 100644 --- a/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java +++ b/java/java-tests/testSrc/com/intellij/find/FindManagerTest.java @@ -615,7 +615,7 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { FindModel findModel = FindManagerTestUtils.configureFindModel("^done$"); findModel.setRegularExpressions(true); - findModel.setSearchContext(FindModel.SearchContext.IN_STRINGS); + findModel.setSearchContext(FindModel.SearchContext.IN_STRING_LITERALS); String text = "\"done\"; 'done'; 'done' \"done2\""; FindManagerTestUtils.runFindForwardAndBackward(myFindManager, findModel, text, "java"); @@ -676,7 +676,7 @@ public class FindManagerTest extends DaemonAnalyzerTestCase { String prefix = "\""; String text = prefix + "done\"done"; - findModel.setSearchContext(FindModel.SearchContext.EXCEPT_STRINGS); + findModel.setSearchContext(FindModel.SearchContext.EXCEPT_STRING_LITERALS); LightVirtualFile file = new LightVirtualFile("A.java", text); FindResult findResult = myFindManager.findString(text, prefix.length(), findModel, file); diff --git a/java/testFramework/src/com/intellij/find/FindManagerTestUtils.java b/java/testFramework/src/com/intellij/find/FindManagerTestUtils.java index ea08e96427c0..b65b58b8638c 100644 --- a/java/testFramework/src/com/intellij/find/FindManagerTestUtils.java +++ b/java/testFramework/src/com/intellij/find/FindManagerTestUtils.java @@ -24,7 +24,7 @@ public class FindManagerTestUtils { } public static void runFindInCommentsAndLiterals(FindManager findManager, FindModel findModel, String text, String ext) { - findModel.setSearchContext(FindModel.SearchContext.IN_STRINGS); + findModel.setSearchContext(FindModel.SearchContext.IN_STRING_LITERALS); runFindForwardAndBackward(findManager, findModel, text, ext); findModel.setSearchContext(FindModel.SearchContext.IN_COMMENTS); diff --git a/platform/lang-api/src/com/intellij/find/FindModel.java b/platform/lang-api/src/com/intellij/find/FindModel.java index 8c34697ac0fa..b8caf760446d 100644 --- a/platform/lang-api/src/com/intellij/find/FindModel.java +++ b/platform/lang-api/src/com/intellij/find/FindModel.java @@ -851,11 +851,11 @@ public class FindModel extends UserDataHolderBase implements Cloneable { } public enum SearchContext { - ANY, IN_STRINGS, IN_COMMENTS, EXCEPT_STRINGS, EXCEPT_COMMENTS, EXCEPT_STRINGS_AND_COMMENTS + ANY, IN_STRING_LITERALS, IN_COMMENTS, EXCEPT_STRING_LITERALS, EXCEPT_COMMENTS, EXCEPT_COMMENTS_AND_STRING_LITERALS } public boolean isInStringLiteralsOnly() { - return searchContext == SearchContext.IN_STRINGS; + return searchContext == SearchContext.IN_STRING_LITERALS; } public boolean isExceptComments() { @@ -863,7 +863,7 @@ public class FindModel extends UserDataHolderBase implements Cloneable { } public boolean isExceptStringLiterals() { - return searchContext == SearchContext.EXCEPT_STRINGS; + return searchContext == SearchContext.EXCEPT_STRING_LITERALS; } public boolean isInCommentsOnly() { @@ -871,7 +871,32 @@ public class FindModel extends UserDataHolderBase implements Cloneable { } public boolean isExceptCommentsAndStringLiterals() { - return searchContext == SearchContext.EXCEPT_STRINGS_AND_COMMENTS; + return searchContext == SearchContext.EXCEPT_COMMENTS_AND_STRING_LITERALS; + } + + @Deprecated + public void setInCommentsOnly(boolean inCommentsOnly) { + doApplyContextChange(inCommentsOnly, SearchContext.IN_COMMENTS); + } + + @Deprecated + public void setInStringLiteralsOnly(boolean inStringLiteralsOnly) { + doApplyContextChange(inStringLiteralsOnly, SearchContext.IN_STRING_LITERALS); + } + + public void doApplyContextChange(boolean inCommentsOnly, SearchContext option) { + boolean changed = false; + if (inCommentsOnly) { + changed = searchContext == option; + searchContext = option; + } else if (searchContext == option) { // do not reset unrelated value + changed = true; + searchContext = SearchContext.ANY; + } + + if (changed) { + notifyObservers(); + } } public @NotNull SearchContext getSearchContext() { diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAndLiteralsAction.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAndLiteralsAction.java index ba7cc0e7be3e..d3ff1015e610 100644 --- a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAndLiteralsAction.java +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptCommentsAndLiteralsAction.java @@ -33,6 +33,6 @@ public class ToggleExceptCommentsAndLiteralsAction extends EditorHeaderToggleAct @Override public void setSelected(AnActionEvent e, boolean state) { - getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.EXCEPT_STRINGS_AND_COMMENTS : FindModel.SearchContext.ANY); + getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.EXCEPT_COMMENTS_AND_STRING_LITERALS : FindModel.SearchContext.ANY); } } diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptLiteralsAction.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptLiteralsAction.java index c6c0c7901e82..4b7f09c2b211 100644 --- a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptLiteralsAction.java +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleExceptLiteralsAction.java @@ -33,6 +33,6 @@ public class ToggleExceptLiteralsAction extends EditorHeaderToggleAction impleme @Override public void setSelected(AnActionEvent e, boolean state) { - getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.EXCEPT_STRINGS : FindModel.SearchContext.ANY); + getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.EXCEPT_STRING_LITERALS : FindModel.SearchContext.ANY); } } diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInLiteralsOnlyAction.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInLiteralsOnlyAction.java index 9046c92412ac..d8fc5d530095 100644 --- a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInLiteralsOnlyAction.java +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleInLiteralsOnlyAction.java @@ -18,6 +18,6 @@ public class ToggleInLiteralsOnlyAction extends EditorHeaderToggleAction implem @Override public void setSelected(AnActionEvent e, boolean state) { - getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.IN_STRINGS : FindModel.SearchContext.ANY); + getEditorSearchComponent().getFindModel().setSearchContext(state ? FindModel.SearchContext.IN_STRING_LITERALS : FindModel.SearchContext.ANY); } } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java index 1182e133f62d..4c21ddbefeb4 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindDialog.java @@ -1000,7 +1000,7 @@ public class FindDialog extends DialogWrapper { String selectedSearchContextInUi = (String)mySearchContext.getSelectedItem(); FindModel.SearchContext searchContext = FindModel.SearchContext.ANY; if (FindBundle.message("find.context.in.literals.scope.label").equals(selectedSearchContextInUi)) { - searchContext = FindModel.SearchContext.IN_STRINGS; + searchContext = FindModel.SearchContext.IN_STRING_LITERALS; } else if (FindBundle.message("find.context.in.comments.scope.label").equals(selectedSearchContextInUi)) { searchContext = FindModel.SearchContext.IN_COMMENTS; @@ -1009,9 +1009,9 @@ public class FindDialog extends DialogWrapper { searchContext = FindModel.SearchContext.EXCEPT_COMMENTS; } else if (FindBundle.message("find.context.except.literals.scope.label").equals(selectedSearchContextInUi)) { - searchContext = FindModel.SearchContext.EXCEPT_STRINGS; + searchContext = FindModel.SearchContext.EXCEPT_STRING_LITERALS; } else if (FindBundle.message("find.context.except.comments.and.literals.scope.label").equals(selectedSearchContextInUi)) { - searchContext = FindModel.SearchContext.EXCEPT_STRINGS_AND_COMMENTS; + searchContext = FindModel.SearchContext.EXCEPT_COMMENTS_AND_STRING_LITERALS; } model.setSearchContext(searchContext); diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index c1c5185f3d7a..27e2b7215f25 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -353,7 +353,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo } if (model.isExceptStringLiterals() || model.isExceptCommentsAndStringLiterals()) { - addRanges(file, model, text, result, FindModel.SearchContext.IN_STRINGS); + addRanges(file, model, text, result, FindModel.SearchContext.IN_STRING_LITERALS); } mySkipRangesSet = result; @@ -399,7 +399,7 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo if (file == null) return null; FindModel.SearchContext context = model.getSearchContext(); if( context == FindModel.SearchContext.ANY || context == FindModel.SearchContext.IN_COMMENTS || - context == FindModel.SearchContext.IN_STRINGS) { + context == FindModel.SearchContext.IN_STRING_LITERALS) { return null; } diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindSettingsImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindSettingsImpl.java index b96d14b08c59..c9c8e3492834 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindSettingsImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindSettingsImpl.java @@ -104,8 +104,8 @@ public class FindSettingsImpl extends FindSettings implements PersistentStateCom @SuppressWarnings({"WeakerAccess"}) public boolean COMMENTS_ONLY = false; @SuppressWarnings({"WeakerAccess"}) public boolean STRING_LITERALS_ONLY = false; @SuppressWarnings({"WeakerAccess"}) public boolean EXCEPT_COMMENTS = false; - @SuppressWarnings({"WeakerAccess"}) public boolean EXCEPT_COMMENTS_AND_LITERALS = false; - @SuppressWarnings({"WeakerAccess"}) public boolean EXCEPT_LITERALS = false; + @SuppressWarnings({"WeakerAccess"}) public boolean EXCEPT_COMMENTS_AND_STRING_LITERALS = false; + @SuppressWarnings({"WeakerAccess"}) public boolean EXCEPT_STRING_LITERALS = false; @SuppressWarnings({"WeakerAccess"}) public boolean LOCAL_WHOLE_WORDS_ONLY = false; @SuppressWarnings({"WeakerAccess"}) public boolean REGULAR_EXPRESSIONS = false; @SuppressWarnings({"WeakerAccess"}) public boolean LOCAL_REGULAR_EXPRESSIONS = false; @@ -282,13 +282,13 @@ public class FindSettingsImpl extends FindSettings implements PersistentStateCom FindModel.SearchContext searchContext = isInCommentsOnly() ? FindModel.SearchContext.IN_COMMENTS : isInStringLiteralsOnly() ? - FindModel.SearchContext.IN_STRINGS : + FindModel.SearchContext.IN_STRING_LITERALS : isExceptComments() ? FindModel.SearchContext.EXCEPT_COMMENTS : isExceptStringLiterals() ? - FindModel.SearchContext.EXCEPT_STRINGS : + FindModel.SearchContext.EXCEPT_STRING_LITERALS : isExceptCommentsAndLiterals() ? - FindModel.SearchContext.EXCEPT_STRINGS_AND_COMMENTS : + FindModel.SearchContext.EXCEPT_COMMENTS_AND_STRING_LITERALS : FindModel.SearchContext.ANY; model.setSearchContext(searchContext); model.setWithSubdirectories(isWithSubdirectories()); @@ -405,12 +405,12 @@ public class FindSettingsImpl extends FindSettings implements PersistentStateCom @Override public void setExceptCommentsAndLiterals(boolean selected) { - EXCEPT_COMMENTS_AND_LITERALS = selected; + EXCEPT_COMMENTS_AND_STRING_LITERALS = selected; } @Override public boolean isExceptCommentsAndLiterals() { - return EXCEPT_COMMENTS_AND_LITERALS; + return EXCEPT_COMMENTS_AND_STRING_LITERALS; } @Override @@ -420,11 +420,11 @@ public class FindSettingsImpl extends FindSettings implements PersistentStateCom @Override public boolean isExceptStringLiterals() { - return EXCEPT_LITERALS; + return EXCEPT_STRING_LITERALS; } @Override public void setExceptStringLiterals(boolean selected) { - EXCEPT_LITERALS = selected; + EXCEPT_STRING_LITERALS = selected; } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/GrFindManagerTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/GrFindManagerTest.java index c2a62a7b2a9c..6112a2151a7e 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/GrFindManagerTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/GrFindManagerTest.java @@ -44,7 +44,8 @@ public class GrFindManagerTest extends DaemonAnalyzerTestCase { public void testFindInLiteralToSkipQuotes() { FindModel findModel = FindManagerTestUtils.configureFindModel("^done$"); findModel.setRegularExpressions(true); - findModel.setSearchContext(FindModel.SearchContext.IN_STRINGS); + findModel.setInStringLiteralsOnly(true); + findModel.setInCommentsOnly(false); String text = "def n = \"\"\"done\"\"\"\n def n = /done/\n def n = \"done\"\n def n = \"done2\""; FindManagerTestUtils.runFindForwardAndBackward(myFindManager, findModel, text, "groovy"); } @@ -52,7 +53,7 @@ public class GrFindManagerTest extends DaemonAnalyzerTestCase { public void testFindInShellCommentsOfGroovy() { FindModel findModel = FindManagerTestUtils.configureFindModel("done"); findModel.setWholeWordsOnly(true); - findModel.setSearchContext(FindModel.SearchContext.IN_COMMENTS); + findModel.setInCommentsOnly(true); String text = "#! done done done\n"; FindManagerTestUtils.runFindForwardAndBackward(myFindManager, findModel, text, "groovy"); } From b5b317ec57ae1977399945a88cb6a1929b031799 Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Fri, 1 Aug 2014 14:19:50 +0400 Subject: [PATCH 80/85] [git] cleanup: remove trivial javadocs --- .../src/git4idea/merge/GitPullDialog.java | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/plugins/git4idea/src/git4idea/merge/GitPullDialog.java b/plugins/git4idea/src/git4idea/merge/GitPullDialog.java index f9d8085cfab8..68b16164b335 100644 --- a/plugins/git4idea/src/git4idea/merge/GitPullDialog.java +++ b/plugins/git4idea/src/git4idea/merge/GitPullDialog.java @@ -44,66 +44,23 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -/** - * Git pull dialog - */ public class GitPullDialog extends DialogWrapper { private static final Logger LOG = Logger.getInstance(GitPullDialog.class); - /** - * root panel - */ private JPanel myPanel; - /** - * The selected git root - */ private JComboBox myGitRoot; - /** - * Current branch label - */ private JLabel myCurrentBranch; - /** - * The merge strategy - */ private JComboBox myStrategy; - /** - * No commit option - */ private JCheckBox myNoCommitCheckBox; - /** - * Squash commit option - */ private JCheckBox mySquashCommitCheckBox; - /** - * No fast forward option - */ private JCheckBox myNoFastForwardCheckBox; - /** - * Add log info to commit option - */ private JCheckBox myAddLogInformationCheckBox; - /** - * Selected remote option - */ private JComboBox myRemote; - /** - * The branch chooser - */ private ElementsChooser myBranchChooser; - /** - * The context project - */ private final Project myProject; private final GitRepositoryManager myRepositoryManager; - /** - * A constructor - * - * @param project a project to select - * @param roots a git repository roots for the project - * @param defaultRoot a guessed default root - */ public GitPullDialog(Project project, List roots, VirtualFile defaultRoot) { super(project, true); setTitle(GitBundle.getString("pull.title")); @@ -138,9 +95,6 @@ public class GitPullDialog extends DialogWrapper { init(); } - /** - * Validate dialog and enable buttons - */ private void validateDialog() { String selectedRemote = getRemote(); if (StringUtil.isEmptyOrSpaces(selectedRemote)) { @@ -150,9 +104,6 @@ public class GitPullDialog extends DialogWrapper { setOKActionEnabled(myBranchChooser.getMarkedElements().size() != 0); } - /** - * @return a pull handler configured according to dialog options - */ public GitLineHandler makeHandler(@NotNull String url) { GitLineHandler h = new GitLineHandler(myProject, gitRoot(), GitCommand.PULL); // ignore merge failure for the pull @@ -232,9 +183,6 @@ public class GitPullDialog extends DialogWrapper { return branch.getName().startsWith(remote + "/"); } - /** - * Update remotes for the git root - */ private void updateRemotes() { GitRepository repository = getRepository(); if (repository == null) { @@ -331,39 +279,23 @@ public class GitPullDialog extends DialogWrapper { }; } - /** - * @return a currently selected git root - */ public VirtualFile gitRoot() { return (VirtualFile)myGitRoot.getSelectedItem(); } - - /** - * Create branch chooser - */ private void createUIComponents() { myBranchChooser = new ElementsChooser(true); } - /** - * {@inheritDoc} - */ protected JComponent createCenterPanel() { return myPanel; } - /** - * {@inheritDoc} - */ @Override protected String getDimensionServiceKey() { return getClass().getName(); } - /** - * {@inheritDoc} - */ @Override protected String getHelpId() { return "reference.VersionControl.Git.Pull"; From 84feff03ea671c703fe9f5a3f2e7a2e0cfc2e126 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 1 Aug 2014 14:37:36 +0400 Subject: [PATCH 81/85] EA-57757 - assert: IconLoader.getIcon --- .../resources/icons/xmlProperties.png | Bin .../resources/icons/PropertiesIcons.java | 32 ------------------ 2 files changed, 32 deletions(-) rename plugins/properties/{ => properties-psi-api}/resources/icons/xmlProperties.png (100%) delete mode 100644 plugins/properties/resources/icons/PropertiesIcons.java diff --git a/plugins/properties/resources/icons/xmlProperties.png b/plugins/properties/properties-psi-api/resources/icons/xmlProperties.png similarity index 100% rename from plugins/properties/resources/icons/xmlProperties.png rename to plugins/properties/properties-psi-api/resources/icons/xmlProperties.png diff --git a/plugins/properties/resources/icons/PropertiesIcons.java b/plugins/properties/resources/icons/PropertiesIcons.java deleted file mode 100644 index 156d14467707..000000000000 --- a/plugins/properties/resources/icons/PropertiesIcons.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2000-2013 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 icons; - -import com.intellij.openapi.util.IconLoader; - -import javax.swing.*; - -/** - * NOTE THIS FILE IS AUTO-GENERATED - * DO NOT EDIT IT BY HAND, run build/scripts/icons.gant instead - */ -public class PropertiesIcons { - private static Icon load(String path) { - return IconLoader.getIcon(path, PropertiesIcons.class); - } - - public static final Icon XmlProperties = load("/icons/xmlProperties.png"); // 16x16 -} From 01b5b37d4d7255375c402ab442d766b6d4774a5d Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Fri, 1 Aug 2014 14:37:22 +0400 Subject: [PATCH 82/85] We should not set CANCEL if popup can't be closed. Let's cancel it later. The issue can be reproduced with the following steps: 1. Double-Shift 2. Type GUI 3: Select GUI Designer in Preferences and popup stays opened... --- .../platform-impl/src/com/intellij/ui/popup/AbstractPopup.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java index 6daf7e6057c0..e50403abef45 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java @@ -613,6 +613,8 @@ public class AbstractPopup implements JBPopup { if (myPopup != null) { if (!canClose()) { + debugState("cannot cancel popup", State.CANCEL); + myState = State.SHOWN; return; } storeDimensionSize(myContent.getSize()); From 05f994c753dd34dcabb3f8494ded87d198afa052 Mon Sep 17 00:00:00 2001 From: Sergey Savenko Date: Fri, 1 Aug 2014 15:01:13 +0400 Subject: [PATCH 83/85] hide table/list item hint when item is obscure --- .../src/com/intellij/ui/AbstractExpandableItemsHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java b/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java index 4e65063e7939..d8b204cfd76c 100644 --- a/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java +++ b/platform/platform-impl/src/com/intellij/ui/AbstractExpandableItemsHandler.java @@ -223,6 +223,7 @@ public abstract class AbstractExpandableItemsHandler Date: Fri, 1 Aug 2014 15:25:57 +0400 Subject: [PATCH 84/85] Gradle: remove extra jars from make process classpath for non-gradle project --- .../GradleBuildProcessParametersProvider.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java index 4a9dc92e022b..4337686235f5 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/compiler/GradleBuildProcessParametersProvider.java @@ -41,15 +41,27 @@ import java.util.List; * @since 7/22/2014 */ public class GradleBuildProcessParametersProvider extends BuildProcessParametersProvider { + @NotNull private final Project myProject; + private List myClasspath; + public GradleBuildProcessParametersProvider(@NotNull Project project) { + myProject = project; + } + @Override @NotNull public List getClassPath() { if (myClasspath == null) { myClasspath = ContainerUtil.newArrayList(); addGradleClassPath(myClasspath); - addOtherClassPath(myClasspath); + final ModuleManager moduleManager = ModuleManager.getInstance(myProject); + for (Module module : moduleManager.getModules()) { + if (ExternalSystemApiUtil.isExternalSystemAwareModule(GradleConstants.SYSTEM_ID, module)) { + addOtherClassPath(myClasspath); + break; + } + } } return myClasspath; } From ffb3b43af77a6e82294a165c89dbf0c7cdad663d Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 1 Aug 2014 14:46:05 +0400 Subject: [PATCH 85/85] EA-56960 - IAE: TextResult. --- .../intellij/codeInsight/template/impl/TemplateState.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java index 516ed9118c8c..0c838cd05ec5 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java @@ -212,8 +212,11 @@ public class TemplateState implements Disposable { if (variableName.equals(TemplateImpl.END)) { return new TextResult(""); } - if (myPredefinedVariableValues != null && myPredefinedVariableValues.containsKey(variableName)) { - return new TextResult(myPredefinedVariableValues.get(variableName)); + if (myPredefinedVariableValues != null) { + String text = myPredefinedVariableValues.get(variableName); + if (text != null) { + return new TextResult(text); + } } CharSequence text = myDocument.getCharsSequence(); int segmentNumber = myTemplate.getVariableSegmentNumber(variableName);