Merge remote-tracking branch 'origin/master'

This commit is contained in:
anna
2012-02-28 19:29:58 +01:00
75 changed files with 2031 additions and 826 deletions
@@ -0,0 +1,8 @@
class Foo {
{
hashCo<caret>x
}
int hashCodeMine() {}
}
@@ -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 {
@@ -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;
@@ -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 {
@@ -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;
}
@@ -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;
}
@@ -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;
@@ -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;
}
@@ -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
@@ -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
}
}
@@ -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}
@@ -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);
}
}
@@ -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());
}
}
@@ -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));
}
}
}
@@ -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";
}
}
@@ -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;
@@ -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);
}
}
}
@@ -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");
}
}
@@ -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}?
+4
View File
@@ -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>
@@ -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;
}
}
+8
View File
@@ -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()) {
@@ -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));
}
}
});
@@ -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']
}
}
@@ -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);
}
@@ -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
@@ -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());
}
});
}
}
@@ -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
@@ -134,7 +134,7 @@ public abstract class RadComponent {
//////////////////////////////////////////////////////////////////////////////////////////
public List<Property> getProperties() {
return null;
return Collections.emptyList();
}
public final Object getClientProperty(@NotNull Object key) {
@@ -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
@@ -26,4 +26,6 @@ public interface PropertyEditor {
JComponent getComponent(RadComponent component, Object value);
Object getValue();
void updateUI();
}
@@ -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();
}
@@ -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;
}
}
}
@@ -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;
}
}
@@ -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}