mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
+34
-56
@@ -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));
|
||||
}
|
||||
|
||||
+13
-8
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -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,
|
||||
|
||||
+17
-1
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 &all processes"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
|
||||
+36
-10
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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()]);
|
||||
}
|
||||
|
||||
|
||||
+11
-9
@@ -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();
|
||||
|
||||
|
||||
+4
@@ -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();
|
||||
}
|
||||
|
||||
+44
@@ -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();
|
||||
}
|
||||
}
|
||||
+2
@@ -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;
|
||||
|
||||
|
||||
+5
@@ -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();
|
||||
|
||||
+48
@@ -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;
|
||||
}
|
||||
}
|
||||
+116
-90
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -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;
|
||||
}
|
||||
}
|
||||
+60
-24
@@ -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);
|
||||
}
|
||||
});
|
||||
}*/
|
||||
}
|
||||
|
||||
+56
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
Reference in New Issue
Block a user