Merge remote-tracking branch 'origin/master'

This commit is contained in:
Alexander Lobas
2012-05-29 18:32:20 +04:00
52 changed files with 2406 additions and 434 deletions
@@ -79,7 +79,7 @@ public class DownloadingOptionsDialog extends DialogWrapper {
myVersionComboBox.setRenderer(new ListCellRendererWrapper<FrameworkLibraryVersion>(myVersionComboBox) {
@Override
public void customize(JList list, FrameworkLibraryVersion value, int index, boolean selected, boolean hasFocus) {
setText(value.getVersionString());
setText(value.getName() + value.getVersionString());
}
});
myVersionComboBox.setSelectedItem(settings.getVersion());
@@ -22,4 +22,14 @@ public final class RootDescriptor {
this.isTestRoot = isTestRoot;
this.isGeneratedSources = isGenerated;
}
@Override
public String toString() {
return "RootDescriptor{" +
"module='" + module + '\'' +
", root=" + root +
", test=" + isTestRoot +
", generated=" + isGeneratedSources +
'}';
}
}
@@ -760,24 +760,16 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
private static Map<File, Set<File>> buildOutputDirectoriesMap(CompileContext context, ModuleChunk chunk) {
final Map<File, Set<File>> map = new HashMap<File, Set<File>>();
final Map<File, Set<File>> map = new LinkedHashMap<File, Set<File>>();
final boolean compilingTests = context.isCompilingTests();
for (Module module : chunk.getModules()) {
final String outputPath;
final Collection<String> srcPaths;
if (compilingTests) {
outputPath = module.getTestOutputPath();
srcPaths = module.getTestRoots();
final Set<File> roots = new LinkedHashSet<File>();
for (RootDescriptor descriptor : context.getModuleRoots(module)) {
if (descriptor.isTestRoot == compilingTests) {
roots.add(descriptor.root);
}
}
else {
outputPath = module.getOutputPath();
srcPaths = module.getSourceRoots();
}
final Set<File> roots = new HashSet<File>();
for (String path : srcPaths) {
roots.add(new File(path));
}
map.put(new File(outputPath), roots);
map.put(new File(compilingTests ? module.getTestOutputPath() : module.getOutputPath()), roots);
}
return map;
}
@@ -809,24 +809,17 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder, AS
@Override
public final boolean eof() {
if (!markTokenTypeChecked()) {
if (!myTokenTypeChecked) {
myTokenTypeChecked = true;
skipWhitespace();
}
return myCurrentLexeme >= myLexemeCount;
}
public boolean markTokenTypeChecked() {
if (!myTokenTypeChecked) {
myTokenTypeChecked = true;
return false;
}
return true;
}
@SuppressWarnings({"SuspiciousMethodCalls"})
private void rollbackTo(Marker marker) {
myCurrentLexeme = ((StartMarker)marker).myLexemeIndex;
markTokenTypeChecked();
myTokenTypeChecked = true;
int idx = myProduction.lastIndexOf(marker);
if (idx < 0) {
LOG.error("The marker must be added before rolled back to.");
@@ -1072,7 +1065,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder, AS
@NotNull
private StartMarker prepareLightTree() {
markTokenTypeChecked();
myTokenTypeChecked = true;
balanceWhiteSpaces();
if (myProduction.isEmpty()) {
@@ -181,6 +181,7 @@ public final class FileContentImpl extends UserDataHolderBase implements FileCon
if (myContentAsText == null) {
if (myContent != null) {
myContentAsText = LoadTextUtil.getTextByBinaryPresentation(myContent, myCharset);
myContent = null; // help gc, indices are expected to use bytes or chars but not both
}
}
return myContentAsText;
@@ -0,0 +1,62 @@
/*
* 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.conversion;
import com.intellij.facet.FacetManagerImpl;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class DetachFacetConversionProcessor extends ConversionProcessor<ModuleSettings>{
private String[] myFacetNames;
public DetachFacetConversionProcessor(@NotNull String... names) {
myFacetNames = names;
}
@Override
public boolean isConversionNeeded(ModuleSettings moduleSettings) {
for (String facetName : myFacetNames) {
if (facetName != null && !moduleSettings.getFacetElements(facetName).isEmpty()) {
return true;
}
}
return false;
}
@Override
public void process(ModuleSettings moduleSettings) throws CannotConvertException {
final Element facetManagerElement = moduleSettings.getComponentElement(FacetManagerImpl.COMPONENT_NAME);
if (facetManagerElement == null) return;
final Element[] facetElements = getChildren(facetManagerElement, FacetManagerImpl.FACET_ELEMENT);
for (Element facetElement : facetElements) {
final String facetType = facetElement.getAttributeValue(FacetManagerImpl.TYPE_ATTRIBUTE);
for (String facetName : myFacetNames) {
if (facetName.equals(facetType)) {
facetElement.detach();
}
}
}
}
private static Element[] getChildren(Element parent, final String name) {
final List<?> children = parent.getChildren(name);
return children.toArray(new Element[children.size()]);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -163,9 +163,13 @@ public class FindInProjectUtil {
@NotNull
public static List<UsageInfo> findUsages(@NotNull final FindModel findModel, final PsiDirectory psiDirectory, @NotNull final Project project) {
return findUsages(findModel, psiDirectory, project, true);
}
@NotNull
public static List<UsageInfo> findUsages(@NotNull final FindModel findModel, final PsiDirectory psiDirectory, @NotNull final Project project, boolean showWarnings) {
final CommonProcessors.CollectProcessor<UsageInfo> collector = new CommonProcessors.CollectProcessor<UsageInfo>();
findUsages(findModel, psiDirectory, project, collector);
findUsages(findModel, psiDirectory, project, collector, showWarnings);
return new ArrayList<UsageInfo>(collector.getResults());
}
@@ -202,6 +206,15 @@ public class FindInProjectUtil {
final PsiDirectory psiDirectory,
@NotNull final Project project,
@NotNull final Processor<UsageInfo> consumer) {
findUsages(findModel, psiDirectory, project, consumer, true);
}
public static void findUsages(@NotNull final FindModel findModel,
final PsiDirectory psiDirectory,
@NotNull final Project project,
@NotNull final Processor<UsageInfo> consumer,
boolean showWarnings) {
final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
final Collection<PsiFile> psiFiles = getFilesToSearchIn(findModel, project, psiDirectory);
@@ -252,7 +265,7 @@ public class FindInProjectUtil {
}
}
if (!largeFiles.isEmpty()) {
if (showWarnings && !largeFiles.isEmpty()) {
@Language("HTML")
String message = "<html><body>";
if (largeFiles.size() == 1) {
@@ -0,0 +1,415 @@
/*
* 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.internal;
import com.intellij.codeInsight.documentation.DocumentationManager;
import com.intellij.codeInsight.hint.ImplementationViewComponent;
import com.intellij.ide.DataManager;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.ide.util.PropertyName;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.ui.*;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.popup.NotLookupOrSearchCondition;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Function;
import com.intellij.util.NotNullFunction;
import com.intellij.util.PlatformIcons;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.*;
import java.util.List;
/**
* @author Konstantin Bulenkov
*/
@SuppressWarnings("UseOfObsoleteCollectionType")
public class ImageDuplicateResultsDialog extends DialogWrapper {
private final Project myProject;
private final List<VirtualFile> myImages;
private final Map<String, Set<VirtualFile>> myDuplicates;
private Tree myTree;
private ResourceModules myResourceModules = new ResourceModules();
public ImageDuplicateResultsDialog(Project project, List<VirtualFile> images, Map<String, Set<VirtualFile>> duplicates) {
super(project);
myProject = project;
myImages = images;
PropertiesComponent.getInstance(myProject).loadFields(myResourceModules);
myDuplicates = duplicates;
setModal(false);
myTree = new Tree(new MyRootNode());
myTree.setRootVisible(true);
myTree.setCellRenderer(new MyCellRenderer());
init();
TreeUtil.expandAll(myTree);
setTitle("Image Duplicates");
TreeUtil.selectFirstNode(myTree);
}
@Override
protected Action[] createActions() {
final Action[] actions = new Action[4];
actions[0] = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
}
};
actions[0].putValue(Action.NAME, "Fix all");
actions[0].putValue(DEFAULT_ACTION, Boolean.TRUE);
actions[0].putValue(FOCUSED_ACTION, Boolean.TRUE);
actions[1] = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
}
};
actions[1].putValue(Action.NAME, "Fix selected");
actions[2] = getCancelAction();
actions[3] = getHelpAction();
//return actions;
return new Action[0];
}
@Override
protected JComponent createCenterPanel() {
final JPanel panel = new JPanel(new BorderLayout());
DataManager.registerDataProvider(panel, new DataProvider() {
@Override
public Object getData(@NonNls String dataId) {
final TreePath path = myTree.getSelectionPath();
if (path != null) {
Object component = path.getLastPathComponent();
VirtualFile file = null;
if (component instanceof MyFileNode) {
component = ((MyFileNode)component).getParent();
}
if (component instanceof MyDuplicatesNode) {
file = ((MyDuplicatesNode)component).getUserObject().iterator().next();
}
if (PlatformDataKeys.VIRTUAL_FILE.is(dataId)) {
return file;
}
if (PlatformDataKeys.VIRTUAL_FILE_ARRAY.is(dataId) && file != null) {
return new VirtualFile[]{file};
}
}
return null;
}
});
final JBList list = new JBList(new ResourceModules().getModuleNames());
final NotNullFunction<Object, JComponent> modulesRenderer = new NotNullFunction<Object, JComponent>() {
@NotNull
@Override
public JComponent fun(Object dom) {
return new JLabel(dom instanceof Module ? ((Module)dom).getName() : dom.toString(), PlatformIcons.SOURCE_FOLDERS_ICON, SwingConstants.LEFT);
}
};
list.installCellRenderer(modulesRenderer);
final JPanel modulesPanel = ToolbarDecorator.createDecorator(list)
.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
final Module[] all = ModuleManager.getInstance(myProject).getModules();
Arrays.sort(all, new Comparator<Module>() {
@Override
public int compare(Module o1, Module o2) {
return o1.getName().compareTo(o2.getName());
}
});
final JBList modules = new JBList(all);
modules.installCellRenderer(modulesRenderer);
JBPopupFactory.getInstance().createListPopupBuilder(modules)
.setTitle("Add Resource Module")
.setFilteringEnabled(new Function<Object, String>() {
@Override
public String fun(Object o) {
return ((Module)o).getName();
}
})
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
final Object value = modules.getSelectedValue();
if (value instanceof Module && !myResourceModules.contains((Module)value)) {
myResourceModules.add((Module)value);
((DefaultListModel)list.getModel()).addElement(((Module)value).getName());
}
((DefaultTreeModel)myTree.getModel()).reload();
TreeUtil.expandAll(myTree);
}
}).createPopup().show(button.getPreferredPopupPoint());
}
})
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
final Object[] values = list.getSelectedValues();
for (Object value : values) {
myResourceModules.remove((String)value);
((DefaultListModel)list.getModel()).removeElement(value);
}
((DefaultTreeModel)myTree.getModel()).reload();
TreeUtil.expandAll(myTree);
}
})
.disableDownAction()
.disableUpAction()
.createPanel();
modulesPanel.setPreferredSize(new Dimension(-1, 60));
final JPanel top = new JPanel(new BorderLayout());
top.add(new JLabel("Image modules:"), BorderLayout.NORTH);
top.add(modulesPanel, BorderLayout.CENTER);
panel.add(top, BorderLayout.NORTH);
panel.add(new JBScrollPane(myTree), BorderLayout.CENTER);
new AnAction() {
@Override
public void actionPerformed(AnActionEvent e) {
VirtualFile file = getFileFromSelection();
if (file != null) {
final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
if (psiFile != null) {
final ImplementationViewComponent viewComponent = new ImplementationViewComponent(new PsiElement[]{psiFile}, 0);
final TreeSelectionListener listener = new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
final VirtualFile selection = getFileFromSelection();
if (selection != null) {
final PsiFile newElement = PsiManager.getInstance(myProject).findFile(selection);
if (newElement != null) {
viewComponent.update(new PsiElement[]{newElement}, 0);
}
}
}
};
myTree.addTreeSelectionListener(listener);
final JBPopup popup =
JBPopupFactory.getInstance().createComponentPopupBuilder(viewComponent, viewComponent.getPrefferedFocusableComponent())
.setRequestFocusCondition(myProject, NotLookupOrSearchCondition.INSTANCE)
.setProject(myProject)
.setDimensionServiceKey(myProject, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false)
.setResizable(true)
.setMovable(true)
.setRequestFocus(false)
.setCancelCallback(new Computable<Boolean>() {
@Override
public Boolean compute() {
myTree.removeTreeSelectionListener(listener);
return true;
}
})
.setTitle("Image Preview")
.createPopup();
final Window window = ImageDuplicateResultsDialog.this.getWindow();
popup.show(new RelativePoint(window, new Point(window.getWidth(), 0)));
viewComponent.setHint(popup, "Image Preview");
}
}
}
}.registerCustomShortcutSet(CustomShortcutSet.fromString("ENTER"), panel);
int total = 0;
for (Set set : myDuplicates.values()) total+=set.size();
total-=myDuplicates.size();
final JLabel label = new JLabel(
"<html>Press <b>Enter</b> to preview image<br>Total images found: " + myImages.size() + ". Total duplicates found: " + total+"</html>");
panel.add(label, BorderLayout.SOUTH);
return panel;
}
@Override
protected String getDimensionServiceKey() {
return "image.duplicates.dialog";
}
@Override
public JComponent getPreferredFocusedComponent() {
return myTree;
}
@Nullable
private VirtualFile getFileFromSelection() {
final TreePath path = myTree.getSelectionPath();
if (path != null) {
Object component = path.getLastPathComponent();
VirtualFile file = null;
if (component instanceof MyFileNode) {
component = ((MyFileNode)component).getParent();
}
if (component instanceof MyDuplicatesNode) {
file = ((MyDuplicatesNode)component).getUserObject().iterator().next();
}
return file;
}
return null;
}
private class MyRootNode extends DefaultMutableTreeNode {
private MyRootNode() {
final Vector vector = new Vector();
for (Set<VirtualFile> files : myDuplicates.values()) {
vector.add(new MyDuplicatesNode(this, files));
}
children = vector;
}
}
private class MyDuplicatesNode extends DefaultMutableTreeNode {
private final Set<VirtualFile> myFiles;
public MyDuplicatesNode(DefaultMutableTreeNode node, Set<VirtualFile> files) {
super(files);
myFiles = files;
setParent(node);
final Vector vector = new Vector();
for (VirtualFile file : files) {
vector.add(new MyFileNode(this, file));
}
children = vector;
}
@Override
public Set<VirtualFile> getUserObject() {
return (Set<VirtualFile>)super.getUserObject();
}
}
private static class MyFileNode extends DefaultMutableTreeNode {
public MyFileNode(DefaultMutableTreeNode node, VirtualFile file) {
super(file);
setParent(node);
}
@Override
public VirtualFile getUserObject() {
return (VirtualFile)super.getUserObject();
}
}
private class MyCellRenderer extends ColoredTreeCellRenderer {
@Override
public void customizeCellRenderer(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
if (value instanceof MyFileNode) {
final VirtualFile file = ((MyFileNode)value).getUserObject();
final Module module = ModuleUtil.findModuleForFile(file, myProject);
if (module != null) {
setIcon(PlatformIcons.CONTENT_ROOT_ICON_CLOSED);
append("[" + module.getName() + "] ", new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, UIUtil.getTreeForeground()));
append(getRelativePathToProject(myProject, file));
}
else {
append(getRelativePathToProject(myProject, file));
}
}
else if (value instanceof MyDuplicatesNode) {
final Set<VirtualFile> files = ((MyDuplicatesNode)value).getUserObject();
for (VirtualFile file : files) {
final Module module = ModuleUtil.findModuleForFile(file, myProject);
if (module != null && myResourceModules.contains(module)) {
append("Icons can be replaced to ");
append(getRelativePathToProject(myProject, file),
new SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, ColorUtil.fromHex("008000")));
return;
}
}
append("Icon conflict");
} else if (value instanceof MyRootNode) {
append("All conflicts");
}
}
}
private static String getRelativePathToProject(Project project, VirtualFile file) {
final String path = project.getBasePath();
assert path != null;
final String result = FileUtil.getRelativePath(path, file.getPath().replace('/', File.separatorChar), File.separatorChar);
assert result != null;
return result;
}
static class ResourceModules {
@PropertyName(value = "resource.modules", defaultValue = "icons")
public String modules;
public List<String> getModuleNames() {
return Arrays.asList(StringUtil.splitByLines(modules == null ? "icons" : modules));
}
public boolean contains(Module module) {
return getModuleNames().contains(module.getName());
}
public void add(Module module) {
if (StringUtil.isEmpty(modules)) {
modules = module.getName();
} else {
modules += "\n" + module.getName();
}
}
public void remove(String value) {
final List<String> names = new ArrayList<String>(getModuleNames());
names.remove(value);
modules = StringUtil.join(names, "\n");
}
}
}
@@ -0,0 +1,155 @@
/*
* 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.internal;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.FilenameIndex;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.*;
/**
* @author Konstantin Bulenkov
*/
public class ShowImageDuplicatesAction extends AnAction {
//FileTypeManager.getInstance().getFileTypeByExtension("png").getAllPossibleExtensions() ?
private static final List<String> IMAGE_EXTENSIONS = Arrays.asList("png", "jpg", "jpeg", "gif", "tiff", "bmp");
@Override
public void actionPerformed(AnActionEvent e) {
final Project project = getEventProject(e);
assert project != null;
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
collectAndShowDuplicates(project);
}
}, "Gathering images", true, project);
}
private static void collectAndShowDuplicates(final Project project) {
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
if (indicator != null && !indicator.isCanceled()) {
indicator.setText("Collecting project images...");
indicator.setIndeterminate(false);
final List<VirtualFile> images = new ArrayList<VirtualFile>();
for (String ext : IMAGE_EXTENSIONS) {
images.addAll(FilenameIndex.getAllFilesByExt(project, ext));
}
final Map<Long, Set<VirtualFile>> duplicates = new HashMap<Long, Set<VirtualFile>>();
final Map<Long, VirtualFile> all = new HashMap<Long, VirtualFile>();
for (int i = 0; i < images.size(); i++) {
indicator.setFraction((double)(i + 1) / (double)images.size());
final VirtualFile file = images.get(i);
if (!(file.getFileSystem() instanceof LocalFileSystem)) continue;
final long length = file.getLength();
if (all.containsKey(length)) {
if (!duplicates.containsKey(length)) {
final HashSet<VirtualFile> files = new HashSet<VirtualFile>();
files.add(all.get(length));
duplicates.put(length, files);
}
duplicates.get(length).add(file);
} else {
all.put(length, file);
}
indicator.checkCanceled();
}
showResults(project, images, duplicates, all);
}
}
private static void showResults(final Project project, final List<VirtualFile> images,
Map<Long, Set<VirtualFile>> duplicates,
Map<Long, VirtualFile> all) {
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
if (indicator == null || indicator.isCanceled()) return;
indicator.setText("MD5 check");
int count = 0;
for (Set set : duplicates.values()) count+=set.size();
final Map<String, Set<VirtualFile>> realDuplicates = new HashMap<String, Set<VirtualFile>>();
int seek = 0;
for (Set<VirtualFile> files : duplicates.values()) {
for (VirtualFile file : files) {
seek++;
indicator.setFraction((double)seek / (double)count);
try {
final String md5 = getMD5Checksum(file.getInputStream());
if (realDuplicates.containsKey(md5)) {
realDuplicates.get(md5).add(file);
} else {
final HashSet<VirtualFile> set = new HashSet<VirtualFile>();
set.add(file);
realDuplicates.put(md5, set);
}
}
catch (Exception ignored) {
}
}
}
count = 0;
for (String key : new ArrayList<String>(realDuplicates.keySet())) {
final int size = realDuplicates.get(key).size();
if (size == 1) {
realDuplicates.remove(key);
} else {
count+=size;
}
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
new ImageDuplicateResultsDialog(project, images, realDuplicates).show();
}
});
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabledAndVisible(getEventProject(e) != null);
}
public static byte[] createChecksum(InputStream fis) throws Exception {
byte[] buffer = new byte[1024];
MessageDigest md5 = MessageDigest.getInstance("MD5");
int read;
while ((read = fis.read(buffer)) > 0) md5.update(buffer, 0, read);
fis.close();
return md5.digest();
}
public static String getMD5Checksum(InputStream fis) throws Exception {
byte[] bytes = createChecksum(fis);
String md5 = "";
for (byte b : bytes) md5 += Integer.toString((b & 0xff) + 0x100, 16).substring(1);
return md5;
}
}
@@ -46,20 +46,40 @@ public class LibraryRuntimeClasspathScope extends GlobalSearchScope {
myIndex = ProjectRootManager.getInstance(project).getFileIndex();
final Set<Sdk> processedSdk = new THashSet<Sdk>();
final Set<Library> processedLibraries = new THashSet<Library>();
final Set<Module> processedModules = new THashSet<Module>();
final Condition<OrderEntry> condition = new Condition<OrderEntry>() {
@Override
public boolean value(OrderEntry orderEntry) {
if (orderEntry instanceof ModuleOrderEntry) {
final Module module = ((ModuleOrderEntry)orderEntry).getModule();
return module != null && processedModules.add(module);
}
return true;
}
};
for (Module module : modules) {
buildEntries(module, processedModules, processedLibraries, processedSdk, condition);
}
ProjectRootManager.getInstance(project).orderEntries(modules).recursively().process(new RootPolicy<LinkedHashSet<VirtualFile>>() {
public LinkedHashSet<VirtualFile> visitLibraryOrderEntry(final LibraryOrderEntry libraryOrderEntry,
final LinkedHashSet<VirtualFile> value) {
final Library library = libraryOrderEntry.getLibrary();
if (library != null && processedLibraries.add(library)) {
ContainerUtil.addAll(value, libraryOrderEntry.getRootFiles(OrderRootType.CLASSES));
}
return value;
}
public LinkedHashSet<VirtualFile> visitModuleSourceOrderEntry(final ModuleSourceOrderEntry moduleSourceOrderEntry,
final LinkedHashSet<VirtualFile> value) {
ContainerUtil.addAll(value, moduleSourceOrderEntry.getFiles(OrderRootType.SOURCES));
return value;
}
@Override
public LinkedHashSet<VirtualFile> visitModuleOrderEntry(ModuleOrderEntry moduleOrderEntry, LinkedHashSet<VirtualFile> value) {
final Module depModule = moduleOrderEntry.getModule();
if (depModule != null) {
ContainerUtil.addAll(value, ModuleRootManager.getInstance(depModule).getSourceRoots());
}
return value;
}
public LinkedHashSet<VirtualFile> visitJdkOrderEntry(final JdkOrderEntry jdkOrderEntry, final LinkedHashSet<VirtualFile> value) {
final Sdk jdk = jdkOrderEntry.getJdk();
if (jdk != null && processedSdk.add(jdk)) {
ContainerUtil.addAll(value, jdkOrderEntry.getRootFiles(OrderRootType.CLASSES));
}
return value;
}
}, myEntries);
}
public LibraryRuntimeClasspathScope(Project project, LibraryOrderEntry entry) {
@@ -80,48 +100,6 @@ public class LibraryRuntimeClasspathScope extends GlobalSearchScope {
return that.myEntries.equals(myEntries);
}
private void buildEntries(@NotNull final Module module,
@NotNull final Set<Module> processedModules,
@NotNull final Set<Library> processedLibraries,
@NotNull final Set<Sdk> processedSdk,
Condition<OrderEntry> condition) {
if (!processedModules.add(module)) return;
ModuleRootManager.getInstance(module).orderEntries().recursively().satisfying(condition).process(new RootPolicy<LinkedHashSet<VirtualFile>>() {
public LinkedHashSet<VirtualFile> visitLibraryOrderEntry(final LibraryOrderEntry libraryOrderEntry,
final LinkedHashSet<VirtualFile> value) {
final Library library = libraryOrderEntry.getLibrary();
if (library != null && processedLibraries.add(library)) {
ContainerUtil.addAll(value, libraryOrderEntry.getRootFiles(OrderRootType.CLASSES));
}
return value;
}
public LinkedHashSet<VirtualFile> visitModuleSourceOrderEntry(final ModuleSourceOrderEntry moduleSourceOrderEntry,
final LinkedHashSet<VirtualFile> value) {
ContainerUtil.addAll(value, moduleSourceOrderEntry.getFiles(OrderRootType.SOURCES));
return value;
}
@Override
public LinkedHashSet<VirtualFile> visitModuleOrderEntry(ModuleOrderEntry moduleOrderEntry, LinkedHashSet<VirtualFile> value) {
final Module depModule = moduleOrderEntry.getModule();
if (depModule != null) {
ContainerUtil.addAll(value, ModuleRootManager.getInstance(depModule).getSourceRoots());
}
return value;
}
public LinkedHashSet<VirtualFile> visitJdkOrderEntry(final JdkOrderEntry jdkOrderEntry, final LinkedHashSet<VirtualFile> value) {
final Sdk jdk = jdkOrderEntry.getJdk();
if (jdk != null && processedSdk.add(jdk)) {
ContainerUtil.addAll(value, jdkOrderEntry.getRootFiles(OrderRootType.CLASSES));
}
return value;
}
}, myEntries);
}
public boolean contains(VirtualFile file) {
return myEntries.contains(getFileRoot(file));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -107,20 +107,25 @@ public class FileReference implements FileReferenceOwner, PsiPolyVariantReferenc
@NotNull
protected Collection<PsiFileSystemItem> getContexts() {
final FileReference contextRef = getContextReference();
ArrayList<PsiFileSystemItem> result = new ArrayList<PsiFileSystemItem>();
if (contextRef == null) {
Collection<PsiFileSystemItem> defaultContexts = myFileReferenceSet.getDefaultContexts();
for (PsiFileSystemItem context : defaultContexts) {
LOG.assertTrue(context != null, myFileReferenceSet.getClass() + " provided a null context");
}
return defaultContexts;
}
ResolveResult[] resolveResults = contextRef.multiResolve(false);
ArrayList<PsiFileSystemItem> result = new ArrayList<PsiFileSystemItem>();
for (ResolveResult resolveResult : resolveResults) {
if (resolveResult.getElement() != null) {
result.add((PsiFileSystemItem)resolveResult.getElement());
result.addAll(defaultContexts);
} else {
ResolveResult[] resolveResults = contextRef.multiResolve(false);
for (ResolveResult resolveResult : resolveResults) {
if (resolveResult.getElement() != null) {
result.add((PsiFileSystemItem)resolveResult.getElement());
}
}
}
result.addAll(myFileReferenceSet.getExtraContexts());
return result;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -120,6 +120,10 @@ public class FileReferenceSet {
return "/";
}
protected Collection<PsiFileSystemItem> getExtraContexts() {
return Collections.emptyList();
}
public static FileReferenceSet createSet(PsiElement element,
final boolean soft,
boolean endingSlashNotAllowed,
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* 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.
@@ -19,6 +19,7 @@ import com.intellij.openapi.paths.PsiDynaReference;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -64,4 +65,19 @@ public class FileReferenceUtil {
}
return null;
}
@Nullable
public static FileReference findFileReference(@NotNull PsiElement element) {
final PsiReference[] references = element.getReferences();
for (int i = references.length - 1; i >= 0; i--) {
PsiReference ref = references[i];
if (ref instanceof PsiDynaReference) {
ref = ((PsiDynaReference)ref).getLastFileReference();
}
if (ref instanceof FileReference) {
return (FileReference)references[i];
}
}
return null;
}
}
@@ -30,6 +30,7 @@ import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
@@ -83,6 +84,7 @@ public abstract class FileColorSettingsTable extends JBTable {
@Override
public boolean editCellAt(int row, int column, EventObject e) {
if (e instanceof MouseEvent && ((MouseEvent)e).getClickCount() == 1) return false;
final Object at = getModel().getValueAt(row, column);
final FileColorConfigurationEditDialog dialog = new FileColorConfigurationEditDialog(myManager, ((FileColorConfiguration)at));
dialog.getScopeComboBox().setEnabled(false);
@@ -90,11 +92,6 @@ public abstract class FileColorSettingsTable extends JBTable {
return false;
}
@Override
public boolean isCellEditable(int row, int column) {
return column == 1;
}
public boolean isModified() {
final List<FileColorConfiguration> current = getModel().getConfigurations();
@@ -27,6 +27,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -166,6 +167,9 @@ public class ExecUtil {
final String script = "do shell script \"" + scriptPath + "\" with administrator privileges";
return execAndGetOutput(Arrays.asList(getOsascriptPath(), "-e", script), workDir);
}
else if ("root".equals(System.getenv("USER"))) {
return execAndGetOutput(Collections.singletonList(scriptPath), workDir);
}
else if (hasKdeSudo.getValue()) {
return execAndGetOutput(Arrays.asList("kdesudo", "--comment", prompt, scriptPath), workDir);
}
@@ -40,7 +40,6 @@ import com.intellij.util.PlatformUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.script.ScriptException;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
@@ -54,7 +53,7 @@ public class CreateDesktopEntryAction extends DumbAwareAction {
private static final int MIN_ICON_SIZE = 32;
public static boolean isAvailable() {
return SystemInfo.hasXdgOpen();
return SystemInfo.isUnix && SystemInfo.hasXdgOpen();
}
@Override
@@ -202,19 +201,20 @@ public class CreateDesktopEntryAction extends DumbAwareAction {
return null;
}
private static void install(final File entryFile, final boolean globalEntry) throws IOException, ExecutionException, InterruptedException, ScriptException {
private static void install(final File entryFile, final boolean globalEntry) throws IOException, ExecutionException, InterruptedException {
try {
final int result;
if (globalEntry) {
final String source = "#!/bin/sh\n" +
"xdg-desktop-menu install --mode system \"" + entryFile.getAbsolutePath() + "\"";
"xdg-desktop-menu install --mode system \"" + entryFile.getAbsolutePath() + "\"";
final File script = ExecUtil.createTempExecutableScript("sudo", ".sh", source);
result = ExecUtil.sudoAndGetResult(script.getAbsolutePath(), ApplicationBundle.message("desktop.entry.sudo.prompt"));
script.deleteOnExit();
final int result = ExecUtil.sudoAndGetResult(script.getAbsolutePath(), ApplicationBundle.message("desktop.entry.sudo.prompt"));
if (result != 0) throw new RuntimeException("'" + script.getAbsolutePath() + "' : " + result);
}
else {
result = ExecUtil.execAndGetResult("xdg-desktop-menu", "install", "--mode", "user", entryFile.getAbsolutePath());
final int result = ExecUtil.execAndGetResult("xdg-desktop-menu", "install", "--mode", "user", entryFile.getAbsolutePath());
if (result != 0) throw new RuntimeException("'" + entryFile.getAbsolutePath() + "' : " + result);
}
if (result != 0) throw new RuntimeException("'" + entryFile.getAbsolutePath() + "' : " + result);
}
finally {
if (!entryFile.delete()) LOG.error("Failed to delete temp file '" + entryFile + "'");
@@ -527,6 +527,7 @@
<action id="UiInspector" internal="true" class="com.intellij.internal.inspector.UiInspectorAction" text="UI Inspector"/>
<action id="ShowSplash" internal="true" class="com.intellij.ui.ShowSplashAction" text="Show Splash" />
<action id="ValidationTest" internal="true" class="com.intellij.internal.validation.TestDialogWithValidationAction" text="Validation Dialog Test" />
<action id="ImageDuplicates" internal="true" class="com.intellij.internal.ShowImageDuplicatesAction" text="Find Image Duplicates"/>
<action id="TreeExpandAll" internal="true" class="com.intellij.internal.tree.ExpandAll" text="Expand Tree" />
<separator/>
<reference ref="MaintenanceGroup"/>
@@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.hash.LinkedHashMap;
import jsr166e.SequenceLock;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -34,7 +35,6 @@ import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author max
@@ -42,7 +42,7 @@ import java.util.concurrent.locks.ReentrantLock;
public class PagedFileStorage implements Forceable {
protected static final Logger LOG = Logger.getInstance("#com.intellij.util.io.PagedFileStorage");
private static final int MB = 1024 * 1024;
public static final int MB = 1024 * 1024;
private final static int LOWER_LIMIT;
private final static int UPPER_LIMIT;
@@ -604,11 +604,11 @@ public class PagedFileStorage implements Forceable {
}
public static class StorageLockContext {
private final ReentrantLock myReentrantLock;
private final SequenceLock myReentrantLock;
private final StorageLock myLock;
public StorageLockContext(StorageLock lock) {
myReentrantLock = new ReentrantLock();
myReentrantLock = new SequenceLock();
myLock = lock;
}
}
@@ -45,6 +45,9 @@ abstract class PersistentEnumeratorBase<Data> implements Forceable, Closeable {
protected static final int DATA_START = META_DATA_OFFSET + 16;
protected final ResizeableMappedFile myStorage;
private final byte[] myKeyStoreFileBuffer;
private volatile int myKeyStoreFileLength;
private volatile int myKeyStoreBufferPosition;
private final ResizeableMappedFile myKeyStorage;
private boolean myClosed = false;
@@ -211,10 +214,13 @@ abstract class PersistentEnumeratorBase<Data> implements Forceable, Closeable {
if (myDataDescriptor instanceof InlineKeyDescriptor) {
myKeyStorage = null;
myKeyReadStream = null;
myKeyStoreFileBuffer = null;
}
else {
myKeyStorage = new ResizeableMappedFile(keystreamFile(), initialSize, myStorage.getPagedFileStorage().getStorageLockContext(), -1, false);
myKeyStorage = new ResizeableMappedFile(keystreamFile(), initialSize, myStorage.getPagedFileStorage().getStorageLockContext(), PagedFileStorage.MB, false);
myKeyReadStream = new MyDataIS(myKeyStorage);
myKeyStoreFileLength = (int)myKeyStorage.length();
myKeyStoreFileBuffer = new byte[initialSize];
}
}
@@ -367,13 +373,27 @@ abstract class PersistentEnumeratorBase<Data> implements Forceable, Closeable {
try {
markDirty(true);
final int dataOff = myKeyStorage != null ? (int)myKeyStorage.length() : ((InlineKeyDescriptor<Data>)myDataDescriptor).toInt(value);
final int dataOff = myKeyStorage != null ? myKeyStoreBufferPosition + myKeyStoreFileLength : ((InlineKeyDescriptor<Data>)myDataDescriptor).toInt(value);
if (myKeyStorage != null) {
final BufferExposingByteArrayOutputStream bos = new BufferExposingByteArrayOutputStream();
DataOutput out = new DataOutputStream(bos);
myDataDescriptor.save(out, value);
myKeyStorage.put(dataOff, bos.getInternalBuffer(), 0, bos.size());
final int size = bos.size();
final byte[] buffer = bos.getInternalBuffer();
if (size > myKeyStoreFileBuffer.length) {
flushKeyStoreBuffer();
myKeyStorage.put(dataOff, buffer, 0, size);
myKeyStoreFileLength += size;
} else {
if (size > myKeyStoreFileBuffer.length - myKeyStoreBufferPosition) {
flushKeyStoreBuffer();
}
// myKeyStoreFileBuffer will contain complete records
System.arraycopy(buffer, 0, myKeyStoreFileBuffer, myKeyStoreBufferPosition, size);
myKeyStoreBufferPosition += size;
}
}
return setupValueId(hashCode, dataOff);
@@ -383,6 +403,14 @@ abstract class PersistentEnumeratorBase<Data> implements Forceable, Closeable {
}
}
private void flushKeyStoreBuffer() throws IOException {
if (myKeyStoreBufferPosition > 0) {
myKeyStorage.put(myKeyStoreFileLength, myKeyStoreFileBuffer, 0, myKeyStoreBufferPosition);
myKeyStoreFileLength += myKeyStoreBufferPosition;
myKeyStoreBufferPosition = 0;
}
}
protected int setupValueId(int hashCode, int dataOff) {
final byte[] buf = myRecordHandler.getRecordBuffer(this);
myRecordHandler.setupRecord(this, hashCode, dataOff, buf);
@@ -399,10 +427,11 @@ abstract class PersistentEnumeratorBase<Data> implements Forceable, Closeable {
throw new UnsupportedOperationException("Iteration over InlineIntegerKeyDescriptors is not supported");
}
flushKeyStoreBuffer();
myKeyStorage.force();
DataInputStream keysStream = new DataInputStream(new BufferedInputStream(new LimitedInputStream(new FileInputStream(keystreamFile()),
(int)myKeyStorage.length())));
myKeyStoreFileLength)));
try {
try {
while (true) {
@@ -435,7 +464,11 @@ abstract class PersistentEnumeratorBase<Data> implements Forceable, Closeable {
if (myKeyReadStream == null) return ((InlineKeyDescriptor<Data>)myDataDescriptor).fromInt(addr);
myKeyReadStream.setup(addr, myKeyStorage.length());
if (myKeyStoreFileLength <= addr) {
return myDataDescriptor.read(new DataInputStream(new UnsyncByteArrayInputStream(myKeyStoreFileBuffer, addr - myKeyStoreFileLength, myKeyStoreBufferPosition)));
}
// we do not need to flushKeyBuffer since we store complete records
myKeyReadStream.setup(addr, myKeyStoreFileLength);
return myDataDescriptor.read(myKeyReadStream);
}
catch (IOException io) {
@@ -501,6 +534,7 @@ abstract class PersistentEnumeratorBase<Data> implements Forceable, Closeable {
protected void doClose() throws IOException {
try {
if (myKeyStorage != null) {
flushKeyStoreBuffer();
myKeyStorage.close();
}
flush();
@@ -542,6 +576,7 @@ abstract class PersistentEnumeratorBase<Data> implements Forceable, Closeable {
try {
if (myKeyStorage != null) {
flushKeyStoreBuffer();
myKeyStorage.force();
}
flush();
@@ -15,16 +15,22 @@
*/
package com.intellij.util.io;
import com.intellij.util.containers.ConcurrentSLRUMap;
import com.intellij.util.containers.SLRUMap;
import jsr166e.SequenceLock;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
public class PersistentStringEnumerator extends PersistentEnumeratorDelegate<String>{
@Nullable private final ConcurrentSLRUMap<Integer, String> myIdToStringCache;
@Nullable private final ConcurrentSLRUMap<Integer, Integer> myHashcodeToIdCache;
private static final int STRIPE_POWER = 4;
private static final int STRIPE_COUNT = 1 << STRIPE_POWER;
private static final int STRIPE_MASK = STRIPE_COUNT - 1;
@Nullable private final SLRUMap<Integer, Integer>[] myHashcodeToIdCache;
@Nullable private final SLRUMap<Integer, String>[] myIdToStringCache;
@Nullable private final Lock[] myStripeLocks;
public PersistentStringEnumerator(@NotNull final File file) throws IOException {
this(file, 1024 * 4);
@@ -41,50 +47,111 @@ public class PersistentStringEnumerator extends PersistentEnumeratorDelegate<Str
private PersistentStringEnumerator(@NotNull final File file, final int initialSize, boolean cacheLastMappings) throws IOException {
super(file, new EnumeratorStringDescriptor(), initialSize);
if (cacheLastMappings) {
myIdToStringCache = new ConcurrentSLRUMap<Integer, String>(8192, 8192);
myHashcodeToIdCache = new ConcurrentSLRUMap<Integer, Integer>(8192, 8192);
myIdToStringCache = new SLRUMap[STRIPE_COUNT];
myHashcodeToIdCache = new SLRUMap[STRIPE_COUNT];
myStripeLocks = new Lock[STRIPE_COUNT];
int protectedSize = 8192;
int probationalSize = 8192;
for(int i = 0; i < STRIPE_COUNT; ++i) {
myHashcodeToIdCache[i] = new SLRUMap<Integer, Integer>(protectedSize / STRIPE_COUNT, probationalSize / STRIPE_COUNT);
myIdToStringCache[i] = new SLRUMap<Integer, String>(protectedSize / STRIPE_COUNT, probationalSize / STRIPE_COUNT);
myStripeLocks[i] = new SequenceLock();
}
} else {
myIdToStringCache = null;
myHashcodeToIdCache = null;
myStripeLocks = null;
}
}
@Override
public int enumerate(@Nullable String value) throws IOException {
int valueHashCode = -1;
int valueHashCode =-1;
int stripe = -1;
if (myHashcodeToIdCache != null && value != null) {
Integer cachedId = myHashcodeToIdCache.get(valueHashCode = value.hashCode());
valueHashCode = value.hashCode();
stripe = Math.abs(valueHashCode) & STRIPE_MASK;
Integer cachedId;
myStripeLocks[stripe].lock();
try {
cachedId = myHashcodeToIdCache[stripe].get(valueHashCode);
}
finally {
myStripeLocks[stripe].unlock();
}
if (cachedId != null) {
String s = myIdToStringCache.get(cachedId);
if (s != null && value.equals(s)) return cachedId.intValue();
int stripe2 = idStripe(cachedId.intValue());
myStripeLocks[stripe2].lock();
try {
String s = myIdToStringCache[stripe2].get(cachedId);
if (s != null && value.equals(s)) return cachedId.intValue();
}
finally {
myStripeLocks[stripe2].unlock();
}
}
}
int enumerate = super.enumerate(value);
Integer enumeratedInteger = null;
if (myHashcodeToIdCache != null) {
enumeratedInteger = enumerate;
if (value != null) myHashcodeToIdCache.put(valueHashCode, enumeratedInteger);
if (stripe != -1) {
Integer enumeratedInteger;
myStripeLocks[stripe].lock();
try {
enumeratedInteger = enumerate;
myHashcodeToIdCache[stripe].put(valueHashCode, enumeratedInteger);
} finally {
myStripeLocks[stripe].unlock();
}
int stripe2 = idStripe(enumerate);
myStripeLocks[stripe2].lock();
try {
myIdToStringCache[stripe2].put(enumeratedInteger, value);
} finally {
myStripeLocks[stripe2].unlock();
}
}
if (myIdToStringCache != null) {
myIdToStringCache.put(enumeratedInteger, value);
}
return enumerate;
}
private int idStripe(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return Math.abs(h ^ (h >>> 7) ^ (h >>> 4)) & STRIPE_MASK;
}
@Nullable
@Override
public String valueOf(int idx) throws IOException {
int stripe = -1;
if (myIdToStringCache != null) {
String s = myIdToStringCache.get(idx);
if (s != null) return s;
stripe = idStripe(idx);
myStripeLocks[stripe].lock();
try {
String s = myIdToStringCache[stripe].get(idx);
if (s != null) return s;
}
finally {
myStripeLocks[stripe].unlock();
}
}
String s = super.valueOf(idx);
if (myIdToStringCache != null && s != null) {
myIdToStringCache.put(idx, s);
if (stripe != -1 && s != null) {
myStripeLocks[stripe].lock();
try {
myIdToStringCache[stripe].put(idx, s);
}
finally {
myStripeLocks[stripe].unlock();
}
}
return s;
}
@@ -94,11 +161,12 @@ public class PersistentStringEnumerator extends PersistentEnumeratorDelegate<Str
super.close();
if (myIdToStringCache != null) {
myIdToStringCache.clear();
}
if (myHashcodeToIdCache != null) {
myHashcodeToIdCache.clear();
for(int i = 0; i < myIdToStringCache.length; ++i) {
myStripeLocks[i].lock();
myIdToStringCache[i].clear();
myHashcodeToIdCache[i].clear();
myStripeLocks[i].unlock();
}
}
}
+636
View File
@@ -0,0 +1,636 @@
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package jsr166e;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* A reentrant mutual exclusion {@link Lock} in which each lock
* acquisition or release advances a sequence number. When the
* sequence number (accessible using {@link #getSequence()}) is odd,
* the lock is held. When it is even (i.e., ({@code lock.getSequence()
* & 1L) == 0L}), the lock is released. Method {@link
* #awaitAvailability} can be used to await availability of the lock,
* returning its current sequence number. Sequence numbers (as well as
* reentrant hold counts) are of type {@code long} to ensure that they
* will not wrap around until hundreds of years of use under current
* processor rates. A SequenceLock can be created with a specified
* number of spins. Attempts to acquire the lock in method {@link
* #lock} will retry at least the given number of times before
* blocking. If not specified, a default, possibly platform-specific,
* value is used.
*
* <p>Except for the lack of support for specified fairness policies,
* or {@link Condition} objects, a SequenceLock can be used in the
* same way as {@link ReentrantLock}. It provides similar status and
* monitoring methods, such as {@link #isHeldByCurrentThread}.
* SequenceLocks may be preferable in contexts in which multiple
* threads invoke short read-only methods much more frequently than
* fully locked methods.
*
* <p> Methods {@code awaitAvailability} and {@code getSequence} can
* be used together to define (partially) optimistic read-only methods
* that are usually more efficient than ReadWriteLocks when they
* apply. These methods should in general be structured as loops that
* await lock availability, then read {@code volatile} fields into
* local variables (and may further read other values derived from
* these, for example the {@code length} of a {@code volatile} array),
* and retry if the sequence number changed while doing so.
* Alternatively, because {@code awaitAvailability} accommodates
* reentrancy, a method can retry a bounded number of times before
* switching to locking mode. While conceptually straightforward,
* expressing these ideas can be verbose. For example:
*
* <pre> {@code
* class Point {
* private volatile double x, y;
* private final SequenceLock sl = new SequenceLock();
*
* // an exclusively locked method
* void move(double deltaX, double deltaY) {
* sl.lock();
* try {
* x += deltaX;
* y += deltaY;
* } finally {
* sl.unlock();
* }
* }
*
* // A read-only method
* double distanceFromOriginV1() {
* double currentX, currentY;
* long seq;
* do {
* seq = sl.awaitAvailability();
* currentX = x;
* currentY = y;
* } while (sl.getSequence() != seq); // retry if sequence changed
* return Math.sqrt(currentX * currentX + currentY * currentY);
* }
*
* // Uses bounded retries before locking
* double distanceFromOriginV2() {
* double currentX, currentY;
* long seq;
* int retries = RETRIES_BEFORE_LOCKING; // for example 8
* try {
* do {
* if (--retries < 0)
* sl.lock();
* seq = sl.awaitAvailability();
* currentX = x;
* currentY = y;
* } while (sl.getSequence() != seq);
* } finally {
* if (retries < 0)
* sl.unlock();
* }
* return Math.sqrt(currentX * currentX + currentY * currentY);
* }
* }}</pre>
*
* @since 1.8
* @author Doug Lea
*/
public class SequenceLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
static final class Sync extends AbstractQueuedLongSynchronizer {
static final long serialVersionUID = 2540673546047039555L;
/**
* The number of times to spin in lock() and awaitAvailability().
*/
final int spins;
/**
* The number of reentrant holds on this lock. Uses a long for
* compatibility with other AbstractQueuedLongSynchronizer
* operations. Accessed only by lock holder.
*/
long holds;
Sync(int spins) { this.spins = spins; }
// overrides of AQLS methods
public final boolean isHeldExclusively() {
return (getState() & 1L) != 0L &&
getExclusiveOwnerThread() == Thread.currentThread();
}
public final boolean tryAcquire(long acquires) {
long c = getState();
if ((c & 1L) == 0L) {
if (compareAndSetState(c, c + 1L)) {
holds = acquires;
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
}
else if (Thread.currentThread() == getExclusiveOwnerThread()) {
holds += acquires;
return true;
}
return false;
}
public final boolean tryRelease(long releases) {
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
if ((holds -= releases) == 0L) {
setExclusiveOwnerThread(null);
setState(getState() + 1L);
return true;
}
return false;
}
public final long tryAcquireShared(long unused) {
return (((getState() & 1L) == 0L) ? 1L :
(getExclusiveOwnerThread() == Thread.currentThread()) ? 0L:
-1L);
}
public final boolean tryReleaseShared(long unused) {
return (getState() & 1L) == 0L;
}
public final Condition newCondition() {
throw new UnsupportedOperationException();
}
// Other methods in support of SequenceLock
final long getSequence() {
return getState();
}
final void lock() {
int k = spins;
while (!tryAcquire(1L)) {
if (k == 0) {
acquire(1L);
break;
}
--k;
}
}
final long awaitAvailability() {
long s;
while (((s = getState()) & 1L) != 0L &&
getExclusiveOwnerThread() != Thread.currentThread()) {
acquireShared(1L);
releaseShared(1L);
}
return s;
}
final long tryAwaitAvailability(long nanos)
throws InterruptedException, TimeoutException {
Thread current = Thread.currentThread();
for (;;) {
long s = getState();
if ((s & 1L) == 0L || getExclusiveOwnerThread() == current) {
releaseShared(1L);
return s;
}
if (!tryAcquireSharedNanos(1L, nanos))
throw new TimeoutException();
// since tryAcquireSharedNanos doesn't return seq
// retry with minimal wait time.
nanos = 1L;
}
}
final boolean isLocked() {
return (getState() & 1L) != 0L;
}
final Thread getOwner() {
return (getState() & 1L) == 0L ? null : getExclusiveOwnerThread();
}
final long getHoldCount() {
return isHeldExclusively() ? holds : 0;
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
holds = 0L;
setState(0L); // reset to unlocked state
}
}
private final Sync sync;
/**
* The default spin value for constructor. Future versions of this
* class might choose platform-specific values. Currently, except
* on uniprocessors, it is set to a small value that overcomes near
* misses between releases and acquires.
*/
static final int DEFAULT_SPINS =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 0;
/**
* Creates an instance of {@code SequenceLock} with the default
* number of retry attempts to acquire the lock before blocking.
*/
public SequenceLock() { sync = new Sync(DEFAULT_SPINS); }
/**
* Creates an instance of {@code SequenceLock} that will retry
* attempts to acquire the lock at least the given number times
* before blocking.
*/
public SequenceLock(int spins) { sync = new Sync(spins); }
/**
* Returns the current sequence number of this lock. The sequence
* number is advanced upon each acquire or release action. When
* this value is odd, the lock is held; when even, it is released.
*
* @return the current sequence number
*/
public long getSequence() { return sync.getSequence(); }
/**
* Returns the current sequence number when the lock is, or
* becomes, available. A lock is available if it is either
* released, or is held by the current thread. If the lock is not
* available, the current thread becomes disabled for thread
* scheduling purposes and lies dormant until the lock has been
* released by some other thread.
*
* @return the current sequence number
*/
public long awaitAvailability() { return sync.awaitAvailability(); }
/**
* Returns the current sequence number if the lock is, or
* becomes, available within the specified waiting time.
*
* <p>If the lock is not available, the current thread becomes
* disabled for thread scheduling purposes and lies dormant until
* one of three things happens:
*
* <ul>
*
* <li>The lock becomes available, in which case the current
* sequence number is returned.
*
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread, in which case this method throws
* {@link InterruptedException}.
*
* <li>The specified waiting time elapses, in which case
* this method throws {@link TimeoutException}.
*
* </ul>
*
* @param timeout the time to wait for availability
* @param unit the time unit of the timeout argument
* @return the current sequence number if the lock is available
* upon return from this method
* @throws InterruptedException if the current thread is interrupted
* @throws TimeoutException if the lock was not available within
* the specified waiting time
* @throws NullPointerException if the time unit is null
*/
public long tryAwaitAvailability(long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
return sync.tryAwaitAvailability(unit.toNanos(timeout));
}
/**
* Acquires the lock.
*
* <p>If the current thread already holds this lock then the hold count
* is incremented by one and the method returns immediately without
* incrementing the sequence number.
*
* <p>If this lock not held by another thread, this method
* increments the sequence number (which thus becomes an odd
* number), sets the lock hold count to one, and returns
* immediately.
*
* <p>If the lock is held by another thread then the current
* thread may retry acquiring this lock, depending on the {@code
* spin} count established in constructor. If the lock is still
* not acquired, the current thread becomes disabled for thread
* scheduling purposes and lies dormant until enabled by
* some other thread releasing the lock.
*/
public void lock() { sync.lock(); }
/**
* Acquires the lock unless the current thread is
* {@linkplain Thread#interrupt interrupted}.
*
* <p>If the current thread already holds this lock then the hold count
* is incremented by one and the method returns immediately without
* incrementing the sequence number.
*
* <p>If this lock not held by another thread, this method
* increments the sequence number (which thus becomes an odd
* number), sets the lock hold count to one, and returns
* immediately.
*
* <p>If the lock is held by another thread then the current
* thread may retry acquiring this lock, depending on the {@code
* spin} count established in constructor. If the lock is still
* not acquired, the current thread becomes disabled for thread
* scheduling purposes and lies dormant until one of two things
* happens:
*
* <ul>
*
* <li>The lock is acquired by the current thread; or
*
* <li>Some other thread {@linkplain Thread#interrupt interrupts} the
* current thread.
*
* </ul>
*
* <p>If the lock is acquired by the current thread then the lock hold
* count is set to one and the sequence number is incremented.
*
* <p>If the current thread:
*
* <ul>
*
* <li>has its interrupted status set on entry to this method; or
*
* <li>is {@linkplain Thread#interrupt interrupted} while acquiring
* the lock,
*
* </ul>
*
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* <p>In this implementation, as this method is an explicit
* interruption point, preference is given to responding to the
* interrupt over normal or reentrant acquisition of the lock.
*
* @throws InterruptedException if the current thread is interrupted
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1L);
}
/**
* Acquires the lock only if it is not held by another thread at the time
* of invocation.
*
* <p>If the current thread already holds this lock then the hold
* count is incremented by one and the method returns {@code true}
* without incrementing the sequence number.
*
* <p>If this lock not held by another thread, this method
* increments the sequence number (which thus becomes an odd
* number), sets the lock hold count to one, and returns {@code
* true}.
*
* <p>If the lock is held by another thread then this method
* returns {@code false}.
*
* @return {@code true} if the lock was free and was acquired by the
* current thread, or the lock was already held by the current
* thread; and {@code false} otherwise
*/
public boolean tryLock() { return sync.tryAcquire(1L); }
/**
* Acquires the lock if it is not held by another thread within the given
* waiting time and the current thread has not been
* {@linkplain Thread#interrupt interrupted}.
*
* <p>If the current thread already holds this lock then the hold count
* is incremented by one and the method returns immediately without
* incrementing the sequence number.
*
* <p>If this lock not held by another thread, this method
* increments the sequence number (which thus becomes an odd
* number), sets the lock hold count to one, and returns
* immediately.
*
* <p>If the lock is held by another thread then the current
* thread may retry acquiring this lock, depending on the {@code
* spin} count established in constructor. If the lock is still
* not acquired, the current thread becomes disabled for thread
* scheduling purposes and lies dormant until one of three things
* happens:
*
* <ul>
*
* <li>The lock is acquired by the current thread; or
*
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
*
* <li>The specified waiting time elapses
*
* </ul>
*
* <p>If the lock is acquired then the value {@code true} is returned and
* the lock hold count is set to one.
*
* <p>If the current thread:
*
* <ul>
*
* <li>has its interrupted status set on entry to this method; or
*
* <li>is {@linkplain Thread#interrupt interrupted} while
* acquiring the lock,
*
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* <p>If the specified waiting time elapses then the value {@code false}
* is returned. If the time is less than or equal to zero, the method
* will not wait at all.
*
* <p>In this implementation, as this method is an explicit
* interruption point, preference is given to responding to the
* interrupt over normal or reentrant acquisition of the lock, and
* over reporting the elapse of the waiting time.
*
* @param timeout the time to wait for the lock
* @param unit the time unit of the timeout argument
* @return {@code true} if the lock was free and was acquired by the
* current thread, or the lock was already held by the current
* thread; and {@code false} if the waiting time elapsed before
* the lock could be acquired
* @throws InterruptedException if the current thread is interrupted
* @throws NullPointerException if the time unit is null
*
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1L, unit.toNanos(timeout));
}
/**
* Attempts to release this lock.
*
* <p>If the current thread is the holder of this lock then the
* hold count is decremented. If the hold count is now zero then
* the sequence number is incremented (thus becoming an even
* number) and the lock is released. If the current thread is not
* the holder of this lock then {@link
* IllegalMonitorStateException} is thrown.
*
* @throws IllegalMonitorStateException if the current thread does not
* hold this lock
*/
public void unlock() { sync.release(1); }
/**
* Throws UnsupportedOperationException. SequenceLocks
* do not support Condition objects.
*
* @throws UnsupportedOperationException
*/
public Condition newCondition() {
throw new UnsupportedOperationException();
}
/**
* Queries the number of holds on this lock by the current thread.
*
* <p>A thread has a hold on a lock for each lock action that is not
* matched by an unlock action.
*
* <p>The hold count information is typically only used for testing and
* debugging purposes.
*
* @return the number of holds on this lock by the current thread,
* or zero if this lock is not held by the current thread
*/
public long getHoldCount() { return sync.getHoldCount(); }
/**
* Queries if this lock is held by the current thread.
*
* @return {@code true} if current thread holds this lock and
* {@code false} otherwise
*/
public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); }
/**
* Queries if this lock is held by any thread. This method is
* designed for use in monitoring of the system state,
* not for synchronization control.
*
* @return {@code true} if any thread holds this lock and
* {@code false} otherwise
*/
public boolean isLocked() { return sync.isLocked(); }
/**
* Returns the thread that currently owns this lock, or
* {@code null} if not owned. When this method is called by a
* thread that is not the owner, the return value reflects a
* best-effort approximation of current lock status. For example,
* the owner may be momentarily {@code null} even if there are
* threads trying to acquire the lock but have not yet done so.
* This method is designed to facilitate construction of
* subclasses that provide more extensive lock monitoring
* facilities.
*
* @return the owner, or {@code null} if not owned
*/
protected Thread getOwner() { return sync.getOwner(); }
/**
* Queries whether any threads are waiting to acquire this lock. Note that
* because cancellations may occur at any time, a {@code true}
* return does not guarantee that any other thread will ever
* acquire this lock. This method is designed primarily for use in
* monitoring of the system state.
*
* @return {@code true} if there may be other threads waiting to
* acquire the lock
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
* Queries whether the given thread is waiting to acquire this
* lock. Note that because cancellations may occur at any time, a
* {@code true} return does not guarantee that this thread
* will ever acquire this lock. This method is designed primarily for use
* in monitoring of the system state.
*
* @param thread the thread
* @return {@code true} if the given thread is queued waiting for this lock
* @throws NullPointerException if the thread is null
*/
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
* Returns an estimate of the number of threads waiting to
* acquire this lock. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
* monitoring of the system state, not for synchronization
* control.
*
* @return the estimated number of threads waiting for this lock
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
* Returns a collection containing threads that may be waiting to
* acquire this lock. Because the actual set of threads may change
* dynamically while constructing this result, the returned
* collection is only a best-effort estimate. The elements of the
* returned collection are in no particular order. This method is
* designed to facilitate construction of subclasses that provide
* more extensive monitoring facilities.
*
* @return the collection of threads
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
* Returns a string identifying this lock, as well as its lock state.
* The state, in brackets, includes either the String {@code "Unlocked"}
* or the String {@code "Locked by"} followed by the
* {@linkplain Thread#getName name} of the owning thread.
*
* @return a string identifying this lock, as well as its lock state
*/
public String toString() {
Thread o = sync.getOwner();
return super.toString() + ((o == null) ?
"[Unlocked]" :
"[Locked by thread " + o.getName() + "]");
}
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.android.actions.AndroidProcessChooserDialog">
<grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="136" height="400"/>
@@ -19,8 +19,8 @@
</component>
<scrollpane id="4f662" class="com.intellij.ui.components.JBScrollPane">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="250" height="200"/>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="250" height="300"/>
</grid>
</constraints>
<properties/>
@@ -35,6 +35,14 @@
</component>
</children>
</scrollpane>
<component id="bf4c2" class="com.intellij.ui.components.JBCheckBox" binding="myShowAllProcessesCheckBox">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Show &amp;all processes"/>
</properties>
</component>
</children>
</grid>
</form>
@@ -39,6 +39,7 @@ import com.intellij.psi.XmlRecursiveElementVisitor;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlElement;
import com.intellij.ui.JBDefaultTreeCellRenderer;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.content.Content;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.NotNullFunction;
@@ -63,10 +64,7 @@ import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.*;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -78,12 +76,14 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.actions.AndroidProcessChooserDialog");
@NonNls private static final String DEBUGGABLE_PROCESS_PROPERTY = "DEBUGGABLE_PROCESS";
@NonNls private static final String SHOW_ALL_PROCESSES_PROPERTY = "SHOW_ALL_PROCESSES";
@NonNls private static final String DEBUGGABLE_DEVICE_PROPERTY = "DEBUGGABLE_DEVICE";
@NonNls private static final String RUN_CONFIGURATION_NAME_PATTERN = "Android Debugger (%s)";
private final Project myProject;
private JPanel myContentPanel;
private Tree myProcessTree;
private JBCheckBox myShowAllProcessesCheckBox;
private final MergingUpdateQueue myUpdatesQueue;
private final AndroidDebugBridge.IClientChangeListener myClientChangeListener;
@@ -97,7 +97,11 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
myUpdatesQueue =
new MergingUpdateQueue("AndroidProcessChooserDialogUpdatingQueue", 500, true, MergingUpdateQueue.ANY_COMPONENT, myProject);
doUpdateTree();
final String showAllProcessesStr = PropertiesComponent.getInstance(project).getValue(SHOW_ALL_PROCESSES_PROPERTY);
final boolean showAllProcesses = Boolean.parseBoolean(showAllProcessesStr);
myShowAllProcessesCheckBox.setSelected(showAllProcesses);
doUpdateTree(showAllProcesses);
myClientChangeListener = new AndroidDebugBridge.IClientChangeListener() {
@Override
@@ -125,6 +129,13 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
};
AndroidDebugBridge.addDeviceChangeListener(myDeviceChangeListener);
myShowAllProcessesCheckBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateTree();
}
});
myProcessTree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
@@ -219,6 +230,8 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
}
private void updateTree() {
final boolean showAllProcesses = myShowAllProcessesCheckBox.isSelected();
myUpdatesQueue.queue(new Update(AndroidProcessChooserDialog.this) {
@Override
public void run() {
@@ -234,7 +247,7 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
return;
}
doUpdateTree();
doUpdateTree(showAllProcesses);
}
@Override
@@ -244,7 +257,7 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
});
}
private void doUpdateTree() {
private void doUpdateTree(boolean showAllProcesses) {
final AndroidDebugBridge debugBridge = AndroidSdkUtils.getDebugBridge(myProject);
final DefaultMutableTreeNode root = new DefaultMutableTreeNode();
@@ -275,7 +288,8 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
for (Client client : device.getClients()) {
final String clientDescription = getClientDescription(client);
if (clientDescription != null && processNames.contains(clientDescription)) {
if (clientDescription != null &&
(showAllProcesses || isRelatedProcess(processNames, clientDescription))) {
final DefaultMutableTreeNode clientNode = new DefaultMutableTreeNode(client);
deviceNode.add(clientNode);
@@ -314,6 +328,17 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
});
}
private boolean isRelatedProcess(Set<String> processNames, String clientDescription) {
final String lc = clientDescription.toLowerCase();
for (String processName : processNames) {
if (lc.startsWith(processName)) {
return true;
}
}
return false;
}
@NotNull
private static Set<String> collectAllProcessNames(Project project) {
final List<AndroidFacet> facets = ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID);
@@ -325,7 +350,7 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
final String packageName = manifest.getPackage().getValue();
if (packageName != null) {
result.add(packageName);
result.add(packageName.toLowerCase());
}
final XmlElement xmlElement = manifest.getXmlElement();
@@ -352,7 +377,7 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
final String value = attribute.getValue();
if (value != null) {
result.add(value);
result.add(value.toLowerCase());
}
}
}
@@ -382,6 +407,7 @@ public class AndroidProcessChooserDialog extends DialogWrapper {
properties.setValue(DEBUGGABLE_DEVICE_PROPERTY, getPresentableName(selectedDevice));
properties.setValue(DEBUGGABLE_PROCESS_PROPERTY, selectedClient.getClientData().getClientDescription());
properties.setValue(SHOW_ALL_PROCESSES_PROPERTY, Boolean.toString(myShowAllProcessesCheckBox.isSelected()));
final String debugPort = Integer.toString(selectedClient.getDebuggerListenPort());
@@ -75,6 +75,8 @@
<moduleConfigurationEditorProvider implementation="org.jetbrains.idea.devkit.module.PluginModuleEditorsProvider"/>
<implicitUsageProvider implementation="org.jetbrains.idea.devkit.inspections.DevKitEntryPoints"/>
<psi.referenceContributor implementation="org.jetbrains.idea.devkit.dom.impl.InspectionsPropertiesReferenceProviderContributor"/>
<psi.referenceContributor implementation="org.jetbrains.idea.devkit.references.IconsReferencesContributor"/>
<referencesSearch implementation="org.jetbrains.idea.devkit.references.IconsReferencesContributor"/>
<unusedDeclarationFixProvider implementation="org.jetbrains.idea.devkit.inspections.quickfix.RegisterInspectionFixProvider"/>
</extensions>
@@ -0,0 +1,157 @@
/*
* 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 org.jetbrains.idea.devkit.references;
import com.intellij.find.FindModel;
import com.intellij.find.impl.FindInProjectUtil;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.patterns.*;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceUtil;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.ProcessingContext;
import com.intellij.util.Processor;
import com.intellij.util.QueryExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static com.intellij.patterns.PsiJavaPatterns.*;
/**
* @author Konstantin Bulenkov
*/
public class IconsReferencesContributor extends PsiReferenceContributor implements QueryExecutor<PsiReference, ReferencesSearch.SearchParameters> {
@Override
public void registerReferenceProviders(PsiReferenceRegistrar registrar) {
final StringPattern methodName = string().oneOf("findIcon", "getIcon");
final PsiMethodPattern method = psiMethod().withName(methodName).definedInClass(IconLoader.class.getName());
final PsiJavaElementPattern.Capture<PsiLiteralExpression> javaFile
= literalExpression().and(psiExpression().methodCallParameter(0, method));
final XmlAttributeValuePattern pluginXml = XmlPatterns.xmlAttributeValue().withLocalName("icon");
registrar.registerReferenceProvider(or(javaFile, pluginXml), new PsiReferenceProvider() {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull ProcessingContext context) {
if (!isIdeaProject(element.getProject())) return PsiReference.EMPTY_ARRAY;
return new FileReferenceSet(element) {
@Override
protected Collection<PsiFileSystemItem> getExtraContexts() {
final Module icons = ModuleManager.getInstance(element.getProject()).findModuleByName("icons");
if (icons != null) {
final ArrayList<PsiFileSystemItem> result = new ArrayList<PsiFileSystemItem>();
final VirtualFile[] roots = ModuleRootManager.getInstance(icons).getSourceRoots();
final PsiManager psiManager = element.getManager();
for (VirtualFile root : roots) {
final PsiDirectory directory = psiManager.findDirectory(root);
if (directory != null) {
result.add(directory);
}
}
return result;
}
return super.getExtraContexts();
}
}.getAllReferences();
}
});
}
public static boolean isIdeaProject(@Nullable Project project) {
final VirtualFile baseDir;
return project != null
&& ("IDEA".equals(project.getName()) || "community".equals(project.getName()))
&& (baseDir = project.getBaseDir()) != null
&& baseDir.findFileByRelativePath("plugins") != null;
}
@Override
public boolean execute(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull Processor<PsiReference> consumer) {
final PsiElement file = queryParameters.getElementToSearch();
if (file instanceof PsiBinaryFile) {
final Module module = ModuleUtil.findModuleForPsiElement(file);
final VirtualFile image = ((PsiBinaryFile)file).getVirtualFile();
if (isImage(image) && isIconsModule(module)) {
final Project project = file.getProject();
final FindModel model = new FindModel();
final String path = getPathToImage(image, module);
if (path == null) return true;
model.setStringToFind(path);
model.setCaseSensitive(true);
model.setFindAll(true);
final List<UsageInfo> usages = FindInProjectUtil.findUsages(model, FindInProjectUtil.getPsiDirectory(model, project), project, false);
if (!usages.isEmpty()) {
for (UsageInfo usage : usages) {
final PsiElement element = usage.getElement();
final ProperTextRange textRange = usage.getRangeInElement();
if (element != null && textRange != null) {
final PsiElement start = element.findElementAt(textRange.getStartOffset());
final PsiElement end = element.findElementAt(textRange.getEndOffset());
if (start != null && end != null) {
PsiElement value = PsiTreeUtil.findCommonParent(start, end);
if (value instanceof PsiJavaToken) {
value = value.getParent();
}
if (value != null) {
final FileReference reference = FileReferenceUtil.findFileReference(value);
if (reference != null) {
consumer.process(reference);
}
}
}
}
}
}
}
}
return true;
}
@Nullable
private static String getPathToImage(VirtualFile image, Module module) {
final String path = ModuleRootManager.getInstance(module).getSourceRoots()[0].getPath();
return "/" + FileUtil.getRelativePath(path, image.getPath(), '/');
}
private static boolean isIconsModule(Module module) {
return module != null && "icons".equals(module.getName())
&& ModuleRootManager.getInstance(module).getSourceRoots().length == 1;
}
private static boolean isImage(VirtualFile image) {
final FileTypeManager mgr = FileTypeManager.getInstance();
return image != null && mgr.getFileTypeByFile(image) == mgr.getFileTypeByExtension("png");
}
}
@@ -128,7 +128,7 @@ public class GroovyBuilder extends ModuleLevelBuilder {
private Map<Module, String> getGenerationOutputs(ModuleChunk chunk, Map<Module, String> finalOutputs) throws IOException {
Map<Module, String> generationOutputs = new HashMap<Module, String>();
for (Module module : chunk.getModules()) {
generationOutputs.put(module, myForStubs ? FileUtil.createTempDirectory("groovyStubs", module.getName()).getPath() : finalOutputs.get(module));
generationOutputs.put(module, myForStubs ? FileUtil.createTempDirectory("groovyStubs", "__" + module.getName()).getPath() : finalOutputs.get(module));
}
return generationOutputs;
}
@@ -16,33 +16,26 @@
package org.jetbrains.plugins.groovy.config;
import com.intellij.conversion.CannotConvertException;
import com.intellij.conversion.ConversionProcessor;
import com.intellij.conversion.DetachFacetConversionProcessor;
import com.intellij.conversion.ModuleSettings;
import com.intellij.facet.FacetManagerImpl;
import com.intellij.openapi.module.StdModuleTypes;
import org.jdom.Element;
import java.util.List;
/**
* @author peter
*/
public class GroovyModuleConverter extends ConversionProcessor<ModuleSettings> {
public class GroovyModuleConverter extends DetachFacetConversionProcessor {
public GroovyModuleConverter() {
super("Grails", "Groovy");
}
@Override
public boolean isConversionNeeded(ModuleSettings moduleSettings) {
if ("GRAILS_MODULE".equals(moduleSettings.getModuleType())) {
return true;
}
if (!moduleSettings.getFacetElements("Grails").isEmpty()) {
return true;
}
if (!moduleSettings.getFacetElements("Groovy").isEmpty()) {
return true;
}
return false;
return super.isConversionNeeded(moduleSettings);
}
@Override
@@ -51,20 +44,6 @@ public class GroovyModuleConverter extends ConversionProcessor<ModuleSettings> {
moduleSettings.setModuleType(StdModuleTypes.JAVA.getId());
}
final Element facetManagerElement = moduleSettings.getComponentElement(FacetManagerImpl.COMPONENT_NAME);
if (facetManagerElement == null) return;
final Element[] facetElements = getChildren(facetManagerElement, FacetManagerImpl.FACET_ELEMENT);
for (Element facetElement : facetElements) {
final String facetType = facetElement.getAttributeValue(FacetManagerImpl.TYPE_ATTRIBUTE);
if ("Grails".equals(facetType) || "Groovy".equals(facetType)) {
facetElement.detach();
}
}
}
private static Element[] getChildren(Element parent, final String name) {
final List<?> children = parent.getChildren(name);
return children.toArray(new Element[children.size()]);
super.process(moduleSettings);
}
}
@@ -66,7 +66,7 @@ public class ControlFlowBuilderUtil {
int[] invpostorder = invPostorder(postorder);
findReadsBeforeWrites(flow, definitelyAssigned, result, namesIndex, postorder, invpostorder);
if (result.size() == 0) return ReadWriteVariableInstruction.EMPTY_ARRAY;
return result.toArray(new ReadWriteVariableInstruction[result.size()]);
}
@@ -23,31 +23,28 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrInstanceOfExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ConditionInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.InstructionImpl;
/**
* @author peter
*/
public class InstanceOfInstruction extends InstructionImpl implements MixinTypeInstruction {
private final boolean myNegate;
private final ConditionInstruction myCondition;
public InstanceOfInstruction(int num, GrExpression assertion, boolean negate) {
public InstanceOfInstruction(int num, GrExpression assertion, ConditionInstruction cond) {
super(assertion, num);
myNegate = negate;
}
public boolean isNegate() {
return myNegate;
myCondition = cond;
}
protected String getElementPresentation() {
return "instanceof: " + (myNegate ? "! " : "") + getElement().getText();
return "instanceof: " + getElement().getText();
}
@Nullable
private GrInstanceOfExpression getApplicableInstanceof() {
final PsiElement element = getElement();
if (element instanceof GrInstanceOfExpression && !isNegate()) {
if (element instanceof GrInstanceOfExpression) {
GrExpression operand = ((GrInstanceOfExpression)element).getOperand();
final GrTypeElement typeElement = ((GrInstanceOfExpression)element).getTypeElement();
if (operand instanceof GrReferenceExpression && ((GrReferenceExpression)operand).getQualifier() == null && typeElement != null) {
@@ -85,4 +82,9 @@ public class InstanceOfInstruction extends InstructionImpl implements MixinTypeI
return instanceOf.getOperand().getText();
}
@Override
public ConditionInstruction getConditionInstruction() {
return myCondition;
}
}
@@ -30,6 +30,9 @@ public interface Instruction {
int num();
@Nullable
NegatingGotoInstruction getNegatingGotoInstruction();
@Nullable
PsiElement getElement();
@@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.controlFlow;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ConditionInstruction;
/**
* @author Max Medvedev
@@ -30,4 +31,7 @@ public interface MixinTypeInstruction extends Instruction {
@Nullable
String getVariableName();
@Nullable
ConditionInstruction getConditionInstruction();
}
@@ -0,0 +1,44 @@
/*
* 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 org.jetbrains.plugins.groovy.lang.psi.controlFlow;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ConditionInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.InstructionImpl;
/**
* @author Max Medvedev
*/
public class NegatingGotoInstruction extends InstructionImpl {
@NotNull private final ConditionInstruction myCondition;
public NegatingGotoInstruction(@Nullable PsiElement element, int num, @NotNull ConditionInstruction condition) {
super(element, num);
myCondition = condition;
}
@NotNull
public ConditionInstruction getCondition() {
return myCondition;
}
@Override
protected String getElementPresentation() {
return " Negating goto instruction, condition=" + myCondition.num();
}
}
@@ -23,6 +23,8 @@ import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.InstructionImpl;
* @author ven
*/
public class ReadWriteVariableInstruction extends InstructionImpl {
public static final ReadWriteVariableInstruction[] EMPTY_ARRAY = new ReadWriteVariableInstruction[0];
public static final int WRITE = -1;
public static final int READ = 1;
@@ -100,6 +100,11 @@ public class ArgumentInstruction extends InstructionImpl implements MixinTypeIns
return ((GrReferenceExpression)getElement()).getReferenceName();
}
@Override
public ConditionInstruction getConditionInstruction() {
return null;
}
@Override
protected String getElementPresentation() {
return "ARGUMENT " + super.getElementPresentation();
@@ -0,0 +1,48 @@
/*
* 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 org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import java.util.Set;
/**
* @author Max Medvedev
*/
public class ConditionInstruction extends InstructionImpl implements Instruction {
private final Set<ConditionInstruction> myDependent = new HashSet<ConditionInstruction>();
public ConditionInstruction(@NotNull PsiElement element, int num) {
super(element, num);
myDependent.add(this);
}
@Override
protected String getElementPresentation() {
return "Condition";
}
void addDependent(ConditionInstruction i) {
myDependent.add(i);
}
public Set<ConditionInstruction> getDependentConditions() {
return myDependent;
}
}
@@ -23,7 +23,6 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
@@ -31,7 +30,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection;
@@ -53,6 +51,7 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.*;
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
import static org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction.READ;
import static org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction.WRITE;
@@ -83,6 +82,17 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
*/
private Deque<ExceptionInfo> myCaughtExceptionInfos;
/**
* stack of current conditions
*/
private Deque<ConditionInstruction> myConditions;
/**
* stack of negating instructions
*/
private Deque<NegatingGotoInstruction> myNegatingStack;
/**
* count of finally blocks surrounding current statement
*/
@@ -92,8 +102,6 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
* last visited node
*/
private InstructionImpl myHead;
private boolean myNegate;
private boolean myAssertionsOnly;
private GroovyPsiElement myLastInScope;
/**
@@ -150,6 +158,9 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
myInstructions = new ArrayList<InstructionImpl>();
myProcessingStack = new ArrayDeque<InstructionImpl>();
myCaughtExceptionInfos = new ArrayDeque<ExceptionInfo>();
myConditions = new ArrayDeque<ConditionInstruction>();
myNegatingStack = new ArrayDeque<NegatingGotoInstruction>();
myFinallyCount = 0;
myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>();
myInstructionNumber = 0;
@@ -193,27 +204,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, WRITE));
}
final Set<String> names = new LinkedHashSet<String>();
closure.accept(new GroovyRecursiveElementVisitor() {
public void visitReferenceExpression(GrReferenceExpression refExpr) {
super.visitReferenceExpression(refExpr);
if (refExpr.getQualifierExpression() == null && !PsiUtil.isLValue(refExpr)) {
if (!(refExpr.getParent() instanceof GrCall)) {
final String refName = refExpr.getReferenceName();
if (!hasDeclaredVariable(refName, closure, refExpr)) {
//names.add(refName);
}
}
}
}
});
names.add("owner");
for (String name : names) {
addNode(new ReadWriteVariableInstruction(name, closure.getLBrace(), myInstructionNumber++, WRITE));
}
addNode(new ReadWriteVariableInstruction("owner", closure.getLBrace(), myInstructionNumber++, WRITE));
PsiElement child = closure.getFirstChild();
while (child != null) {
@@ -229,17 +220,20 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
}
}
private void addNode(InstructionImpl instruction) {
private <T extends InstructionImpl> T addNode(T instruction) {
myInstructions.add(instruction);
instruction.setNegating(myNegatingStack.peek());
if (myHead != null) {
addEdge(myHead, instruction);
}
myHead = instruction;
return instruction;
}
private void addNodeAndCheckPending(InstructionImpl i) {
private <T extends InstructionImpl> T addNodeAndCheckPending(T i) {
addNode(i);
checkPending(i);
return i;
}
private static void addEdge(InstructionImpl begin, InstructionImpl end) {
@@ -309,6 +303,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
public void visitAssertStatement(GrAssertStatement assertStatement) {
final GrExpression assertion = assertStatement.getAssertion();
if (assertion != null) {
myConditions.push(addNodeAndCheckPending(new ConditionInstruction(assertion, myInstructionNumber++)));
assertion.accept(this);
final InstructionImpl assertInstruction = startNode(assertStatement);
GrExpression errorMessage = assertStatement.getErrorMessage();
@@ -378,7 +373,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
public void visitAssignmentExpression(GrAssignmentExpression expression) {
GrExpression lValue = expression.getLValue();
if (expression.getOperationToken() != GroovyTokenTypes.mASSIGN) {
if (expression.getOperationToken() != mASSIGN) {
if (lValue instanceof GrReferenceExpression) {
String referenceName = ((GrReferenceExpression)lValue).getReferenceName();
if (referenceName != null) {
@@ -404,22 +399,29 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
public void visitUnaryExpression(GrUnaryExpression expression) {
final GrExpression operand = expression.getOperand();
if (operand != null) {
final boolean negation = expression.getOperationTokenType() == GroovyTokenTypes.mLNOT;
if (negation) {
myNegate = !myNegate;
ConditionInstruction cond = null;
if (expression.getOperationTokenType() == mLNOT) {
cond = new ConditionInstruction(expression, myInstructionNumber++);
addNodeAndCheckPending(cond);
registerCondition(cond);
}
operand.accept(this);
if (negation) {
myNegate = !myNegate;
}
visitCall(expression);
if (cond != null) {
myConditions.removeFirstOccurrence(cond);
myNegatingStack.push(addNodeAndCheckPending(new NegatingGotoInstruction(expression, myInstructionNumber++, cond)));
}
}
}
@Override
public void visitInstanceofExpression(GrInstanceOfExpression expression) {
expression.getOperand().accept(this);
addNode(new InstanceOfInstruction(myInstructionNumber++, expression, myNegate));
final ConditionInstruction cond = myConditions.peek();
if (cond != null) {
addNode(new InstanceOfInstruction(myInstructionNumber++, expression, cond));
}
}
public void visitReferenceExpression(GrReferenceExpression refExpr) {
@@ -428,14 +430,14 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
String name = refExpr.getReferenceName();
if (name == null) return;
if (ControlFlowUtils.isIncOrDecOperand(refExpr) && !myAssertionsOnly) {
if (ControlFlowUtils.isIncOrDecOperand(refExpr)) {
final InstructionImpl i = new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, READ);
addNodeAndCheckPending(i);
addNode(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, WRITE));
}
else {
boolean isWrite = !myAssertionsOnly && PsiUtil.isLValue(refExpr);
addNodeAndCheckPending(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, isWrite ? WRITE : READ));
final int type = PsiUtil.isLValue(refExpr) ? WRITE : READ;
addNodeAndCheckPending(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, type));
if (refExpr.getParent() instanceof GrArgumentList && refExpr.getParent().getParent() instanceof GrCall) {
addNodeAndCheckPending(new ArgumentInstruction(refExpr, myInstructionNumber++));
}
@@ -476,23 +478,41 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
final GrExpression right = expression.getRightOperand();
final IElementType opType = expression.getOperationTokenType();
InstructionImpl start = myHead;
final ConditionInstruction cond;
if (opType == mLOR || opType == mLAND) {
cond = addNodeAndCheckPending(new ConditionInstruction(expression, myInstructionNumber++));
registerCondition(cond);
}
else {
cond = null;
}
left.accept(this);
if (right != null) {
if (opType == GroovyTokenTypes.mLOR) {
addPendingEdge(expression, myHead);
myHead = start;
if (cond != null) {
myConditions.removeFirstOccurrence(cond);
}
myNegate = !myNegate;
left.accept(this);
myNegate = !myNegate;
NegatingGotoInstruction first = null;
if (right != null) {
if (cond != null) {
final InstructionImpl head = myHead;
if (opType == mLAND) {
first = addNodeAndCheckPending(new NegatingGotoInstruction(expression, myInstructionNumber++, cond));
}
addPendingEdge(expression, myHead);
myHead = head;
if (opType == mLOR) {
myNegatingStack.push(addNodeAndCheckPending(new NegatingGotoInstruction(expression, myInstructionNumber++, cond)));
}
}
right.accept(this);
}
visitCall(expression);
if (first != null) {
myNegatingStack.push(first);
}
}
/**
@@ -518,39 +538,65 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
public void visitIfStatement(GrIfStatement ifStatement) {
InstructionImpl ifInstruction = startNode(ifStatement);
final GrCondition condition = ifStatement.getCondition();
final InstructionImpl head = myHead;
final GrCondition condition = ifStatement.getCondition();
final GrStatement thenBranch = ifStatement.getThenBranch();
final GrStatement elseBranch = ifStatement.getElseBranch();
InstructionImpl conditionEnd = null;
InstructionImpl thenEnd = null;
if (thenBranch != null) {
if (condition != null) {
condition.accept(this);
InstructionImpl elseEnd = null;
List<NegatingGotoInstruction> pendingNegations = new ArrayList<NegatingGotoInstruction>();
ConditionInstruction conditionStart = null;
if (condition != null) {
final List<Pair<InstructionImpl, GroovyPsiElement>> oldPending = myPending;
myPending = new ArrayList<Pair<InstructionImpl, GroovyPsiElement>>();
conditionStart = addNodeAndCheckPending(new ConditionInstruction(condition, myInstructionNumber++));
registerCondition(conditionStart);
condition.accept(this);
conditionEnd = myHead;
List<Pair<InstructionImpl, GroovyPsiElement>> pendingFromCondition = myPending;
myPending = oldPending;
for (Pair<InstructionImpl, GroovyPsiElement> pair : pendingFromCondition) {
if (pair.first instanceof NegatingGotoInstruction) {
pendingNegations.add((NegatingGotoInstruction)pair.first);
}
else {
addPendingEdge(pair.second, pair.first);
}
}
}
if (thenBranch != null) {
thenBranch.accept(this);
handlePossibleReturn(thenBranch);
thenEnd = myHead;
interruptFlow();
}
myHead = head;
final GrStatement elseBranch = ifStatement.getElseBranch();
InstructionImpl elseEnd = null;
if (elseBranch != null) {
if (condition != null) {
myNegate = !myNegate;
final boolean old = myAssertionsOnly;
myAssertionsOnly = true;
condition.accept(this);
myNegate = !myNegate;
myAssertionsOnly = old;
}
if (condition != null) {
myHead = conditionEnd;
myNegatingStack.push(addNode(new NegatingGotoInstruction(condition, myInstructionNumber++, conditionStart)));
}
else {
myHead = head;
}
for (NegatingGotoInstruction negation : pendingNegations) {
assert condition != null;
addPendingEdge(condition, negation);
}
if (elseBranch != null) {
elseBranch.accept(this);
handlePossibleReturn(elseBranch);
elseEnd = myHead;
}
if (thenBranch != null || elseBranch != null) {
final InstructionImpl end = new IfEndInstruction(ifStatement, myInstructionNumber++);
addNode(end);
@@ -560,6 +606,13 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
finishNode(ifInstruction);
}
private void registerCondition(ConditionInstruction conditionStart) {
for (ConditionInstruction condition : myConditions) {
condition.addDependent(conditionStart);
}
myConditions.push(conditionStart);
}
public void visitForStatement(GrForStatement forStatement) {
final GrForClause clause = forStatement.getClause();
if (clause instanceof GrTraditionalForClause) {
@@ -963,31 +1016,4 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
return null;
}
private static boolean hasDeclaredVariable(String name, GrClosableBlock scope, PsiElement place) {
PsiElement prev = null;
while (place != null) {
if (place instanceof GrCodeBlock) {
GrStatement[] statements = ((GrCodeBlock)place).getStatements();
for (GrStatement statement : statements) {
if (statement == prev) break;
if (statement instanceof GrVariableDeclaration) {
GrVariable[] variables = ((GrVariableDeclaration)statement).getVariables();
for (GrVariable variable : variables) {
if (name.equals(variable.getName())) return true;
}
}
}
}
if (place == scope) {
break;
}
else {
prev = place;
place = place.getParent();
}
}
return false;
}
}
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.CallEnvironment;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.CallInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.NegatingGotoInstruction;
import java.util.Deque;
import java.util.LinkedHashSet;
@@ -33,6 +34,7 @@ public class InstructionImpl implements Instruction {
PsiElement myPsiElement;
private final int myNumber;
private NegatingGotoInstruction myNegating;
@Nullable
public PsiElement getElement() {
@@ -91,6 +93,11 @@ public class InstructionImpl implements Instruction {
return myNumber;
}
@Override
public NegatingGotoInstruction getNegatingGotoInstruction() {
return myNegating;
}
public void addSuccessor(InstructionImpl instruction) {
mySuccessors.add(instruction);
}
@@ -98,4 +105,8 @@ public class InstructionImpl implements Instruction {
public void addPredecessor(InstructionImpl instruction) {
myPredecessors.add(instruction);
}
void setNegating(NegatingGotoInstruction negating) {
myNegating = negating;
}
}
@@ -0,0 +1,154 @@
/*
* 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 org.jetbrains.plugins.groovy.lang.psi.dataFlow;
import com.intellij.psi.PsiIntersectionType;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.NegatingGotoInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ConditionInstruction;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
/**
* @author Max Medvedev
*/
public class DFAType {
private static class Mixin {
private final int ID;
private PsiType myType;
private ConditionInstruction myCondition;
private boolean myNegated;
private Mixin(PsiType type, ConditionInstruction condition, boolean negated) {
this(-1, type, condition, negated);
}
private Mixin(int ID, PsiType type, ConditionInstruction condition, boolean negated) {
if (ID == -1) ID = hashCode();
this.ID = ID;
myType = type;
myCondition = condition;
myNegated = negated;
}
Mixin negate() {
return new Mixin(ID, myType, myCondition, !myNegated);
}
}
private final PsiType primary;
private final List<Mixin> mixins = new ArrayList<Mixin>();
private DFAType(@Nullable PsiType primary) {
this.primary = primary;
}
public void addMixin(@Nullable PsiType mixin, ConditionInstruction instruction) {
mixins.add(new Mixin(mixin, instruction, false));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DFAType)) return false;
final DFAType other = (DFAType)obj;
if (!eq(primary, other.primary)) return false;
if (mixins.size() != other.mixins.size()) return false;
for (Mixin mixin1 : mixins) {
boolean contains = false;
for (Mixin mixin2 : other.mixins) {
if (mixin1.ID == mixin2.ID) {
contains = mixin1.myNegated == mixin2.myNegated;
break;
}
}
if (!contains) return false;
}
return true;
}
public DFAType negate(NegatingGotoInstruction negating) {
final DFAType type = new DFAType(primary);
for (Mixin mixin : mixins) {
type.mixins.add(mixin);
}
for (; negating != null; negating = negating.getNegatingGotoInstruction()) {
final Set<ConditionInstruction> conditionsToNegate = negating.getCondition().getDependentConditions();
for (ListIterator<Mixin> iterator = type.mixins.listIterator(); iterator.hasNext(); ) {
Mixin mixin = iterator.next();
if (conditionsToNegate.contains(mixin.myCondition)) {
iterator.set(mixin.negate());
}
}
}
return type;
}
@Nullable
public PsiType getType() {
if (mixins.size() == 0) return primary;
List<PsiType> types = new ArrayList<PsiType>();
if (primary != null) {
types.add(primary);
}
for (Mixin mixin : mixins) {
if (!mixin.myNegated) {
types.add(mixin.myType);
}
}
if (types.size() == 0) return null;
return PsiIntersectionType.createIntersection(types.toArray(new PsiType[types.size()]));
}
public static DFAType create(@Nullable PsiType type) {
return new DFAType(type);
}
private static boolean eq(PsiType t1, PsiType t2) {
return !TypeConversionUtil.erasure(t1).equals(TypeConversionUtil.erasure(t2));
}
@Nullable
public static DFAType create(DFAType t1, DFAType t2, PsiManager manager) {
final PsiType primary = TypesUtil.getLeastUpperBoundNullable(t1.primary, t2.primary, manager);
final DFAType type = new DFAType(primary);
for (Mixin mixin1 : t1.mixins) {
for (Mixin mixin2 : t2.mixins) {
if (mixin1.ID == mixin2.ID && mixin1.myNegated == mixin2.myNegated) {
type.mixins.add(mixin1);
}
}
}
return type;
}
}
@@ -21,7 +21,6 @@ import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiIntersectionType;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.*;
import gnu.trove.TIntHashSet;
@@ -35,12 +34,10 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.InstanceOfInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.MixinTypeInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.*;
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ArgumentInstruction;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAType;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsDfaInstance;
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsSemilattice;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
@@ -74,7 +71,9 @@ public class TypeInferenceHelper {
return getInitializerType(refExpr);
}
return getInferredType(refExpr.getReferenceName(), instruction, flow, scope);
final DFAType type = getInferredType(refExpr.getReferenceName(), instruction, flow, scope);
if (type == null) return null;
return type.getType();
}
});
@@ -89,11 +88,12 @@ public class TypeInferenceHelper {
Instruction instruction = findInstructionAt(place, flow);
if (instruction == null) return null;
return getInferredType(variableName, instruction, flow, scope);
final DFAType type = getInferredType(variableName, instruction, flow, scope);
return type != null ? type.getType() : null;
}
public static boolean isTooComplexTooAnalyze(GrControlFlowOwner scope) {
return getDefUseMaps(scope).second == null;
return getDefUseMaps(scope) == null;
}
@Nullable
@@ -134,7 +134,7 @@ public class TypeInferenceHelper {
}
@Nullable
private static PsiType getInferredType(@NotNull String varName, @NotNull Instruction instruction, @NotNull Instruction[] flow, @NotNull GrControlFlowOwner scope) {
private static DFAType getInferredType(@NotNull String varName, @NotNull Instruction instruction, @NotNull Instruction[] flow, @NotNull GrControlFlowOwner scope) {
final Pair<ReachingDefinitionsDfaInstance, List<TIntObjectHashMap<TIntHashSet>>> pair = getDefUseMaps(scope);
List<TIntObjectHashMap<TIntHashSet>> dfaResult = pair.second;
@@ -146,12 +146,17 @@ public class TypeInferenceHelper {
final TIntHashSet varDefs = allDefs.get(varIndex);
if (varDefs == null) return null;
PsiType result = null;
DFAType result = null;
for (int defIndex : varDefs.toArray()) {
PsiType defType = getDefinitionType(flow[defIndex], flow, scope);
DFAType defType = getDefinitionType(flow[defIndex], flow, scope);
final NegatingGotoInstruction negating = instruction.getNegatingGotoInstruction();
if (defType != null) {
defType = TypesUtil.boxPrimitiveType(defType, scope.getManager(), scope.getResolveScope());
result = result == null ? defType : TypesUtil.getLeastUpperBound(result, defType, scope.getManager());
defType = defType.negate(negating);
}
if (defType != null) {
result = result == null ? defType : DFAType.create(defType, result, scope.getManager());
}
}
return result;
@@ -165,10 +170,10 @@ public class TypeInferenceHelper {
final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow) {
@Override
public void fun(TIntObjectHashMap<TIntHashSet> m, Instruction instruction) {
if (instruction instanceof InstanceOfInstruction) { //todo assertions are not defs, they just add to type intersection and don't overwrite it completely
if (instruction instanceof InstanceOfInstruction) {
final InstanceOfInstruction instanceOfInstruction = (InstanceOfInstruction)instruction;
final PsiElement element = instanceOfInstruction.getElement();
if (element instanceof GrInstanceOfExpression && !instanceOfInstruction.isNegate()) {
if (element instanceof GrInstanceOfExpression) {
final GrExpression operand = ((GrInstanceOfExpression)element).getOperand();
final GrTypeElement typeElement = ((GrInstanceOfExpression)element).getTypeElement();
if (typeElement != null) {
@@ -197,11 +202,11 @@ public class TypeInferenceHelper {
}
@Nullable
private static PsiType getDefinitionType(Instruction instruction, Instruction[] flow, GrControlFlowOwner scope) {
private static DFAType getDefinitionType(Instruction instruction, Instruction[] flow, GrControlFlowOwner scope) {
if (instruction instanceof ReadWriteVariableInstruction && ((ReadWriteVariableInstruction) instruction).isWrite()) {
final PsiElement element = instruction.getElement();
if (element != null) {
return getInitializerType(element);
return DFAType.create(TypesUtil.boxPrimitiveType(getInitializerType(element), scope.getManager(), scope.getResolveScope()));
}
}
if (instruction instanceof MixinTypeInstruction) {
@@ -211,21 +216,24 @@ public class TypeInferenceHelper {
}
@Nullable
private static PsiType mixinType(final MixinTypeInstruction instruction, final Instruction[] flow, final GrControlFlowOwner scope) {
return RecursionManager.doPreventingRecursion(instruction, false, new NullableComputable<PsiType>() {
private static DFAType mixinType(final MixinTypeInstruction instruction, final Instruction[] flow, final GrControlFlowOwner scope) {
return RecursionManager.doPreventingRecursion(instruction, false, new NullableComputable<DFAType>() {
@Override
@Nullable
public PsiType compute() {
public DFAType compute() {
String varName = instruction.getVariableName();
if (varName == null) return null;
ReadWriteVariableInstruction originalInstr = instruction.getInstructionToMixin(flow);
LOG.assertTrue(originalInstr != null, scope.getContainingFile().getName() + ":" + scope.getText());
final PsiType original = getInferredType(varName, originalInstr, flow, scope);
DFAType original = getInferredType(varName, originalInstr, flow, scope);
final PsiType mixin = instruction.inferMixinType();
if (mixin == null) return original;
if (original == null) return mixin;
if (TypesUtil.isAssignableByMethodCallConversion(mixin, original, scope)) return original;
return PsiIntersectionType.createIntersection(mixin, original);
if (original == null) {
original = DFAType.create(null);
}
original.addMixin(mixin, instruction.getConditionInstruction());
return original;
}
});
}
@@ -291,4 +299,32 @@ public class TypeInferenceHelper {
return null;
}
/*@Nullable
private static PsiType getInferredType(@NotNull String varName,
@NotNull Instruction instruction,
@NotNull GrControlFlowOwner scope) {
final ArrayList<Map<String, DFAType>> dfaResult = getDefUseMaps(scope);
if (dfaResult == null) return null;
final Map<String, DFAType> allDefs = dfaResult.get(instruction.num());
final DFAType dfaType = allDefs.get(varName);
if (dfaType == null) return null;
return dfaType.getType();
}
private static ArrayList<Map<String, DFAType>> getDefUseMaps(final GrControlFlowOwner scope) {
return CachedValuesManager.getManager(scope.getProject()).getCachedValue(scope, new CachedValueProvider<ArrayList<Map<String, DFAType>>>() {
@Override
public Result<ArrayList<Map<String, DFAType>>> compute() {
final Instruction[] flow = scope.getControlFlow();
final DFAEngine<Map<String, DFAType>> engine = new DFAEngine<Map<String, DFAType>>(flow, new TypeDFAInstance(), new TypesSemilattice(scope.getManager()));
final ArrayList<Map<String, DFAType>> result = engine.performDFAWithTimeout();
return Result.create(result, PsiModificationTracker.MODIFICATION_COUNT);
}
});
}*/
}
@@ -478,10 +478,66 @@ use(Cat){
''', "$JAVA_UTIL_ARRAY_LIST<$JAVA_UTIL_ARRAY_LIST<$JAVA_LANG_INTEGER>>")
}
void testInstanceOfInferring1() {
doTest('''\
def bar(oo) {
boolean b = oo instanceof String || oo != null
o<caret>o
}
''', null)
}
void testInstanceOfInferring2() {
doTest('''\
def bar(oo) {
boolean b = oo instanceof String || o<caret>o != null
oo
}
''', null)
}
void testInstanceOfInferring3() {
doTest('''\
def bar(oo) {
boolean b = oo instanceof String && o<caret>o != null
oo
}
''', String.canonicalName)
}
void testInstanceOfInferring4() {
doTest('''\
def bar(oo) {
boolean b = oo instanceof String && oo != null
o<caret>o
}
''', null)
}
void testInstanceOfInferring5() {
doTest('''\
def foo(def oo) {
if (oo instanceof String && oo instanceof CharSequence) {
oo
}
else {
o<caret>o
}
}
''', null)
}
private void doTest(String text, String type) {
def file = myFixture.configureByText('_.groovy', text)
def ref = file.findReferenceAt(myFixture.editor.caretModel.offset) as GrReferenceExpression
def actual = ref.type
if (type == null) {
assertNull(actual)
return
}
assertNotNull(actual)
if (actual instanceof PsiIntersectionType) {
assertEquals(type, genIntersectionTypeText(actual))
}
+13 -11
View File
@@ -5,14 +5,16 @@ println blah
-----
0(1) element: null
1(2) WRITE blah
2(3,5) element: IF statement
3(4) READ blah
4(5) WRITE blah
5(6) element: IF statement
6(7) READ blah
7(8) WRITE blah
8(9) READ println
9(10) READ blah
10(11) ARGUMENT element: Reference expression
11(12) element: Call expression MAYBE_RETURN
12() element: null
2(3) element: IF statement
3(4,6) Condition
4(5) READ blah
5(7) WRITE blah
6(7) Negating goto instruction, condition=3
7(8) element: IF statement
8(9) READ blah
9(10) WRITE blah
10(11) READ println
11(12) READ blah
12(13) ARGUMENT element: Reference expression
13(14) element: Call expression MAYBE_RETURN
14() element: null
+9 -7
View File
@@ -5,10 +5,12 @@ if (true) {
a = 2
-----
0(1) element: null
1(2,4) element: IF statement
2(3) WRITE a
3(6) element: Assignment expression MAYBE_RETURN
4(5) WRITE a
5(6) element: Assignment expression MAYBE_RETURN
6(7) element: IF statement
7() element: null
1(2) element: IF statement
2(3,5) Condition
3(4) WRITE a
4(8) element: Assignment expression MAYBE_RETURN
5(6) Negating goto instruction, condition=2
6(7) WRITE a
7(8) element: Assignment expression MAYBE_RETURN
8(9) element: IF statement
9() element: null
@@ -3,22 +3,24 @@ else if (!(o instanceof Integer)) b = 2
else b = 3
-----
0(1) element: null
1(2,6) element: IF statement
2(3) READ o
3(4) instanceof: o instanceof String
4(5) WRITE b
5(18) element: Assignment expression MAYBE_RETURN
6(7) READ o
7(8) instanceof: ! o instanceof String
8(9,13) element: IF statement
9(10) READ o
10(11) instanceof: ! o instanceof Integer
11(12) WRITE b
12(17) element: Assignment expression MAYBE_RETURN
13(14) READ o
14(15) instanceof: o instanceof Integer
15(16) WRITE b
16(17) element: Assignment expression MAYBE_RETURN
17(18) element: IF statement
18(19) element: IF statement
19() element: null
1(2) element: IF statement
2(3) Condition
3(4) READ o
4(5,7) instanceof: o instanceof String
5(6) WRITE b
6(20) element: Assignment expression MAYBE_RETURN
7(8) Negating goto instruction, condition=2
8(9) element: IF statement
9(10) Condition
10(11) Condition
11(12) READ o
12(13) instanceof: o instanceof Integer
13(14,16) Negating goto instruction, condition=10
14(15) WRITE b
15(19) element: Assignment expression MAYBE_RETURN
16(17) Negating goto instruction, condition=9
17(18) WRITE b
18(19) element: Assignment expression MAYBE_RETURN
19(20) element: IF statement
20(21) element: IF statement
21() element: null
+9 -7
View File
@@ -9,14 +9,16 @@ for (e in [1,2,3,4]) {
0(1) element: null
1(2) element: Labeled statement
2(3) WRITE e
3(4,13) element: For statement
3(4,15) element: For statement
4(5) element: Block statement
5(6) WRITE ee
6(7,3) element: For statement
7(8) element: Block statement
8(1,9) element: IF statement
9(10) element: IF statement
10(11) READ print
11(12) READ e
12(6) READ ee
13() element: null
8(9) element: IF statement
9(1,10) Condition
10(11) Negating goto instruction, condition=9
11(12) element: IF statement
12(13) READ print
13(14) READ e
14(6) READ ee
15() element: null
+9 -7
View File
@@ -1,10 +1,12 @@
if (true) return a else return b
-----
0(1) element: null
1(2,4) element: IF statement
2(3) READ a
3(7) element: RETURN statement
4(5) READ b
5(7) element: RETURN statement
6(7) element: IF statement
7() element: null
1(2) element: IF statement
2(3,5) Condition
3(4) READ a
4(9) element: RETURN statement
5(6) Negating goto instruction, condition=2
6(7) READ b
7(9) element: RETURN statement
8(9) element: IF statement
9() element: null
+18 -16
View File
@@ -7,19 +7,21 @@ print e
-----
0(1) element: null
1(2) element: Open block
2(3,5) element: IF statement
3(4) READ c
4(7) element: RETURN statement
5(9) element: IF statement
6(11) element: Finally clause
7(6,8) CALL 6
8(17) AFTER CALL 7
9(6,10) CALL 6
10(13) AFTER CALL 9
11(12) READ e
12(8,10) RETURN
13(14) READ print
14(15) READ e
15(16) ARGUMENT element: Reference expression
16(17) element: Call expression MAYBE_RETURN
17() element: null
2(3) element: IF statement
3(4) Condition
4(5,6) READ c
5(9) element: RETURN statement
6(7) Negating goto instruction, condition=3
7(11) element: IF statement
8(13) element: Finally clause
9(8,10) CALL 8
10(19) AFTER CALL 9
11(8,12) CALL 8
12(15) AFTER CALL 11
13(14) READ e
14(10,12) RETURN
15(16) READ print
16(17) READ e
17(18) ARGUMENT element: Reference expression
18(19) element: Call expression MAYBE_RETURN
19() element: null
+10 -8
View File
@@ -10,13 +10,15 @@ cde
1(3) element: Open block
2(5) element: Finally clause
3(2,4) CALL 2
4(11) AFTER CALL 3
4(13) AFTER CALL 3
5(6) element: For statement
6(7) element: Block statement
7(8,9) element: IF statement
8(10) READ abc
9(5) element: IF statement
10(4) RETURN
11(12) READ cde
12(13) element: Reference expression MAYBE_RETURN
13() element: null
7(8) element: IF statement
8(9) Condition
9(10,12) READ abc
10(11) Negating goto instruction, condition=8
11(5) element: IF statement
12(4) RETURN
13(14) READ cde
14(15) element: Reference expression MAYBE_RETURN
15() element: null
+18 -16
View File
@@ -11,19 +11,21 @@ finally {
fScript.previewTask(taskName)
-----
0(1) element: null
1(2,5) element: IF statement
2(3) READ ddd
3(4) READ fXRec
4(16) element: RETURN statement
5(6) element: IF statement
6(7) WRITE fScript
7(9) element: Open block
8(11) element: Finally clause
9(8,10) CALL 8
10(12) AFTER CALL 9
11(10) RETURN
12(13) READ fScript
13(14) READ taskName
14(15) ARGUMENT element: Reference expression
15(16) element: Method call MAYBE_RETURN
16() element: null
1(2) element: IF statement
2(3) Condition
3(4,6) READ ddd
4(5) READ fXRec
5(18) element: RETURN statement
6(7) Negating goto instruction, condition=2
7(8) element: IF statement
8(9) WRITE fScript
9(11) element: Open block
10(13) element: Finally clause
11(10,12) CALL 10
12(14) AFTER CALL 11
13(12) RETURN
14(15) READ fScript
15(16) READ taskName
16(17) ARGUMENT element: Reference expression
17(18) element: Method call MAYBE_RETURN
18() element: null
+15 -13
View File
@@ -9,16 +9,18 @@ if (url == null) {
return url
-----
0(1) element: null
1(2,10) element: IF statement
2(3) READ url
3(4) element: Open block
4(11) WRITE url
5(6) element: Catch clause
6(7) WRITE e
7(8) READ e
8(9) ARGUMENT element: Reference expression
9(13) THROW. element: THROW statement
10(11) element: IF statement
11(12) READ url
12(13) element: RETURN statement
13() element: null
1(2) element: IF statement
2(3) Condition
3(4,11) READ url
4(5) element: Open block
5(13) WRITE url
6(7) element: Catch clause
7(8) WRITE e
8(9) READ e
9(10) ARGUMENT element: Reference expression
10(15) THROW. element: THROW statement
11(12) Negating goto instruction, condition=2
12(13) element: IF statement
13(14) READ url
14(15) element: RETURN statement
15() element: null
+8 -6
View File
@@ -7,9 +7,11 @@ while (true) {
0(1) element: null
1(2) WRITE i
2(3) element: WHILE statement
3(4,5) element: IF statement
4(8) READ i
5(6) element: IF statement
6(7) READ i
7(2) WRITE i
8() element: null
3(4) element: IF statement
4(5) Condition
5(6,10) READ i
6(7) Negating goto instruction, condition=4
7(8) element: IF statement
8(9) READ i
9(2) WRITE i
10() element: null
+8 -6
View File
@@ -7,9 +7,11 @@ while (true) {
0(1) element: null
1(2) WRITE i
2(3) element: WHILE statement
3(4,5) element: IF statement
4(2) READ i
5(6) element: IF statement
6(7) READ i
7(2) WRITE j
8() element: null
3(4) element: IF statement
4(5) Condition
5(2,6) READ i
6(7) Negating goto instruction, condition=4
7(8) element: IF statement
8(9) READ i
9(2) WRITE j
10() element: null
@@ -7,10 +7,12 @@ while (condition()) {
0(1) element: null
1(2) WRITE i
2(3) element: WHILE statement
3(4,9) READ condition
4(5,6) element: IF statement
5(9) READ i
6(7) element: IF statement
7(8) READ i
8(2) WRITE i
9() element: null
3(4,11) READ condition
4(5) element: IF statement
5(6) Condition
6(7,11) READ i
7(8) Negating goto instruction, condition=5
8(9) element: IF statement
9(10) READ i
10(2) WRITE i
11() element: null