From f9f00fcc39dd84e546bed794f954e90e430f863c Mon Sep 17 00:00:00 2001 From: Evgeny Zakrevsky Date: Tue, 28 Feb 2012 15:51:36 +0400 Subject: [PATCH 01/23] JSTestDriver. Some layout improvements. --- .../util/src/com/intellij/util/ui/UIUtil.java | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 962822d61445..c273a0d355b4 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -19,10 +19,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.ui.ColorUtil; -import com.intellij.ui.Gray; -import com.intellij.ui.PanelWithAnchor; -import com.intellij.ui.SideBorder; +import com.intellij.ui.*; import com.intellij.util.ArrayUtil; import com.intellij.util.PairFunction; import com.intellij.util.Processor; @@ -2533,23 +2530,30 @@ public class UIUtil { return false; } - public static void mergeComponentsWithAnchor(PanelWithAnchor c1, PanelWithAnchor c2) { - if (c1 == null || c2 == null) return; - - if (c1.getAnchor() == null) { - c1.setAnchor(c2.getAnchor()); - } else { - if (c2.getAnchor() == null) { - c2.setAnchor(c1.getAnchor()); - } else { - JComponent anchor = c1.getAnchor().getPreferredSize().getWidth() > c2.getAnchor().getPreferredSize().getWidth() ? - c1.getAnchor() : c2.getAnchor(); - c2.setAnchor(anchor); - c1.setAnchor(anchor); - } - } + @Nullable + public static JComponent mergeComponentsWithAnchor(PanelWithAnchor...panels) { + return mergeComponentsWithAnchor(Arrays.asList(panels)); } + @Nullable + public static JComponent mergeComponentsWithAnchor(Collection panels) { + JComponent tempAnchor = null; + int maxWidth = 0; + for (PanelWithAnchor panel : panels) { + if (panel == null) continue; + if (panel.getAnchor() == null) continue; + if (maxWidth < panel.getAnchor().getPreferredSize().width) { + maxWidth = panel.getAnchor().getPreferredSize().width; + tempAnchor = panel.getAnchor(); + } + } + for (PanelWithAnchor panel : panels) { + if (panel == null) continue; + panel.setAnchor(tempAnchor); + } + return tempAnchor; + } + public static void setNotOpaqueRecursively(@NotNull Component component) { if (!isUnderAquaLookAndFeel()) return; From e01867ac8cd249491e24df280e6ac75a6a418cff Mon Sep 17 00:00:00 2001 From: Evgeny Zakrevsky Date: Tue, 28 Feb 2012 15:52:12 +0400 Subject: [PATCH 02/23] IDEA-78905 Run configuration must be unreasonably wide --- .../impl/ConfigurationSettingsEditorWrapper.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConfigurationSettingsEditorWrapper.java b/platform/lang-impl/src/com/intellij/execution/impl/ConfigurationSettingsEditorWrapper.java index 76169fa0771b..6a8b5f35018d 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConfigurationSettingsEditorWrapper.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConfigurationSettingsEditorWrapper.java @@ -96,7 +96,17 @@ public class ConfigurationSettingsEditorWrapper extends SettingsEditor Date: Mon, 27 Feb 2012 21:23:51 +0400 Subject: [PATCH 03/23] OC-2974: More correct definition of what's outside the project (takes certain extension points into an account) --- .../com/intellij/psi/search/scope/NonProjectFilesScope.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/search/scope/NonProjectFilesScope.java b/platform/lang-api/src/com/intellij/psi/search/scope/NonProjectFilesScope.java index e7a9c5557845..ca2e3c6eb61f 100644 --- a/platform/lang-api/src/com/intellij/psi/search/scope/NonProjectFilesScope.java +++ b/platform/lang-api/src/com/intellij/psi/search/scope/NonProjectFilesScope.java @@ -18,6 +18,7 @@ package com.intellij.psi.search.scope; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.search.ProjectScope; import com.intellij.psi.search.scope.packageSet.AbstractPackageSet; import com.intellij.psi.search.scope.packageSet.NamedScope; import com.intellij.psi.search.scope.packageSet.NamedScopesHolder; @@ -37,8 +38,7 @@ public class NonProjectFilesScope extends NamedScope { super(NAME, new AbstractPackageSet("NonProject") { public boolean contains(VirtualFile file, NamedScopesHolder holder) { if (file == null) return true; - final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(holder.getProject()).getFileIndex(); - return !(holder.getProject().isInitialized() && !fileIndex.isIgnored(file) && fileIndex.getContentRootForFile(file) != null); + return !ProjectScope.getProjectScope(holder.getProject()).contains(file); } }); } From cd1a56a6798dc00192f1519ea9d95e233a43ea29 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Mon, 27 Feb 2012 21:29:17 +0400 Subject: [PATCH 04/23] OC-2970 Default key mapping for 'close' in Xcode scheme --- platform/platform-resources/src/idea/Keymap_Xcode.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/platform-resources/src/idea/Keymap_Xcode.xml b/platform/platform-resources/src/idea/Keymap_Xcode.xml index 8beab27de036..17b81e175451 100644 --- a/platform/platform-resources/src/idea/Keymap_Xcode.xml +++ b/platform/platform-resources/src/idea/Keymap_Xcode.xml @@ -38,8 +38,8 @@ - - + + From cb2fc3de63b98e11a6339b57c1d933ac42886ac0 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Tue, 28 Feb 2012 14:59:51 +0400 Subject: [PATCH 05/23] OC-2684. Xcode seem to save dirty files asynchronously, after it actually lost focus to AppCode. --- .../src/com/intellij/ide/SaveAndSyncHandler.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/SaveAndSyncHandler.java b/platform/platform-impl/src/com/intellij/ide/SaveAndSyncHandler.java index 1ff03c99cda6..661a20a207d5 100644 --- a/platform/platform-impl/src/com/intellij/ide/SaveAndSyncHandler.java +++ b/platform/platform-impl/src/com/intellij/ide/SaveAndSyncHandler.java @@ -30,6 +30,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.openapi.vfs.newvfs.RefreshQueue; import com.intellij.openapi.vfs.newvfs.RefreshSession; +import com.intellij.util.Alarm; import org.jetbrains.annotations.NotNull; import java.beans.PropertyChangeEvent; @@ -48,6 +49,7 @@ public class SaveAndSyncHandler implements ApplicationComponent { private final AtomicInteger myBlockSaveOnFrameDeactivationCount = new AtomicInteger(); private final AtomicInteger myBlockSyncOnFrameActivationCount = new AtomicInteger(); + private final Alarm myRefreshDelayAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); public static SaveAndSyncHandler getInstance(){ return ApplicationManager.getApplication().getComponent(SaveAndSyncHandler.class); @@ -157,7 +159,8 @@ public class SaveAndSyncHandler implements ApplicationComponent { } - ApplicationManager.getApplication().invokeLater(new Runnable() { + myRefreshDelayAlarm.cancelAllRequests(); + myRefreshDelayAlarm.addRequest(new Runnable() { @Override public void run() { if (canSyncOrSave()) { @@ -166,7 +169,7 @@ public class SaveAndSyncHandler implements ApplicationComponent { maybeRefresh(ModalityState.NON_MODAL); } - }, ModalityState.NON_MODAL); + }, 300, ModalityState.NON_MODAL); if (LOG.isDebugEnabled()) { LOG.debug("exit: synchronize()"); @@ -213,4 +216,4 @@ public class SaveAndSyncHandler implements ApplicationComponent { public void unblockSyncOnFrameActivation() { myBlockSyncOnFrameActivationCount.decrementAndGet(); } -} \ No newline at end of file +} From 26418b37a180165ee6cddc1005139060d7145558 Mon Sep 17 00:00:00 2001 From: nik Date: Tue, 28 Feb 2012 18:18:11 +0400 Subject: [PATCH 06/23] compile server: added extension to provide additional source roots --- .../idea/AdditionalRootsProviderService.java | 22 +++++++++++++++++++ .../jps/idea/IdeaProjectLoader.groovy | 10 +++++++++ 2 files changed, 32 insertions(+) create mode 100644 jps/model/src/org/jetbrains/jps/idea/AdditionalRootsProviderService.java diff --git a/jps/model/src/org/jetbrains/jps/idea/AdditionalRootsProviderService.java b/jps/model/src/org/jetbrains/jps/idea/AdditionalRootsProviderService.java new file mode 100644 index 000000000000..7687d3cf75f2 --- /dev/null +++ b/jps/model/src/org/jetbrains/jps/idea/AdditionalRootsProviderService.java @@ -0,0 +1,22 @@ +package org.jetbrains.jps.idea; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.Module; + +import java.util.Collections; +import java.util.List; + +/** + * @author nik + */ +public abstract class AdditionalRootsProviderService { + @NotNull + public List getAdditionalSourceRoots(@NotNull Module module) { + return Collections.emptyList(); + } + + @NotNull + public List getAdditionalTestSourceRoots(@NotNull Module module) { + return Collections.emptyList(); + } +} diff --git a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy index 96bab165bc75..cb2c7f76b102 100644 --- a/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy +++ b/jps/model/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy @@ -16,6 +16,7 @@ public class IdeaProjectLoader { private Map pathVariables private ProjectMacroExpander projectMacroExpander private ProjectLoadingErrorReporter errorReporter + private static final OwnServiceLoader rootsProviderLoader = OwnServiceLoader.load(AdditionalRootsProviderService.class) public static String guessHome(Script script) { File home = new File(script["gant.file"].substring("file:".length())) @@ -508,6 +509,15 @@ public class IdeaProjectLoader { currentModule.languageLevel = convertLanguageLevel(languageLevel) } + rootsProviderLoader.each {AdditionalRootsProviderService service -> + def sourceRoots = service.getAdditionalSourceRoots(currentModule) + def testSourceRoots = service.getAdditionalTestSourceRoots(currentModule) + currentModule.sourceRoots.addAll(sourceRoots) + currentModule.testRoots.addAll(testSourceRoots) + if (!sourceRoots.isEmpty() || !testSourceRoots.isEmpty()) { + srcFolderExists = true + } + } if (srcFolderExists) { if (componentTag."@inherit-compiler-output" == "true") { if (projectOutputPath == null) { From 671d31cfbb94cf9a354696c0d2f11a72ae848eab Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 28 Feb 2012 15:35:31 +0100 Subject: [PATCH 07/23] edt save --- .../ide/diff/VirtualFileDiffElement.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java b/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java index 90ec7c08a626..7ad3430e1848 100644 --- a/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java +++ b/platform/platform-impl/src/com/intellij/ide/diff/VirtualFileDiffElement.java @@ -19,8 +19,7 @@ import com.intellij.ide.presentation.VirtualFilePresentation; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.application.AccessToken; -import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.diff.DiffRequest; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileChooser.FileChooserDescriptor; @@ -275,15 +274,14 @@ public class VirtualFileDiffElement extends DiffElement { } if (!docsToSave.isEmpty()) { - AccessToken token = WriteAction.start(); - try { - for (Document document : docsToSave) { - manager.saveDocument(document); + ApplicationManagerEx.getApplicationEx().runEdtSafeAction(new Runnable() { + @Override + public void run() { + for (Document document : docsToSave) { + manager.saveDocument(document); + } } - } - finally { - token.finish(); - } + }); } if (!FileWatcher.getInstance().isWatched(virtualFile)) { ((NewVirtualFile)virtualFile).markDirtyRecursively(); From 6f2922669b27a75ef7a77502fee4c1572437fb99 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 28 Feb 2012 15:51:24 +0100 Subject: [PATCH 08/23] p4: apply patch to a newly created changelist should really move all files to that changelist --- .../intellij/openapi/vcs/changes/VcsDirtyScopeManager.java | 4 ++-- .../src/com/intellij/util/ui/VcsBackgroundTask.java | 7 ------- .../openapi/vcs/changes/VcsDirtyScopeManagerImpl.java | 4 ++-- .../openapi/vcs/changes/VcsDirtyScopeManagerProxy.java | 4 ++-- .../src/com/intellij/openapi/vcs/changes/VcsGuess.java | 5 +++-- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManager.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManager.java index ce42d25f3ec2..60caeb50e53a 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManager.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManager.java @@ -46,14 +46,14 @@ public abstract class VcsDirtyScopeManager { * * @param file the file for which the status update is requested. */ - public abstract void fileDirty(VirtualFile file); + public abstract void fileDirty(@NotNull VirtualFile file); /** * Requests an asynchronous file status update for the specified file path. Must be called from a read action. * * @param file the file path for which the status update is requested. */ - public abstract void fileDirty(FilePath file); + public abstract void fileDirty(@NotNull FilePath file); /** * Requests an asynchronous file status update for all files under the specified directory. diff --git a/platform/vcs-api/src/com/intellij/util/ui/VcsBackgroundTask.java b/platform/vcs-api/src/com/intellij/util/ui/VcsBackgroundTask.java index 78a5f6abb0a2..06cc1a094892 100644 --- a/platform/vcs-api/src/com/intellij/util/ui/VcsBackgroundTask.java +++ b/platform/vcs-api/src/com/intellij/util/ui/VcsBackgroundTask.java @@ -32,22 +32,15 @@ import java.util.List; * @author yole */ public abstract class VcsBackgroundTask extends Task.ConditionalModal { - @NotNull private final PerformInBackgroundOption myBackgroundOption; private final Collection myItems; private final List myExceptions = new ArrayList(); public VcsBackgroundTask(final Project project, @NotNull final String title, @NotNull final PerformInBackgroundOption backgroundOption, final Collection itemsToProcess) { super(project, title, false, backgroundOption); - myBackgroundOption = backgroundOption; myItems = itemsToProcess; } - @NotNull - public PerformInBackgroundOption getBackgroundOption() { - return myBackgroundOption; - } - public void run(@NotNull ProgressIndicator indicator) { for(T item: myItems) { try { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java index decb13a4393a..8ffa72209ea7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java @@ -252,7 +252,7 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr } } - public void fileDirty(final VirtualFile file) { + public void fileDirty(@NotNull final VirtualFile file) { try { final AbstractVcs vcs = myGuess.getVcsForDirty(file); if (vcs == null) return; @@ -269,7 +269,7 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr } } - public void fileDirty(final FilePath file) { + public void fileDirty(@NotNull final FilePath file) { try { final AbstractVcs vcs = myGuess.getVcsForDirty(file); if (vcs == null) return; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerProxy.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerProxy.java index 135d73f7fa4e..fdcf04749ef8 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerProxy.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerProxy.java @@ -54,11 +54,11 @@ class VcsDirtyScopeManagerProxy extends VcsDirtyScopeManager { myFiles.clear(); } - public void fileDirty(final VirtualFile file) { + public void fileDirty(@NotNull final VirtualFile file) { myVFiles.add(file); } - public void fileDirty(final FilePath file) { + public void fileDirty(@NotNull final FilePath file) { myFiles.add(file); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java index 93bd355171fe..2ce7ea62b1fe 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsGuess.java @@ -27,6 +27,7 @@ import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class VcsGuess { @@ -41,7 +42,7 @@ public class VcsGuess { } @Nullable - public AbstractVcs getVcsForDirty(final VirtualFile file) { + public AbstractVcs getVcsForDirty(@NotNull final VirtualFile file) { if (!file.isInLocalFileSystem()) { return null; } @@ -56,7 +57,7 @@ public class VcsGuess { } @Nullable - public AbstractVcs getVcsForDirty(final FilePath filePath) { + public AbstractVcs getVcsForDirty(@NotNull final FilePath filePath) { if (filePath.isNonLocal()) { return null; } From ee3c392e33216821f435e1b5624f2b2b544b3054 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 28 Feb 2012 16:15:19 +0100 Subject: [PATCH 09/23] complete private members on second invocation in groovy --- .../expressions/CompleteReferenceExpression.java | 3 ++- .../groovy/completion/GroovyCompletionTest.groovy | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java index cba92f066030..431bd788388b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java @@ -384,7 +384,8 @@ public class CompleteReferenceExpression { } GroovyResolveResult result = (GroovyResolveResult)o; - if (!result.isStaticsOK() || !result.isAccessible()) return; + if (!result.isStaticsOK()) return; + if (!result.isAccessible() && myParameters.getInvocationCount() < 2) return; if (mySkipPackages && result.getElement() instanceof PsiPackage) return; PsiElement element = result.getElement(); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy index bd693a882af2..058d28cc9305 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/completion/GroovyCompletionTest.groovy @@ -1172,4 +1172,15 @@ class Base { new Base().@prefixField''') } + public void testPrivateFieldOnSecondInvocation() { + myFixture.configureByText('_a.groovy', '''\ +class Base { + private int field1 +} + +new Base().fiex''') + myFixture.complete(CompletionType.BASIC, 2) + assert myFixture.lookupElementStrings == ['field1'] + } + } \ No newline at end of file From 5bedec59de09f672434d71d64e4da1021eef1605 Mon Sep 17 00:00:00 2001 From: irengrig Date: Tue, 28 Feb 2012 19:22:43 +0400 Subject: [PATCH 10/23] IDEA-81569 Poor performance when moving lines in web.xml (Alt-Shift-DOWN/UP) while the web.xml is displayed in change details (Changes tool window) --- .../vcs/changes/TodoForBaseRevision.java | 60 +++++++++++++++++ .../vcs/changes/TodoForExistingFile.java | 67 +++++++++++++++++++ .../openapi/vcs/changes/TodoForRanges.java | 50 ++++++++------ .../vcs/impl/ContentRevisionCache.java | 16 +++++ .../FragmentedDiffRequestFromChange.java | 7 +- .../changes/PreparedFragmentedContent.java | 44 +++++++++--- 6 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForBaseRevision.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForExistingFile.java diff --git a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForBaseRevision.java b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForBaseRevision.java new file mode 100644 index 000000000000..63ddf87e0c60 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForBaseRevision.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.changes; + +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.search.PsiSearchHelper; +import com.intellij.psi.search.TodoItem; +import com.intellij.util.Consumer; + +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/28/12 + * Time: 6:06 PM + */ +public class TodoForBaseRevision extends TodoForRanges { + private final Getter myGetter; + private final Consumer mySaver; + + public TodoForBaseRevision(Project project, + List ranges, + int additionalOffset, + String name, + String text, + boolean revision, FileType type, final Getter cacheGetter, + final Consumer cacheSaver) { + super(project, ranges, additionalOffset, name, text, revision, type); + myGetter = cacheGetter; + mySaver = cacheSaver; + } + + @Override + protected TodoItem[] getTodoItems() { + final TodoItem[] items = (TodoItem[])myGetter.get(); + if (items != null) return items; + final TodoItem[] todoItems = getTodoForText(PsiSearchHelper.SERVICE.getInstance(myProject)); + if (todoItems != null) { + mySaver.consume(todoItems); + } + return todoItems; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForExistingFile.java b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForExistingFile.java new file mode 100644 index 000000000000..5a0f02dae3bc --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForExistingFile.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.changes; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.PsiManager; +import com.intellij.psi.search.PsiSearchHelper; +import com.intellij.psi.search.TodoItem; + +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 2/28/12 + * Time: 4:48 PM + */ +public class TodoForExistingFile extends TodoForRanges { + private final VirtualFile myFile; + + public TodoForExistingFile(Project project, + List ranges, + int additionalOffset, + String name, + String text, + boolean revision, FileType type, VirtualFile file) { + super(project, ranges, additionalOffset, name, text, revision, type); + myFile = file; + } + + protected TodoItem[] getTodoItems() { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public TodoItem[] compute() { + final PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(myProject); + + PsiFile psiFile = myFile == null ? null : PsiManager.getInstance(myProject).findFile(myFile); + if (psiFile != null) { + return helper.findTodoItems(psiFile); + } + + return getTodoForText(helper); + } + }); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForRanges.java b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForRanges.java index 8cbe705310e2..a6e65a39fa68 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForRanges.java +++ b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TodoForRanges.java @@ -41,36 +41,32 @@ import java.util.List; * Date: 9/9/11 * Time: 9:55 AM */ -public class TodoForRanges { - private final Project myProject; - private final String myFileName; - private final String myText; - private final boolean myOldRevision; +public abstract class TodoForRanges { + protected final Project myProject; private final List myRanges; - private final FileType myFileType; private final int myAdditionalOffset; + protected final String myFileName; + protected final String myText; + protected final boolean myOldRevision; + protected final FileType myFileType; - public TodoForRanges(final Project project, final String fileName, final String text, final boolean oldRevision, - final List ranges, FileType fileType, int additionalOffset) { + protected TodoForRanges(final Project project, + final List ranges, + int additionalOffset, + String name, + String text, + boolean revision, FileType type) { myProject = project; - myFileName = fileName; - myText = text; - myOldRevision = oldRevision; myRanges = ranges; - myFileType = fileType; myAdditionalOffset = additionalOffset; + myFileName = name; + myText = text; + myOldRevision = revision; + myFileType = type; } public List> execute() { - final TodoItem[] todoItems = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public TodoItem[] compute() { - final PsiFile psiFile = PsiFileFactory.getInstance(myProject).createFileFromText((myOldRevision ? "old" : "") + myFileName, myFileType, myText); - - final PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(myProject); - return helper.findTodoItemsLight(psiFile); - } - }); + final TodoItem[] todoItems = getTodoItems(); final StepIntersection stepIntersection = new StepIntersection(new Convertor() { @@ -105,4 +101,16 @@ public class TodoForRanges { } return result; } + + protected abstract TodoItem[] getTodoItems(); + + protected TodoItem[] getTodoForText(PsiSearchHelper helper) { + final PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiFile compute() { + return PsiFileFactory.getInstance(myProject).createFileFromText((myOldRevision ? "old" : "") + myFileName, myFileType, myText); + } + }); + return helper.findTodoItemsLight(psiFile); + } } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/impl/ContentRevisionCache.java b/platform/vcs-api/src/com/intellij/openapi/vcs/impl/ContentRevisionCache.java index ad1e50a3b3ae..5dc1948d8da6 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/impl/ContentRevisionCache.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/impl/ContentRevisionCache.java @@ -16,6 +16,7 @@ package com.intellij.openapi.vcs.impl; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Throwable2Computable; import com.intellij.openapi.vcs.FilePath; @@ -48,12 +49,14 @@ public class ContentRevisionCache { private final Object myLock; private final SLRUMap myCache; private final SLRUMap myCurrentRevisionsCache; + private final SLRUMap, Object> myCustom; private long myCounter; public ContentRevisionCache() { myLock = new Object(); myCache = new SLRUMap(100, 50); myCurrentRevisionsCache = new SLRUMap(200, 50); + myCustom = new SLRUMap, Object>(30,30); myCounter = 0; } @@ -73,6 +76,19 @@ public class ContentRevisionCache { } } + public void putCustom(FilePath path, VcsRevisionNumber number, final Object o) { + synchronized (myLock) { + myCustom.put(new Pair(path, number), o); + } + } + + @Nullable + public Object getCustom(FilePath path, VcsRevisionNumber number) { + synchronized (myLock) { + return myCustom.get(new Pair(path, number)); + } + } + public void clearAllCurrent() { synchronized (myLock) { ++ myCounter; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java index f867efc436ad..0b84302699fb 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java @@ -81,14 +81,15 @@ public class FragmentedDiffRequestFromChange { final FileStatus fs = change.getFileStatus(); fragmentedContent.setIsAddition(FileStatus.ADDED.equals(fs)); fragmentedContent.setOneSide(FileStatus.ADDED.equals(fs) || FileStatus.DELETED.equals(fs)); - final PreparedFragmentedContent preparedFragmentedContent = new PreparedFragmentedContent(myProject, fragmentedContent, - filePath.getName(), filePath.getFileType()); VirtualFile file = filePath.getVirtualFile(); if (file == null) { filePath.hardRefresh(); file = filePath.getVirtualFile(); } - preparedFragmentedContent.setVirtualFile(file); + final PreparedFragmentedContent preparedFragmentedContent = new PreparedFragmentedContent(myProject, fragmentedContent, + filePath.getName(), filePath.getFileType(), + change.getBeforeRevision() == null ? null : change.getBeforeRevision().getRevisionNumber(), + change.getAfterRevision() == null ? null : change.getAfterRevision().getRevisionNumber(), filePath, file); return preparedFragmentedContent; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/PreparedFragmentedContent.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/PreparedFragmentedContent.java index 71c6b4a4acab..9f1d666f9b17 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/PreparedFragmentedContent.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/PreparedFragmentedContent.java @@ -24,11 +24,18 @@ import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.SyntaxHighlighter; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsConfiguration; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.openapi.vcs.impl.ContentRevisionCache; +import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.BeforeAfter; +import com.intellij.util.Consumer; import java.util.ArrayList; import java.util.Collections; @@ -60,14 +67,25 @@ public class PreparedFragmentedContent { private final FragmentedContent myFragmentedContent; private final String myFileName; private final FileType myFileType; + private final VcsRevisionNumber myBeforeNumber; + private final VcsRevisionNumber myAfterNumber; private VirtualFile myFile; + private FilePath myFilePath; public PreparedFragmentedContent(final Project project, final FragmentedContent fragmentedContent, final String fileName, - final FileType fileType) { + final FileType fileType, + VcsRevisionNumber beforeNumber, + VcsRevisionNumber afterNumber, + FilePath path, + VirtualFile file) { + myFile = file; myProject = project; myFragmentedContent = fragmentedContent; myFileName = fileName; myFileType = fileType; + myBeforeNumber = beforeNumber; + myAfterNumber = afterNumber; + myFilePath = path; oldConvertor = new LineNumberConvertor(); newConvertor = new LineNumberConvertor(); sbOld = new StringBuilder(); @@ -301,18 +319,26 @@ public class PreparedFragmentedContent { } private void setTodoHighlighting(final Document oldDocument, final Document document) { - final List> beforeTodoRanges = new TodoForRanges(myProject, myFileName, oldDocument.getText(), true, - getBeforeFragments(), myFileType, 1).execute(); - final List> afterTodoRanges = new TodoForRanges(myProject, myFileName, document.getText(), false, - getAfterFragments(), myFileType, 1).execute(); + final ContentRevisionCache cache = ProjectLevelVcsManager.getInstance(myProject).getContentRevisionCache(); + final List> beforeTodoRanges = myBeforeNumber == null ? Collections.>emptyList() : + new TodoForBaseRevision(myProject, getBeforeFragments(), 1, myFileName, oldDocument.getText(), true, myFileType, new Getter() { + @Override + public Object get() { + return cache.getCustom(myFilePath, myBeforeNumber); + } + }, new Consumer() { + @Override + public void consume(Object items) { + cache.putCustom(myFilePath, myBeforeNumber, items); + } + }).execute(); + + final List> afterTodoRanges = new TodoForExistingFile(myProject, getAfterFragments(), 1, + myFileName, document.getText(), false, myFileType, myFile).execute(); setBeforeTodoRanges(beforeTodoRanges); setAfterTodoRanges(afterTodoRanges); } - public void setVirtualFile(VirtualFile file) { - myFile = file; - } - public VirtualFile getFile() { return myFile; } From 675e8056dd433732cb1defa017ff0daacc678570 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Tue, 28 Feb 2012 19:35:40 +0400 Subject: [PATCH 11/23] RUBY-10527 SCSS Editor is already broken --- .../editorActions/enter/BaseIndentEnterHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java index e96aa03e9c21..da9e2e01c431 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/BaseIndentEnterHandler.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.editorActions.enter; import com.intellij.lang.Language; +import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.editor.Document; @@ -122,6 +123,9 @@ public class BaseIndentEnterHandler extends EnterHandlerDelegateAdapter { return Result.Stop; } + if (LanguageFormatting.INSTANCE.forLanguage(myLanguage) != null) { + return Result.Continue; + } EditorModificationUtil.insertStringAtCaret(editor, "\n" + lineIndent); editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(lineNumber + 1, lineIndent.length())); return Result.Stop; From 2e2b1decc71331978cfa208f94b87bda9049734e Mon Sep 17 00:00:00 2001 From: Dmitry Boulytchev Date: Tue, 28 Feb 2012 19:47:03 +0400 Subject: [PATCH 12/23] Rolling back integrate speedup (compile-server). --- .../org/jetbrains/ether/dependencyView/Mappings.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java index 058870f86f18..e793168989ab 100644 --- a/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java +++ b/jps/model/src/org/jetbrains/ether/dependencyView/Mappings.java @@ -260,8 +260,10 @@ public class Mappings { private abstract class PostPass { boolean myPerformed = false; + abstract void perform(); - void run () { + + void run() { if (!myPerformed) { myPerformed = true; perform(); @@ -1605,7 +1607,7 @@ public class Mappings { } } - if (delta.isDifferentiated() && false) { + if (delta.isDifferentiated()) { for (DependencyContext.S c : delta.getChangedClasses()) { myClassToSubclasses.remove(c); @@ -1629,7 +1631,7 @@ public class Mappings { myClassToSourceFile.putAll(delta.myClassToSourceFile); } - if (delta.isDifferentiated()) { + if (delta.isDifferentiated() && false) { for (DependencyContext.S f : delta.getChangedFiles()) { mySourceFileToClasses.remove(f); final Collection classes = delta.mySourceFileToClasses.get(f); @@ -1767,9 +1769,9 @@ public class Mappings { @Override public void registerImports(final String className, final Collection imports, Collection staticImports) { - for (String s: staticImports) { + for (String s : staticImports) { int i = s.length() - 1; - for (; s.charAt(i) != '.'; i--); + for (; s.charAt(i) != '.'; i--) ; imports.add(s.substring(0, i)); } From c547cd8064da86bc66ab1c6bc3f3299a00bbde1b Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 27 Feb 2012 21:58:24 +0400 Subject: [PATCH 13/23] replace AndroidClassesAndJarsStateStorage by more generic AndroidFileSetStorage --- .../android/AndroidClassesAndJarsState.java | 60 ------------------- .../AndroidClassesAndJarsStateStorage.java | 43 ------------- .../jps/android/AndroidDexBuilder.java | 23 +++---- .../jps/android/AndroidFileSetState.java | 7 ++- .../jetbrains/jps/android/AndroidJpsUtil.java | 10 ++++ .../AndroidLibraryPackagingBuilder.java | 7 +-- .../AndroidResourcePackagingBuilder.java | 6 +- 7 files changed, 34 insertions(+), 122 deletions(-) delete mode 100644 plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidClassesAndJarsState.java delete mode 100644 plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidClassesAndJarsStateStorage.java diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidClassesAndJarsState.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidClassesAndJarsState.java deleted file mode 100644 index 3a6c4bbeccd1..000000000000 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidClassesAndJarsState.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.jetbrains.jps.android; - -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.Processor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.incremental.storage.ValidityState; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * @author Eugene.Kudelevsky - */ -class AndroidClassesAndJarsState implements ValidityState { - private Map myFiles; - - public AndroidClassesAndJarsState(@NotNull Collection roots) { - myFiles = new HashMap(); - - for (String rootPath : roots) { - AndroidJpsUtil.processClassFilesAndJarsRecursively(rootPath, new Processor() { - @Override - public boolean process(File file) { - myFiles.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified()); - return true; - } - }); - } - } - - public AndroidClassesAndJarsState(@NotNull DataInput in) throws IOException { - myFiles = new HashMap(); - int size = in.readInt(); - - while (size-- > 0) { - final String path = in.readUTF(); - final long timestamp = in.readLong(); - myFiles.put(path, timestamp); - } - } - - public boolean equalsTo(ValidityState otherState) { - return otherState instanceof AndroidClassesAndJarsState - && myFiles.equals(((AndroidClassesAndJarsState)otherState).myFiles); - } - - public void save(DataOutput out) throws IOException { - out.writeInt(myFiles.size()); - - for (String path : myFiles.keySet()) { - out.writeUTF(path); - out.writeLong(myFiles.get(path)); - } - } -} diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidClassesAndJarsStateStorage.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidClassesAndJarsStateStorage.java deleted file mode 100644 index cb765dd8a35c..000000000000 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidClassesAndJarsStateStorage.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.jetbrains.jps.android; - -import com.intellij.util.io.DataExternalizer; -import com.intellij.util.io.EnumeratorStringDescriptor; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.incremental.storage.AbstractStateStorage; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.File; -import java.io.IOException; - -/** - * @author Eugene.Kudelevsky - */ -public class AndroidClassesAndJarsStateStorage extends AbstractStateStorage { - - @NonNls private static final String CLASSES_AND_JARS_STORAGE = "classes_and_jars"; - - public AndroidClassesAndJarsStateStorage(@NotNull File dataStorageRoot, @NotNull String suffix) throws IOException { - super(getStorageFile(dataStorageRoot, suffix), new EnumeratorStringDescriptor(), new MyDataExternalizer()); - } - - @NotNull - private static File getStorageFile(@NotNull File dataStorageRoot, @NotNull String suffix) { - return new File(new File(new File(dataStorageRoot, AndroidJpsUtil.ANDROID_STORAGE_DIR), CLASSES_AND_JARS_STORAGE + suffix), - CLASSES_AND_JARS_STORAGE); - } - - private static class MyDataExternalizer implements DataExternalizer { - - @Override - public void save(DataOutput out, AndroidClassesAndJarsState value) throws IOException { - value.save(out); - } - - @Override - public AndroidClassesAndJarsState read(DataInput in) throws IOException { - return new AndroidClassesAndJarsState(in); - } - } -} diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java index 35e4c6dfb139..8cc59ebf9254 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java @@ -55,11 +55,12 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { private static void doBuild(CompileContext context) throws IOException, ProjectBuildException { final File root = context.getDataManager().getDataStorageRoot(); - AndroidClassesAndJarsStateStorage dexStateStorage = null; - AndroidClassesAndJarsStateStorage proguardStateStorage = null; + AndroidFileSetStorage dexStateStorage = null; + AndroidFileSetStorage proguardStateStorage = null; try { - dexStateStorage = new AndroidClassesAndJarsStateStorage(root, "_dex"); - proguardStateStorage = new AndroidClassesAndJarsStateStorage(root, "_proguard"); + dexStateStorage = new AndroidFileSetStorage(root, "dex"); + proguardStateStorage = new AndroidFileSetStorage(root, "proguard"); + if (!doDexBuild(context, dexStateStorage, proguardStateStorage)) { throw new ProjectBuildException(); } @@ -75,8 +76,8 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { } private static boolean doDexBuild(@NotNull CompileContext context, - @NotNull AndroidClassesAndJarsStateStorage dexStateStorage, - @NotNull AndroidClassesAndJarsStateStorage proguardStateStorage) { + @NotNull AndroidFileSetStorage dexStateStorage, + @NotNull AndroidFileSetStorage proguardStateStorage) { boolean success = true; for (Module module : context.getProject().getModules().values()) { @@ -145,10 +146,10 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { } } } - final AndroidClassesAndJarsState newState = new AndroidClassesAndJarsState(fileSet); + final AndroidFileSetState newState = new AndroidFileSetState(fileSet, AndroidJpsUtil.CLASSES_AND_JARS_FILTER); if (context.isMake()) { - final AndroidClassesAndJarsState oldState = dexStateStorage.getState(module.getName()); + final AndroidFileSetState oldState = dexStateStorage.getState(module.getName()); if (oldState != null && oldState.equalsTo(newState)) { continue; } @@ -254,7 +255,7 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { @NotNull CompileContext context, @NotNull String outputJarPath, @NotNull String proguardCfgPath, - @NotNull AndroidClassesAndJarsStateStorage proguardStateStorage) throws IOException { + @NotNull AndroidFileSetStorage proguardStateStorage) throws IOException { final Module module = facet.getModule(); final File proguardCfgFile = new File(proguardCfgPath); @@ -294,8 +295,8 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { allFiles.addAll(libClassFilesDirs); allFiles.addAll(externalJars); - final AndroidClassesAndJarsState newState = new AndroidClassesAndJarsState(allFiles); - final AndroidClassesAndJarsState oldState = proguardStateStorage.getState(module.getName()); + final AndroidFileSetState newState = new AndroidFileSetState(allFiles, AndroidJpsUtil.CLASSES_AND_JARS_FILTER); + final AndroidFileSetState oldState = proguardStateStorage.getState(module.getName()); if (context.getTimestampStorage().getStamp(proguardCfgFile) == proguardCfgFile.lastModified() && newState.equalsTo(oldState)) { return true; diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFileSetState.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFileSetState.java index 925e71361fac..17e5210c4452 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFileSetState.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFileSetState.java @@ -1,5 +1,6 @@ package org.jetbrains.jps.android; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.Processor; import com.intellij.util.containers.HashMap; @@ -19,14 +20,16 @@ import java.util.Map; public class AndroidFileSetState implements ValidityState { private final Map myTimestamps; - public AndroidFileSetState(@NotNull Collection roots) { + public AndroidFileSetState(@NotNull Collection roots, @NotNull final Condition filter) { myTimestamps = new HashMap(); for (String resourceDir : roots) { FileUtil.processFilesRecursively(new File(resourceDir), new Processor() { @Override public boolean process(File file) { - myTimestamps.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified()); + if (filter.value(file)) { + myTimestamps.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified()); + } return true; } }); diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java index 0d254ac1e252..3027a7b49e34 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java @@ -3,6 +3,7 @@ package org.jetbrains.jps.android; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.ArrayUtil; @@ -34,9 +35,18 @@ import java.util.regex.Matcher; */ class AndroidJpsUtil { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.android.AndroidJpsUtil"); + @NonNls public static final String ANDROID_STORAGE_DIR = "android"; @NonNls private static final String RESOURCE_CACHE_STORAGE = "res-cache"; + public static final Condition CLASSES_AND_JARS_FILTER = new Condition() { + @Override + public boolean value(File file) { + final String ext = FileUtil.getExtension(file.getName()); + return "jar".equals(ext) || "class".equals(ext); + } + }; + private AndroidJpsUtil() { } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java index 7b09dc105286..303cc2793b4a 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java @@ -45,8 +45,7 @@ public class AndroidLibraryPackagingBuilder extends ModuleLevelBuilder { private static ModuleLevelBuilder.ExitCode doBuild(CompileContext context, ModuleChunk chunk) throws IOException { boolean success = true; - final AndroidClassesAndJarsStateStorage storage = - new AndroidClassesAndJarsStateStorage(context.getDataManager().getDataStorageRoot(), "_libs"); + final AndroidFileSetStorage storage = new AndroidFileSetStorage(context.getDataManager().getDataStorageRoot(), "libs_packaging"); try { for (Module module : chunk.getModules()) { @@ -73,8 +72,8 @@ public class AndroidLibraryPackagingBuilder extends ModuleLevelBuilder { final Set subdirs = new HashSet(); AndroidJpsUtil.addSubdirectories(classesDir, subdirs); - final AndroidClassesAndJarsState newState = new AndroidClassesAndJarsState(subdirs); - final AndroidClassesAndJarsState oldState = storage.getState(module.getName()); + final AndroidFileSetState newState = new AndroidFileSetState(subdirs, AndroidJpsUtil.CLASSES_AND_JARS_FILTER); + final AndroidFileSetState oldState = storage.getState(module.getName()); if (oldState != null && oldState.equalsTo(newState)) { continue; diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java index b44f58920990..96cdbfeadf63 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java @@ -1,6 +1,7 @@ package org.jetbrains.jps.android; import com.android.sdklib.IAndroidTarget; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.util.containers.HashMap; import org.jetbrains.android.compiler.tools.AndroidApt; @@ -64,6 +65,7 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { } } + @SuppressWarnings("unchecked") private static void fillStates(@NotNull Collection modules, @NotNull Map resourcesStates, @NotNull Map assetsStates) throws IOException { @@ -75,13 +77,13 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { final List resourceDirs = resourceDir != null ? Arrays.asList(resourceDir.getPath()) : Collections.emptyList(); - resourcesStates.put(module, new AndroidFileSetState(resourceDirs)); + resourcesStates.put(module, new AndroidFileSetState(resourceDirs, Condition.TRUE)); final File assetsDir = facet.getAssetsDir(); final List assetDirs = assetsDir != null ? Arrays.asList(assetsDir.getPath()) : Collections.emptyList(); - assetsStates.put(module, new AndroidFileSetState(assetDirs)); + assetsStates.put(module, new AndroidFileSetState(assetDirs, Condition.TRUE)); } } } From 899b8219129b9dedbcb5da97a6ef11c6445ebe71 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 27 Feb 2012 22:55:43 +0400 Subject: [PATCH 14/23] move AndroidApkBuilder to android-rt module to use by android-jps plugin --- .../messages/AndroidBundle.properties | 1 - plugins/android/rt/android-rt.iml | 4 + .../compiler/tools/AndroidApkBuilder.java | 225 ++++++++---------- .../android/util/AndroidCommonUtils.java | 4 + plugins/android/src/META-INF/plugin.xml | 8 + .../actions/RunAndroidAvdManagerAction.java | 4 +- .../actions/RunAndroidSdkManagerAction.java | 4 +- .../android/compiler/AndroidCompileUtil.java | 36 +++ .../compiler/AndroidPackagingCompiler.java | 41 ++-- .../compiler/ExcludedSourcesFilter.java | 23 ++ .../android/exportSignedPackage/ApkStep.java | 4 +- .../jetbrains/android/facet/AndroidFacet.java | 4 +- .../newProject/AndroidModuleBuilder.java | 3 +- .../org/jetbrains/android/run/AvdChooser.java | 3 +- .../jetbrains/android/sdk/AndroidSdkData.java | 2 +- .../android/sdk/AndroidSdkUtils.java | 4 - 16 files changed, 211 insertions(+), 159 deletions(-) rename plugins/android/{ => rt}/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java (62%) create mode 100644 plugins/android/src/org/jetbrains/android/compiler/ExcludedSourcesFilter.java diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties index 321c9738d4b4..873713f765d3 100644 --- a/plugins/android/resources/messages/AndroidBundle.properties +++ b/plugins/android/resources/messages/AndroidBundle.properties @@ -139,7 +139,6 @@ avd.not.valid.error=AVD {0} is not valid avd.not.compatible.error=Build target of AVD {0} is not compatible with your build target avd.cannot.be.loaded.error=Cannot load virtual devices navigate.to.android.resource.action=Navigate to Android resource definition -android.debug.certificate.expired.error=Debug certificate expired on {0}. Cannot regenerate it, please delete file \"{1}\" manually. android.identifier.expected={0} is not an identifier android.directory.cannot.be.found.error="{0}" directory cannot be found create.run.configuration.question=Do you want to create run configuration for module {0}? diff --git a/plugins/android/rt/android-rt.iml b/plugins/android/rt/android-rt.iml index 1b33185c2722..a45948b193fb 100644 --- a/plugins/android/rt/android-rt.iml +++ b/plugins/android/rt/android-rt.iml @@ -14,11 +14,15 @@ + + + + diff --git a/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java b/plugins/android/rt/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java similarity index 62% rename from plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java rename to plugins/android/rt/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java index 9eee93725d1b..16fadec427af 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java +++ b/plugins/android/rt/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java @@ -20,25 +20,15 @@ import com.android.jarutils.JavaResourceFilter; import com.android.jarutils.SignedJarBuilder; import com.android.prefs.AndroidLocation; import com.android.sdklib.SdkConstants; -import com.intellij.ide.highlighter.ArchiveFileType; -import com.intellij.openapi.compiler.CompilerManager; -import com.intellij.openapi.compiler.CompilerMessageCategory; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtil; -import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import com.intellij.util.text.DateFormatUtil; -import org.jetbrains.android.compiler.AndroidCompileUtil; -import org.jetbrains.android.facet.AndroidFacet; -import org.jetbrains.android.sdk.AndroidPlatform; -import org.jetbrains.android.sdk.AndroidSdkUtils; -import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidCommonUtils; +import org.jetbrains.android.util.AndroidCompilerMessageKind; +import org.jetbrains.android.util.AndroidExecutionUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -54,7 +44,7 @@ import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import static com.intellij.openapi.compiler.CompilerMessageCategory.*; +import static org.jetbrains.android.util.AndroidCompilerMessageKind.*; /** * @author yole @@ -68,7 +58,7 @@ public class AndroidApkBuilder { private AndroidApkBuilder() { } - private static Map> filterUsingKeystoreMessages(Map> messages) { + private static Map> filterUsingKeystoreMessages(Map> messages) { List infoMessages = messages.get(INFORMATION); if (infoMessages == null) { infoMessages = new ArrayList(); @@ -118,48 +108,49 @@ public class AndroidApkBuilder { } } - public static Map> execute(Project project, - @NotNull String resPackagePath, - @NotNull String dexPath, - @NotNull VirtualFile[] sourceRoots, - @NotNull String[] externalJars, - @NotNull VirtualFile[] nativeLibsFolders, - @NotNull String finalApk, - boolean unsigned, - @NotNull String sdkPath, - @Nullable String customKeystorePath) throws IOException { + public static Map> execute(@NotNull String resPackagePath, + @NotNull String dexPath, + @NotNull String[] sourceRoots, + @NotNull String[] externalJars, + @NotNull String[] nativeLibsFolders, + @NotNull String finalApk, + boolean unsigned, + @NotNull String sdkPath, + @Nullable String customKeystorePath, + @NotNull Condition resourceFilter) throws IOException { if (unsigned) { return filterUsingKeystoreMessages( - finalPackage(project, dexPath, sourceRoots, externalJars, nativeLibsFolders, finalApk, resPackagePath, customKeystorePath, false)); + finalPackage(dexPath, sourceRoots, externalJars, nativeLibsFolders, finalApk, resPackagePath, customKeystorePath, false, + resourceFilter)); } - final Map> map = new HashMap>(); - final String zipAlignPath = sdkPath + File.separator + AndroidSdkUtils.toolPath(SdkConstants.FN_ZIPALIGN); + final Map> map = new HashMap>(); + final String zipAlignPath = sdkPath + File.separator + AndroidCommonUtils.toolPath(SdkConstants.FN_ZIPALIGN); boolean withAlignment = new File(zipAlignPath).exists(); String unalignedApk = finalApk + UNALIGNED_SUFFIX; - Map> map2 = filterUsingKeystoreMessages( - finalPackage(project, dexPath, sourceRoots, externalJars, nativeLibsFolders, withAlignment ? unalignedApk : finalApk, resPackagePath, - customKeystorePath, true)); + Map> map2 = filterUsingKeystoreMessages( + finalPackage(dexPath, sourceRoots, externalJars, nativeLibsFolders, withAlignment ? unalignedApk : finalApk, resPackagePath, + customKeystorePath, true, resourceFilter)); map.putAll(map2); if (withAlignment && map.get(ERROR).size() == 0) { - map2 = AndroidCompileUtil.execute(zipAlignPath, "-f", "4", unalignedApk, finalApk); + map2 = AndroidExecutionUtil.doExecute(zipAlignPath, "-f", "4", unalignedApk, finalApk); map.putAll(map2); } return map; } - private static Map> finalPackage(Project project, - @NotNull String dexPath, - @NotNull VirtualFile[] sourceRoots, - @NotNull String[] externalJars, - @NotNull VirtualFile[] nativeLibsFolders, - @NotNull String outputApk, - @NotNull String apkPath, - @Nullable String customKeystorePath, - boolean signed) { - final Map> result = new HashMap>(); + private static Map> finalPackage(@NotNull String dexPath, + @NotNull String[] sourceRoots, + @NotNull String[] externalJars, + @NotNull String[] nativeLibsFolders, + @NotNull String outputApk, + @NotNull String apkPath, + @Nullable String customKeystorePath, + boolean signed, + @NotNull Condition resourceFilter) { + final Map> result = new HashMap>(); result.put(ERROR, new ArrayList()); result.put(INFORMATION, new ArrayList()); result.put(WARNING, new ArrayList()); @@ -187,35 +178,36 @@ public class AndroidApkBuilder { if (certificate != null && certificate.getNotAfter().compareTo(new Date()) < 0) { String date = DateFormatUtil.formatPrettyDateTime(certificate.getNotAfter()); - result.get(ERROR).add(AndroidBundle.message("android.debug.certificate.expired.error", date, keyStoreOsPath)); + result.get(ERROR).add( + ("Debug certificate expired on " + date + ". Cannot regenerate it, please delete file \"" + keyStoreOsPath + "\" manually.")); return result; } PrivateKey key = provider.getDebugKey(); if (key == null) { - result.get(ERROR).add(AndroidBundle.message("android.cannot.create.new.key.error")); + result.get(ERROR).add("Cannot create new key or keystore"); return result; } if (!new File(apkPath).exists()) { - result.get(CompilerMessageCategory.ERROR).add("File " + apkPath + " not found. Try to rebuild project"); + result.get(ERROR).add("File " + apkPath + " not found. Try to rebuild project"); return result; } File dexEntryFile = new File(dexPath); if (!dexEntryFile.exists()) { - result.get(CompilerMessageCategory.ERROR).add("File " + dexEntryFile.getPath() + " not found. Try to rebuild project"); + result.get(ERROR).add("File " + dexEntryFile.getPath() + " not found. Try to rebuild project"); return result; } for (String externalJar : externalJars) { if (new File(externalJar).isDirectory()) { - result.get(CompilerMessageCategory.ERROR).add(externalJar + " is directory. Directory libraries are not supported"); + result.get(ERROR).add(externalJar + " is directory. Directory libraries are not supported"); } } - if (result.get(CompilerMessageCategory.ERROR).size() > 0) { + if (result.get(ERROR).size() > 0) { return result; } @@ -233,9 +225,10 @@ public class AndroidApkBuilder { builder.writeFile(dexEntryFile, AndroidCommonUtils.CLASSES_FILE_NAME); final HashSet added = new HashSet(); - for (VirtualFile sourceRoot : sourceRoots) { - final HashSet sourceFolderResources = new HashSet(); - collectStandardSourceFolderResources(sourceRoot, new HashSet(), sourceFolderResources, project); + for (String sourceRootPath : sourceRoots) { + final HashSet sourceFolderResources = new HashSet(); + final File sourceRoot = new File(sourceRootPath); + collectStandardSourceFolderResources(sourceRoot, sourceFolderResources, resourceFilter); writeStandardSourceFolderResources(sourceFolderResources, sourceRoot, builder, added); } @@ -246,7 +239,7 @@ public class AndroidApkBuilder { } for (String duplicate : duplicates) { - result.get(CompilerMessageCategory.WARNING).add("Duplicate entry " + duplicate + ". The file won't be added"); + result.get(WARNING).add("Duplicate entry " + duplicate + ". The file won't be added"); } MyResourceFilter filter = new MyResourceFilter(duplicates); @@ -262,9 +255,14 @@ public class AndroidApkBuilder { } final HashSet nativeLibs = new HashSet(); - for (VirtualFile nativeLibsFolder : nativeLibsFolders) { - for (VirtualFile child : nativeLibsFolder.getChildren()) { - writeNativeLibraries(builder, nativeLibsFolder, child, signed, nativeLibs); + for (String nativeLibsFolderPath : nativeLibsFolders) { + final File nativeLibsFolder = new File(nativeLibsFolderPath); + final File[] children = nativeLibsFolder.listFiles(); + + if (children != null) { + for (File child : children) { + writeNativeLibraries(builder, nativeLibsFolder, child, signed, nativeLibs); + } } } builder.close(); @@ -305,7 +303,7 @@ public class AndroidApkBuilder { return result; } - private static DebugKeyProvider createDebugKeyProvider(final Map> result, String path) throws + private static DebugKeyProvider createDebugKeyProvider(final Map> result, String path) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, @@ -326,18 +324,20 @@ public class AndroidApkBuilder { } private static void writeNativeLibraries(SignedJarBuilder builder, - VirtualFile nativeLibsFolder, - VirtualFile child, + File nativeLibsFolder, + File child, boolean debugBuild, Set added) throws IOException { - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList(); collectNativeLibraries(child, list, debugBuild); - for (VirtualFile file : list) { - String relativePath = VfsUtilCore.getRelativePath(file, nativeLibsFolder, File.separatorChar); + + for (File file : list) { + final String relativePath = FileUtil.getRelativePath(nativeLibsFolder, file); String path = FileUtil.toSystemIndependentName(SdkConstants.FD_APK_NATIVE_LIBS + File.separator + relativePath); + if (added.add(path)) { - builder.writeFile(toIoFile(file), path); + builder.writeFile(file, path); LOG.info("Native lib file added to APK: " + file.getPath()); } else { @@ -346,104 +346,73 @@ public class AndroidApkBuilder { } } - private static Map> addExceptionMessage(Exception e, - Map> result) { + private static Map> addExceptionMessage(Exception e, + Map> result) { LOG.info(e); String simpleExceptionName = e.getClass().getCanonicalName(); result.get(ERROR).add(simpleExceptionName + ": " + e.getMessage()); return result; } - public static void collectNativeLibraries(@NotNull VirtualFile file, @NotNull List result, boolean debugBuild) { + public static void collectNativeLibraries(@NotNull File file, @NotNull List result, boolean debugBuild) { if (!file.isDirectory()) { - String ext = file.getExtension(); + String ext = FileUtil.getExtension(file.getName()); // some users store jars and *.so libs in the same directory. Do not pack JARs to APK's "lib" folder! if (EXT_NATIVE_LIB.equalsIgnoreCase(ext) || - (debugBuild && !(file.getFileType() instanceof ArchiveFileType))) { + (debugBuild && !("jar".equals(ext)))) { result.add(file); } } else if (JavaResourceFilter.checkFolderForPackaging(file.getName())) { - for (VirtualFile child : file.getChildren()) { - collectNativeLibraries(child, result, debugBuild); + final File[] children = file.listFiles(); + + if (children != null) { + for (File child : children) { + collectNativeLibraries(child, result, debugBuild); + } } } } - public static void collectStandardSourceFolderResources(VirtualFile sourceFolder, - Set visited, - Set result, - @Nullable Project project) { - visited.add(sourceFolder); - - for (VirtualFile child : sourceFolder.getChildren()) { - if (child.exists()) { - if (child.isDirectory()) { - if (!visited.contains(child) && - JavaResourceFilter.checkFolderForPackaging(child.getName()) && !isExcludedFromCompilation(child, project)) { - collectStandardSourceFolderResources(child, visited, result, project); + public static void collectStandardSourceFolderResources(@NotNull File sourceFolder, + @NotNull Collection result, + @NotNull Condition filter) { + final File[] children = sourceFolder.listFiles(); + + if (children != null) { + for (File child : children) { + if (child.exists()) { + if (child.isDirectory()) { + if (JavaResourceFilter.checkFolderForPackaging(child.getName()) && !filter.value(child)) { + collectStandardSourceFolderResources(child, result, filter); + } + } + else if (checkFileForPackaging(child) && !filter.value(child)) { + result.add(child); } } - else if (checkFileForPackaging(child) && !isExcludedFromCompilation(child, project)) { - result.add(child); - } } } } - private static boolean isExcludedFromCompilation(VirtualFile child, @Nullable Project project) { - final CompilerManager compilerManager = project != null ? CompilerManager.getInstance(project) : null; - - if (compilerManager == null) { - return false; - } - - if (!compilerManager.isExcludedFromCompilation(child)) { - return false; - } - - final Module module = ModuleUtil.findModuleForFile(child, project); - if (module == null) { - return true; - } - - final AndroidFacet facet = AndroidFacet.getInstance(module); - if (facet == null || !facet.getConfiguration().LIBRARY_PROJECT) { - return true; - } - - final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform(); - if (platform == null) { - return true; - } - - // we exclude sources of library modules automatically for tools r7 or previous - return platform.getSdkData().getPlatformToolsRevision() > 7; - } - - private static void writeStandardSourceFolderResources(Collection resources, - VirtualFile sourceRoot, + private static void writeStandardSourceFolderResources(Collection resources, + File sourceRoot, SignedJarBuilder jarBuilder, Set added) throws IOException { - for (VirtualFile child : resources) { - final String relativePath = FileUtil.toSystemIndependentName(VfsUtilCore.getRelativePath(child, sourceRoot, File.separatorChar)); - if (!added.contains(relativePath)) { - File file = toIoFile(child); - jarBuilder.writeFile(file, FileUtil.toSystemIndependentName(relativePath)); + for (File child : resources) { + final String relativePath = FileUtil.getRelativePath(sourceRoot, child); + if (relativePath != null && !added.contains(relativePath)) { + jarBuilder.writeFile(child, FileUtil.toSystemIndependentName(relativePath)); added.add(relativePath); } } } - private static File toIoFile(VirtualFile child) { - return new File(FileUtil.toSystemDependentName(child.getPath())).getAbsoluteFile(); - } - - private static boolean checkFileForPackaging(VirtualFile file) { - String fileName = file.getNameWithoutExtension(); + private static boolean checkFileForPackaging(File file) { + String fileName = FileUtil.getNameWithoutExtension(file); if (fileName.length() > 0) { - return JavaResourceFilter.checkFileForPackaging(fileName, file.getExtension()); + return JavaResourceFilter.checkFileForPackaging(fileName, FileUtil.getExtension(file.getName())); } return false; } diff --git a/plugins/android/rt/src/org/jetbrains/android/util/AndroidCommonUtils.java b/plugins/android/rt/src/org/jetbrains/android/util/AndroidCommonUtils.java index 763c9a4c90f0..f6ea4e35cf36 100644 --- a/plugins/android/rt/src/org/jetbrains/android/util/AndroidCommonUtils.java +++ b/plugins/android/rt/src/org/jetbrains/android/util/AndroidCommonUtils.java @@ -343,4 +343,8 @@ public class AndroidCommonUtils { return FileUtil.toSystemDependentName(inputJar.getPath()); } + + public static String toolPath(@NotNull String toolFileName) { + return SdkConstants.OS_SDK_TOOLS_FOLDER + toolFileName; + } } diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 104af71b4115..978846c81b1f 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -205,6 +205,14 @@ + + + + + + + + diff --git a/plugins/android/src/org/jetbrains/android/actions/RunAndroidAvdManagerAction.java b/plugins/android/src/org/jetbrains/android/actions/RunAndroidAvdManagerAction.java index a87b5ff15869..5560def58061 100644 --- a/plugins/android/src/org/jetbrains/android/actions/RunAndroidAvdManagerAction.java +++ b/plugins/android/src/org/jetbrains/android/actions/RunAndroidAvdManagerAction.java @@ -20,8 +20,8 @@ import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import org.jetbrains.android.sdk.AndroidSdkUtils; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.android.util.ExecutionStatus; import org.jetbrains.annotations.NotNull; @@ -47,7 +47,7 @@ public class RunAndroidAvdManagerAction extends AndroidRunSdkToolAction { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @Override public void run() { - final String toolPath = sdkPath + File.separator + AndroidSdkUtils.toolPath(SdkConstants.androidCmdName()); + final String toolPath = sdkPath + File.separator + AndroidCommonUtils.toolPath(SdkConstants.androidCmdName()); GeneralCommandLine commandLine = new GeneralCommandLine(); commandLine.setExePath(toolPath); commandLine.addParameter("avd"); diff --git a/plugins/android/src/org/jetbrains/android/actions/RunAndroidSdkManagerAction.java b/plugins/android/src/org/jetbrains/android/actions/RunAndroidSdkManagerAction.java index f2fed28b4bc6..52029d98ca81 100644 --- a/plugins/android/src/org/jetbrains/android/actions/RunAndroidSdkManagerAction.java +++ b/plugins/android/src/org/jetbrains/android/actions/RunAndroidSdkManagerAction.java @@ -20,8 +20,8 @@ import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import org.jetbrains.android.sdk.AndroidSdkUtils; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.android.util.ExecutionStatus; import org.jetbrains.annotations.NotNull; @@ -43,7 +43,7 @@ public class RunAndroidSdkManagerAction extends AndroidRunSdkToolAction { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @Override public void run() { - final String toolPath = sdkPath + File.separator + AndroidSdkUtils.toolPath(SdkConstants.androidCmdName()); + final String toolPath = sdkPath + File.separator + AndroidCommonUtils.toolPath(SdkConstants.androidCmdName()); GeneralCommandLine commandLine = new GeneralCommandLine(); commandLine.setExePath(toolPath); commandLine.addParameter("sdk"); diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index 4e001e29c78e..3b69b90b5b00 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -35,6 +35,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; @@ -769,4 +770,39 @@ public class AndroidCompileUtil { if (compilerOutput == null) return null; return new File(compilerOutput.getPath(), getApkName(module)).getPath(); } + + public static boolean isExcludedFromCompilation(@NotNull File file, @Nullable Project project) { + final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); + return vFile != null && isExcludedFromCompilation(vFile, project); + } + + public static boolean isExcludedFromCompilation(VirtualFile child, @Nullable Project project) { + final CompilerManager compilerManager = project != null ? CompilerManager.getInstance(project) : null; + + if (compilerManager == null) { + return false; + } + + if (!compilerManager.isExcludedFromCompilation(child)) { + return false; + } + + final Module module = ModuleUtil.findModuleForFile(child, project); + if (module == null) { + return true; + } + + final AndroidFacet facet = AndroidFacet.getInstance(module); + if (facet == null || !facet.getConfiguration().LIBRARY_PROJECT) { + return true; + } + + final AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform(); + if (platform == null) { + return true; + } + + // we exclude sources of library modules automatically for tools r7 or previous + return platform.getSdkData().getPlatformToolsRevision() > 7; + } } \ No newline at end of file diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java index bb10c2191d2d..f116300d4be1 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java @@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.*; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.roots.ModuleOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; @@ -233,9 +234,15 @@ public class AndroidPackagingCompiler implements PackagingCompiler { ? item.getFinalPath() + UNSIGNED_SUFFIX : item.getFinalPath(); - final Map> messages = AndroidApkBuilder - .execute(context.getProject(), resPackagePath, item.getClassesDexPath(), item.getSourceRoots(), externalLibPaths, item.getNativeLibsFolders(), - finalPath, unsigned, item.mySdkPath, item.getCustomKeystorePath()); + final String[] sourceRoots = AndroidCompileUtil.toOsPaths(item.getSourceRoots()); + final String[] nativeLibsFolders = AndroidCompileUtil.toOsPaths(item.getNativeLibsFolders()); + final Project project = context.getProject(); + + final Map> messages = AndroidCompileUtil.toCompilerMessageCategoryKeys( + AndroidApkBuilder.execute(resPackagePath, item.getClassesDexPath(), sourceRoots, externalLibPaths, + nativeLibsFolders, finalPath, unsigned, item.mySdkPath, item.getCustomKeystorePath(), + new ExcludedSourcesFilter(project))); + AndroidCompileUtil.addMessages(context, messages); } catch (final IOException e) { @@ -296,7 +303,7 @@ public class AndroidPackagingCompiler implements PackagingCompiler { private final boolean myGenerateUnsigendApk; private final Module myModule; private boolean myReleaseBuild; - + private final String myCustomKeystorePath; private AptPackagingItem(String sdkPath, @@ -375,8 +382,8 @@ public class AndroidPackagingCompiler implements PackagingCompiler { @Nullable public ValidityState getValidityState() { - return new MyValidityState(myResPackagePath, myClassesDexPath, myFinalPath, myGenerateUnsigendApk, myReleaseBuild, - mySourceRoots, myExternalLibraries, myNativeLibsFolders, myCustomKeystorePath); + return new MyValidityState(myModule.getProject(), myResPackagePath, myClassesDexPath, myFinalPath, myGenerateUnsigendApk, + myReleaseBuild, mySourceRoots, myExternalLibraries, myNativeLibsFolders, myCustomKeystorePath); } } @@ -400,7 +407,8 @@ public class AndroidPackagingCompiler implements PackagingCompiler { myCustomKeystorePath = CompilerIOUtil.readString(is); } - MyValidityState(String resPackagePath, + MyValidityState(Project project, + String resPackagePath, String classesDexPath, String apkPath, boolean generateUnsignedApk, @@ -416,25 +424,26 @@ public class AndroidPackagingCompiler implements PackagingCompiler { myReleaseBuild = releaseBuild; myCustomKeystorePath = customKeystorePath != null ? customKeystorePath : ""; - final HashSet resourcesFromSourceRoot = new HashSet(); + final HashSet resourcesFromSourceRoot = new HashSet(); for (VirtualFile sourceRoot : sourceRoots) { - AndroidApkBuilder.collectStandardSourceFolderResources(sourceRoot, new HashSet(), resourcesFromSourceRoot, null); + AndroidApkBuilder.collectStandardSourceFolderResources(new File(sourceRoot.getPath()), resourcesFromSourceRoot, + new ExcludedSourcesFilter(project)); } - for (VirtualFile resource : resourcesFromSourceRoot) { - myResourceTimestamps.put(resource.getPath(), resource.getTimeStamp()); + for (File resource : resourcesFromSourceRoot) { + myResourceTimestamps.put(FileUtil.toSystemIndependentName(resource.getPath()), resource.lastModified()); } for (VirtualFile externalLib : externalLibs) { myResourceTimestamps.put(externalLib.getPath(), externalLib.getTimeStamp()); } - ArrayList nativeLibs = new ArrayList(); + ArrayList nativeLibs = new ArrayList(); for (VirtualFile nativeLibFolder : nativeLibFolders) { for (VirtualFile child : nativeLibFolder.getChildren()) { - AndroidApkBuilder.collectNativeLibraries(child, nativeLibs, !releaseBuild); + AndroidApkBuilder.collectNativeLibraries(new File(child.getPath()), nativeLibs, !releaseBuild); } } - for (VirtualFile nativeLib : nativeLibs) { - myResourceTimestamps.put(nativeLib.getPath(), nativeLib.getTimeStamp()); + for (File nativeLib : nativeLibs) { + myResourceTimestamps.put(FileUtil.toSystemIndependentName(nativeLib.getPath()), nativeLib.lastModified()); } } @@ -447,7 +456,7 @@ public class AndroidPackagingCompiler implements PackagingCompiler { return mvs.myGenerateUnsignedApk == myGenerateUnsignedApk && mvs.myReleaseBuild == myReleaseBuild && mvs.myResourceTimestamps.equals(myResourceTimestamps) && - mvs.myApkPath.equals(myApkPath) && + mvs.myApkPath.equals(myApkPath) && mvs.myCustomKeystorePath.equals(myCustomKeystorePath); } diff --git a/plugins/android/src/org/jetbrains/android/compiler/ExcludedSourcesFilter.java b/plugins/android/src/org/jetbrains/android/compiler/ExcludedSourcesFilter.java new file mode 100644 index 000000000000..0b5d756cf6d7 --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/compiler/ExcludedSourcesFilter.java @@ -0,0 +1,23 @@ +package org.jetbrains.android.compiler; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** +* @author Eugene.Kudelevsky +*/ +class ExcludedSourcesFilter implements Condition { + private final Project myProject; + + public ExcludedSourcesFilter(@NotNull Project project) { + myProject = project; + } + + @Override + public boolean value(File file) { + return AndroidCompileUtil.isExcludedFromCompilation(file, myProject); + } +} diff --git a/plugins/android/src/org/jetbrains/android/exportSignedPackage/ApkStep.java b/plugins/android/src/org/jetbrains/android/exportSignedPackage/ApkStep.java index ef5dbc269b2d..caa4c355b524 100644 --- a/plugins/android/src/org/jetbrains/android/exportSignedPackage/ApkStep.java +++ b/plugins/android/src/org/jetbrains/android/exportSignedPackage/ApkStep.java @@ -51,8 +51,8 @@ import org.jetbrains.android.compiler.AndroidProguardCompiler; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidRootUtil; import org.jetbrains.android.sdk.AndroidPlatform; -import org.jetbrains.android.sdk.AndroidSdkUtils; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.SaveFileListener; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -196,7 +196,7 @@ class ApkStep extends ExportSignedPackageWizardStep { AndroidPlatform platform = myWizard.getFacet().getConfiguration().getAndroidPlatform(); assert platform != null; String sdkPath = platform.getSdkData().getLocation(); - String zipAlignPath = sdkPath + File.separatorChar + AndroidSdkUtils.toolPath(SdkConstants.FN_ZIPALIGN); + String zipAlignPath = sdkPath + File.separatorChar + AndroidCommonUtils.toolPath(SdkConstants.FN_ZIPALIGN); File zipalign = new File(zipAlignPath); final boolean runZipAlign = zipalign.isFile(); File destFile = null; diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java index bc00911c7862..41b18b479b30 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFacet.java @@ -81,6 +81,7 @@ import org.jetbrains.android.resourceManagers.ResourceManager; import org.jetbrains.android.resourceManagers.SystemResourceManager; import org.jetbrains.android.sdk.*; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.android.util.ResourceEntry; import org.jetbrains.annotations.NotNull; @@ -392,7 +393,8 @@ public class AndroidFacet extends Facet { public void launchEmulator(@Nullable final String avdName, @NotNull final String commands, @Nullable ProcessHandler handler) { AndroidPlatform platform = getConfiguration().getAndroidPlatform(); if (platform != null) { - final String emulatorPath = platform.getSdkData().getLocation() + File.separator + AndroidSdkUtils.toolPath(SdkConstants.FN_EMULATOR); + final String emulatorPath = platform.getSdkData().getLocation() + File.separator + AndroidCommonUtils + .toolPath(SdkConstants.FN_EMULATOR); final GeneralCommandLine commandLine = new GeneralCommandLine(); commandLine.setExePath(FileUtil.toSystemDependentName(emulatorPath)); if (avdName != null) { diff --git a/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleBuilder.java b/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleBuilder.java index 4c834de25152..40d6e70d5618 100644 --- a/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleBuilder.java +++ b/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleBuilder.java @@ -63,6 +63,7 @@ import org.jetbrains.android.run.testing.AndroidTestRunConfigurationType; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.sdk.AndroidSdkUtils; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -218,7 +219,7 @@ public class AndroidModuleBuilder extends JavaModuleBuilder { IAndroidTarget target = platform.getTarget(); final String androidToolPath = - platform.getSdkData().getLocation() + File.separator + AndroidSdkUtils.toolPath(SdkConstants.androidCmdName()); + platform.getSdkData().getLocation() + File.separator + AndroidCommonUtils.toolPath(SdkConstants.androidCmdName()); if (!new File(androidToolPath).exists()) { return false; diff --git a/plugins/android/src/org/jetbrains/android/run/AvdChooser.java b/plugins/android/src/org/jetbrains/android/run/AvdChooser.java index aedabf9da3b4..7b7fa2d1e8df 100644 --- a/plugins/android/src/org/jetbrains/android/run/AvdChooser.java +++ b/plugins/android/src/org/jetbrains/android/run/AvdChooser.java @@ -30,6 +30,7 @@ import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.sdk.AndroidSdkData; import org.jetbrains.android.sdk.AndroidSdkUtils; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.android.util.BooleanCellRenderer; import org.jetbrains.annotations.NotNull; @@ -71,7 +72,7 @@ public class AvdChooser extends DialogWrapper { AndroidSdkData sdkData = facet.getConfiguration().getAndroidSdk(); if (sdkData == null) return null; String androidCmd = SdkConstants.androidCmdName(); - return sdkData.getLocation() + File.separator + AndroidSdkUtils.toolPath(androidCmd); + return sdkData.getLocation() + File.separator + AndroidCommonUtils.toolPath(androidCmd); } public AvdChooser(@NotNull final Project project, diff --git a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java index e7e71b61bf4e..a55e43cf8812 100644 --- a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java +++ b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java @@ -300,7 +300,7 @@ public class AndroidSdkData { private String getAdbPath() { String path = getLocation() + File.separator + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER + SdkConstants.FN_ADB; if (!new File(path).exists()) { - return getLocation() + File.separator + AndroidSdkUtils.toolPath(SdkConstants.FN_ADB); + return getLocation() + File.separator + AndroidCommonUtils.toolPath(SdkConstants.FN_ADB); } return path; } diff --git a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkUtils.java b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkUtils.java index 812bf196a652..9ec7dcb35697 100644 --- a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkUtils.java +++ b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkUtils.java @@ -423,10 +423,6 @@ public class AndroidSdkUtils { }; } - public static String toolPath(@NotNull String toolFileName) { - return SdkConstants.OS_SDK_TOOLS_FOLDER + toolFileName; - } - @Nullable public static Sdk findAppropriateAndroidPlatform(@NotNull IAndroidTarget target, @NotNull AndroidSdkData sdkData) { for (Sdk library : ProjectJdkTable.getInstance().getAllJdks()) { From 29382675d513d24881623b7fe3a0de14191dd725 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 28 Feb 2012 19:35:40 +0400 Subject: [PATCH 15/23] android-jps: read APK_PATH, CUSTOM_DEBUG_KEYSTORE_PATH and LIBS_FOLDER_RELATIVE_PATH options --- .../src/org/jetbrains/jps/android/AndroidFacet.groovy | 9 +++++++++ .../org/jetbrains/jps/android/AndroidFacetType.groovy | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacet.groovy b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacet.groovy index ab62251c4642..b2d76d583f29 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacet.groovy +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacet.groovy @@ -23,6 +23,10 @@ class AndroidFacet extends Facet { boolean useCustomManifestForCompilation; boolean packTestCode; + String apkRelativePath; + String customDebugKeyStorePath; + String nativeLibsFolderRelativePath; + AndroidFacet(Module module, String name) { this.module = module this.name = name; @@ -54,6 +58,11 @@ class AndroidFacet extends Facet { return manifestFile != null ? manifestFile.getCanonicalFile() : null; } + File getNativeLibsDir() throws IOException { + def nativeLibsFolder = findFileByRelativeModulePath(nativeLibsFolderRelativePath, false); + return nativeLibsFolder != null ? nativeLibsFolder.getCanonicalFile() : null; + } + private File findFileByRelativeModulePath(String relativePath, boolean lookInContentRoot) { if (module.basePath != null) { def absPath = FileUtil.toSystemIndependentName(module.basePath + relativePath) diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacetType.groovy b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacetType.groovy index e8a35c562df1..3fe084ef291a 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacetType.groovy +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFacetType.groovy @@ -4,6 +4,7 @@ import org.jetbrains.jps.MacroExpander import org.jetbrains.jps.Module import org.jetbrains.jps.idea.Facet import org.jetbrains.jps.idea.FacetTypeService +import org.jetbrains.jps.idea.IdeaProjectLoadingUtil /** * @author Eugene.Kudelevsky @@ -49,6 +50,16 @@ class AndroidFacetType extends FacetTypeService { break case "ASSETS_FOLDER_RELATIVE_PATH": facet.assetsFolderRelativePath = value + break + case "APK_PATH": + facet.apkRelativePath = value + break + case "CUSTOM_DEBUG_KEYSTORE_PATH": + facet.customDebugKeyStorePath = IdeaProjectLoadingUtil.pathFromUrl(value) + break + case "LIBS_FOLDER_RELATIVE_PATH": + facet.nativeLibsFolderRelativePath = value + break } } return facet; From d4266b854395518eb46427494a014cbd1d581762 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 28 Feb 2012 19:37:34 +0400 Subject: [PATCH 16/23] android-jps: support apk builder --- .../src/messages/AndroidJpsBundle.properties | 4 +- .../android/AndroidApkBuilderConfigState.java | 42 +++ .../AndroidApkBuilderConfigStateStorage.java | 38 +++ .../jps/android/AndroidDexBuilder.java | 14 +- .../jps/android/AndroidFileSetState.java | 28 +- .../jetbrains/jps/android/AndroidJpsUtil.java | 84 ++++- .../AndroidLibraryPackagingBuilder.java | 2 +- .../AndroidResourcePackagingBuilder.java | 290 ++++++++++++++---- .../AndroidSourceGeneratingBuilder.java | 42 +-- .../compiler/tools/AndroidApkBuilder.java | 4 +- .../compiler/ExcludedSourcesFilter.java | 2 +- 11 files changed, 426 insertions(+), 124 deletions(-) create mode 100644 plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidApkBuilderConfigState.java create mode 100644 plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidApkBuilderConfigStateStorage.java diff --git a/plugins/android/jps-plugin/src/messages/AndroidJpsBundle.properties b/plugins/android/jps-plugin/src/messages/AndroidJpsBundle.properties index a41ec5f6e526..0acb4f447d8c 100644 --- a/plugins/android/jps-plugin/src/messages/AndroidJpsBundle.properties +++ b/plugins/android/jps-plugin/src/messages/AndroidJpsBundle.properties @@ -13,4 +13,6 @@ android.jps.errors.package.not.specified=Package is not specified in AndroidMani android.jps.progress.library.packaging=Packaging Android libraries for module {0} android.jps.errors.main.content.root.not.found=Cannot find main content root for module {0} android.jps.progress.proguard=Executing ProGuard for module {0} -android.jps.progress.res.caching=Caching resources for module {0} \ No newline at end of file +android.jps.progress.res.caching=Caching resources for module {0} +android.jps.progress.packaging=Packaging {0} +android.jps.progress.packaging.resources=Packaging resources for module {0} \ No newline at end of file diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidApkBuilderConfigState.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidApkBuilderConfigState.java new file mode 100644 index 000000000000..0bc4758d373a --- /dev/null +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidApkBuilderConfigState.java @@ -0,0 +1,42 @@ +package org.jetbrains.jps.android; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.incremental.storage.ValidityState; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidApkBuilderConfigState implements ValidityState { + private final String myOutputApkPath; + private final String myCustomKeystorePath; + + public AndroidApkBuilderConfigState(@NotNull String outputApkPath, @NotNull String customKeystorePath) { + myOutputApkPath = outputApkPath; + myCustomKeystorePath = customKeystorePath; + } + + public AndroidApkBuilderConfigState(DataInput in) throws IOException { + myOutputApkPath = in.readUTF(); + myCustomKeystorePath = in.readUTF(); + } + + @Override + public boolean equalsTo(ValidityState otherState) { + if (!(otherState instanceof AndroidApkBuilderConfigState)) { + return false; + } + final AndroidApkBuilderConfigState apkBuilderConfigState = (AndroidApkBuilderConfigState)otherState; + return apkBuilderConfigState.myOutputApkPath.equals(myOutputApkPath) && + apkBuilderConfigState.myCustomKeystorePath.equals(myCustomKeystorePath); + } + + @Override + public void save(DataOutput out) throws IOException { + out.writeUTF(myOutputApkPath); + out.writeUTF(myCustomKeystorePath); + } +} diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidApkBuilderConfigStateStorage.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidApkBuilderConfigStateStorage.java new file mode 100644 index 000000000000..06a43f38332d --- /dev/null +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidApkBuilderConfigStateStorage.java @@ -0,0 +1,38 @@ +package org.jetbrains.jps.android; + +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.EnumeratorStringDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.incremental.storage.AbstractStateStorage; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.File; +import java.io.IOException; + +/** + * @author Eugene.Kudelevsky + */ +public class AndroidApkBuilderConfigStateStorage extends AbstractStateStorage { + public AndroidApkBuilderConfigStateStorage(@NotNull File dataStorageRoot, @NotNull String storageName) throws IOException { + super(getStorageFile(dataStorageRoot, storageName), new EnumeratorStringDescriptor(), new MyDataExternalizer()); + } + + @NotNull + private static File getStorageFile(@NotNull File dataStorageRoot, @NotNull String storageName) { + return new File(new File(new File(dataStorageRoot, AndroidJpsUtil.ANDROID_STORAGE_DIR), storageName), storageName); + } + + private static class MyDataExternalizer implements DataExternalizer { + + @Override + public void save(DataOutput out, AndroidApkBuilderConfigState value) throws IOException { + value.save(out); + } + + @Override + public AndroidApkBuilderConfigState read(DataInput in) throws IOException { + return new AndroidApkBuilderConfigState(in); + } + } +} diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java index 8cc59ebf9254..62c00c78e32b 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java @@ -146,7 +146,7 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { } } } - final AndroidFileSetState newState = new AndroidFileSetState(fileSet, AndroidJpsUtil.CLASSES_AND_JARS_FILTER); + final AndroidFileSetState newState = new AndroidFileSetState(fileSet, AndroidJpsUtil.CLASSES_AND_JARS_FILTER, true); if (context.isMake()) { final AndroidFileSetState oldState = dexStateStorage.getState(module.getName()); @@ -193,11 +193,11 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { return "Android Dex Builder"; } - public static boolean runDex(@NotNull AndroidSdk sdk, - @NotNull IAndroidTarget target, - @NotNull String outputDir, - @NotNull String[] compileTargets, - @NotNull CompileContext context) throws IOException { + private static boolean runDex(@NotNull AndroidSdk sdk, + @NotNull IAndroidTarget target, + @NotNull String outputDir, + @NotNull String[] compileTargets, + @NotNull CompileContext context) throws IOException { @SuppressWarnings("deprecation") final String dxJarPath = FileUtil.toSystemDependentName(target.getPath(IAndroidTarget.DX_JAR)); @@ -295,7 +295,7 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { allFiles.addAll(libClassFilesDirs); allFiles.addAll(externalJars); - final AndroidFileSetState newState = new AndroidFileSetState(allFiles, AndroidJpsUtil.CLASSES_AND_JARS_FILTER); + final AndroidFileSetState newState = new AndroidFileSetState(allFiles, AndroidJpsUtil.CLASSES_AND_JARS_FILTER, true); final AndroidFileSetState oldState = proguardStateStorage.getState(module.getName()); if (context.getTimestampStorage().getStamp(proguardCfgFile) == proguardCfgFile.lastModified() && newState.equalsTo(oldState)) { diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFileSetState.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFileSetState.java index 17e5210c4452..4a6efad7f9f7 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFileSetState.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidFileSetState.java @@ -20,23 +20,29 @@ import java.util.Map; public class AndroidFileSetState implements ValidityState { private final Map myTimestamps; - public AndroidFileSetState(@NotNull Collection roots, @NotNull final Condition filter) { + public AndroidFileSetState(@NotNull Collection roots, @NotNull final Condition filter, boolean recursively) { myTimestamps = new HashMap(); - for (String resourceDir : roots) { - FileUtil.processFilesRecursively(new File(resourceDir), new Processor() { - @Override - public boolean process(File file) { - if (filter.value(file)) { - myTimestamps.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified()); + for (String rootPath : roots) { + final File root = new File(rootPath); + + if (recursively) { + FileUtil.processFilesRecursively(root, new Processor() { + @Override + public boolean process(File file) { + if (filter.value(file)) { + myTimestamps.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified()); + } + return true; } - return true; - } - }); + }); + } + else if (filter.value(root)) { + myTimestamps.put(FileUtil.toSystemIndependentName(root.getPath()), root.lastModified()); + } } } - public AndroidFileSetState(DataInput in) throws IOException { final int resourcesCount = in.readInt(); myTimestamps = new HashMap(resourcesCount); diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java index 3027a7b49e34..a8aada3c883f 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java @@ -116,6 +116,19 @@ class AndroidJpsUtil { return result; } + @NotNull + public static List toPaths(@NotNull Collection files) { + if (files.size() == 0) { + return Collections.emptyList(); + } + + final List result = new ArrayList(files.size()); + for (File file : files) { + result.add(file.getPath()); + } + return result; + } + @Nullable public static File getOutputDirectoryForPackagedFiles(@NotNull ProjectPaths paths, @NotNull Module module) { // todo: return build directory for mavenized modules to place .dex and .apk files into target dir (not target/classes) @@ -160,6 +173,7 @@ class AndroidJpsUtil { } if (libraries != null) { + // todo: do not include provided libs there for (ClasspathItem item : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE, exportedLibrariesOnly)) { if (item instanceof Library && !(item instanceof Sdk)) { for (String filePath : item.getClasspathRoots(ClasspathKind.PRODUCTION_COMPILE)) { @@ -190,7 +204,7 @@ class AndroidJpsUtil { if (depLibrary) { final File packagedClassesJar = new File(depClassDir, AndroidCommonUtils.CLASSES_JAR_FILE_NAME); - if (packagedClassesJar.isDirectory()) { + if (packagedClassesJar.isFile()) { outputDirs.add(packagedClassesJar.getPath()); } } @@ -412,4 +426,72 @@ class AndroidJpsUtil { final File androidStorage = new File(context.getDataManager().getDataStorageRoot(), ANDROID_STORAGE_DIR); return new File(new File(androidStorage, RESOURCE_CACHE_STORAGE), module.getName()); } + + private static void fillSourceRoots(@NotNull Module module, @NotNull Set visited, @NotNull Set result) + throws IOException { + visited.add(module); + final AndroidFacet facet = getFacet(module); + File resDir = null; + File resDirForCompilation = null; + + if (facet != null) { + resDir = facet.getResourceDir(); + resDirForCompilation = facet.getResourceDirForCompilation(); + } + + for (String sourceRootPath : module.getSourceRoots()) { + final File sourceRoot = new File(sourceRootPath).getCanonicalFile(); + + if (!sourceRoot.equals(resDir) && !sourceRoot.equals(resDirForCompilation)) { + result.add(sourceRoot); + } + } + + if (facet != null && facet.isPackTestCode()) { + for (String testRootPath : module.getTestRoots()) { + final File testRoot = new File(testRootPath).getCanonicalFile(); + + if (!testRoot.equals(resDir) && !testRoot.equals(resDirForCompilation)) { + result.add(testRoot); + } + } + } + + for (ClasspathItem classpathItem : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE)) { + if (classpathItem instanceof Module) { + final Module depModule = (Module)classpathItem; + + if (!visited.contains(depModule)) { + fillSourceRoots(depModule, visited, result); + } + } + } + } + + @NotNull + public static File[] getSourceRootsForModuleAndDependencies(@NotNull Module module) throws IOException { + Set result = new HashSet(); + fillSourceRoots(module, new HashSet(), result); + return result.toArray(new File[result.size()]); + } + + @Nullable + public static String getApkPath(@NotNull AndroidFacet facet, @NotNull File outputDirForPackagedArtifacts) { + final String apkRelativePath = facet.getApkRelativePath(); + final Module module = facet.getModule(); + + if (apkRelativePath.length() == 0) { + return new File(outputDirForPackagedArtifacts, getApkName(module)).getPath(); + } + final String moduleDirPath = module.getBasePath(); + + return moduleDirPath != null + ? FileUtil.toSystemDependentName(moduleDirPath + apkRelativePath) + : null; + } + + @NotNull + public static String getApkName(@NotNull Module module) { + return module.getName() + ".apk"; + } } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java index 303cc2793b4a..0720446f5288 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java @@ -72,7 +72,7 @@ public class AndroidLibraryPackagingBuilder extends ModuleLevelBuilder { final Set subdirs = new HashSet(); AndroidJpsUtil.addSubdirectories(classesDir, subdirs); - final AndroidFileSetState newState = new AndroidFileSetState(subdirs, AndroidJpsUtil.CLASSES_AND_JARS_FILTER); + final AndroidFileSetState newState = new AndroidFileSetState(subdirs, AndroidJpsUtil.CLASSES_AND_JARS_FILTER, true); final AndroidFileSetState oldState = storage.getState(module.getName()); if (oldState != null && oldState.equalsTo(newState)) { diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java index 96cdbfeadf63..cf7bb6bacdb1 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java @@ -3,13 +3,20 @@ package org.jetbrains.jps.android; import com.android.sdklib.IAndroidTarget; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashMap; +import org.jetbrains.android.compiler.tools.AndroidApkBuilder; import org.jetbrains.android.compiler.tools.AndroidApt; +import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidCompilerMessageKind; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.CompilerExcludes; import org.jetbrains.jps.Module; +import org.jetbrains.jps.Project; +import org.jetbrains.jps.ProjectPaths; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ProjectBuildException; import org.jetbrains.jps.incremental.ProjectLevelBuilder; @@ -26,6 +33,8 @@ import java.util.*; */ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { @NonNls private static final String BUILDER_NAME = "android-packager"; + @NonNls private static final String RELEASE_SUFFIX = ".release"; + @NonNls private static final String UNSIGNED_SUFFIX = ".unsigned"; @Override public String getName() { @@ -53,7 +62,11 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { throw new ProjectBuildException(); } - if (!doPackaging(context, modules, resourcesStates, assetsStates)) { + if (!doResourcePackaging(context, modules, resourcesStates, assetsStates)) { + throw new ProjectBuildException(); + } + + if (!doPackaging(context, modules)) { throw new ProjectBuildException(); } } @@ -77,13 +90,13 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { final List resourceDirs = resourceDir != null ? Arrays.asList(resourceDir.getPath()) : Collections.emptyList(); - resourcesStates.put(module, new AndroidFileSetState(resourceDirs, Condition.TRUE)); + resourcesStates.put(module, new AndroidFileSetState(resourceDirs, Condition.TRUE, true)); final File assetsDir = facet.getAssetsDir(); final List assetDirs = assetsDir != null ? Arrays.asList(assetsDir.getPath()) : Collections.emptyList(); - assetsStates.put(module, new AndroidFileSetState(assetDirs, Condition.TRUE)); + assetsStates.put(module, new AndroidFileSetState(assetDirs, Condition.TRUE, true)); } } } @@ -121,17 +134,18 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { @Nullable AndroidFileSetState state) throws IOException { final AndroidFileSetState savedState = storage.getState(module.getName()); - if (savedState != null && savedState.equalsTo(state)) { + if (context.isMake() && savedState != null && savedState.equalsTo(state)) { return true; } - context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, - AndroidJpsBundle.message("android.jps.progress.res.caching", module.getName()))); final AndroidFacet facet = AndroidJpsUtil.getFacet(module); if (facet == null) { return true; } + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, + AndroidJpsBundle.message("android.jps.progress.res.caching", module.getName()))); + final File resourceDir = AndroidJpsUtil.getResourceDirForCompilationPath(facet); if (resourceDir == null) { return true; @@ -164,54 +178,220 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { return success; } - private static boolean doPackaging(@NotNull CompileContext context, - @NotNull Collection modules, - @NotNull Map resourcesStates, - @NotNull Map assetsStates) throws IOException { + private static boolean doResourcePackaging(@NotNull CompileContext context, + @NotNull Collection modules, + @NotNull Map resourcesStates, + @NotNull Map assetsStates) throws IOException { boolean success = true; final File dataStorageRoot = context.getDataManager().getDataStorageRoot(); - AndroidFileSetStorage devResourcesStorage = null; - AndroidFileSetStorage releaseResourcesStorage = null; - AndroidFileSetStorage devAssetsStorage = null; - AndroidFileSetStorage releaseAssetsStorage = null; + final boolean releaseBuild = AndroidJpsUtil.isReleaseBuild(context); + AndroidFileSetStorage resourcesStorage = null; + AndroidFileSetStorage assetsStorage = null; try { - devResourcesStorage = new AndroidFileSetStorage(dataStorageRoot, "resources_packaging_dev"); - releaseResourcesStorage = new AndroidFileSetStorage(dataStorageRoot, "resources_packaging_release"); + final String resourcesStorageName = releaseBuild ? "resources_packaging_release" : "resources_packaging_dev"; + resourcesStorage = new AndroidFileSetStorage(dataStorageRoot, resourcesStorageName); - devAssetsStorage = new AndroidFileSetStorage(dataStorageRoot, "assets_packaging_dev"); - releaseAssetsStorage = new AndroidFileSetStorage(dataStorageRoot, "assets_packaging_release"); + final String assetsStorageName = releaseBuild ? "assets_packaging_release" : "assets_packaging_dev"; + assetsStorage = new AndroidFileSetStorage(dataStorageRoot, assetsStorageName); for (Module module : modules) { final AndroidFacet facet = AndroidJpsUtil.getFacet(module); - if (facet == null || facet.isLibrary()) { + if (facet == null) { continue; } - if (!packageResources(facet, context, devResourcesStorage, devAssetsStorage, releaseResourcesStorage, releaseAssetsStorage, - resourcesStates, assetsStates)) { + boolean updateState = true; + + if (!facet.isLibrary() && + !(context.isMake() && + checkUpToDate(module, resourcesStates, resourcesStorage) && + checkUpToDate(module, assetsStates, assetsStorage))) { + + updateState = packageResources(facet, context); + + if (!updateState) { + success = false; + } + } + resourcesStorage.update(module.getName(), updateState ? resourcesStates.get(module) : null); + assetsStorage.update(module.getName(), updateState ? assetsStates.get(module) : null); + } + } + finally { + if (resourcesStorage != null) { + resourcesStorage.close(); + } + + if (assetsStorage != null) { + assetsStorage.close(); + } + } + return success; + } + + private static boolean doPackaging(@NotNull CompileContext context, @NotNull Collection modules) throws IOException { + final boolean release = AndroidJpsUtil.isReleaseBuild(context); + final File dataStorageRoot = context.getDataManager().getDataStorageRoot(); + + boolean success = true; + + AndroidFileSetStorage apkFileSetStorage = null; + AndroidApkBuilderConfigStateStorage apkBuilderConfigStateStorage = null; + try { + final String apkFileSetStorageName = "apk_builder_file_set" + (release ? "_release" : "_dev"); + apkFileSetStorage = new AndroidFileSetStorage(dataStorageRoot, apkFileSetStorageName); + + final String apkBuilderStateStorageName = "apk_builder_config" + (release ? "_release" : "_dev"); + apkBuilderConfigStateStorage = new AndroidApkBuilderConfigStateStorage(dataStorageRoot, apkBuilderStateStorageName); + + for (Module module : modules) { + try { + if (!doPackagingForModule(context, module, apkFileSetStorage, apkBuilderConfigStateStorage, release)) { + success = false; + } + } + catch (IOException e) { + AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME); success = false; } } } finally { - if (devResourcesStorage != null) { - devResourcesStorage.close(); + if (apkFileSetStorage != null) { + apkFileSetStorage.close(); } - if (releaseResourcesStorage != null) { - releaseResourcesStorage.close(); - } - if (devAssetsStorage != null) { - devAssetsStorage.close(); - } - if (releaseAssetsStorage != null) { - releaseAssetsStorage.close(); + + if (apkBuilderConfigStateStorage != null) { + apkBuilderConfigStateStorage.close(); } } + return success; } + private static boolean doPackagingForModule(@NotNull CompileContext context, + @NotNull Module module, + @NotNull AndroidFileSetStorage apkFileSetStorage, + @NotNull AndroidApkBuilderConfigStateStorage apkBuilderConfigStateStorage, + boolean release) throws IOException { + final AndroidFacet facet = AndroidJpsUtil.getFacet(module); + if (facet == null || facet.isLibrary()) { + return true; + } + + final String[] sourceRoots = AndroidJpsUtil.toPaths(AndroidJpsUtil.getSourceRootsForModuleAndDependencies(module)); + final ProjectPaths paths = context.getProjectPaths(); + + final File outputDir = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(paths, module); + if (outputDir == null) { + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, AndroidJpsBundle + .message("android.jps.errors.output.dir.not.specified", module.getName()))); + return false; + } + + final Pair pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME); + if (pair == null) { + return false; + } + + final Set externalJarsSet = AndroidJpsUtil.getExternalLibraries(paths, module); + final File resPackage = getPackagedResourcesFile(module, outputDir); + + final File classesDexFile = new File(outputDir.getPath(), AndroidCommonUtils.CLASSES_FILE_NAME); + + final String sdkPath = pair.getFirst().getSdkPath(); + final String outputPath = AndroidJpsUtil.getApkPath(facet, outputDir); + if (outputPath == null) { + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, + "Cannot compute output path for file " + AndroidJpsUtil.getApkName(module))); + return false; + } + final String customKeyStorePath = FileUtil.toSystemDependentName(facet.getCustomDebugKeyStorePath()); + final String[] nativeLibDirs = collectNativeLibsFolders(facet); + + final String resPackagePath = release ? resPackage.getPath() + RELEASE_SUFFIX : resPackage.getPath(); + final String outputApkPath = release ? outputPath + UNSIGNED_SUFFIX : outputPath; + final String classesDexFilePath = classesDexFile.getPath(); + final String[] externalJars = ArrayUtil.toStringArray(externalJarsSet); + + final AndroidFileSetState currentFileSetState = + buildCurrentApkBuilderState(context.getProject(), resPackagePath, classesDexFilePath, nativeLibDirs, + sourceRoots, externalJars, release); + + final AndroidApkBuilderConfigState currentApkBuilderConfigState = + new AndroidApkBuilderConfigState(outputApkPath, customKeyStorePath); + + final AndroidFileSetState savedApkFileSetState = apkFileSetStorage.getState(module.getName()); + final AndroidApkBuilderConfigState savedApkBuilderConfigState = apkBuilderConfigStateStorage.getState(module.getName()); + + if (context.isMake() && + currentFileSetState.equalsTo(savedApkFileSetState) && + currentApkBuilderConfigState.equalsTo(savedApkBuilderConfigState)) { + return true; + } + context + .processMessage(new ProgressMessage(AndroidJpsBundle.message("android.jps.progress.packaging", AndroidJpsUtil.getApkName(module)))); + + final Map> messages = AndroidApkBuilder + .execute(resPackagePath, classesDexFilePath, sourceRoots, externalJars, nativeLibDirs, outputApkPath, + release, sdkPath, customKeyStorePath, new MyExcludedSourcesFilter(context.getProject())); + + AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME); + final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty(); + + apkFileSetStorage.update(module.getName(), success ? currentFileSetState : null); + apkBuilderConfigStateStorage.update(module.getName(), success ? currentApkBuilderConfigState : null); + return success; + } + + @SuppressWarnings("unchecked") + private static AndroidFileSetState buildCurrentApkBuilderState(@NotNull Project project, + @NotNull String resPackagePath, + @NotNull String classesDexFilePath, + @NotNull String[] nativeLibDirs, + @NotNull String[] sourceRoots, + @NotNull String[] externalJars, + boolean release) { + final List roots = new ArrayList(); + roots.add(resPackagePath); + roots.add(classesDexFilePath); + roots.addAll(Arrays.asList(externalJars)); + + for (String sourceRootPath : sourceRoots) { + final List files = new ArrayList(); + AndroidApkBuilder.collectStandardSourceFolderResources(new File(sourceRootPath), files, new MyExcludedSourcesFilter(project)); + roots.addAll(AndroidJpsUtil.toPaths(files)); + } + + for (String nativeLibDir : nativeLibDirs) { + final List files = new ArrayList(); + AndroidApkBuilder.collectNativeLibraries(new File(nativeLibDir), files, !release); + roots.addAll(AndroidJpsUtil.toPaths(files)); + } + + return new AndroidFileSetState(roots, Condition.TRUE, false); + } + + @NotNull + private static String[] collectNativeLibsFolders(@NotNull AndroidFacet facet) throws IOException { + final List result = new ArrayList(); + final File libsDir = facet.getNativeLibsDir(); + + if (libsDir != null) { + result.add(libsDir.getPath()); + } + + for (AndroidFacet depFacet : AndroidJpsUtil.getAllDependentAndroidLibraries(facet.getModule())) { + final File depLibsDir = depFacet.getNativeLibsDir(); + if (depLibsDir != null) { + result.add(depLibsDir.getPath()); + } + } + return ArrayUtil.toStringArray(result); + } + private static boolean checkUpToDate(@NotNull Module module, @NotNull Map module2state, @NotNull AndroidFileSetStorage storage) throws IOException { @@ -233,26 +413,11 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { return true; } - private static boolean packageResources(@NotNull AndroidFacet facet, - @NotNull CompileContext context, - @NotNull AndroidFileSetStorage devResourcesStorage, - @NotNull AndroidFileSetStorage devAssetsStorage, - @NotNull AndroidFileSetStorage releaseResourcesStorage, - @NotNull AndroidFileSetStorage releaseAssetsStorage, - @NotNull Map resourcesStates, - @NotNull Map assetsStates) { + private static boolean packageResources(@NotNull AndroidFacet facet, @NotNull CompileContext context) { final Module module = facet.getModule(); - final boolean releaseBuild = AndroidJpsUtil.isReleaseBuild(context); - final AndroidFileSetStorage resourcesStorage = releaseBuild ? releaseResourcesStorage : devResourcesStorage; - final AndroidFileSetStorage assetsStorage = releaseBuild ? releaseAssetsStorage : devAssetsStorage; - try { - if (checkUpToDate(module, resourcesStates, resourcesStorage) && - checkUpToDate(module, assetsStates, assetsStorage)) { - return true; - } - context.processMessage(new ProgressMessage("Packaging resources for module " + module.getName())); + context.processMessage(new ProgressMessage(AndroidJpsBundle.message("android.jps.progress.packaging.resources", module.getName()))); final File manifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(facet); if (manifestFile == null) { @@ -275,18 +440,12 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { } final IAndroidTarget target = pair.getSecond(); - final String outputFilePath = getOutputFile(module, outputDir).getPath(); + final String outputFilePath = getPackagedResourcesFile(module, outputDir).getPath(); final String assetsDirPath = assetsDir != null ? assetsDir.getPath() : null; final String[] resourceDirPaths = AndroidJpsUtil.collectResourceDirsForCompilation(facet, true, context); - if (!doPackageResources(context, manifestFile, target, resourceDirPaths, assetsDirPath, outputFilePath, releaseBuild)) { - resourcesStorage.update(module.getName(), null); - assetsStorage.update(module.getName(), null); - return false; - } - resourcesStorage.update(module.getName(), resourcesStates.get(module)); - assetsStorage.update(module.getName(), assetsStates.get(module)); - return true; + return doPackageResources(context, manifestFile, target, resourceDirPaths, assetsDirPath, outputFilePath, + AndroidJpsUtil.isReleaseBuild(context)); } catch (IOException e) { AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME); @@ -303,7 +462,7 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { boolean releasePackage) { try { final String outputPath = releasePackage - ? outputFilePath + ".release" + ? outputFilePath + RELEASE_SUFFIX : outputFilePath; final Map> messages = AndroidApt .packageResources(target, -1, manifestFile.getPath(), resourceDirPaths, assetsDirPath, outputPath, null, !releasePackage, 0); @@ -318,7 +477,20 @@ public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { } @NotNull - static File getOutputFile(@NotNull Module module, @NotNull File outputDir) { + private static File getPackagedResourcesFile(@NotNull Module module, @NotNull File outputDir) { return new File(outputDir.getPath(), module.getName() + ".apk.res"); } + + private static class MyExcludedSourcesFilter implements Condition { + private final CompilerExcludes myExcludes; + + public MyExcludedSourcesFilter(@NotNull Project project) { + myExcludes = project.getCompilerConfiguration().getExcludes(); + } + + @Override + public boolean value(File file) { + return !myExcludes.isExcluded(file); + } + } } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java index 0a5772e09cb8..8a7068105be8 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java @@ -18,8 +18,6 @@ import org.jetbrains.android.util.ValueResourcesFileParser; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.ClasspathItem; -import org.jetbrains.jps.ClasspathKind; import org.jetbrains.jps.Module; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.incremental.*; @@ -153,7 +151,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { final IAndroidTarget target = moduleData.getAndroidTarget(); try { - final File[] sourceRoots = getSourceRootsForModuleAndDependencies(module); + final File[] sourceRoots = AndroidJpsUtil.getSourceRootsForModuleAndDependencies(module); final String[] sourceRootPaths = AndroidJpsUtil.toPaths(sourceRoots); final String packageName = computePackageForFile(context, file); @@ -569,44 +567,6 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { return FileUtil.toSystemIndependentName(relPath).replace('/', '.'); } - private static void fillSourceRoots(@NotNull Module module, @NotNull Set visited, @NotNull Set result) - throws IOException { - visited.add(module); - final AndroidFacet facet = AndroidJpsUtil.getFacet(module); - File resDir = null; - File resDirForCompilation = null; - - if (facet != null) { - resDir = facet.getResourceDir(); - resDirForCompilation = facet.getResourceDirForCompilation(); - } - - for (String sourceRootPath : module.getSourceRoots()) { - final File sourceRoot = new File(sourceRootPath).getCanonicalFile(); - - if (!sourceRoot.equals(resDir) && !sourceRoot.equals(resDirForCompilation)) { - result.add(sourceRoot); - } - } - - for (ClasspathItem classpathItem : module.getClasspath(ClasspathKind.PRODUCTION_COMPILE)) { - if (classpathItem instanceof Module) { - final Module depModule = (Module)classpathItem; - - if (!visited.contains(depModule)) { - fillSourceRoots(depModule, visited, result); - } - } - } - } - - @NotNull - public static File[] getSourceRootsForModuleAndDependencies(@NotNull Module module) throws IOException { - Set result = new HashSet(); - fillSourceRoots(module, new HashSet(), result); - return result.toArray(new File[result.size()]); - } - @Override public String getDescription() { return "Android Source Generating Builder"; diff --git a/plugins/android/rt/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java b/plugins/android/rt/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java index 16fadec427af..70daf97f8e74 100644 --- a/plugins/android/rt/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java +++ b/plugins/android/rt/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java @@ -384,11 +384,11 @@ public class AndroidApkBuilder { for (File child : children) { if (child.exists()) { if (child.isDirectory()) { - if (JavaResourceFilter.checkFolderForPackaging(child.getName()) && !filter.value(child)) { + if (JavaResourceFilter.checkFolderForPackaging(child.getName()) && filter.value(child)) { collectStandardSourceFolderResources(child, result, filter); } } - else if (checkFileForPackaging(child) && !filter.value(child)) { + else if (checkFileForPackaging(child) && filter.value(child)) { result.add(child); } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/ExcludedSourcesFilter.java b/plugins/android/src/org/jetbrains/android/compiler/ExcludedSourcesFilter.java index 0b5d756cf6d7..1d3a8db262bf 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/ExcludedSourcesFilter.java +++ b/plugins/android/src/org/jetbrains/android/compiler/ExcludedSourcesFilter.java @@ -18,6 +18,6 @@ class ExcludedSourcesFilter implements Condition { @Override public boolean value(File file) { - return AndroidCompileUtil.isExcludedFromCompilation(file, myProject); + return !AndroidCompileUtil.isExcludedFromCompilation(file, myProject); } } From 2c8169e36608fe502a3e940f4696fcd3830b52b8 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 28 Feb 2012 19:39:03 +0400 Subject: [PATCH 17/23] rename AndroidResourcePackagingBuilder -> AndroidPackagingBuilder --- .../src/org/jetbrains/jps/android/AndroidBuilderService.java | 2 +- ...sourcePackagingBuilder.java => AndroidPackagingBuilder.java} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename plugins/android/jps-plugin/src/org/jetbrains/jps/android/{AndroidResourcePackagingBuilder.java => AndroidPackagingBuilder.java} (99%) diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidBuilderService.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidBuilderService.java index 39d38ef9bcdb..9a3bc389b6ea 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidBuilderService.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidBuilderService.java @@ -24,6 +24,6 @@ public class AndroidBuilderService extends BuilderService { @Override public List createProjectLevelBuilders() { return Arrays.asList(new AndroidDexBuilder(), - new AndroidResourcePackagingBuilder()); + new AndroidPackagingBuilder()); } } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java similarity index 99% rename from plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java rename to plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java index cf7bb6bacdb1..276f45c32357 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidResourcePackagingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java @@ -31,7 +31,7 @@ import java.util.*; /** * @author Eugene.Kudelevsky */ -public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder { +public class AndroidPackagingBuilder extends ProjectLevelBuilder { @NonNls private static final String BUILDER_NAME = "android-packager"; @NonNls private static final String RELEASE_SUFFIX = ".release"; @NonNls private static final String UNSIGNED_SUFFIX = ".unsigned"; From 095b65512ab1618ec6fcec56afb0d90cbe1650c1 Mon Sep 17 00:00:00 2001 From: Sergey Evdokimov Date: Tue, 28 Feb 2012 16:46:34 +0400 Subject: [PATCH 18/23] Add button to run interactive console. --- .../plugins/groovy/mvc/MvcFramework.java | 7 ++++ .../groovy/mvc/MvcRunTargetDialog.java | 41 ++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java index c92d5b61b675..8b0510528336 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java @@ -115,6 +115,13 @@ public abstract class MvcFramework { } } + public boolean isInteractiveConsoleSupport(@NotNull Module module) { + return false; + } + + public void runInteractiveConsole(@NotNull Module module) { + throw new UnsupportedOperationException(); + } public abstract void upgradeFramework(@NotNull Module module); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java index f9c4ff502463..f6a802abc6c7 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java @@ -54,15 +54,49 @@ public class MvcRunTargetDialog extends DialogWrapper { private ComboBox myTargetField; private Module myModule; - public MvcRunTargetDialog(@NotNull Module module, MvcFramework framework) { + private final MvcFramework myFramework; + + private Action myInteractiveRunAction; + + public MvcRunTargetDialog(@NotNull Module module, @NotNull MvcFramework framework) { super(module.getProject(), true); myModule = module; + myFramework = framework; setTitle("Run " + framework.getDisplayName() + " target"); setUpDialog(); setModal(true); init(); } + @Override + protected Action[] createLeftSideActions() { + boolean hasOneSupportedModule = false; + for (Module module : ModuleManager.getInstance(myModule.getProject()).getModules()) { + if (module == myModule || myFramework.hasSupport(module)) { + if (myFramework.isInteractiveConsoleSupport(module)) { + hasOneSupportedModule = true; + break; + } + } + } + + if (hasOneSupportedModule) { + myInteractiveRunAction = new DialogWrapperAction("&Run Interactive Console") { + @Override + protected void doAction(ActionEvent e) { + myFramework.runInteractiveConsole(getSelectedModule()); + doCancelAction(); + } + }; + + myInteractiveRunAction.setEnabled(myFramework.isInteractiveConsoleSupport(myModule)); + + return new Action[]{myInteractiveRunAction}; + } + + return new Action[0]; + } + private void setUpDialog() { myTargetLabel.setLabelFor(myTargetField); myTargetField.setFocusable(true); @@ -72,7 +106,7 @@ public class MvcRunTargetDialog extends DialogWrapper { List mvcModules = new ArrayList(); for (Module module : ModuleManager.getInstance(myModule.getProject()).getModules()) { - if (module == myModule || MvcFramework.getInstance(module) != null) { + if (module == myModule || myFramework.hasSupport(module)) { mvcModules.add(module); } } @@ -85,6 +119,9 @@ public class MvcRunTargetDialog extends DialogWrapper { @Override public void actionPerformed(ActionEvent e) { myModule = (Module)myModuleBox.getSelectedItem(); + if (myInteractiveRunAction != null) { + myInteractiveRunAction.setEnabled(myFramework.isInteractiveConsoleSupport(myModule)); + } } }); From a8ba60bfe0ad60e68afa34996f5304fe970fa768 Mon Sep 17 00:00:00 2001 From: Sergey Evdokimov Date: Tue, 28 Feb 2012 20:00:45 +0400 Subject: [PATCH 19/23] Bug fix: Optimize imports invoked on directory reformats entire project. --- .../com/intellij/codeInsight/actions/OptimizeImportsAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/OptimizeImportsAction.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/OptimizeImportsAction.java index 416e2e4bc09b..2b162e02c80e 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/OptimizeImportsAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/OptimizeImportsAction.java @@ -107,7 +107,7 @@ public class OptimizeImportsAction extends AnAction { if (ApplicationManager.getApplication().isUnitTestMode()) { includeSubdirectories = processDirectory = false; } - else if (!EditorSettingsExternalizable.getInstance().getOptions().SHOW_OPIMIZE_IMPORTS_DIALOG) { + else if (!EditorSettingsExternalizable.getInstance().getOptions().SHOW_OPIMIZE_IMPORTS_DIALOG && file != null) { includeSubdirectories = processDirectory = false; } else { From 433aeadc0d3d327adfd3603e5df3d44af6a81bf5 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 28 Feb 2012 17:48:01 +0100 Subject: [PATCH 20/23] resize problems --- .../src/com/intellij/openapi/ui/DialogWrapper.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java index 8ddf512c3376..4d04608abd92 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java @@ -142,6 +142,7 @@ public abstract class DialogWrapper { }; private List myOptionsButtons = new ArrayList(); private int myCurrentOptionsButtonIndex = -1; + private boolean myResizeInProgress = false; protected String getDoNotShowMessage() { return CommonBundle.message("dialog.options.do.not.show"); @@ -167,6 +168,14 @@ public abstract class DialogWrapper { */ protected DialogWrapper(Project project, boolean canBeParent) { myPeer = createPeer(project, canBeParent); + myPeer.getWindow().addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + if (!myResizeInProgress) { + myActualSize = myPeer.getSize(); + } + } + }); createDefaultActions(); } @@ -1611,8 +1620,10 @@ public abstract class DialogWrapper { private void resizeWithAnimation(final Dimension size) { //todo[kb]: fix this PITA + myResizeInProgress = true; if (!Registry.is("enable.animation.on.dialogs")) { setSize(size.width, size.height); + myResizeInProgress = false; return; } @@ -1637,6 +1648,7 @@ public abstract class DialogWrapper { if (myErrorText.shouldBeVisible()) { myErrorText.setVisible(true); } + myResizeInProgress = false; } }.start(); } From d22e2d07c0ef0bb6d28c5fea3df8e2550f94d306 Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Tue, 28 Feb 2012 21:24:18 +0400 Subject: [PATCH 21/23] PropertyTable --- .../AndroidDesignerEditorPanel.java | 37 +- .../designer/model/AttributeProperty.java | 55 +++ .../designer/model/RadViewComponent.java | 23 ++ .../designer/DesignerToolWindowManager.java | 2 + .../actions/CommonEditActionsProvider.java | 32 +- .../componentTree/ComponentTreeBuilder.java | 42 +- .../componentTree/TreeEditableArea.java | 20 +- .../intellij/designer/model/RadComponent.java | 2 +- .../designer/propertyTable/Property.java | 77 +++- .../propertyTable/PropertyEditor.java | 2 + .../propertyTable/PropertyRenderer.java | 15 +- .../designer/propertyTable/PropertyTable.java | 361 +++++++++++++++++- .../propertyTable/PropertyTablePanel.java | 13 +- .../renderers/LabelPropertyRenderer.java | 73 ++++ .../src/messages/DesignerBundle.properties | 5 +- 15 files changed, 685 insertions(+), 74 deletions(-) create mode 100644 plugins/android-designer/src/com/intellij/android/designer/model/AttributeProperty.java create mode 100644 plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/renderers/LabelPropertyRenderer.java diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java index e457e76c2242..61056228f969 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java @@ -43,7 +43,6 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.IconLoader; @@ -65,7 +64,6 @@ import java.awt.*; import java.awt.image.BufferedImage; import java.util.Collections; import java.util.List; -import java.util.concurrent.Callable; /** * @author Alexander Lobas @@ -74,7 +72,7 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { private final TreeComponentDecorator myTreeDecorator = new AndroidTreeDecorator(); private final XmlFile myXmlFile; private final ExternalPSIChangeListener myPSIChangeListener; - private RenderSession mySession; + private volatile RenderSession mySession; public AndroidDesignerEditorPanel(@NotNull Module module, @NotNull VirtualFile file) { super(module, file); @@ -253,19 +251,26 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { private void createRenderer(final String layoutXmlText, final ThrowableRunnable runnable) throws Exception { // TODO: (profile|device|target|...|theme) panel - disposeSession(); - ApplicationManager.getApplication().invokeLater(new DumbAwareRunnable() { - @Override - public void run() { - showProgress("Create RenderLib"); - } - }, new Condition() { - @Override - public boolean value(Object o) { - return mySession != null; - } - } - ); + if (mySession == null) { + ApplicationManager.getApplication().invokeLater( + new Runnable() { + @Override + public void run() { + if (mySession == null) { + showProgress("Create RenderLib"); + } + } + }, new Condition() { + @Override + public boolean value(Object o) { + return mySession != null; + } + } + ); + } + else { + disposeSession(); + } ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { @Override diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/AttributeProperty.java b/plugins/android-designer/src/com/intellij/android/designer/model/AttributeProperty.java new file mode 100644 index 000000000000..67ba3edeadc3 --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/AttributeProperty.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.android.designer.model; + +import com.intellij.designer.model.RadComponent; +import com.intellij.designer.propertyTable.Property; +import com.intellij.designer.propertyTable.PropertyEditor; +import com.intellij.designer.propertyTable.PropertyRenderer; +import com.intellij.designer.propertyTable.renderers.LabelPropertyRenderer; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * @author Alexander Lobas + */ +public class AttributeProperty extends Property { + private LabelPropertyRenderer myRenderer = new LabelPropertyRenderer(null); + @NotNull private final String myAttribute; + + public AttributeProperty(Property parent, @NotNull @NonNls String name, @NotNull @NonNls String attribute) { + super(parent, name); + myAttribute = attribute; + } + + @Override + public Object getValue(RadComponent component) throws Exception { + XmlTag tag = ((RadViewComponent)component).getTag(); + return tag == null ? null : tag.getAttributeValue(myAttribute); + } + + @NotNull + @Override + public PropertyRenderer getRenderer() { + return myRenderer; + } + + @Override + public PropertyEditor getEditor() { + return null; // TODO: Auto-generated method stub + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java b/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java index 5b923514ea3a..05e5718da36f 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/RadViewComponent.java @@ -16,10 +16,12 @@ package com.intellij.android.designer.model; import com.intellij.designer.model.RadComponent; +import com.intellij.designer.propertyTable.Property; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.util.IconLoader; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; import org.jdom.Element; @@ -45,6 +47,7 @@ public class RadViewComponent extends RadComponent { private Component myNativeComponent; private final Rectangle myBounds = new Rectangle(); private XmlTag myTag; + private List myProperties; public RadViewComponent(RadViewComponent parent) { setParent(parent); @@ -107,4 +110,24 @@ public class RadViewComponent extends RadComponent { actionGroup.add(action); } } + + @Override + public List getProperties() { + if (myProperties == null && myTag != null) { + myProperties = new ArrayList(); + for (XmlAttribute attribute : myTag.getAttributes()) { + String name = attribute.getName(); + if (name.equals("xmlns:android")) { + continue; + } + + Property property = new AttributeProperty(null, new String(name).replace("android:", "").replace('_', ' '), name); + property.setImportant(name.equals("android:text")); + property.setExpert(name.equals("android:id")); + property.setDeprecated(name.equals("android:background")); + myProperties.add(property); + } + } + return myProperties == null ? super.getProperties() : myProperties; + } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/DesignerToolWindowManager.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/DesignerToolWindowManager.java index a6da593dcadf..623d2f546360 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/DesignerToolWindowManager.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/DesignerToolWindowManager.java @@ -164,12 +164,14 @@ public final class DesignerToolWindowManager implements ProjectComponent { if (designer == null) { myComponentTree.setDecorator(null); myComponentTree.setActionPanel(null); + myPropertyTablePanel.getPropertyTable().setArea(null); myToolWindow.setAvailable(false, null); } else { myComponentTree.setDecorator(designer.getTreeDecorator()); myComponentTree.setActionPanel(designer.getActionPanel()); myTreeBuilder = new ComponentTreeBuilder(myComponentTree, designer); + myPropertyTablePanel.getPropertyTable().setArea(myTreeBuilder.getTreeArea()); myToolWindow.setAvailable(true, null); myToolWindow.show(null); } diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/CommonEditActionsProvider.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/CommonEditActionsProvider.java index 8a8e49639826..8f1262547bf1 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/CommonEditActionsProvider.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/actions/CommonEditActionsProvider.java @@ -19,6 +19,7 @@ import com.intellij.designer.DesignerBundle; import com.intellij.designer.clipboard.SerializedComponentData; import com.intellij.designer.clipboard.SimpleTransferable; import com.intellij.designer.designSurface.DesignerEditorPanel; +import com.intellij.designer.designSurface.EditableArea; import com.intellij.designer.designSurface.tools.ComponentPasteFactory; import com.intellij.designer.designSurface.tools.PasteTool; import com.intellij.designer.model.RadComponent; @@ -78,16 +79,45 @@ public class CommonEditActionsProvider implements DeleteProvider, CopyProvider, myDesigner.getToolProvider().execute(new ThrowableRunnable() { @Override public void run() throws Exception { - List components = RadComponent.getPureSelection(myDesigner.getActionsArea().getSelection()); + EditableArea area = myDesigner.getActionsArea(); + List selection = area.getSelection(); + List components = RadComponent.getPureSelection(selection); + RadComponent newSelection = getNewSelection(components.get(0), selection); + for (RadComponent component : components) { component.delete(); } + + if (newSelection == null) { + area.deselectAll(); + } + else { + area.select(newSelection); + } } }); } }, DesignerBundle.message("command.delete.selection"), null); } + @Nullable + private static RadComponent getNewSelection(RadComponent component, List excludes) { + RadComponent parent = component.getParent(); + if (parent == null) { + return null; + } + + List children = parent.getChildren(); + int size = children.size(); + for (int i = children.indexOf(component) + 1; i < size; i++) { + RadComponent next = children.get(i); + if (!excludes.contains(next)) { + return next; + } + } + + return parent; + } ////////////////////////////////////////////////////////////////////////////////////////// // // Copy diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTreeBuilder.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTreeBuilder.java index 71f7675d413c..5c152b4637d4 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTreeBuilder.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/ComponentTreeBuilder.java @@ -20,16 +20,12 @@ import com.intellij.designer.designSurface.DesignerEditorPanel; import com.intellij.designer.designSurface.EditableArea; import com.intellij.ide.util.treeView.AbstractTreeBuilder; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeSelectionModel; /** * @author Alexander Lobas */ -public final class ComponentTreeBuilder extends AbstractTreeBuilder implements ComponentSelectionListener, TreeSelectionListener { - private final TreeSelectionModel myTreeSelectionModel; +public final class ComponentTreeBuilder extends AbstractTreeBuilder implements ComponentSelectionListener { private final EditableArea mySurfaceArea; private final TreeEditableArea myTreeArea; private final TreeGlassLayer myGlassLayer; @@ -39,7 +35,6 @@ public final class ComponentTreeBuilder extends AbstractTreeBuilder implements C initRootNode(); - myTreeSelectionModel = getTree().getSelectionModel(); mySurfaceArea = designer.getSurfaceArea(); myTreeArea = new TreeEditableArea(tree, this); myGlassLayer = new TreeGlassLayer(tree, designer.getToolProvider(), myTreeArea); @@ -57,47 +52,34 @@ public final class ComponentTreeBuilder extends AbstractTreeBuilder implements C @Override public void dispose() { removeListeners(); + myTreeArea.dispose(); myGlassLayer.dispose(); super.dispose(); } private void addListeners() { mySurfaceArea.addSelectionListener(this); - myTreeSelectionModel.addTreeSelectionListener(this); + myTreeArea.addSelectionListener(this); } private void removeListeners() { mySurfaceArea.removeSelectionListener(this); - myTreeSelectionModel.removeTreeSelectionListener(this); + myTreeArea.removeSelectionListener(this); } - private void handleSelection(Runnable runnable) { + @Override + public void selectionChanged(EditableArea area) { try { removeListeners(); - runnable.run(); + if (mySurfaceArea == area) { + myTreeArea.setSelection(mySurfaceArea.getSelection()); + } + else { + mySurfaceArea.setSelection(myTreeArea.getSelection()); + } } finally { addListeners(); } } - - @Override - public void selectionChanged(EditableArea area) { - handleSelection(new Runnable() { - @Override - public void run() { - myTreeArea.setSelection(mySurfaceArea.getSelection()); - } - }); - } - - @Override - public void valueChanged(TreeSelectionEvent e) { - handleSelection(new Runnable() { - @Override - public void run() { - mySurfaceArea.setSelection(myTreeArea.getSelection()); - } - }); - } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/TreeEditableArea.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/TreeEditableArea.java index 7ea992b34532..7abdf062a042 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/TreeEditableArea.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/componentTree/TreeEditableArea.java @@ -24,6 +24,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.event.EventListenerList; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreePath; import java.awt.*; import java.util.ArrayList; @@ -33,13 +36,19 @@ import java.util.List; /** * @author Alexander Lobas */ -public final class TreeEditableArea implements EditableArea, FeedbackTreeLayer { +public final class TreeEditableArea implements EditableArea, FeedbackTreeLayer, TreeSelectionListener { + private final EventListenerList myListenerList = new EventListenerList(); private final ComponentTree myTree; private final AbstractTreeBuilder myTreeBuilder; public TreeEditableArea(ComponentTree tree, AbstractTreeBuilder treeBuilder) { myTree = tree; myTreeBuilder = treeBuilder; + tree.getSelectionModel().addTreeSelectionListener(this); + } + + public void dispose() { + myTree.getSelectionModel().removeTreeSelectionListener(this); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -50,10 +59,19 @@ public final class TreeEditableArea implements EditableArea, FeedbackTreeLayer { @Override public void addSelectionListener(ComponentSelectionListener listener) { + myListenerList.add(ComponentSelectionListener.class, listener); } @Override public void removeSelectionListener(ComponentSelectionListener listener) { + myListenerList.remove(ComponentSelectionListener.class, listener); + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + for (ComponentSelectionListener listener : myListenerList.getListeners(ComponentSelectionListener.class)) { + listener.selectionChanged(this); + } } @NotNull diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java index 757d130325a7..414b6e5efcc6 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/model/RadComponent.java @@ -134,7 +134,7 @@ public abstract class RadComponent { ////////////////////////////////////////////////////////////////////////////////////////// public List getProperties() { - return null; + return Collections.emptyList(); } public final Object getClientProperty(@NotNull Object key) { diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/Property.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/Property.java index bceba0636a2f..2a1fcffe2111 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/Property.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/Property.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.List; /** @@ -28,28 +29,94 @@ import java.util.List; public abstract class Property { private final Property myParent; @NotNull private final String myName; + private boolean myImportant; + private boolean myExpert; + private boolean myDeprecated; public Property(Property parent, @NotNull @NonNls String name) { - myParent = parent; myName = name; } + ////////////////////////////////////////////////////////////////////////////////////////// + // + // Hierarchy + // + ////////////////////////////////////////////////////////////////////////////////////////// + public final Property getParent() { return myParent; } + public List getChildren(@Nullable RadComponent component) { + return Collections.emptyList(); + } + + public int getIndent() { + if (myParent != null) { + return myParent.getParent() != null ? 2 : 1; + } + return 0; + } + + public String getPath() { + return myParent == null ? myName : myParent.getPath() + "/" + myName; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // Value + // + ////////////////////////////////////////////////////////////////////////////////////////// + + public Object getValue(RadComponent component) throws Exception { + return null; + } + + public boolean isDefaultValue(RadComponent component) throws Exception { + return false; + } + + public void setDefaultValue(RadComponent component) throws Exception { + } + + public boolean availableFor(List components) { + return true; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // Presentation + // + ////////////////////////////////////////////////////////////////////////////////////////// + @NotNull public final String getName() { return myName; } - public List getChildren(RadComponent component) { - return null; + public boolean isImportant() { + return myImportant; } - public Object getValue(RadComponent component) { - return null; + public void setImportant(boolean important) { + myImportant = important; + } + + public boolean isExpert() { + return myExpert; + } + + public void setExpert(boolean expert) { + myExpert = expert; + } + + public boolean isDeprecated() { + return myDeprecated; + } + + public void setDeprecated(boolean deprecated) { + myDeprecated = deprecated; } @NotNull diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyEditor.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyEditor.java index 6e3c188e729b..ae6e3fc9b99e 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyEditor.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyEditor.java @@ -26,4 +26,6 @@ public interface PropertyEditor { JComponent getComponent(RadComponent component, Object value); Object getValue(); + + void updateUI(); } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyRenderer.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyRenderer.java index 235a9e835caf..5e5658c7fd06 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyRenderer.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyRenderer.java @@ -16,6 +16,7 @@ package com.intellij.designer.propertyTable; import com.intellij.designer.model.RadComponent; +import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -23,19 +24,7 @@ import javax.swing.*; * @author Alexander Lobas */ public interface PropertyRenderer { - /** - * @return JComponent to represent the value - * somewhere in UI (for example in the JList of in the JTree). To be - * consistent with other UI additional parameter abount selection and - * focus are also passed. - */ - JComponent getComponent(RadComponent component, Object value, boolean selected, boolean hasFocus); + JComponent getComponent(@Nullable RadComponent component, @Nullable Object value, boolean selected, boolean hasFocus); - /** - * Renderer should update UI of all its internal components to fit current - * IDEA Look And Feel. We cannot directly update UI of the component - * that is returned by {@link #getComponent } method - * because hidden component that are not in the Swing tree can exist. - */ void updateUI(); } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTable.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTable.java index 87c66b426e36..465fda8a8fc4 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTable.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTable.java @@ -15,16 +15,375 @@ */ package com.intellij.designer.propertyTable; +import com.intellij.designer.DesignerBundle; +import com.intellij.designer.designSurface.ComponentSelectionListener; +import com.intellij.designer.designSurface.EditableArea; +import com.intellij.designer.model.RadComponent; import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.IconLoader; +import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.Gray; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.table.JBTable; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.EmptyIcon; +import com.intellij.util.ui.IndentedIcon; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.util.*; +import java.util.List; /** * @author Alexander Lobas */ -public final class PropertyTable extends JBTable implements DataProvider { +public final class PropertyTable extends JBTable implements ComponentSelectionListener, DataProvider { + private static final Logger LOG = Logger.getInstance("#com.intellij.designer.propertyTable"); + + private final AbstractTableModel myModel = new PropertyTableModel(); + private List myComponents = Collections.emptyList(); + private List myProperties = Collections.emptyList(); + private final Set myExpandedProperties = new HashSet(); + + private final TableCellRenderer myCellRenderer = new PropertyTableCellRenderer(); + + @Nullable private EditableArea myArea; + + private boolean myShowExpert; + + public PropertyTable() { + setModel(myModel); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + setEnableAntialiasing(true); + } + + public void setArea(@Nullable EditableArea area) { + if (myArea != null) { + myArea.removeSelectionListener(this); + } + + myArea = area; + + if (myArea != null) { + myArea.addSelectionListener(this); + } + + selectionChanged(area); + } + + @Override + public void selectionChanged(EditableArea area) { + if (isEditing()) { + cellEditor.stopCellEditing(); + } + + if (myArea == null) { + myComponents = Collections.emptyList(); + myProperties = Collections.emptyList(); + myModel.fireTableDataChanged(); + } + else { + myComponents = new ArrayList(myArea.getSelection()); + fillProperties(); + myModel.fireTableDataChanged(); + } + } + + private void fillProperties() { + myProperties = new ArrayList(); + int size = myComponents.size(); + + if (size > 0) { + fillProperties(myComponents.get(0), myProperties); + + if (size > 1) { + for (Iterator I = myProperties.iterator(); I.hasNext(); ) { + if (!I.next().availableFor(myComponents)) { + I.remove(); + } + } + + for (int i = 1; i < size; i++) { + List properties = new ArrayList(); + fillProperties(myComponents.get(i), properties); + + for (Iterator I = myProperties.iterator(); I.hasNext(); ) { + final Property property = I.next(); + Property testProperty = ContainerUtil.find(properties, new Condition() { + @Override + public boolean value(Property next) { + return property.getName().equals(next.getName()); + } + }); + + if (testProperty == null || !property.getClass().equals(testProperty.getClass())) { + I.remove(); + continue; + } + + List children = getChildren(property); + List testChildren = getChildren(testProperty); + int pSize = children.size(); + + if (pSize != testChildren.size()) { + I.remove(); + continue; + } + + for (int j = 0; j < pSize; j++) { + if (!children.get(j).getName().equals(testChildren.get(j).getName())) { + I.remove(); + break; + } + } + } + } + } + } + } + + private void fillProperties(RadComponent component, List properties) { + for (Property property : component.getProperties()) { + addProperty(property, properties); + } + } + + private void addProperty(Property property, List properties) { + if (property.isExpert() && !myShowExpert) { + return; + } + + properties.add(property); + + if (isExpanded(property)) { + for (Property child : getChildren(property)) { + addProperty(child, properties); + } + } + } + + public boolean isShowExpert() { + return myShowExpert; + } + + public void setShowExpert(boolean showExpert) { + myShowExpert = showExpert; + selectionChanged(myArea); + } + + public TableCellRenderer getCellRenderer(int row, int column) { + return myCellRenderer; + } + + @Nullable + private RadComponent getCurrentComponent() { + return myComponents.size() == 1 ? myComponents.get(0) : null; + } + + private List getChildren(Property property) { + return property.getChildren(getCurrentComponent()); + } + + private boolean isDefault(Property property) throws Exception { + for (RadComponent component : myComponents) { + if (!property.isDefaultValue(component)) { + return false; + } + } + return true; + } + + @Nullable + private Object getValue(Property property) throws Exception { + int size = myComponents.size(); + if (size == 0) { + return null; + } + + Object value = property.getValue(myComponents.get(0)); + for (int i = 1; i < size; i++) { + if (!Comparing.equal(value, property.getValue(myComponents.get(i)))) { + return null; + } + } + + return value; + } + + private boolean isExpanded(Property property) { + return myExpandedProperties.contains(property.getPath()); + } + @Override public Object getData(@NonNls String dataId) { return null; //To change body of implemented methods use File | Settings | File Templates. } + + private class PropertyTableModel extends AbstractTableModel { + private final String[] myColumnNames = + {DesignerBundle.message("designer.properties.column1"), DesignerBundle.message("designer.properties.column2")}; + + @Override + public int getColumnCount() { + return myColumnNames.length; + } + + @Override + public String getColumnName(int column) { + return myColumnNames[column]; + } + + public boolean isCellEditable(final int row, final int column) { + return column == 1 && myProperties.get(row).getEditor() != null; + } + + @Override + public int getRowCount() { + return myProperties.size(); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return myProperties.get(rowIndex); + } + } + + private class PropertyTableCellRenderer implements TableCellRenderer { + private final ColoredTableCellRenderer myPropertyNameRenderer; + private final ColoredTableCellRenderer myErrorRenderer; + private final Icon myExpandIcon; + private final Icon myCollapseIcon; + private final Icon myIndentedExpandIcon; + private final Icon myIndentedCollapseIcon; + private final Icon[] myIndentIcons = new Icon[3]; + + private PropertyTableCellRenderer() { + myPropertyNameRenderer = new ColoredTableCellRenderer() { + protected void customizeCellRenderer( + final JTable table, + final Object value, + final boolean selected, + final boolean hasFocus, + final int row, + final int column + ) { + setPaintFocusBorder(false); + setFocusBorderAroundIcon(true); + } + }; + + myErrorRenderer = new ColoredTableCellRenderer() { + protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { + setPaintFocusBorder(false); + } + }; + + myExpandIcon = IconLoader.getIcon("/com/intellij/uiDesigner/icons/expandNode.png"); + myCollapseIcon = IconLoader.getIcon("/com/intellij/uiDesigner/icons/collapseNode.png"); + for (int i = 0; i < myIndentIcons.length; i++) { + myIndentIcons[i] = new EmptyIcon(9 + 11 * i, 9); + } + myIndentedExpandIcon = new IndentedIcon(myExpandIcon, 11); + myIndentedCollapseIcon = new IndentedIcon(myCollapseIcon, 11); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { + myPropertyNameRenderer.getTableCellRendererComponent(table, value, selected, hasFocus, row, column); + + column = table.convertColumnIndexToModel(column); + Property property = (Property)value; + Color background = table.getBackground(); + + try { + if (isDefault(property)) { + background = Gray._240; + } + } + catch (Throwable e) { + LOG.debug(e); + } + + if (!selected) { + myPropertyNameRenderer.setBackground(background); + } + + if (column == 0) { + SimpleTextAttributes attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES; + if (property.isImportant()) { + attributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES; + } + else if (property.isExpert()) { + attributes = SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES; + } + if (property.isDeprecated()) { + attributes = new SimpleTextAttributes(attributes.getBgColor(), attributes.getFgColor(), attributes.getWaveColor(), + attributes.getStyle() | SimpleTextAttributes.STYLE_STRIKEOUT); + } + + myPropertyNameRenderer.append(property.getName(), attributes); + + if (!getChildren(property).isEmpty()) { + if (property.getParent() == null) { + if (isExpanded(property)) { + myPropertyNameRenderer.setIcon(myCollapseIcon); + } + else { + myPropertyNameRenderer.setIcon(myExpandIcon); + } + } + else { + if (isExpanded(property)) { + myPropertyNameRenderer.setIcon(myIndentedCollapseIcon); + } + else { + myPropertyNameRenderer.setIcon(myIndentedExpandIcon); + } + } + } + else { + myPropertyNameRenderer.setIcon(myIndentIcons[property.getIndent()]); + } + + if (!selected) { + myPropertyNameRenderer.setForeground(property.isExpert() ? Color.LIGHT_GRAY : table.getForeground()); + } + } + else { + try { + PropertyRenderer renderer = property.getRenderer(); + JComponent component = renderer.getComponent(getCurrentComponent(), getValue(property), selected, hasFocus); + + if (!selected) { + component.setBackground(background); + } + + component.setFont(table.getFont()); + + if (component instanceof JCheckBox) { + component.putClientProperty("JComponent.sizeVariant", UIUtil.isUnderAquaLookAndFeel() ? "small" : null); + } + + return component; + } + catch (Throwable e) { + LOG.debug(e); + myErrorRenderer.clear(); + myErrorRenderer + .append(DesignerBundle.message("designer.properties.getting.error", e.getMessage()), SimpleTextAttributes.ERROR_ATTRIBUTES); + return myErrorRenderer; + } + } + + return myPropertyNameRenderer; + } + } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTablePanel.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTablePanel.java index 224badbb84ac..56036e8b7cac 100644 --- a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTablePanel.java +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/PropertyTablePanel.java @@ -16,6 +16,7 @@ package com.intellij.designer.propertyTable; import com.intellij.designer.DesignerBundle; +import com.intellij.designer.designSurface.EditableArea; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.util.IconLoader; import com.intellij.ui.IdeBorderFactory; @@ -32,8 +33,7 @@ import java.awt.*; public final class PropertyTablePanel extends JPanel { private final DefaultActionGroup myActionGroup = new DefaultActionGroup(); private final ActionToolbar myToolbar; - private final PropertyTable myPropertyTable; - private boolean myShowExpert; + private final PropertyTable myPropertyTable = new PropertyTable(); public PropertyTablePanel() { createActions(); @@ -49,7 +49,6 @@ public final class PropertyTablePanel extends JPanel { titlePanel.add(myToolbar.getComponent(), BorderLayout.LINE_END); add(titlePanel, BorderLayout.PAGE_START); - myPropertyTable = new PropertyTable(); JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myPropertyTable); scrollPane.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); add(scrollPane, BorderLayout.CENTER); @@ -60,13 +59,17 @@ public final class PropertyTablePanel extends JPanel { myActionGroup.add(new ToggleAction(expert, expert, IconLoader.getIcon("/com/intellij/designer/icons/filter.png")) { @Override public boolean isSelected(AnActionEvent e) { - return myShowExpert; + return myPropertyTable.isShowExpert(); } @Override public void setSelected(AnActionEvent e, boolean state) { - myShowExpert = state; + myPropertyTable.setShowExpert(state); } }); } + + public PropertyTable getPropertyTable() { + return myPropertyTable; + } } \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/renderers/LabelPropertyRenderer.java b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/renderers/LabelPropertyRenderer.java new file mode 100644 index 000000000000..9af5f66d9c34 --- /dev/null +++ b/plugins/ui-designer/ui-designer-new/src/com/intellij/designer/propertyTable/renderers/LabelPropertyRenderer.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.designer.propertyTable.renderers; + +import com.intellij.designer.model.RadComponent; +import com.intellij.designer.propertyTable.PropertyRenderer; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * This is convenient class for implementing property renderers which + * are based on JLabel. + * + * @author Anton Katilin + * @author Vladimir Kondratyev + */ +public class LabelPropertyRenderer extends JLabel implements PropertyRenderer { + @Nullable private final String myStaticText; + + public LabelPropertyRenderer(@Nullable String staticText) { + myStaticText = staticText; + setOpaque(true); + putClientProperty("html.disable", true); + setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 0)); + } + + public JLabel getComponent(@Nullable RadComponent component, @Nullable Object value, boolean selected, boolean hasFocus) { + // Reset text and icon + setText(null); + setIcon(null); + + // Background and foreground + if (selected) { + setForeground(UIUtil.getTableSelectionForeground()); + setBackground(UIUtil.getTableSelectionBackground()); + } + else { + setForeground(UIUtil.getTableForeground()); + setBackground(UIUtil.getTableBackground()); + } + + if (value != null) { + customize(value); + } + + return this; + } + + /** + * Here all subclasses should customize their text, icon and other + * attributes. Note, that background and foreground colors are already + * set. + */ + protected void customize(@NotNull Object value) { + setText(myStaticText != null ? myStaticText : value.toString()); + } +} \ No newline at end of file diff --git a/plugins/ui-designer/ui-designer-new/src/messages/DesignerBundle.properties b/plugins/ui-designer/ui-designer-new/src/messages/DesignerBundle.properties index b518317658d9..98f91d7801aa 100644 --- a/plugins/ui-designer/ui-designer-new/src/messages/DesignerBundle.properties +++ b/plugins/ui-designer/ui-designer-new/src/messages/DesignerBundle.properties @@ -4,4 +4,7 @@ designer.toolwindow.title=Component Tree command.delete.selection=Delete Selection designer.properties.title=Properties -designer.properties.show.expert=Show expert properties \ No newline at end of file +designer.properties.show.expert=Show expert properties +designer.properties.column1=Property +designer.properties.column2=Value +designer.properties.getting.error=Error getting value: {0} From 71267f23b93c75e1bcca9f89f742bab7227721bb Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 28 Feb 2012 18:35:41 +0100 Subject: [PATCH 22/23] resize problems --- .../src/com/intellij/openapi/ui/DialogWrapper.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java index 4d04608abd92..7567b8ff984d 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java @@ -173,6 +173,9 @@ public abstract class DialogWrapper { public void componentResized(ComponentEvent e) { if (!myResizeInProgress) { myActualSize = myPeer.getSize(); + if (myErrorText.isVisible()) { + myActualSize.height -= myErrorText.getHeight() + 10; + } } } }); From 4cbb17ac3035c41bc1bf20dda10c3c6ed3453045 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 28 Feb 2012 19:08:48 +0100 Subject: [PATCH 23/23] a test for skipping lifted items --- .../codeInsight/completion/normalSorting/SkipLifted.java | 8 ++++++++ .../completion/NormalCompletionOrderingTest.groovy | 4 ++++ 2 files changed, 12 insertions(+) create mode 100644 java/java-tests/testData/codeInsight/completion/normalSorting/SkipLifted.java diff --git a/java/java-tests/testData/codeInsight/completion/normalSorting/SkipLifted.java b/java/java-tests/testData/codeInsight/completion/normalSorting/SkipLifted.java new file mode 100644 index 000000000000..4763b02ebe62 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normalSorting/SkipLifted.java @@ -0,0 +1,8 @@ +class Foo { + { + hashCox + } + + int hashCodeMine() {} + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy index cc17fc46d057..4ce01ee5f6c0 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionOrderingTest.groovy @@ -196,6 +196,10 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase { assertEquals("Baaaaaaar", ((JavaPsiClassReferenceElement)lookup.getItems().get(1)).getQualifiedName()); } + public void _testSkipLifted() { + checkPreferredItems(1, "hashCode", "hashCodeMine") + } + public void testDispreferInnerClasses() { checkPreferredItems(0); //no chosen items assertFalse(getLookup().getItems().get(0).getObject() instanceof PsiClass);