mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
class Foo {
|
||||
{
|
||||
hashCo<caret>x
|
||||
}
|
||||
|
||||
int hashCodeMine() {}
|
||||
|
||||
}
|
||||
+4
@@ -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);
|
||||
|
||||
@@ -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<ClassRepr> classes = delta.mySourceFileToClasses.get(f);
|
||||
@@ -1767,9 +1769,9 @@ public class Mappings {
|
||||
|
||||
@Override
|
||||
public void registerImports(final String className, final Collection<String> imports, Collection<String> 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String> getAdditionalSourceRoots(@NotNull Module module) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> getAdditionalTestSourceRoots(@NotNull Module module) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ public class IdeaProjectLoader {
|
||||
private Map<String, String> pathVariables
|
||||
private ProjectMacroExpander projectMacroExpander
|
||||
private ProjectLoadingErrorReporter errorReporter
|
||||
private static final OwnServiceLoader<AdditionalRootsProviderService> 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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+4
@@ -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;
|
||||
|
||||
+11
-1
@@ -96,7 +96,17 @@ public class ConfigurationSettingsEditorWrapper extends SettingsEditor<RunnerAnd
|
||||
}
|
||||
}
|
||||
|
||||
myStepsPanel.setLayout(new GridLayout((stepsRows.size() + 1) / 2, 1, UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
|
||||
int maxStepRowWidth = 0;
|
||||
for (StepBeforeLaunchRow stepRow : stepsRows) {
|
||||
maxStepRowWidth = Math.max(maxStepRowWidth, stepRow.getPreferredSize().width);
|
||||
}
|
||||
if (maxStepRowWidth * 3 < getComponent().getPreferredSize().width || maxStepRowWidth < 200) {
|
||||
myStepsPanel.setLayout(new GridLayout(0, 3, UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
|
||||
} else if (maxStepRowWidth * 2 < getComponent().getPreferredSize().width || maxStepRowWidth < 300) {
|
||||
myStepsPanel.setLayout(new GridLayout(0, 2, UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
|
||||
} else {
|
||||
myStepsPanel.setLayout(new GridLayout(0, 1, UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP));
|
||||
}
|
||||
for (StepBeforeLaunchRow stepRow : stepsRows) {
|
||||
myStepsPanel.add(stepRow);
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@ public abstract class DialogWrapper {
|
||||
};
|
||||
private List<JBOptionButton> myOptionsButtons = new ArrayList<JBOptionButton>();
|
||||
private int myCurrentOptionsButtonIndex = -1;
|
||||
private boolean myResizeInProgress = false;
|
||||
|
||||
protected String getDoNotShowMessage() {
|
||||
return CommonBundle.message("dialog.options.do.not.show");
|
||||
@@ -167,6 +168,17 @@ 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();
|
||||
if (myErrorText.isVisible()) {
|
||||
myActualSize.height -= myErrorText.getHeight() + 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
createDefaultActions();
|
||||
}
|
||||
|
||||
@@ -1611,8 +1623,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 +1651,7 @@ public abstract class DialogWrapper {
|
||||
if (myErrorText.shouldBeVisible()) {
|
||||
myErrorText.setVisible(true);
|
||||
}
|
||||
myResizeInProgress = false;
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<VirtualFile> {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -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<Object> myGetter;
|
||||
private final Consumer<Object> mySaver;
|
||||
|
||||
public TodoForBaseRevision(Project project,
|
||||
List<TextRange> ranges,
|
||||
int additionalOffset,
|
||||
String name,
|
||||
String text,
|
||||
boolean revision, FileType type, final Getter<Object> cacheGetter,
|
||||
final Consumer<Object> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<TextRange> 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<TodoItem[]>() {
|
||||
@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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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<TextRange> 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<TextRange> ranges, FileType fileType, int additionalOffset) {
|
||||
protected TodoForRanges(final Project project,
|
||||
final List<TextRange> 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<Pair<TextRange, TextAttributes>> execute() {
|
||||
final TodoItem[] todoItems = ApplicationManager.getApplication().runReadAction(new Computable<TodoItem[]>() {
|
||||
@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<TodoItem, TextRange> stepIntersection =
|
||||
new StepIntersection<TodoItem, TextRange>(new Convertor<TodoItem, TextRange>() {
|
||||
@@ -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<PsiFile>() {
|
||||
@Override
|
||||
public PsiFile compute() {
|
||||
return PsiFileFactory.getInstance(myProject).createFileFromText((myOldRevision ? "old" : "") + myFileName, myFileType, myText);
|
||||
}
|
||||
});
|
||||
return helper.findTodoItemsLight(psiFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
<action id="CIDR.CreateXcodeProject">
|
||||
<keyboard-shortcut first-keystroke="shift meta N" />
|
||||
</action>
|
||||
<action id="CloseProject">
|
||||
<keyboard-shortcut first-keystroke="meta W" />
|
||||
<action id="CloseContent">
|
||||
<keyboard-shortcut first-keystroke="meta W"/>
|
||||
</action>
|
||||
<action id="CodeCompletion">
|
||||
<keyboard-shortcut first-keystroke="control SPACE" />
|
||||
|
||||
@@ -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;
|
||||
@@ -2556,23 +2553,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<? extends PanelWithAnchor> 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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<Key, byte[]> myCache;
|
||||
private final SLRUMap<CurrentKey, VcsRevisionNumber> myCurrentRevisionsCache;
|
||||
private final SLRUMap<Pair<FilePath, VcsRevisionNumber>, Object> myCustom;
|
||||
private long myCounter;
|
||||
|
||||
public ContentRevisionCache() {
|
||||
myLock = new Object();
|
||||
myCache = new SLRUMap<Key, byte[]>(100, 50);
|
||||
myCurrentRevisionsCache = new SLRUMap<CurrentKey, VcsRevisionNumber>(200, 50);
|
||||
myCustom = new SLRUMap<Pair<FilePath, VcsRevisionNumber>, 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<FilePath, VcsRevisionNumber>(path, number), o);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Object getCustom(FilePath path, VcsRevisionNumber number) {
|
||||
synchronized (myLock) {
|
||||
return myCustom.get(new Pair<FilePath, VcsRevisionNumber>(path, number));
|
||||
}
|
||||
}
|
||||
|
||||
public void clearAllCurrent() {
|
||||
synchronized (myLock) {
|
||||
++ myCounter;
|
||||
|
||||
@@ -32,22 +32,15 @@ import java.util.List;
|
||||
* @author yole
|
||||
*/
|
||||
public abstract class VcsBackgroundTask<T> extends Task.ConditionalModal {
|
||||
@NotNull private final PerformInBackgroundOption myBackgroundOption;
|
||||
private final Collection<T> myItems;
|
||||
private final List<VcsException> myExceptions = new ArrayList<VcsException>();
|
||||
|
||||
public VcsBackgroundTask(final Project project, @NotNull final String title, @NotNull final PerformInBackgroundOption backgroundOption,
|
||||
final Collection<T> 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 {
|
||||
|
||||
+4
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+35
-9
@@ -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<Pair<TextRange,TextAttributes>> beforeTodoRanges = new TodoForRanges(myProject, myFileName, oldDocument.getText(), true,
|
||||
getBeforeFragments(), myFileType, 1).execute();
|
||||
final List<Pair<TextRange, TextAttributes>> afterTodoRanges = new TodoForRanges(myProject, myFileName, document.getText(), false,
|
||||
getAfterFragments(), myFileType, 1).execute();
|
||||
final ContentRevisionCache cache = ProjectLevelVcsManager.getInstance(myProject).getContentRevisionCache();
|
||||
final List<Pair<TextRange,TextAttributes>> beforeTodoRanges = myBeforeNumber == null ? Collections.<Pair<TextRange,TextAttributes>>emptyList() :
|
||||
new TodoForBaseRevision(myProject, getBeforeFragments(), 1, myFileName, oldDocument.getText(), true, myFileType, new Getter<Object>() {
|
||||
@Override
|
||||
public Object get() {
|
||||
return cache.getCustom(myFilePath, myBeforeNumber);
|
||||
}
|
||||
}, new Consumer<Object>() {
|
||||
@Override
|
||||
public void consume(Object items) {
|
||||
cache.putCustom(myFilePath, myBeforeNumber, items);
|
||||
}
|
||||
}).execute();
|
||||
|
||||
final List<Pair<TextRange, TextAttributes>> 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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+21
-16
@@ -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<Throwable> 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
|
||||
|
||||
+55
@@ -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
|
||||
}
|
||||
}
|
||||
+23
@@ -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<Property> myProperties;
|
||||
|
||||
public RadViewComponent(RadViewComponent parent) {
|
||||
setParent(parent);
|
||||
@@ -107,4 +110,24 @@ public class RadViewComponent extends RadComponent {
|
||||
actionGroup.add(action);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Property> getProperties() {
|
||||
if (myProperties == null && myTag != null) {
|
||||
myProperties = new ArrayList<Property>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
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}
|
||||
+42
@@ -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);
|
||||
}
|
||||
}
|
||||
+38
@@ -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<String, AndroidApkBuilderConfigState> {
|
||||
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<AndroidApkBuilderConfigState> {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,6 @@ public class AndroidBuilderService extends BuilderService {
|
||||
@Override
|
||||
public List<? extends ProjectLevelBuilder> createProjectLevelBuilders() {
|
||||
return Arrays.asList(new AndroidDexBuilder(),
|
||||
new AndroidResourcePackagingBuilder());
|
||||
new AndroidPackagingBuilder());
|
||||
}
|
||||
}
|
||||
|
||||
-60
@@ -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<String, Long> myFiles;
|
||||
|
||||
public AndroidClassesAndJarsState(@NotNull Collection<String> roots) {
|
||||
myFiles = new HashMap<String, Long>();
|
||||
|
||||
for (String rootPath : roots) {
|
||||
AndroidJpsUtil.processClassFilesAndJarsRecursively(rootPath, new Processor<File>() {
|
||||
@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<String, Long>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
-43
@@ -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<String, AndroidClassesAndJarsState> {
|
||||
|
||||
@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<AndroidClassesAndJarsState> {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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, true);
|
||||
|
||||
if (context.isMake()) {
|
||||
final AndroidClassesAndJarsState oldState = dexStateStorage.getState(module.getName());
|
||||
final AndroidFileSetState oldState = dexStateStorage.getState(module.getName());
|
||||
if (oldState != null && oldState.equalsTo(newState)) {
|
||||
continue;
|
||||
}
|
||||
@@ -192,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));
|
||||
|
||||
@@ -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, true);
|
||||
final AndroidFileSetState oldState = proguardStateStorage.getState(module.getName());
|
||||
if (context.getTimestampStorage().getStamp(proguardCfgFile) == proguardCfgFile.lastModified() &&
|
||||
newState.equalsTo(oldState)) {
|
||||
return true;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,21 +20,29 @@ import java.util.Map;
|
||||
public class AndroidFileSetState implements ValidityState {
|
||||
private final Map<String, Long> myTimestamps;
|
||||
|
||||
public AndroidFileSetState(@NotNull Collection<String> roots) {
|
||||
public AndroidFileSetState(@NotNull Collection<String> roots, @NotNull final Condition<File> filter, boolean recursively) {
|
||||
myTimestamps = new HashMap<String, Long>();
|
||||
|
||||
for (String resourceDir : roots) {
|
||||
FileUtil.processFilesRecursively(new File(resourceDir), new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
myTimestamps.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
for (String rootPath : roots) {
|
||||
final File root = new File(rootPath);
|
||||
|
||||
if (recursively) {
|
||||
FileUtil.processFilesRecursively(root, new Processor<File>() {
|
||||
@Override
|
||||
public boolean process(File file) {
|
||||
if (filter.value(file)) {
|
||||
myTimestamps.put(FileUtil.toSystemIndependentName(file.getPath()), file.lastModified());
|
||||
}
|
||||
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<String, Long>(resourcesCount);
|
||||
|
||||
@@ -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<File> CLASSES_AND_JARS_FILTER = new Condition<File>() {
|
||||
@Override
|
||||
public boolean value(File file) {
|
||||
final String ext = FileUtil.getExtension(file.getName());
|
||||
return "jar".equals(ext) || "class".equals(ext);
|
||||
}
|
||||
};
|
||||
|
||||
private AndroidJpsUtil() {
|
||||
}
|
||||
|
||||
@@ -106,6 +116,19 @@ class AndroidJpsUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<String> toPaths(@NotNull Collection<File> files) {
|
||||
if (files.size() == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<String> result = new ArrayList<String>(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)
|
||||
@@ -150,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)) {
|
||||
@@ -180,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());
|
||||
}
|
||||
}
|
||||
@@ -402,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<Module> visited, @NotNull Set<File> 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<File> result = new HashSet<File>();
|
||||
fillSourceRoots(module, new HashSet<Module>(), 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";
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -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<String> subdirs = new HashSet<String>();
|
||||
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, true);
|
||||
final AndroidFileSetState oldState = storage.getState(module.getName());
|
||||
|
||||
if (oldState != null && oldState.equalsTo(newState)) {
|
||||
continue;
|
||||
|
||||
+496
@@ -0,0 +1,496 @@
|
||||
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;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage;
|
||||
import org.jetbrains.jps.incremental.messages.ProgressMessage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
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";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return BUILDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Android Packaging Builder";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(CompileContext context) throws ProjectBuildException {
|
||||
if (!AndroidJpsUtil.containsAndroidFacet(context.getProject()) || AndroidJpsUtil.isLightBuild(context)) {
|
||||
return;
|
||||
}
|
||||
final Collection<Module> modules = context.getProject().getModules().values();
|
||||
final Map<Module, AndroidFileSetState> resourcesStates = new HashMap<Module, AndroidFileSetState>();
|
||||
final Map<Module, AndroidFileSetState> assetsStates = new HashMap<Module, AndroidFileSetState>();
|
||||
|
||||
try {
|
||||
fillStates(modules, resourcesStates, assetsStates);
|
||||
|
||||
if (!doCaching(context, modules, resourcesStates)) {
|
||||
throw new ProjectBuildException();
|
||||
}
|
||||
|
||||
if (!doResourcePackaging(context, modules, resourcesStates, assetsStates)) {
|
||||
throw new ProjectBuildException();
|
||||
}
|
||||
|
||||
if (!doPackaging(context, modules)) {
|
||||
throw new ProjectBuildException();
|
||||
}
|
||||
}
|
||||
catch (ProjectBuildException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
AndroidJpsUtil.handleException(context, e, BUILDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void fillStates(@NotNull Collection<Module> modules,
|
||||
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
|
||||
@NotNull Map<Module, AndroidFileSetState> assetsStates) throws IOException {
|
||||
for (Module module : modules) {
|
||||
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
|
||||
|
||||
if (facet != null) {
|
||||
final File resourceDir = facet.getResourceDir();
|
||||
final List<String> resourceDirs = resourceDir != null
|
||||
? Arrays.asList(resourceDir.getPath())
|
||||
: Collections.<String>emptyList();
|
||||
resourcesStates.put(module, new AndroidFileSetState(resourceDirs, Condition.TRUE, true));
|
||||
|
||||
final File assetsDir = facet.getAssetsDir();
|
||||
final List<String> assetDirs = assetsDir != null
|
||||
? Arrays.asList(assetsDir.getPath())
|
||||
: Collections.<String>emptyList();
|
||||
assetsStates.put(module, new AndroidFileSetState(assetDirs, Condition.TRUE, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean doCaching(@NotNull CompileContext context,
|
||||
@NotNull Collection<Module> modules,
|
||||
@NotNull Map<Module, AndroidFileSetState> module2state) throws IOException {
|
||||
boolean success = true;
|
||||
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
|
||||
final AndroidFileSetStorage storage = new AndroidFileSetStorage(dataStorageRoot, "resource_caching");
|
||||
|
||||
try {
|
||||
for (Module module : modules) {
|
||||
final AndroidFileSetState state = module2state.get(module);
|
||||
|
||||
try {
|
||||
if (!runPngCaching(context, module, storage, state)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
storage.close();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private static boolean runPngCaching(@NotNull CompileContext context,
|
||||
@NotNull Module module,
|
||||
@NotNull AndroidFileSetStorage storage,
|
||||
@Nullable AndroidFileSetState state)
|
||||
throws IOException {
|
||||
final AndroidFileSetState savedState = storage.getState(module.getName());
|
||||
if (context.isMake() && savedState != null && savedState.equalsTo(state)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
final Pair<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
|
||||
if (pair == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final File resCacheDir = AndroidJpsUtil.getResourcesCacheDir(context, module);
|
||||
|
||||
if (!resCacheDir.exists()) {
|
||||
if (!resCacheDir.mkdirs()) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Cannot create directory " + resCacheDir.getPath()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final IAndroidTarget target = pair.second;
|
||||
|
||||
final Map<AndroidCompilerMessageKind, List<String>> messages =
|
||||
AndroidApt.crunch(target, Collections.singletonList(resourceDir.getPath()), resCacheDir.getPath());
|
||||
|
||||
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
|
||||
|
||||
final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty();
|
||||
storage.update(module.getName(), success ? state : null);
|
||||
return success;
|
||||
}
|
||||
|
||||
private static boolean doResourcePackaging(@NotNull CompileContext context,
|
||||
@NotNull Collection<Module> modules,
|
||||
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
|
||||
@NotNull Map<Module, AndroidFileSetState> assetsStates) throws IOException {
|
||||
boolean success = true;
|
||||
|
||||
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
|
||||
final boolean releaseBuild = AndroidJpsUtil.isReleaseBuild(context);
|
||||
AndroidFileSetStorage resourcesStorage = null;
|
||||
AndroidFileSetStorage assetsStorage = null;
|
||||
|
||||
try {
|
||||
final String resourcesStorageName = releaseBuild ? "resources_packaging_release" : "resources_packaging_dev";
|
||||
resourcesStorage = new AndroidFileSetStorage(dataStorageRoot, resourcesStorageName);
|
||||
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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<Module> 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 (apkFileSetStorage != null) {
|
||||
apkFileSetStorage.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<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
|
||||
if (pair == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Set<String> 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<AndroidCompilerMessageKind, List<String>> 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<String> roots = new ArrayList<String>();
|
||||
roots.add(resPackagePath);
|
||||
roots.add(classesDexFilePath);
|
||||
roots.addAll(Arrays.asList(externalJars));
|
||||
|
||||
for (String sourceRootPath : sourceRoots) {
|
||||
final List<File> files = new ArrayList<File>();
|
||||
AndroidApkBuilder.collectStandardSourceFolderResources(new File(sourceRootPath), files, new MyExcludedSourcesFilter(project));
|
||||
roots.addAll(AndroidJpsUtil.toPaths(files));
|
||||
}
|
||||
|
||||
for (String nativeLibDir : nativeLibDirs) {
|
||||
final List<File> files = new ArrayList<File>();
|
||||
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<String> result = new ArrayList<String>();
|
||||
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<Module, AndroidFileSetState> module2state,
|
||||
@NotNull AndroidFileSetStorage storage) throws IOException {
|
||||
final AndroidFileSetState moduleState = module2state.get(module);
|
||||
final AndroidFileSetState savedState = storage.getState(module.getName());
|
||||
if (savedState == null || !savedState.equalsTo(moduleState)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AndroidFacet libFacet : AndroidJpsUtil.getAllDependentAndroidLibraries(module)) {
|
||||
final Module libModule = libFacet.getModule();
|
||||
final AndroidFileSetState currentLibState = module2state.get(libModule);
|
||||
final AndroidFileSetState savedLibState = storage.getState(libModule.getName());
|
||||
|
||||
if (savedLibState == null || !savedLibState.equalsTo(currentLibState)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean packageResources(@NotNull AndroidFacet facet, @NotNull CompileContext context) {
|
||||
final Module module = facet.getModule();
|
||||
|
||||
try {
|
||||
context.processMessage(new ProgressMessage(AndroidJpsBundle.message("android.jps.progress.packaging.resources", module.getName())));
|
||||
|
||||
final File manifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(facet);
|
||||
if (manifestFile == null) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
AndroidJpsBundle.message("android.jps.errors.manifest.not.found", module.getName())));
|
||||
return false;
|
||||
}
|
||||
final File assetsDir = facet.getAssetsDir();
|
||||
|
||||
final File outputDir = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(context.getProjectPaths(), 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<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
|
||||
if (pair == null) {
|
||||
return false;
|
||||
}
|
||||
final IAndroidTarget target = pair.getSecond();
|
||||
|
||||
final String outputFilePath = getPackagedResourcesFile(module, outputDir).getPath();
|
||||
final String assetsDirPath = assetsDir != null ? assetsDir.getPath() : null;
|
||||
final String[] resourceDirPaths = AndroidJpsUtil.collectResourceDirsForCompilation(facet, true, context);
|
||||
|
||||
return doPackageResources(context, manifestFile, target, resourceDirPaths, assetsDirPath, outputFilePath,
|
||||
AndroidJpsUtil.isReleaseBuild(context));
|
||||
}
|
||||
catch (IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean doPackageResources(@NotNull final CompileContext context,
|
||||
@NotNull File manifestFile,
|
||||
@NotNull IAndroidTarget target,
|
||||
@NotNull String[] resourceDirPaths,
|
||||
@Nullable String assetsDirPath,
|
||||
@NotNull String outputFilePath,
|
||||
boolean releasePackage) {
|
||||
try {
|
||||
final String outputPath = releasePackage
|
||||
? outputFilePath + RELEASE_SUFFIX
|
||||
: outputFilePath;
|
||||
final Map<AndroidCompilerMessageKind, List<String>> messages = AndroidApt
|
||||
.packageResources(target, -1, manifestFile.getPath(), resourceDirPaths, assetsDirPath, outputPath, null, !releasePackage, 0);
|
||||
|
||||
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
|
||||
return messages.get(AndroidCompilerMessageKind.ERROR).size() == 0;
|
||||
}
|
||||
catch (final IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
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<File> {
|
||||
private final CompilerExcludes myExcludes;
|
||||
|
||||
public MyExcludedSourcesFilter(@NotNull Project project) {
|
||||
myExcludes = project.getCompilerConfiguration().getExcludes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean value(File file) {
|
||||
return !myExcludes.isExcluded(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
-322
@@ -1,322 +0,0 @@
|
||||
package org.jetbrains.jps.android;
|
||||
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.android.compiler.tools.AndroidApt;
|
||||
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.Module;
|
||||
import org.jetbrains.jps.incremental.CompileContext;
|
||||
import org.jetbrains.jps.incremental.ProjectBuildException;
|
||||
import org.jetbrains.jps.incremental.ProjectLevelBuilder;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.incremental.messages.CompilerMessage;
|
||||
import org.jetbrains.jps.incremental.messages.ProgressMessage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Eugene.Kudelevsky
|
||||
*/
|
||||
public class AndroidResourcePackagingBuilder extends ProjectLevelBuilder {
|
||||
@NonNls private static final String BUILDER_NAME = "android-packager";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return BUILDER_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Android Packaging Builder";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(CompileContext context) throws ProjectBuildException {
|
||||
if (!AndroidJpsUtil.containsAndroidFacet(context.getProject()) || AndroidJpsUtil.isLightBuild(context)) {
|
||||
return;
|
||||
}
|
||||
final Collection<Module> modules = context.getProject().getModules().values();
|
||||
final Map<Module, AndroidFileSetState> resourcesStates = new HashMap<Module, AndroidFileSetState>();
|
||||
final Map<Module, AndroidFileSetState> assetsStates = new HashMap<Module, AndroidFileSetState>();
|
||||
|
||||
try {
|
||||
fillStates(modules, resourcesStates, assetsStates);
|
||||
|
||||
if (!doCaching(context, modules, resourcesStates)) {
|
||||
throw new ProjectBuildException();
|
||||
}
|
||||
|
||||
if (!doPackaging(context, modules, resourcesStates, assetsStates)) {
|
||||
throw new ProjectBuildException();
|
||||
}
|
||||
}
|
||||
catch (ProjectBuildException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
AndroidJpsUtil.handleException(context, e, BUILDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fillStates(@NotNull Collection<Module> modules,
|
||||
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
|
||||
@NotNull Map<Module, AndroidFileSetState> assetsStates) throws IOException {
|
||||
for (Module module : modules) {
|
||||
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
|
||||
|
||||
if (facet != null) {
|
||||
final File resourceDir = facet.getResourceDir();
|
||||
final List<String> resourceDirs = resourceDir != null
|
||||
? Arrays.asList(resourceDir.getPath())
|
||||
: Collections.<String>emptyList();
|
||||
resourcesStates.put(module, new AndroidFileSetState(resourceDirs));
|
||||
|
||||
final File assetsDir = facet.getAssetsDir();
|
||||
final List<String> assetDirs = assetsDir != null
|
||||
? Arrays.asList(assetsDir.getPath())
|
||||
: Collections.<String>emptyList();
|
||||
assetsStates.put(module, new AndroidFileSetState(assetDirs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean doCaching(@NotNull CompileContext context,
|
||||
@NotNull Collection<Module> modules,
|
||||
@NotNull Map<Module, AndroidFileSetState> module2state) throws IOException {
|
||||
boolean success = true;
|
||||
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
|
||||
final AndroidFileSetStorage storage = new AndroidFileSetStorage(dataStorageRoot, "resource_caching");
|
||||
|
||||
try {
|
||||
for (Module module : modules) {
|
||||
final AndroidFileSetState state = module2state.get(module);
|
||||
|
||||
try {
|
||||
if (!runPngCaching(context, module, storage, state)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
storage.close();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private static boolean runPngCaching(@NotNull CompileContext context,
|
||||
@NotNull Module module,
|
||||
@NotNull AndroidFileSetStorage storage,
|
||||
@Nullable AndroidFileSetState state)
|
||||
throws IOException {
|
||||
final AndroidFileSetState savedState = storage.getState(module.getName());
|
||||
if (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;
|
||||
}
|
||||
|
||||
final File resourceDir = AndroidJpsUtil.getResourceDirForCompilationPath(facet);
|
||||
if (resourceDir == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final Pair<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
|
||||
if (pair == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final File resCacheDir = AndroidJpsUtil.getResourcesCacheDir(context, module);
|
||||
|
||||
if (!resCacheDir.exists()) {
|
||||
if (!resCacheDir.mkdirs()) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
"Cannot create directory " + resCacheDir.getPath()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final IAndroidTarget target = pair.second;
|
||||
|
||||
final Map<AndroidCompilerMessageKind, List<String>> messages =
|
||||
AndroidApt.crunch(target, Collections.singletonList(resourceDir.getPath()), resCacheDir.getPath());
|
||||
|
||||
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
|
||||
|
||||
final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty();
|
||||
storage.update(module.getName(), success ? state : null);
|
||||
return success;
|
||||
}
|
||||
|
||||
private static boolean doPackaging(@NotNull CompileContext context,
|
||||
@NotNull Collection<Module> modules,
|
||||
@NotNull Map<Module, AndroidFileSetState> resourcesStates,
|
||||
@NotNull Map<Module, AndroidFileSetState> assetsStates) throws IOException {
|
||||
boolean success = true;
|
||||
|
||||
final File dataStorageRoot = context.getDataManager().getDataStorageRoot();
|
||||
AndroidFileSetStorage devResourcesStorage = null;
|
||||
AndroidFileSetStorage releaseResourcesStorage = null;
|
||||
AndroidFileSetStorage devAssetsStorage = null;
|
||||
AndroidFileSetStorage releaseAssetsStorage = null;
|
||||
|
||||
try {
|
||||
devResourcesStorage = new AndroidFileSetStorage(dataStorageRoot, "resources_packaging_dev");
|
||||
releaseResourcesStorage = new AndroidFileSetStorage(dataStorageRoot, "resources_packaging_release");
|
||||
|
||||
devAssetsStorage = new AndroidFileSetStorage(dataStorageRoot, "assets_packaging_dev");
|
||||
releaseAssetsStorage = new AndroidFileSetStorage(dataStorageRoot, "assets_packaging_release");
|
||||
|
||||
for (Module module : modules) {
|
||||
final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
|
||||
if (facet == null || facet.isLibrary()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!packageResources(facet, context, devResourcesStorage, devAssetsStorage, releaseResourcesStorage, releaseAssetsStorage,
|
||||
resourcesStates, assetsStates)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (devResourcesStorage != null) {
|
||||
devResourcesStorage.close();
|
||||
}
|
||||
if (releaseResourcesStorage != null) {
|
||||
releaseResourcesStorage.close();
|
||||
}
|
||||
if (devAssetsStorage != null) {
|
||||
devAssetsStorage.close();
|
||||
}
|
||||
if (releaseAssetsStorage != null) {
|
||||
releaseAssetsStorage.close();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private static boolean checkUpToDate(@NotNull Module module,
|
||||
@NotNull Map<Module, AndroidFileSetState> module2state,
|
||||
@NotNull AndroidFileSetStorage storage) throws IOException {
|
||||
final AndroidFileSetState moduleState = module2state.get(module);
|
||||
final AndroidFileSetState savedState = storage.getState(module.getName());
|
||||
if (savedState == null || !savedState.equalsTo(moduleState)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AndroidFacet libFacet : AndroidJpsUtil.getAllDependentAndroidLibraries(module)) {
|
||||
final Module libModule = libFacet.getModule();
|
||||
final AndroidFileSetState currentLibState = module2state.get(libModule);
|
||||
final AndroidFileSetState savedLibState = storage.getState(libModule.getName());
|
||||
|
||||
if (savedLibState == null || !savedLibState.equalsTo(currentLibState)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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<Module, AndroidFileSetState> resourcesStates,
|
||||
@NotNull Map<Module, AndroidFileSetState> assetsStates) {
|
||||
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()));
|
||||
|
||||
final File manifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(facet);
|
||||
if (manifestFile == null) {
|
||||
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR,
|
||||
AndroidJpsBundle.message("android.jps.errors.manifest.not.found", module.getName())));
|
||||
return false;
|
||||
}
|
||||
final File assetsDir = facet.getAssetsDir();
|
||||
|
||||
final File outputDir = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(context.getProjectPaths(), 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<AndroidSdk, IAndroidTarget> pair = AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
|
||||
if (pair == null) {
|
||||
return false;
|
||||
}
|
||||
final IAndroidTarget target = pair.getSecond();
|
||||
|
||||
final String outputFilePath = getOutputFile(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;
|
||||
}
|
||||
catch (IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean doPackageResources(@NotNull final CompileContext context,
|
||||
@NotNull File manifestFile,
|
||||
@NotNull IAndroidTarget target,
|
||||
@NotNull String[] resourceDirPaths,
|
||||
@Nullable String assetsDirPath,
|
||||
@NotNull String outputFilePath,
|
||||
boolean releasePackage) {
|
||||
try {
|
||||
final String outputPath = releasePackage
|
||||
? outputFilePath + ".release"
|
||||
: outputFilePath;
|
||||
final Map<AndroidCompilerMessageKind, List<String>> messages = AndroidApt
|
||||
.packageResources(target, -1, manifestFile.getPath(), resourceDirPaths, assetsDirPath, outputPath, null, !releasePackage, 0);
|
||||
|
||||
AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
|
||||
return messages.get(AndroidCompilerMessageKind.ERROR).size() == 0;
|
||||
}
|
||||
catch (final IOException e) {
|
||||
AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static File getOutputFile(@NotNull Module module, @NotNull File outputDir) {
|
||||
return new File(outputDir.getPath(), module.getName() + ".apk.res");
|
||||
}
|
||||
}
|
||||
+1
-41
@@ -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<Module> visited, @NotNull Set<File> 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<File> result = new HashSet<File>();
|
||||
fillSourceRoots(module, new HashSet<Module>(), result);
|
||||
return result.toArray(new File[result.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Android Source Generating Builder";
|
||||
|
||||
@@ -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}?
|
||||
|
||||
@@ -14,11 +14,15 @@
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/../lib/sdklib.jar!/" />
|
||||
<root url="jar://$MODULE_DIR$/../lib/common.jar!/" />
|
||||
<root url="jar://$MODULE_DIR$/../lib/jarutils.jar!/" />
|
||||
<root url="jar://$MODULE_DIR$/../lib/androidprefs.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MODULE_DIR$/../lib/src/sdklib_src.zip!/src" />
|
||||
<root url="jar://$MODULE_DIR$/../lib/src/common_src.zip!/src" />
|
||||
<root url="jar://$MODULE_DIR$/../lib/src/jarutils.zip!/src" />
|
||||
<root url="jar://$MODULE_DIR$/../lib/src/androidprefs_src.zip!/src" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</orderEntry>
|
||||
|
||||
+97
-128
@@ -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<CompilerMessageCategory, List<String>> filterUsingKeystoreMessages(Map<CompilerMessageCategory, List<String>> messages) {
|
||||
private static Map<AndroidCompilerMessageKind, List<String>> filterUsingKeystoreMessages(Map<AndroidCompilerMessageKind, List<String>> messages) {
|
||||
List<String> infoMessages = messages.get(INFORMATION);
|
||||
if (infoMessages == null) {
|
||||
infoMessages = new ArrayList<String>();
|
||||
@@ -118,48 +108,49 @@ public class AndroidApkBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<CompilerMessageCategory, List<String>> 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<AndroidCompilerMessageKind, List<String>> 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<File> 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<CompilerMessageCategory, List<String>> map = new HashMap<CompilerMessageCategory, List<String>>();
|
||||
final String zipAlignPath = sdkPath + File.separator + AndroidSdkUtils.toolPath(SdkConstants.FN_ZIPALIGN);
|
||||
final Map<AndroidCompilerMessageKind, List<String>> map = new HashMap<AndroidCompilerMessageKind, List<String>>();
|
||||
final String zipAlignPath = sdkPath + File.separator + AndroidCommonUtils.toolPath(SdkConstants.FN_ZIPALIGN);
|
||||
boolean withAlignment = new File(zipAlignPath).exists();
|
||||
String unalignedApk = finalApk + UNALIGNED_SUFFIX;
|
||||
|
||||
Map<CompilerMessageCategory, List<String>> map2 = filterUsingKeystoreMessages(
|
||||
finalPackage(project, dexPath, sourceRoots, externalJars, nativeLibsFolders, withAlignment ? unalignedApk : finalApk, resPackagePath,
|
||||
customKeystorePath, true));
|
||||
Map<AndroidCompilerMessageKind, List<String>> 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<CompilerMessageCategory, List<String>> 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<CompilerMessageCategory, List<String>> result = new HashMap<CompilerMessageCategory, List<String>>();
|
||||
private static Map<AndroidCompilerMessageKind, List<String>> 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<File> resourceFilter) {
|
||||
final Map<AndroidCompilerMessageKind, List<String>> result = new HashMap<AndroidCompilerMessageKind, List<String>>();
|
||||
result.put(ERROR, new ArrayList<String>());
|
||||
result.put(INFORMATION, new ArrayList<String>());
|
||||
result.put(WARNING, new ArrayList<String>());
|
||||
@@ -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<String> added = new HashSet<String>();
|
||||
for (VirtualFile sourceRoot : sourceRoots) {
|
||||
final HashSet<VirtualFile> sourceFolderResources = new HashSet<VirtualFile>();
|
||||
collectStandardSourceFolderResources(sourceRoot, new HashSet<VirtualFile>(), sourceFolderResources, project);
|
||||
for (String sourceRootPath : sourceRoots) {
|
||||
final HashSet<File> sourceFolderResources = new HashSet<File>();
|
||||
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<String> nativeLibs = new HashSet<String>();
|
||||
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<CompilerMessageCategory, List<String>> result, String path) throws
|
||||
private static DebugKeyProvider createDebugKeyProvider(final Map<AndroidCompilerMessageKind, List<String>> 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<String> added)
|
||||
throws IOException {
|
||||
ArrayList<VirtualFile> list = new ArrayList<VirtualFile>();
|
||||
ArrayList<File> list = new ArrayList<File>();
|
||||
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<CompilerMessageCategory, List<String>> addExceptionMessage(Exception e,
|
||||
Map<CompilerMessageCategory, List<String>> result) {
|
||||
private static Map<AndroidCompilerMessageKind, List<String>> addExceptionMessage(Exception e,
|
||||
Map<AndroidCompilerMessageKind, List<String>> 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<VirtualFile> result, boolean debugBuild) {
|
||||
public static void collectNativeLibraries(@NotNull File file, @NotNull List<File> 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<VirtualFile> visited,
|
||||
Set<VirtualFile> 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<File> result,
|
||||
@NotNull Condition<File> 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<VirtualFile> resources,
|
||||
VirtualFile sourceRoot,
|
||||
private static void writeStandardSourceFolderResources(Collection<File> resources,
|
||||
File sourceRoot,
|
||||
SignedJarBuilder jarBuilder,
|
||||
Set<String> 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +205,14 @@
|
||||
<compileServer.plugin jar-path="common.jar"/>
|
||||
<compileServer.plugin jar-path="../../../../../community/plugins/android/lib/common.jar"/>
|
||||
<compileServer.plugin jar-path="../../../../../plugins/android/lib/common.jar"/>
|
||||
|
||||
<compileServer.plugin jar-path="jarutils.jar"/>
|
||||
<compileServer.plugin jar-path="../../../../../community/plugins/android/lib/jarutils.jar"/>
|
||||
<compileServer.plugin jar-path="../../../../../plugins/android/lib/jarutils.jar"/>
|
||||
|
||||
<compileServer.plugin jar-path="androidprefs.jar"/>
|
||||
<compileServer.plugin jar-path="../../../../../community/plugins/android/lib/androidprefs.jar"/>
|
||||
<compileServer.plugin jar-path="../../../../../plugins/android/lib/androidprefs.jar"/>
|
||||
</extensions>
|
||||
|
||||
<extensionPoints>
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<CompilerMessageCategory, List<String>> 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<CompilerMessageCategory, List<String>> 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<VirtualFile> resourcesFromSourceRoot = new HashSet<VirtualFile>();
|
||||
final HashSet<File> resourcesFromSourceRoot = new HashSet<File>();
|
||||
for (VirtualFile sourceRoot : sourceRoots) {
|
||||
AndroidApkBuilder.collectStandardSourceFolderResources(sourceRoot, new HashSet<VirtualFile>(), 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<VirtualFile> nativeLibs = new ArrayList<VirtualFile>();
|
||||
ArrayList<File> nativeLibs = new ArrayList<File>();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<File> {
|
||||
private final Project myProject;
|
||||
|
||||
public ExcludedSourcesFilter(@NotNull Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean value(File file) {
|
||||
return !AndroidCompileUtil.isExcludedFromCompilation(file, myProject);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<AndroidFacetConfiguration> {
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
+2
-1
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<Module> mvcModules = new ArrayList<Module>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+11
@@ -1172,4 +1172,15 @@ class Base {
|
||||
new Base().@prefixField<caret>''')
|
||||
}
|
||||
|
||||
public void testPrivateFieldOnSecondInvocation() {
|
||||
myFixture.configureByText('_a.groovy', '''\
|
||||
class Base {
|
||||
private int field1
|
||||
}
|
||||
|
||||
new Base().fie<caret>x''')
|
||||
myFixture.complete(CompletionType.BASIC, 2)
|
||||
assert myFixture.lookupElementStrings == ['field1']
|
||||
}
|
||||
|
||||
}
|
||||
+2
@@ -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);
|
||||
}
|
||||
|
||||
+31
-1
@@ -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<Exception>() {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
List<RadComponent> components = RadComponent.getPureSelection(myDesigner.getActionsArea().getSelection());
|
||||
EditableArea area = myDesigner.getActionsArea();
|
||||
List<RadComponent> selection = area.getSelection();
|
||||
List<RadComponent> 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<RadComponent> excludes) {
|
||||
RadComponent parent = component.getParent();
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<RadComponent> 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
|
||||
|
||||
+12
-30
@@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+19
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ public abstract class RadComponent {
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public List<Property> getProperties() {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public final Object getClientProperty(@NotNull Object key) {
|
||||
|
||||
+72
-5
@@ -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<Property> 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<RadComponent> components) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Presentation
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@NotNull
|
||||
public final String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public List<Property> 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
|
||||
|
||||
+2
@@ -26,4 +26,6 @@ public interface PropertyEditor {
|
||||
JComponent getComponent(RadComponent component, Object value);
|
||||
|
||||
Object getValue();
|
||||
|
||||
void updateUI();
|
||||
}
|
||||
+2
-13
@@ -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 <code>JComponent</code> to represent the <code>value</code>
|
||||
* 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();
|
||||
}
|
||||
+360
-1
@@ -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<RadComponent> myComponents = Collections.emptyList();
|
||||
private List<Property> myProperties = Collections.emptyList();
|
||||
private final Set<String> myExpandedProperties = new HashSet<String>();
|
||||
|
||||
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<RadComponent>(myArea.getSelection());
|
||||
fillProperties();
|
||||
myModel.fireTableDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void fillProperties() {
|
||||
myProperties = new ArrayList<Property>();
|
||||
int size = myComponents.size();
|
||||
|
||||
if (size > 0) {
|
||||
fillProperties(myComponents.get(0), myProperties);
|
||||
|
||||
if (size > 1) {
|
||||
for (Iterator<Property> I = myProperties.iterator(); I.hasNext(); ) {
|
||||
if (!I.next().availableFor(myComponents)) {
|
||||
I.remove();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
List<Property> properties = new ArrayList<Property>();
|
||||
fillProperties(myComponents.get(i), properties);
|
||||
|
||||
for (Iterator<Property> I = myProperties.iterator(); I.hasNext(); ) {
|
||||
final Property property = I.next();
|
||||
Property testProperty = ContainerUtil.find(properties, new Condition<Property>() {
|
||||
@Override
|
||||
public boolean value(Property next) {
|
||||
return property.getName().equals(next.getName());
|
||||
}
|
||||
});
|
||||
|
||||
if (testProperty == null || !property.getClass().equals(testProperty.getClass())) {
|
||||
I.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Property> children = getChildren(property);
|
||||
List<Property> 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<Property> properties) {
|
||||
for (Property property : component.getProperties()) {
|
||||
addProperty(property, properties);
|
||||
}
|
||||
}
|
||||
|
||||
private void addProperty(Property property, List<Property> 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<Property> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-5
@@ -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;
|
||||
}
|
||||
}
|
||||
+73
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
designer.properties.show.expert=Show expert properties
|
||||
designer.properties.column1=Property
|
||||
designer.properties.column2=Value
|
||||
designer.properties.getting.error=Error getting value: {0}
|
||||
|
||||
Reference in New Issue
Block a user