find usages & validation in project structure reworked

This commit is contained in:
nik
2009-10-14 09:49:05 +04:00
parent fd00e86984
commit 9d0148ef0b
45 changed files with 1446 additions and 653 deletions
@@ -45,7 +45,7 @@ public interface ArtifactEditorContext extends PackagingElementResolvingContext
CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact);
void ensureRootIsWritable(@NotNull Artifact artifact);
void editLayout(@NotNull Artifact artifact, Runnable runnable);
ArtifactEditor getOrCreateEditor(Artifact originalArtifact);
@@ -38,6 +38,10 @@ import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryTableEdi
import com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.SdkProjectStructureElement;
import com.intellij.openapi.roots.ui.util.CellAppearance;
import com.intellij.openapi.roots.ui.util.OrderEntryCellAppearanceUtils;
import com.intellij.openapi.ui.Messages;
@@ -330,12 +334,13 @@ public class ClasspathPanel extends JPanel {
if (orderEntry == null) {
continue;
}
ModuleStructureConfigurable.getInstance(myProject).getContext().clearCaches(orderEntry);
myRootModel.removeOrderEntry(orderEntry);
}
final int[] selectedRows = myEntryTable.getSelectedRows();
myModel.fireTableDataChanged();
TableUtil.selectRows(myEntryTable, selectedRows);
final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myProject).getContext();
context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, myRootModel.getModule()));
}
});
@@ -480,7 +485,6 @@ public class ClasspathPanel extends JPanel {
final TableItem tableItem = createTableItem(item);
if ( tableItem != null ) {
myModel.addItem(tableItem);
rootConfigurable.getContext().clearCaches(tableItem.getEntry());
}
}
myModel.fireTableDataChanged();
@@ -489,6 +493,8 @@ public class ClasspathPanel extends JPanel {
selectionModel.setSelectionInterval(myModel.getRowCount() - chosen.size(), myModel.getRowCount() - 1);
TableUtil.scrollSelectionToVisible(myEntryTable);
final StructureConfigurableContext context = rootConfigurable.getContext();
context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, myRootModel.getModule()));
}
finally {
if (dialog instanceof ChooseNamedLibraryAction.MyChooserDialog) {
@@ -1151,7 +1157,6 @@ public class ClasspathPanel extends JPanel {
chosen.removeAll(getAlreadyAddedLibraries());
final Module module = myRootModel.getModule();
final Project project = module.getProject();
ModuleStructureConfigurable.getInstance(project).getContext().clearCaches(module, chosen);
return chosen;
}
@@ -1175,29 +1180,38 @@ public class ClasspathPanel extends JPanel {
}
protected boolean isEnabled() {
return getSelectedObject() != null;
return getSelectedElement() != null;
}
protected Object getSelectedObject() {
protected ProjectStructureElement getSelectedElement() {
int row = myEntryTable.getSelectedRow();
if (0 <= row && row < myModel.getRowCount()) {
TableItem item = myModel.getItemAt(row);
if (item instanceof LibItem) {
LibraryOrderEntry orderEntry = ((LibItem)item).getEntry();
if (orderEntry != null) {
return orderEntry.getLibrary();
final Library library = orderEntry.getLibrary();
if (library != null) {
return new LibraryProjectStructureElement(getContext(), library);
}
}
}
else if (item instanceof ModuleItem) {
ModuleOrderEntry orderEntry = ((ModuleItem)item).getEntry();
if (orderEntry != null) {
return orderEntry.getModule();
final Module module = orderEntry.getModule();
if (module != null) {
return new ModuleProjectStructureElement(getContext(), module);
}
}
}
else if (item instanceof JdkItem) {
JdkOrderEntry orderEntry = ((JdkItem)item).getEntry();
if (orderEntry != null) {
return orderEntry.getJdk();
final Sdk jdk = orderEntry.getJdk();
if (jdk != null) {
return new SdkProjectStructureElement(getContext(), jdk);
}
}
}
}
@@ -25,6 +25,8 @@ import com.intellij.openapi.projectRoots.SdkModel;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectJdksModel;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
@@ -155,7 +157,8 @@ public class ModuleJdkConfigurable implements Disposable {
private void clearCaches(final Sdk oldJdk, final Sdk selectedModuleJdk) {
final Module module = myRootModel.getModule();
final Project project = module.getProject();
ModuleStructureConfigurable.getInstance(project).getContext().clearCaches(module, oldJdk, selectedModuleJdk);
final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(project).getContext();
context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, module));
}
public void reset() {
@@ -40,6 +40,7 @@ import com.intellij.openapi.roots.ui.configuration.actions.ModuleDeleteProvider;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectJdksModel;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
@@ -487,7 +488,7 @@ public class ModulesConfigurator implements ModulesProvider, ModuleEditor.Change
if (module == moduleEditor.getModule() && Comparing.strEqual(moduleEditor.getName(), oldName)) {
moduleEditor.setModuleName(name);
moduleEditor.updateCompilerOutputPathChanged(ProjectStructureConfigurable.getInstance(myProject).getProjectConfig().getCompilerOutputUrl(), name);
myContext.invalidateModuleName(moduleEditor.getModule());
myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, module), true, false);
return;
}
}
@@ -26,6 +26,8 @@ import com.intellij.openapi.projectRoots.SdkModel;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectJdksModel;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import org.jetbrains.annotations.Nullable;
@@ -91,7 +93,7 @@ public class ProjectJdkConfigurable implements UnnamedConfigurable {
myCbProjectJdk.setSelectedJdk(jdk);
} else {
myCbProjectJdk.setInvalidJdk(sdkName);
clearCaches(null);
clearCaches();
}
} else {
myCbProjectJdk.setSelectedJdk(null);
@@ -108,7 +110,7 @@ public class ProjectJdkConfigurable implements UnnamedConfigurable {
if (myFreeze) return;
final Sdk oldJdk = myJdksModel.getProjectJdk();
myJdksModel.setProjectJdk(myCbProjectJdk.getSelectedJdk());
clearCaches(oldJdk);
clearCaches();
}
});
myJdkPanel.add(new JLabel(ProjectBundle.message("module.libraries.target.jdk.project.radio")), new GridBagConstraints(0, 0, 3, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 4, 0), 0, 0));
@@ -123,11 +125,12 @@ public class ProjectJdkConfigurable implements UnnamedConfigurable {
});
}
private void clearCaches(final Sdk oldJdk) {
private void clearCaches() {
final ModuleStructureConfigurable rootConfigurable = ModuleStructureConfigurable.getInstance(myProject);
Module[] modules = rootConfigurable.getModules();
for (Module module : modules) {
rootConfigurable.getContext().clearCaches(module, oldJdk, getSelectedProjectJdk());
final StructureConfigurableContext context = rootConfigurable.getContext();
context.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(context, module));
}
}
@@ -89,7 +89,7 @@ public class ProjectJdksConfigurable extends MasterDetailsComponent implements C
myRoot.removeAllChildren();
final HashMap<Sdk, Sdk> sdks = myProjectJdksModel.getProjectJdks();
for (Sdk sdk : sdks.keySet()) {
final JdkConfigurable configurable = new JdkConfigurable((ProjectJdkImpl)sdks.get(sdk), myProjectJdksModel, TREE_UPDATER, myHistory);
final JdkConfigurable configurable = new JdkConfigurable((ProjectJdkImpl)sdks.get(sdk), myProjectJdksModel, TREE_UPDATER, myHistory, myProject);
addNode(new MyNode(configurable), myRoot);
}
selectJdk(myProjectJdksModel.getProjectJdk()); //restore selection
@@ -153,7 +153,7 @@ public class ProjectJdksConfigurable extends MasterDetailsComponent implements C
group.getTemplatePresentation().setIcon(Icons.ADD_ICON);
myProjectJdksModel.createAddActions(group, myTree, new Consumer<Sdk>() {
public void consume(final Sdk projectJdk) {
addNode(new MyNode(new JdkConfigurable(((ProjectJdkImpl)projectJdk), myProjectJdksModel, TREE_UPDATER, myHistory), false), myRoot);
addNode(new MyNode(new JdkConfigurable(((ProjectJdkImpl)projectJdk), myProjectJdksModel, TREE_UPDATER, myHistory, myProject), false), myRoot);
selectNodeInTree(findNodeByObject(myRoot, projectJdk));
}
});
@@ -111,12 +111,13 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se
myArtifactsStructureConfigurable = artifactsStructureConfigurable;
myModuleConfigurator = new ModulesConfigurator(myProject, myProjectJdksModel);
myContext = new StructureConfigurableContext(myProject, myModuleManager, myModuleConfigurator);
myContext = new StructureConfigurableContext(myProject, myModuleConfigurator);
myModuleConfigurator.setContext(myContext);
myProjectLibrariesConfig = projectLibrariesConfigurable;
myGlobalLibrariesConfig = globalLibrariesConfigurable;
myModulesConfig = moduleStructureConfigurable;
myProjectLibrariesConfig.init(myContext);
myGlobalLibrariesConfig.init(myContext);
myModulesConfig.init(myContext);
@@ -226,8 +227,10 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se
}
private void addJdkListConfig() {
myJdkListConfig = JdkListConfigurable.getInstance(myProject);
myJdkListConfig.init(myContext);
if (myJdkListConfig == null) {
myJdkListConfig = JdkListConfigurable.getInstance(myProject);
myJdkListConfig.init(myContext);
}
addConfigurable(myJdkListConfig);
}
@@ -265,18 +268,13 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se
}
//cleanup
myContext.myUpdateDependenciesAlarm.cancelAllRequests();
myContext.myUpdateDependenciesAlarm.addRequest(new Runnable(){
myContext.getDaemonAnalyzer().clearCaches();
SwingUtilities.invokeLater(new Runnable(){
public void run() {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
if (myWasUiDisposed) return;
reset();
}
});
if (myWasUiDisposed) return;
reset();
}
}, 0);
});
}
public void reset() {
@@ -323,7 +321,7 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se
myUiState.proportion = mySplitter.getProportion();
saveSideProportion();
myContext.getDaemonAnalyzer().stop();
for (Configurable each : myName2Config) {
each.disposeUIResources();
}
@@ -17,8 +17,10 @@ package com.intellij.openapi.roots.ui.configuration.artifacts;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectStructureElementConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.util.Comparing;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.artifacts.ArtifactType;
@@ -34,17 +36,20 @@ import java.awt.event.ActionListener;
/**
* @author nik
*/
public class ArtifactConfigurable extends NamedConfigurable<Artifact> {
public class ArtifactConfigurable extends ProjectStructureElementConfigurable<Artifact> {
private final Artifact myOriginalArtifact;
private final ArtifactsStructureConfigurableContext myArtifactsStructureContext;
private final ArtifactEditorImpl myEditor;
private boolean myIsInUpdateName;
private ProjectStructureElement myProjectStructureElement;
public ArtifactConfigurable(Artifact originalArtifact, ArtifactsStructureConfigurableContextImpl artifactsStructureContext, final Runnable updateTree) {
public ArtifactConfigurable(Artifact originalArtifact, ArtifactsStructureConfigurableContextImpl artifactsStructureContext, final Runnable updateTree,
StructureConfigurableContext context) {
super(true, updateTree);
myOriginalArtifact = originalArtifact;
myArtifactsStructureContext = artifactsStructureContext;
myEditor = artifactsStructureContext.getOrCreateEditor(originalArtifact);
myProjectStructureElement = new ArtifactProjectStructureElement(context, myArtifactsStructureContext, myOriginalArtifact);
}
public void setDisplayName(String name) {
@@ -55,6 +60,11 @@ public class ArtifactConfigurable extends NamedConfigurable<Artifact> {
}
}
@Override
public ProjectStructureElement getProjectStructureElement() {
return myProjectStructureElement;
}
@Override
public void updateName() {
myIsInUpdateName = true;
@@ -15,14 +15,21 @@
*/
package com.intellij.openapi.roots.ui.configuration.artifacts;
import com.intellij.facet.Facet;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootModel;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.impl.ModuleLibraryOrderEntryImpl;
import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.ui.configuration.FacetsProvider;
import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.packaging.ChooseLibrariesDialog;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.ChooseModulesDialog;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ui.configuration.packaging.ChooseLibrariesDialog;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.artifacts.ArtifactModel;
import com.intellij.packaging.artifacts.ArtifactType;
@@ -31,11 +38,10 @@ import com.intellij.packaging.elements.CompositePackagingElement;
import com.intellij.packaging.ui.ArtifactEditor;
import com.intellij.packaging.ui.ArtifactEditorContext;
import com.intellij.packaging.ui.ManifestFileConfiguration;
import com.intellij.facet.Facet;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Collections;
import java.util.List;
/**
* @author nik
@@ -69,8 +75,8 @@ public class ArtifactEditorContextImpl implements ArtifactEditorContext {
return myParent.getRootElement(artifact);
}
public void ensureRootIsWritable(@NotNull Artifact artifact) {
myParent.ensureRootIsWritable(artifact);
public void editLayout(@NotNull Artifact artifact, Runnable runnable) {
myParent.editLayout(artifact, runnable);
}
public ArtifactEditor getOrCreateEditor(Artifact artifact) {
@@ -90,7 +96,26 @@ public class ArtifactEditorContextImpl implements ArtifactEditorContext {
}
public void selectLibrary(@NotNull Library library) {
ProjectStructureConfigurable.getInstance(getProject()).selectProjectOrGlobalLibrary(library, true);
final LibraryTable table = library.getTable();
if (table != null) {
ProjectStructureConfigurable.getInstance(getProject()).selectProjectOrGlobalLibrary(library, true);
}
else {
final Module module = ((LibraryImpl)library).getModule();
if (module != null) {
final ModuleRootModel rootModel = myParent.getModulesProvider().getRootModel(module);
final String libraryName = library.getName();
for (OrderEntry entry : rootModel.getOrderEntries()) {
if (entry instanceof ModuleLibraryOrderEntryImpl) {
final ModuleLibraryOrderEntryImpl libraryEntry = (ModuleLibraryOrderEntryImpl)entry;
if (libraryName.equals(libraryEntry.getLibraryName())) {
ModuleStructureConfigurable.getInstance(getProject()).selectOrderEntry(module, libraryEntry);
return;
}
}
}
}
}
}
public List<Artifact> chooseArtifacts(final List<? extends Artifact> artifacts, final String title) {
@@ -339,7 +339,7 @@ public class ArtifactEditorImpl implements ArtifactEditorEx {
return myLayoutTreeComponent;
}
public void updateOutputPath(@NotNull String oldArtifactName, @NotNull String newArtifactName) {
public void updateOutputPath(@NotNull String oldArtifactName, @NotNull final String newArtifactName) {
final String oldDefaultPath = ArtifactUtil.getDefaultArtifactOutputPath(oldArtifactName, myProject);
if (Comparing.equal(oldDefaultPath, getConfiguredOutputPath())) {
setOutputPath(ArtifactUtil.getDefaultArtifactOutputPath(newArtifactName, myProject));
@@ -349,8 +349,11 @@ public class ArtifactEditorImpl implements ArtifactEditorEx {
final String fileName = FileUtil.getNameWithoutExtension(name);
final String extension = FileUtil.getExtension(name);
if (fileName.equals(oldArtifactName) && extension.length() > 0) {
myLayoutTreeComponent.ensureRootIsWritable();
((ArchivePackagingElement)getRootElement()).setArchiveFileName(newArtifactName + "." + extension);
myLayoutTreeComponent.editLayout(new Runnable() {
public void run() {
((ArchivePackagingElement)getRootElement()).setArchiveFileName(newArtifactName + "." + extension);
}
});
myLayoutTreeComponent.updateTreeNodesPresentation();
}
}
@@ -0,0 +1,102 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.roots.ui.configuration.artifacts;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.*;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.elements.CompositePackagingElement;
import com.intellij.packaging.elements.PackagingElement;
import com.intellij.packaging.impl.artifacts.ArtifactUtil;
import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
import com.intellij.packaging.impl.elements.LibraryPackagingElement;
import com.intellij.packaging.impl.elements.ModuleOutputPackagingElement;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* @author nik
*/
public class ArtifactProjectStructureElement extends ProjectStructureElement {
private final ArtifactsStructureConfigurableContext myArtifactsStructureContext;
private final Artifact myOriginalArtifact;
public ArtifactProjectStructureElement(StructureConfigurableContext context,
ArtifactsStructureConfigurableContext artifactsStructureContext, Artifact originalArtifact) {
super(context);
myArtifactsStructureContext = artifactsStructureContext;
myOriginalArtifact = originalArtifact;
}
@Override
public void check(ProjectStructureProblemsHolder problemsHolder) {
}
@Override
public List<ProjectStructureElementUsage> getUsagesInElement() {
final Artifact artifact = myArtifactsStructureContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
final List<ProjectStructureElementUsage> usages = new ArrayList<ProjectStructureElementUsage>();
ArtifactUtil.processPackagingElements(myArtifactsStructureContext.getRootElement(artifact), null, new PackagingElementProcessor<PackagingElement<?>>() {
@Override
public boolean process(@NotNull List<CompositePackagingElement<?>> parents, @NotNull PackagingElement<?> packagingElement) {
if (packagingElement instanceof ModuleOutputPackagingElement) {
final Module module = ((ModuleOutputPackagingElement)packagingElement).findModule(myArtifactsStructureContext);
if (module != null) {
usages.add(new UsageInArtifact(myOriginalArtifact, myArtifactsStructureContext, new ModuleProjectStructureElement(myContext, module),
ArtifactProjectStructureElement.this, getPathFromRoot(parents, "/"), packagingElement));
}
}
else if (packagingElement instanceof LibraryPackagingElement) {
final Library library = ((LibraryPackagingElement)packagingElement).findLibrary(myArtifactsStructureContext);
if (library != null) {
usages.add(new UsageInArtifact(myOriginalArtifact, myArtifactsStructureContext, new LibraryProjectStructureElement(myContext, library),
ArtifactProjectStructureElement.this, getPathFromRoot(parents, "/"), packagingElement));
}
}
return true;
}
}, myArtifactsStructureContext, false, artifact.getArtifactType());
return usages;
}
@Override
public String toString() {
return "artifact:" + myOriginalArtifact.getName();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ArtifactProjectStructureElement)) return false;
return myOriginalArtifact.equals(((ArtifactProjectStructureElement)o).myOriginalArtifact);
}
@Override
public int hashCode() {
return myOriginalArtifact.hashCode();
}
@Override
public boolean highlightIfUnused() {
return false;
}
}
@@ -27,6 +27,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.ui.configuration.projectRoot.BaseStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.packaging.artifacts.*;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
@@ -34,6 +35,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author nik
@@ -58,6 +62,7 @@ public class ArtifactsStructureConfigurable extends BaseStructureConfigurable {
public void artifactAdded(@NotNull Artifact artifact) {
final MyNode node = addArtifactNode(artifact);
selectNodeInTree(node);
myContext.getDaemonAnalyzer().queueUpdate(new ArtifactProjectStructureElement(myContext, myPackagingEditorContext, artifact));
}
});
}
@@ -75,8 +80,18 @@ public class ArtifactsStructureConfigurable extends BaseStructureConfigurable {
}
}
@NotNull
@Override
protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
final List<ProjectStructureElement> elements = new ArrayList<ProjectStructureElement>();
for (Artifact artifact : myPackagingEditorContext.getArtifactModel().getArtifacts()) {
elements.add(new ArtifactProjectStructureElement(myContext, myPackagingEditorContext, artifact));
}
return elements;
}
private MyNode addArtifactNode(final Artifact artifact) {
final MyNode node = new MyNode(new ArtifactConfigurable(artifact, myPackagingEditorContext, TREE_UPDATER));
final MyNode node = new MyNode(new ArtifactConfigurable(artifact, myPackagingEditorContext, TREE_UPDATER, myContext));
addNode(node, myRoot);
return node;
}
@@ -160,6 +175,7 @@ public class ArtifactsStructureConfigurable extends BaseStructureConfigurable {
@Override
protected void removeArtifact(Artifact artifact) {
myPackagingEditorContext.getModifiableArtifactModel().removeArtifact(artifact);
myContext.getDaemonAnalyzer().removeElement(new ArtifactProjectStructureElement(myContext, myPackagingEditorContext, artifact));
}
protected void processRemovedItems() {
@@ -36,7 +36,7 @@ public interface ArtifactsStructureConfigurableContext extends PackagingElementR
CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact);
void ensureRootIsWritable(@NotNull Artifact artifact);
void editLayout(@NotNull Artifact artifact, Runnable action);
ArtifactEditor getOrCreateEditor(Artifact artifact);
}
@@ -86,13 +86,14 @@ class ArtifactsStructureConfigurableContextImpl implements ArtifactsStructureCon
return root;
}
public void ensureRootIsWritable(@NotNull Artifact artifact) {
public void editLayout(@NotNull Artifact artifact, Runnable action) {
artifact = getOriginalArtifact(artifact);
final ModifiableArtifact modifiableArtifact = getModifiableArtifactModel().getOrCreateModifiableArtifact(artifact);
if (modifiableArtifact.getRootElement() == artifact.getRootElement()) {
modifiableArtifact.setRootElement(getRootElement(artifact));
}
action.run();
myContext.getDaemonAnalyzer().queueUpdate(new ArtifactProjectStructureElement(myContext, this, artifact));
}
public ArtifactEditorImpl getOrCreateEditor(Artifact artifact) {
@@ -23,8 +23,10 @@ import com.intellij.ide.dnd.aware.DnDAwareTree;
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.CompositePackagingElementNode;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.packaging.elements.PackagingElement;
import com.intellij.packaging.elements.RenameablePackagingElement;
import com.intellij.ui.TreeSpeedSearch;
@@ -156,6 +158,19 @@ public class LayoutTree extends SimpleDnDAwareTree implements AdvancedDnDSource
}
}
@Nullable
public PackagingElementNode<?> findCompositeNodeByPath(String parentPath) {
PackagingElementNode<?> node = getRootPackagingNode();
for (String name : StringUtil.split(parentPath, "/")) {
final CompositePackagingElementNode child = node.findCompositeChild(name);
if (child == null) {
return null;
}
node = child;
}
return node;
}
private class LayoutTreeCellEditor extends DefaultCellEditor {
public LayoutTreeCellEditor() {
super(new JTextField());
@@ -189,7 +204,12 @@ public class LayoutTree extends SimpleDnDAwareTree implements AdvancedDnDSource
}
final boolean stopped = super.stopCellEditing();
if (stopped && currentElement != null) {
currentElement.rename(newValue);
final RenameablePackagingElement finalCurrentElement = currentElement;
myArtifactsEditor.getLayoutTreeComponent().editLayout(new Runnable() {
public void run() {
finalCurrentElement.rename(newValue);
}
});
myArtifactsEditor.queueValidation();
myArtifactsEditor.getLayoutTreeComponent().updatePropertiesPanel(true);
addSubtreeToUpdate((DefaultMutableTreeNode)path.getLastPathComponent());
@@ -19,10 +19,7 @@ import com.intellij.ide.dnd.DnDEvent;
import com.intellij.ide.dnd.DnDManager;
import com.intellij.ide.dnd.DnDTarget;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingNodeSource;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingTreeNodeFactory;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.ArtifactRootNode;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.*;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
@@ -37,6 +34,7 @@ import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
import com.intellij.packaging.ui.PackagingSourceItem;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.awt.RelativeRectangle;
import com.intellij.ui.treeStructure.SimpleNode;
import com.intellij.ui.treeStructure.SimpleTreeBuilder;
import com.intellij.ui.treeStructure.SimpleTreeStructure;
import com.intellij.ui.treeStructure.WeightBasedComparator;
@@ -157,14 +155,17 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
final PackagingElement<?> element = parentNode.getElementIfSingle();
if (!checkCanAdd(type, element, parentNode)) return;
ensureRootIsWritable();
CompositePackagingElement<?> parent = element instanceof CompositePackagingElement<?> ? (CompositePackagingElement<?>)element
final CompositePackagingElement<?> parent = element instanceof CompositePackagingElement<?> ? (CompositePackagingElement<?>)element
: getArtifact().getRootElement();
final List<? extends PackagingElement<?>> children = type.chooseAndCreate(myContext, getArtifact(), parent);
for (PackagingElement<?> child : children) {
parent.addOrFindChild(child);
}
editLayout(new Runnable() {
public void run() {
for (PackagingElement<?> child : children) {
parent.addOrFindChild(child);
}
}
});
updateAndSelect(parentNode, children);
}
@@ -210,16 +211,37 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
});
}
public void ensureRootIsWritable() {
myContext.ensureRootIsWritable(myOriginalArtifact);
public void selectNode(@NotNull String parentPath, @NotNull PackagingElement<?> element) {
final PackagingElementNode<?> parent = myTree.findCompositeNodeByPath(parentPath);
if (parent == null) return;
for (SimpleNode node : parent.getChildren()) {
if (node instanceof PackagingElementNode) {
final List<? extends PackagingElement<?>> elements = ((PackagingElementNode<?>)node).getPackagingElements();
for (PackagingElement<?> packagingElement : elements) {
if (packagingElement.isEqualTo(element)) {
myBuilder.select(node);
return;
}
}
}
}
}
public void editLayout(Runnable action) {
myContext.editLayout(myOriginalArtifact, action);
}
public void removeSelectedElements() {
final LayoutTreeSelection selection = myTree.getSelection();
if (!checkCanRemove(selection.getNodes())) return;
ensureRootIsWritable();
removeNodes(selection.getNodes());
editLayout(new Runnable() {
public void run() {
removeNodes(selection.getNodes());
}
});
myArtifactsEditor.rebuildTries();
}
@@ -321,13 +343,16 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
if (!checkCanAdd(null, targetElement, targetNode)) {
return;
}
ensureRootIsWritable();
draggingObject.beforeDrop();
List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
for (PackagingElement<?> element : draggingObject.createPackagingElements(myContext)) {
toSelect.add(element);
targetElement.addOrFindChild(element);
}
final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
editLayout(new Runnable() {
public void run() {
draggingObject.beforeDrop();
for (PackagingElement<?> element : draggingObject.createPackagingElements(myContext)) {
toSelect.add(element);
targetElement.addOrFindChild(element);
}
}
});
updateAndSelect(targetNode, toSelect);
myArtifactsEditor.getSourceItemsTree().rebuildTree();
}
@@ -352,7 +377,7 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) {
}
public void rename(TreePath path) {
public void startRenaming(TreePath path) {
myTree.startEditingAtPath(path);
}
@@ -379,20 +404,23 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
mySelectedElementInfo.showPropertiesPanel();
}
public void putIntoDefaultLocations(@NotNull List<? extends PackagingSourceItem> items) {
ensureRootIsWritable();
public void putIntoDefaultLocations(@NotNull final List<? extends PackagingSourceItem> items) {
final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
final CompositePackagingElement<?> rootElement = getArtifact().getRootElement();
final ArtifactType artifactType = getArtifact().getArtifactType();
List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
for (PackagingSourceItem item : items) {
final String path = artifactType.getDefaultPathFor(item);
if (path != null) {
final CompositePackagingElement<?> directory = PackagingElementFactory.getInstance().getOrCreateDirectory(rootElement, path);
final List<? extends PackagingElement<?>> elements = item.createElements(myContext);
toSelect.addAll(directory.addOrFindChildren(elements));
editLayout(new Runnable() {
public void run() {
for (PackagingSourceItem item : items) {
final String path = artifactType.getDefaultPathFor(item);
if (path != null) {
final CompositePackagingElement<?> directory = PackagingElementFactory.getInstance().getOrCreateDirectory(rootElement, path);
final List<? extends PackagingElement<?>> elements = item.createElements(myContext);
toSelect.addAll(directory.addOrFindChildren(elements));
}
}
}
}
});
myArtifactsEditor.getSourceItemsTree().rebuildTree();
updateAndSelect(myTree.getRootPackagingNode(), toSelect);
}
@@ -432,8 +460,11 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
public void save() {
if (myCurrentPanel != null && myCurrentPanel.isModified()) {
ensureRootIsWritable();
myCurrentPanel.apply();
editLayout(new Runnable() {
public void run() {
myCurrentPanel.apply();
}
});
}
}
@@ -0,0 +1,70 @@
package com.intellij.openapi.roots.ui.configuration.artifacts;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElementUsage;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.elements.PackagingElement;
import javax.swing.*;
/**
* @author nik
*/
public class UsageInArtifact extends ProjectStructureElementUsage {
private Artifact myOriginalArtifact;
private final ArtifactsStructureConfigurableContext myContext;
private ProjectStructureElement mySourceElement;
private ProjectStructureElement myContainingElement;
private final String myParentPath;
private final PackagingElement<?> myPackagingElement;
public UsageInArtifact(Artifact originalArtifact, ArtifactsStructureConfigurableContext context, ProjectStructureElement sourceElement,
ArtifactProjectStructureElement containingElement,
String parentPath, PackagingElement<?> packagingElement) {
myOriginalArtifact = originalArtifact;
myContext = context;
mySourceElement = sourceElement;
myContainingElement = containingElement;
myParentPath = parentPath;
myPackagingElement = packagingElement;
}
@Override
public ProjectStructureElement getSourceElement() {
return mySourceElement;
}
@Override
public ProjectStructureElement getContainingElement() {
return myContainingElement;
}
@Override
public void navigate() {
final Artifact artifact = myContext.getArtifactModel().getArtifactByOriginal(myOriginalArtifact);
final ArtifactEditorEx artifactEditor = (ArtifactEditorEx)myContext.getOrCreateEditor(artifact);
artifactEditor.getLayoutTreeComponent().selectNode(myParentPath, myPackagingElement);
ProjectStructureConfigurable.getInstance(myContext.getProject()).select(myOriginalArtifact, true);
}
@Override
public String getPresentableName() {
return myOriginalArtifact.getName();
}
@Override
public int hashCode() {
return myOriginalArtifact.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof UsageInArtifact && ((UsageInArtifact)obj).myOriginalArtifact.equals(myOriginalArtifact);
}
@Override
public Icon getIcon() {
return myOriginalArtifact.getArtifactType().getIcon();
}
}
@@ -15,10 +15,11 @@
*/
package com.intellij.openapi.roots.ui.configuration.artifacts.actions;
import com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ui.configuration.artifacts.LayoutTreeComponent;
import com.intellij.openapi.roots.ui.configuration.artifacts.nodes.PackagingElementNode;
import com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.ui.awt.RelativePoint;
import java.awt.*;
@@ -39,9 +40,9 @@ public class ArtifactEditorFindUsagesAction extends FindUsagesInProjectStructure
return node != null && node.getElementPresentation().getSourceObject() != null;
}
protected Object getSelectedObject() {
protected ProjectStructureElement getSelectedElement() {
PackagingElementNode<?> node = myLayoutTreeComponent.getSelection().getNodeIfSingle();
return node != null ? node.getElementPresentation().getSourceObject() : null;
return node != null ? (ProjectStructureElement)node.getElementPresentation().getSourceObject() : null;
}
protected RelativePoint getPointToShowResults() {
@@ -64,17 +64,20 @@ public class ExtractArtifactAction extends DumbAwareAction {
final String name = Messages.showInputDialog(myEditor.getMainComponent(), ProjectBundle.message("label.text.specify.artifact.name"),
ProjectBundle.message("dialog.title.extract.artifact"), null);
if (name != null) {
treeComponent.ensureRootIsWritable();
final Project project = myEditor.getContext().getProject();
//todo[nik] select type?
final ModifiableArtifact artifact = myEditor.getContext().getModifiableArtifactModel().addArtifact(name, PlainArtifactType.getInstance());
for (PackagingElement<?> element : selectedElements) {
artifact.getRootElement().addOrFindChild(ArtifactUtil.copyWithChildren(element, project));
}
for (PackagingElement element : selectedElements) {
parent.removeChild(element);
}
parent.addOrFindChild(new ArtifactPackagingElement(project, ArtifactPointerManager.getInstance(project).create(artifact)));
treeComponent.editLayout(new Runnable() {
public void run() {
for (PackagingElement<?> element : selectedElements) {
artifact.getRootElement().addOrFindChild(ArtifactUtil.copyWithChildren(element, project));
}
for (PackagingElement element : selectedElements) {
parent.removeChild(element);
}
parent.addOrFindChild(new ArtifactPackagingElement(project, ArtifactPointerManager.getInstance(project).create(artifact)));
}
});
treeComponent.rebuildTree();
}
}
@@ -56,28 +56,31 @@ public class InlineArtifactAction extends DumbAwareAction {
final PackagingElementNode<?> node = selection.getNodeIfSingle();
if (node == null || !(element instanceof ArtifactPackagingElement)) return;
CompositePackagingElement<?> parent = node.getParentElement(element);
final CompositePackagingElement<?> parent = node.getParentElement(element);
if (parent == null) {
return;
}
if (!treeComponent.checkCanRemove(selection.getNodes())) return;
if (!treeComponent.checkCanAdd(null, parent, node)) return;
treeComponent.ensureRootIsWritable();
parent.removeChild(element);
final ArtifactEditorContext context = myEditor.getContext();
final Artifact artifact = ((ArtifactPackagingElement)element).findArtifact(context);
if (artifact != null) {
final CompositePackagingElement<?> rootElement = artifact.getRootElement();
if (rootElement instanceof ArtifactRootElement<?>) {
for (PackagingElement<?> child : rootElement.getChildren()) {
parent.addOrFindChild(ArtifactUtil.copyWithChildren(child, context.getProject()));
treeComponent.editLayout(new Runnable() {
public void run() {
parent.removeChild(element);
final ArtifactEditorContext context = myEditor.getContext();
final Artifact artifact = ((ArtifactPackagingElement)element).findArtifact(context);
if (artifact != null) {
final CompositePackagingElement<?> rootElement = artifact.getRootElement();
if (rootElement instanceof ArtifactRootElement<?>) {
for (PackagingElement<?> child : rootElement.getChildren()) {
parent.addOrFindChild(ArtifactUtil.copyWithChildren(child, context.getProject()));
}
}
else {
parent.addOrFindChild(ArtifactUtil.copyWithChildren(rootElement, context.getProject()));
}
}
}
else {
parent.addOrFindChild(ArtifactUtil.copyWithChildren(rootElement, context.getProject()));
}
}
});
treeComponent.rebuildTree();
}
}
@@ -24,6 +24,7 @@ import com.intellij.packaging.elements.CompositePackagingElement;
import com.intellij.packaging.elements.PackagingElement;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -80,11 +81,18 @@ public class MoveElementAction extends DumbAwareAction {
if (myLayoutTreeComponent.checkCanRemove(Collections.singletonList(node))
&& myLayoutTreeComponent.checkCanAdd(null, parentElement, parent)) {
myLayoutTreeComponent.ensureRootIsWritable();
final int index = parentElement.getChildren().indexOf(element);
final PackagingElement<?> moved = parentElement.moveChild(index, myDirection);
if (moved != null) {
myLayoutTreeComponent.updateAndSelect(parent, Collections.singletonList(moved));
final List<PackagingElement<?>> toSelect = new ArrayList<PackagingElement<?>>();
myLayoutTreeComponent.editLayout(new Runnable() {
public void run() {
final int index = parentElement.getChildren().indexOf(element);
final PackagingElement<?> moved = parentElement.moveChild(index, myDirection);
if (moved != null) {
toSelect.add(moved);
}
}
});
if (!toSelect.isEmpty()) {
myLayoutTreeComponent.updateAndSelect(parent, toSelect);
}
}
}
@@ -53,7 +53,6 @@ public class RenamePackagingElementAction extends DumbAwareAction {
final PackagingElementNode<?> node = selection.getNodeIfSingle();
if (node == null) return;
final TreePath path = selection.getPath(node);
myArtifactEditor.getLayoutTreeComponent().ensureRootIsWritable();
myArtifactEditor.getLayoutTreeComponent().rename(path);
myArtifactEditor.getLayoutTreeComponent().startRenaming(path);
}
}
@@ -131,4 +131,18 @@ public class PackagingElementNode<E extends PackagingElement<?>> extends Artifac
public ArtifactEditorContext getContext() {
return myContext;
}
@Nullable
public CompositePackagingElementNode findCompositeChild(@NotNull String name) {
final SimpleNode[] children = getChildren();
for (SimpleNode child : children) {
if (child instanceof CompositePackagingElementNode) {
final CompositePackagingElementNode composite = (CompositePackagingElementNode)child;
if (name.equals(composite.getFirstElement().getName())) {
return composite;
}
}
}
return null;
}
}
@@ -100,22 +100,37 @@ public class PutSourceItemIntoParentAndLinkViaManifestAction extends AnAction {
final Artifact artifact = parentsInfo.getGrandparentArtifact();
final ArtifactEditorContext context = myArtifactEditor.getContext();
context.ensureRootIsWritable(artifact);
context.ensureRootIsWritable(parentsInfo.getParentArtifact());
//todo[nik] improve
final Runnable emptyRunnable = new Runnable() {
public void run() {
}
};
context.editLayout(artifact, emptyRunnable);
context.editLayout(parentsInfo.getParentArtifact(), emptyRunnable);
parentsInfo = findParentAndGrandParent(myArtifactEditor.getArtifact());//find elements under modifiable root
if (parentsInfo == null) {
return;
}
final CompositePackagingElement<?> grandParent = parentsInfo.getGrandparentElement();
List<String> classpath = new ArrayList<String>();
for (PackagingSourceItem item : items) {
final List<? extends PackagingElement<?>> elements = item.createElements(context);
grandParent.addOrFindChildren(elements);
classpath.addAll(ManifestFileUtil.getClasspathForElements(elements, context, artifact.getArtifactType()));
}
final List<String> classpath = new ArrayList<String>();
context.editLayout(artifact, new Runnable() {
public void run() {
for (PackagingSourceItem item : items) {
final List<? extends PackagingElement<?>> elements = item.createElements(context);
grandParent.addOrFindChildren(elements);
classpath.addAll(ManifestFileUtil.getClasspathForElements(elements, context, artifact.getArtifactType()));
}
}
});
final ArtifactEditor parentArtifactEditor = context.getOrCreateEditor(parentsInfo.getParentArtifact());
parentArtifactEditor.addToClasspath(parentsInfo.getParentElement(), classpath);
final ParentElementsInfo finalParentsInfo = parentsInfo;
context.editLayout(parentsInfo.getParentArtifact(), new Runnable() {
public void run() {
parentArtifactEditor.addToClasspath(finalParentsInfo.getParentElement(), classpath);
}
});
((ArtifactEditorImpl)context.getOrCreateEditor(parentsInfo.getGrandparentArtifact())).rebuildTries();
}
@@ -30,9 +30,9 @@ import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryTableEditor;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.ui.*;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
@@ -40,15 +40,20 @@ import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
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.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurable {
protected String myLevel;
@@ -91,18 +96,28 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl
}
protected void loadTree() {
final LibraryTablesRegistrar registrar = LibraryTablesRegistrar.getInstance();
final LibrariesModifiableModel provider = myContext.myLevel2Providers.get(myLevel);
createLibrariesNode(registrar.getLibraryTableByLevel(myLevel, myProject), provider,
myContext.createModifiableModelProvider(myLevel, false));
final LibraryTable libraryTable = LibraryTablesRegistrar.getInstance().getLibraryTableByLevel(myLevel, myProject);
createLibrariesNode(myContext.createModifiableModelProvider(myLevel, false), new LibrariesModifiableModel(libraryTable, myProject));
}
@NotNull
@Override
protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
final List<ProjectStructureElement> result = new ArrayList<ProjectStructureElement>();
//todo[nik] improve
for (int i = 0; i < myRoot.getChildCount(); i++) {
final TreeNode node = myRoot.getChildAt(i);
if (node instanceof MyNode) {
final NamedConfigurable configurable = ((MyNode)node).getConfigurable();
if (configurable instanceof LibraryConfigurable) {
result.add(new LibraryProjectStructureElement(myContext, ((LibraryConfigurable)configurable).getEditableObject()));
}
}
}
return result;
}
private void createLibrariesNode(final LibraryTable table,
LibrariesModifiableModel provider,
final LibraryTableModifiableModelProvider modelProvider) {
provider = new LibrariesModifiableModel(table, myProject);
private void createLibrariesNode(final LibraryTableModifiableModelProvider modelProvider, final LibrariesModifiableModel provider) {
final Library[] libraries = provider.getLibraries();
for (Library library : libraries) {
myRoot.add(new MyNode(new LibraryConfigurable(modelProvider, library, myProject, TREE_UPDATER)));
@@ -136,6 +151,7 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl
new LibraryConfigurable(myContext.createModifiableModelProvider(level, false), library, myProject, TREE_UPDATER);
final MyNode node = new MyNode(configurable);
addNode(node, myRoot);
myContext.getDaemonAnalyzer().queueUpdate(new LibraryProjectStructureElement(myContext, library));
return node;
}
@@ -178,8 +194,7 @@ public abstract class BaseLibrariesConfigurable extends BaseStructureConfigurabl
final LibraryTable table = library.getTable();
if (table != null) {
getModelProvider(true).getModifiableModel().removeLibrary(library);
myContext.invalidateModules(myContext.myLibraryDependencyCache.get(library));
myContext.myLibraryDependencyCache.remove(library);
myContext.getDaemonAnalyzer().removeElement(new LibraryProjectStructureElement(myContext, library));
}
}
@@ -18,7 +18,6 @@ package com.intellij.openapi.roots.ui.configuration.projectRoot;
import com.intellij.facet.Facet;
import com.intellij.ide.CommonActionsManager;
import com.intellij.ide.TreeExpander;
import com.intellij.ide.impl.convert.ProjectFileVersion;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.keymap.Keymap;
@@ -31,10 +30,15 @@ import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzer;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemDescription;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemsHolder;
import com.intellij.openapi.ui.MasterDetailsComponent;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Condition;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.TreeSpeedSearch;
@@ -42,11 +46,9 @@ import com.intellij.ui.TreeToolTipHandler;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.navigation.Place;
import com.intellij.util.Icons;
import com.intellij.util.StringBuilderSpinAllocator;
import com.intellij.util.containers.Convertor;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.packaging.artifacts.Artifact;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -74,7 +76,7 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i
public void init(StructureConfigurableContext context) {
myContext = context;
myContext.addCacheUpdateListener(new Runnable() {
myContext.getDaemonAnalyzer().addListener(new Runnable() {
public void run() {
if (!myTree.isShowing()) return;
@@ -149,81 +151,54 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i
if (value instanceof MyNode) {
final MyNode node = (MyNode)value;
if (node.getConfigurable() == null) {
final NamedConfigurable namedConfigurable = node.getConfigurable();
if (namedConfigurable == null) {
return;
}
final String displayName = node.getDisplayName();
final Icon icon = node.getConfigurable().getIcon(expanded);
final Icon icon = namedConfigurable.getIcon(expanded);
setIcon(icon);
setToolTipText(null);
setFont(UIUtil.getTreeFont());
if (node.isDisplayInBold()){
append(displayName, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
} else {
final Object object = node.getConfigurable().getEditableObject();
final boolean unused = myContext.isUnused(object);
final StructureConfigurableContext.ValidityLevel level = myContext.isInvalid(object);
final boolean invalid = level != StructureConfigurableContext.ValidityLevel.VALID;
if (unused || invalid){
Color fg = unused
? UIUtil.getTextInactiveTextColor()
: selected && hasFocus ? UIUtil.getTreeSelectionForeground() : UIUtil.getTreeForeground();
append(displayName, new SimpleTextAttributes(invalid ? SimpleTextAttributes.STYLE_WAVED : SimpleTextAttributes.STYLE_PLAIN,
fg,
level == StructureConfigurableContext.ValidityLevel.ERROR ? Color.RED : Color.GRAY));
setToolTipText(composeTooltipMessage(invalid, object, displayName, unused));
}
else {
append(displayName, selected && hasFocus ? SimpleTextAttributes.SELECTED_SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES);
SimpleTextAttributes textAttributes =
selected && hasFocus ? SimpleTextAttributes.SELECTED_SIMPLE_CELL_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES;
if (node.isDisplayInBold()) {
textAttributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES;
} else if (namedConfigurable instanceof ProjectStructureElementConfigurable) {
final ProjectStructureElement projectStructureElement =
((ProjectStructureElementConfigurable)namedConfigurable).getProjectStructureElement();
if (projectStructureElement != null) {
final ProjectStructureDaemonAnalyzer daemonAnalyzer = myContext.getDaemonAnalyzer();
final boolean unused = daemonAnalyzer.isUnused(projectStructureElement);
final ProjectStructureProblemsHolder problemsHolder = daemonAnalyzer.getProblemsHolder(projectStructureElement);
if (problemsHolder == null) {
daemonAnalyzer.queueUpdate(projectStructureElement, true, false);
}
final ProjectStructureProblemDescription.Severity level = problemsHolder != null ? problemsHolder.getSeverity() : null;
final boolean invalid = level != null;
if (unused || invalid) {
Color fg = unused
? UIUtil.getTextInactiveTextColor()
: selected && hasFocus ? UIUtil.getTreeSelectionForeground() : UIUtil.getTreeForeground();
textAttributes = new SimpleTextAttributes(invalid ? SimpleTextAttributes.STYLE_WAVED : SimpleTextAttributes.STYLE_PLAIN, fg,
level == ProjectStructureProblemDescription.Severity.ERROR ? Color.RED : Color.GRAY);
String text = problemsHolder != null ? problemsHolder.composeTooltipMessage() : "";
if (unused) {
text += ProjectBundle.message("project.roots.tooltip.unused", displayName);
}
setToolTipText(text);
}
}
}
append(displayName, textAttributes);
}
}
});
}
private String composeTooltipMessage(final boolean invalid, final Object object, final String displayName, final boolean unused) {
final StringBuilder buf = StringBuilderSpinAllocator.alloc();
try {
if (invalid) {
if (object instanceof Module) {
final Module module = (Module)object;
final Map<String, Set<String>> problems = myContext.myValidityCache.get(module);
if (problems.containsKey(StructureConfigurableContext.DUPLICATE_MODULE_NAME)) {
buf.append(StructureConfigurableContext.DUPLICATE_MODULE_NAME).append("\n");
}
if (problems.containsKey(StructureConfigurableContext.NO_JDK)){
buf.append(StructureConfigurableContext.NO_JDK).append("\n");
}
final Set<String> deletedLibraries = problems.get(StructureConfigurableContext.DELETED_LIBRARIES);
if (deletedLibraries != null) {
buf.append(ProjectBundle.message("project.roots.library.problem.message", deletedLibraries.size()));
for (String problem : deletedLibraries) {
if (deletedLibraries.size() > 1) {
buf.append(" - ");
}
buf.append("\'").append(problem).append("\'").append("\n");
}
}
} else {
buf.append(ProjectBundle.message("project.roots.tooltip.library.misconfigured", displayName)).append("\n");
}
}
if (unused) {
buf.append(ProjectBundle.message("project.roots.tooltip.unused", displayName));
}
return buf.toString();
}
finally {
StringBuilderSpinAllocator.dispose(buf);
}
}
public void disposeUIResources() {
if (myUiDisposed) return;
@@ -232,16 +207,12 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i
myUiDisposed = true;
myAutoScrollHandler.cancelAllRequests();
myContext.myUpdateDependenciesAlarm.cancelAllRequests();
myContext.myUpdateDependenciesAlarm.addRequest(new Runnable(){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
dispose();
}
});
dispose();
}
}, 0);
});
}
protected void addCollapseExpandActions(final List<AnAction> result) {
@@ -267,6 +238,19 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i
result.add(actionsManager.createCollapseAllAction(expander, myTree));
}
@Nullable
public ProjectStructureElement getSelectedElement() {
final TreePath selectionPath = myTree.getSelectionPath();
if (selectionPath != null && selectionPath.getLastPathComponent() instanceof MyNode) {
MyNode node = (MyNode)selectionPath.getLastPathComponent();
final NamedConfigurable configurable = node.getConfigurable();
if (configurable instanceof ProjectStructureElementConfigurable) {
return ((ProjectStructureElementConfigurable)configurable).getProjectStructureElement();
}
}
return null;
}
private class MyFindUsagesAction extends FindUsagesInProjectStructureActionBase {
public MyFindUsagesAction(JComponent parentComponent) {
@@ -287,8 +271,8 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i
return myContext;
}
protected Object getSelectedObject() {
return BaseStructureConfigurable.this.getSelectedObject();
protected ProjectStructureElement getSelectedElement() {
return BaseStructureConfigurable.this.getSelectedElement();
}
protected RelativePoint getPointToShowResults() {
@@ -313,10 +297,18 @@ public abstract class BaseStructureConfigurable extends MasterDetailsComponent i
myTree.setShowsRootHandles(false);
loadTree();
}
for (ProjectStructureElement element : getProjectStructureElements()) {
myContext.getDaemonAnalyzer().queueUpdate(element, false, true);
}
super.reset();
}
@NotNull
protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
return Collections.emptyList();
}
protected abstract void loadTree();
@@ -20,36 +20,29 @@ import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootModel;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.impl.OrderEntryUtil;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElementUsage;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.IconLoader;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Set;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
/**
* @author nik
*/
public abstract class FindUsagesInProjectStructureActionBase extends AnAction implements DumbAware {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.FindUsagesInProjectStructureActionBase");
private static final Icon FIND_ICON = IconLoader.getIcon("/actions/find.png");
private final JComponent myParentComponent;
private final Project myProject;
@@ -68,60 +61,46 @@ public abstract class FindUsagesInProjectStructureActionBase extends AnAction im
protected abstract boolean isEnabled();
public void actionPerformed(AnActionEvent e) {
final Object selectedObject = getSelectedObject();
if (selectedObject == null) return;
final ProjectStructureElement selected = getSelectedElement();
if (selected == null) return;
final Set<String> dependencies = getContext().getCachedDependencies(selectedObject, true);
if (dependencies == null || dependencies.isEmpty()) {
Messages.showInfoMessage(myParentComponent, FindBundle.message("find.usage.view.no.usages.text"),
FindBundle.message("find.pointcut.applications.not.found.title"));
final Collection<ProjectStructureElementUsage> usages = getContext().getDaemonAnalyzer().getUsages(selected);
if (usages.isEmpty()) {
Messages.showInfoMessage(myParentComponent, FindBundle.message("find.usage.view.no.usages.text"), FindBundle.message("find.pointcut.applications.not.found.title"));
return;
}
RelativePoint point = getPointToShowResults();
JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep<String>(ProjectBundle.message("dependencies.used.in.popup.title"),
ArrayUtil.toStringArray(dependencies)) {
public PopupStep onChosen(final String nameToSelect, final boolean finalChoice) {
navigateToObject(nameToSelect, selectedObject);
RelativePoint point = getPointToShowResults();
final ProjectStructureElementUsage[] usagesArray = usages.toArray(new ProjectStructureElementUsage[usages.size()]);
Arrays.sort(usagesArray, new Comparator<ProjectStructureElementUsage>() {
public int compare(ProjectStructureElementUsage o1, ProjectStructureElementUsage o2) {
return o1.getPresentableName().compareToIgnoreCase(o2.getPresentableName());
}
});
JBPopupFactory.getInstance().createListPopup(new BaseListPopupStep<ProjectStructureElementUsage>(ProjectBundle.message("dependencies.used.in.popup.title"), usagesArray) {
@Override
public PopupStep onChosen(final ProjectStructureElementUsage selected, final boolean finalChoice) {
selected.navigate();
return FINAL_CHOICE;
}
public Icon getIconFor(String selection) {
final Module module = getContext().myModulesConfigurator.getModule(selection);
LOG.assertTrue(module != null, selection + " was not found");
return module.getModuleType().getNodeIcon(false);
@NotNull
@Override
public String getTextFor(ProjectStructureElementUsage value) {
return value.getPresentableName();
}
@Override
public Icon getIconFor(ProjectStructureElementUsage selection) {
return selection.getIcon();
}
}).show(point);
}
private void navigateToObject(final String moduleName, final @NotNull Object selectedObject) {
ModulesConfigurator modulesConfigurator = getContext().myModulesConfigurator;
Module module = modulesConfigurator.getModule(moduleName);
if (module == null) {
ProjectStructureConfigurable.getInstance(myProject).select(moduleName, null, true);
return;
}
ModuleRootModel rootModel = modulesConfigurator.getRootModel(module);
OrderEntry entry;
if (selectedObject instanceof Library) {
entry = OrderEntryUtil.findLibraryOrderEntry(rootModel, (Library)selectedObject);
}
else if (selectedObject instanceof Module) {
entry = OrderEntryUtil.findModuleOrderEntry(rootModel, (Module)selectedObject);
}
else if (selectedObject instanceof Sdk) {
entry = OrderEntryUtil.findJdkOrderEntry(rootModel, (Sdk)selectedObject);
}
else {
entry = null;
}
ModuleStructureConfigurable.getInstance(myProject).selectOrderEntry(module, entry);
}
@Nullable
protected abstract Object getSelectedObject();
protected abstract ProjectStructureElement getSelectedElement();
protected StructureConfigurableContext getContext() {
return ModuleStructureConfigurable.getInstance(myProject).getContext();
@@ -17,11 +17,13 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.projectRoots.ui.SdkEditor;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.SdkProjectStructureElement;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.ui.navigation.History;
import com.intellij.ui.navigation.Place;
@@ -35,16 +37,24 @@ import javax.swing.*;
* User: anna
* Date: 05-Jun-2006
*/
public class JdkConfigurable extends NamedConfigurable<Sdk> implements Place.Navigator {
public class JdkConfigurable extends ProjectStructureElementConfigurable<Sdk> implements Place.Navigator {
private final ProjectJdkImpl myProjectJdk;
private final SdkEditor mySdkEditor;
private SdkProjectStructureElement myProjectStructureElement;
public JdkConfigurable(final ProjectJdkImpl projectJdk,
final ProjectJdksModel configurable,
final Runnable updateTree, @NotNull History history) {
final Runnable updateTree, @NotNull History history, Project project) {
super(true, updateTree);
myProjectJdk = projectJdk;
mySdkEditor = new SdkEditor(configurable, history, myProjectJdk);
final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(project).getContext();
myProjectStructureElement = new SdkProjectStructureElement(context, myProjectJdk);
}
@Override
public ProjectStructureElement getProjectStructureElement() {
return myProjectStructureElement;
}
public void setDisplayName(final String name) {
@@ -29,16 +29,22 @@ import com.intellij.openapi.projectRoots.SdkModel;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.SdkProjectStructureElement;
import com.intellij.openapi.ui.MasterDetailsComponent;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.TreePath;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@State(
name = "JdkListConfigurable.UI",
@@ -120,14 +126,26 @@ public class JdkListConfigurable extends BaseStructureConfigurable {
protected void loadTree() {
final HashMap<Sdk,Sdk> sdks = myJdksTreeModel.getProjectJdks();
for (Sdk sdk : sdks.keySet()) {
final JdkConfigurable configurable = new JdkConfigurable((ProjectJdkImpl)sdks.get(sdk), myJdksTreeModel, TREE_UPDATER, myHistory);
final JdkConfigurable configurable = new JdkConfigurable((ProjectJdkImpl)sdks.get(sdk), myJdksTreeModel, TREE_UPDATER, myHistory,
myProject);
addNode(new MyNode(configurable), myRoot);
}
}
@NotNull
@Override
protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
final List<ProjectStructureElement> result = new ArrayList<ProjectStructureElement>();
for (Sdk sdk : myJdksTreeModel.getProjectJdks().values()) {
result.add(new SdkProjectStructureElement(myContext, sdk));
}
return result;
}
public boolean addJdkNode(final Sdk jdk, final boolean selectInTree) {
if (!myUiDisposed) {
addNode(new MyNode(new JdkConfigurable((ProjectJdkImpl)jdk, myJdksTreeModel, TREE_UPDATER, myHistory)), myRoot);
myContext.getDaemonAnalyzer().queueUpdate(new SdkProjectStructureElement(myContext, jdk));
addNode(new MyNode(new JdkConfigurable((ProjectJdkImpl)jdk, myJdksTreeModel, TREE_UPDATER, myHistory, myProject)), myRoot);
if (selectInTree) {
selectNodeInTree(MasterDetailsComponent.findNodeByObject(myRoot, jdk));
}
@@ -188,8 +206,7 @@ public class JdkListConfigurable extends BaseStructureConfigurable {
protected void removeJdk(final Sdk jdk) {
myJdksTreeModel.removeJdk(jdk);
myContext.myJdkDependencyCache.remove(jdk);
myContext.myValidityCache.clear();
myContext.getDaemonAnalyzer().removeElement(new SdkProjectStructureElement(myContext, jdk));
}
protected
@@ -24,7 +24,8 @@ import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryTableEditor;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.LibraryProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
import org.jetbrains.annotations.NonNls;
@@ -36,13 +37,14 @@ import javax.swing.*;
* User: anna
* Date: 02-Jun-2006
*/
public class LibraryConfigurable extends NamedConfigurable<Library> {
public class LibraryConfigurable extends ProjectStructureElementConfigurable<Library> {
private static final Icon ICON = IconLoader.getIcon("/modules/library.png");
private LibraryTableEditor myLibraryEditor;
private final Library myLibrary;
private final LibraryTableModifiableModelProvider myModel;
private final Project myProject;
private final LibraryProjectStructureElement myProjectStructureElement;
protected LibraryConfigurable(final LibraryTableModifiableModelProvider libraryTable,
final Library library,
@@ -52,10 +54,17 @@ public class LibraryConfigurable extends NamedConfigurable<Library> {
myModel = libraryTable;
myProject = project;
myLibrary = library;
final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myProject).getContext();
myProjectStructureElement = new LibraryProjectStructureElement(context, myLibrary);
}
public JComponent createOptionsPanel() {
myLibraryEditor = LibraryTableEditor.editLibrary(myModel, myLibrary, myProject);
myLibraryEditor.addListener(new Runnable() {
public void run() {
ModuleStructureConfigurable.getInstance(myProject).getContext().getDaemonAnalyzer().queueUpdate(myProjectStructureElement);
}
});
return myLibraryEditor.getComponent();
}
@@ -63,6 +72,11 @@ public class LibraryConfigurable extends NamedConfigurable<Library> {
return myLibraryEditor != null && myLibraryEditor.hasChanges();
}
@Override
public ProjectStructureElement getProjectStructureElement() {
return myProjectStructureElement;
}
public void apply() throws ConfigurationException {
//do nothing
}
@@ -23,12 +23,13 @@ import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ModuleProjectStructureElement;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.navigation.Place;
import com.intellij.ui.navigation.History;
import com.intellij.ui.navigation.Place;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -39,10 +40,11 @@ import javax.swing.*;
* User: anna
* Date: 04-Jun-2006
*/
public class ModuleConfigurable extends NamedConfigurable<Module> implements Place.Navigator {
public class ModuleConfigurable extends ProjectStructureElementConfigurable<Module> implements Place.Navigator {
private final Module myModule;
private final ModulesConfigurator myConfigurator;
private String myModuleName;
private final ModuleProjectStructureElement myProjectStructureElement;
public ModuleConfigurable(ModulesConfigurator modulesConfigurator,
Module module,
@@ -51,6 +53,8 @@ public class ModuleConfigurable extends NamedConfigurable<Module> implements Pla
myModule = module;
myModuleName = myModule.getName();
myConfigurator = modulesConfigurator;
final StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myModule.getProject()).getContext();
myProjectStructureElement = new ModuleProjectStructureElement(context, myModule);
}
public void setDisplayName(String name) {
@@ -69,6 +73,11 @@ public class ModuleConfigurable extends NamedConfigurable<Module> implements Pla
myConfigurator.setModified(!Comparing.strEqual(myModuleName, myModule.getName()));
}
@Override
public ProjectStructureElement getProjectStructureElement() {
return myProjectStructureElement;
}
public Module getEditableObject() {
return myModule;
}
@@ -47,6 +47,7 @@ import com.intellij.openapi.roots.ui.configuration.ClasspathEditor;
import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.*;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.NamedConfigurable;
@@ -136,6 +137,16 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
myUiDisposed = false;
}
@NotNull
@Override
protected Collection<? extends ProjectStructureElement> getProjectStructureElements() {
final List<ProjectStructureElement> result = new ArrayList<ProjectStructureElement>();
for (Module module : myModuleManager.getModules()) {
result.add(new ModuleProjectStructureElement(myContext, module));
}
return result;
}
protected void updateSelection(@Nullable final NamedConfigurable configurable) {
FacetStructureConfigurable.getInstance(myProject).disposeMultipleSettingsEditor();
ApplicationManager.getApplication().assertIsDispatchThread();
@@ -299,7 +310,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
node.removeFromParent();
((DefaultTreeModel)myTree.getModel()).reload(parent);
}
myContext.invalidateModules(myContext.myLibraryDependencyCache.get(library));
myContext.getDaemonAnalyzer().removeElement(new LibraryProjectStructureElement(myContext, library));
}
}
@@ -331,7 +342,6 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
public void disposeUIResources() {
super.disposeUIResources();
myFacetEditorFacade.clearMaps();
myContext.clearCaches();
myContext.myModulesConfigurator.disposeUIResources();
ModuleStructureConfigurable.super.disposeUIResources();
}
@@ -445,12 +455,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
}
}
modelProxy.addLibraryEntry(library);
Set<String> modules = myContext.myLibraryDependencyCache.get(library);
if (modules == null) {
modules = new HashSet<String>();
myContext.myLibraryDependencyCache.put(library, modules);
}
modules.add(module.getName());
myContext.getDaemonAnalyzer().queueUpdate(new ModuleProjectStructureElement(myContext, module));
myTree.repaint();
}
@@ -500,7 +505,9 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
myFacetEditorFacade.addFacetsNodes(module, node);
((DefaultTreeModel)myTree.getModel()).reload(parent);
selectNodeInTree(node);
myContext.myValidityCache.clear(); //missing modules added
final ProjectStructureDaemonAnalyzer daemonAnalyzer = myContext.getDaemonAnalyzer();
daemonAnalyzer.queueUpdate(new ModuleProjectStructureElement(myContext, module));
daemonAnalyzer.clearAllProblems(); //missing modules added
}
@Nullable
@@ -670,9 +677,7 @@ public class ModuleStructureConfigurable extends BaseStructureConfigurable imple
}
List<Facet> removed = modulesConfigurator.getFacetsConfigurator().removeAllFacets(module);
FacetStructureConfigurable.getInstance(myProject).removeFacetNodes(removed);
myContext.myValidityCache.remove(module);
myContext.invalidateModules(myContext.myModulesDependencyCache.get(module));
myContext.myModulesDependencyCache.remove(module);
myContext.getDaemonAnalyzer().removeElement(new ModuleProjectStructureElement(myContext, module));
return true;
}
@@ -0,0 +1,36 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.roots.ui.configuration.projectRoot;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
import com.intellij.openapi.ui.NamedConfigurable;
import org.jetbrains.annotations.Nullable;
/**
* @author nik
*/
public abstract class ProjectStructureElementConfigurable<T> extends NamedConfigurable<T> {
protected ProjectStructureElementConfigurable() {
}
protected ProjectStructureElementConfigurable(boolean isNameEditable, @Nullable Runnable updateTree) {
super(isNameEditable, updateTree);
}
@Nullable
public abstract ProjectStructureElement getProjectStructureElement();
}
@@ -16,362 +16,55 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.impl.libraries.LibraryEx;
import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.libraries.LibraryTablePresentation;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.util.Alarm;
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureDaemonAnalyzer;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.NotNullFunction;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import java.util.List;
import java.util.Map;
public class StructureConfigurableContext implements Disposable {
private static final Logger LOG = Logger.getInstance("#" + StructureConfigurableContext.class.getName());
public enum ValidityLevel { VALID, WARNING, ERROR }
public static final String NO_JDK = ProjectBundle.message("project.roots.module.jdk.problem.message");
public static final String DUPLICATE_MODULE_NAME = ProjectBundle.message("project.roots.module.duplicate.name.message");
@NonNls public static final String DELETED_LIBRARIES = "lib";
public final Map<Library, Set<String>> myLibraryDependencyCache = new HashMap<Library, Set<String>>();
public final Map<Sdk, Set<String>> myJdkDependencyCache = new HashMap<Sdk, Set<String>>();
public final Map<Module, Map<String, Set<String>>> myValidityCache = new HashMap<Module, Map<String, Set<String>>>();
public final Map<Library, ValidityLevel> myLibraryPathValidityCache = new HashMap<Library, ValidityLevel>(); //can be invalidated on startup only
public final Map<Module, Set<String>> myModulesDependencyCache = new HashMap<Module, Set<String>>();
private final ModuleManager myModuleManager;
private final ProjectStructureDaemonAnalyzer myDaemonAnalyzer;
public final ModulesConfigurator myModulesConfigurator;
private boolean myDisposed;
public final Alarm myUpdateDependenciesAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
public final Alarm myReloadProjectAlarm = new Alarm();
private final List<Runnable> myCacheUpdaters = new ArrayList<Runnable>();
public final Map<String, LibrariesModifiableModel> myLevel2Providers = new THashMap<String, LibrariesModifiableModel>();
private final Project myProject;
public StructureConfigurableContext(Project project, final ModuleManager moduleManager, final ModulesConfigurator modulesConfigurator) {
public StructureConfigurableContext(Project project, final ModulesConfigurator modulesConfigurator) {
myProject = project;
myModuleManager = moduleManager;
myModulesConfigurator = modulesConfigurator;
Disposer.register(project, this);
myDaemonAnalyzer = new ProjectStructureDaemonAnalyzer(this);
}
@Nullable
public Set<String> getCachedDependencies(final Object selectedObject, boolean force) {
if (selectedObject instanceof Library){
final Library library = (Library)selectedObject;
if (myLibraryDependencyCache.containsKey(library)){
return myLibraryDependencyCache.get(library);
}
} else if (selectedObject instanceof Sdk){
final Sdk projectJdk = (Sdk)selectedObject;
if (myJdkDependencyCache.containsKey(projectJdk)){
return myJdkDependencyCache.get(projectJdk);
}
} else if (selectedObject instanceof Module) {
final Module module = (Module)selectedObject;
if (myModulesDependencyCache.containsKey(module)) {
return myModulesDependencyCache.get(module);
}
}
if (force){
LOG.assertTrue(ApplicationManager.getApplication().isDispatchThread());
final Set<String> dep = getDependencies(selectedObject);
updateCache(selectedObject, dep);
return dep;
} else {
myUpdateDependenciesAlarm.addRequest(new Runnable(){
public void run() {
final Set<String> dep = getDependencies(selectedObject);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (!myDisposed) {
updateCache(selectedObject, dep);
fireOnCacheChanged();
}
}
});
}
}, 100);
return null;
}
public Project getProject() {
return myProject;
}
private void updateCache(final Object selectedObject, final Set<String> dep) {
if (selectedObject instanceof Library) {
myLibraryDependencyCache.put((Library)selectedObject, dep);
}
else if (selectedObject instanceof Sdk) {
myJdkDependencyCache.put((Sdk)selectedObject, dep);
}
else if (selectedObject instanceof Module) {
myModulesDependencyCache.put((Module)selectedObject, dep);
}
}
private Set<String> getDependencies(final Condition<OrderEntry> condition) {
final Set<String> result = new TreeSet<String>();
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final Module[] modules = myModulesConfigurator.getModules();
for (final Module module : modules) {
final ModuleEditor moduleEditor = myModulesConfigurator.getModuleEditor(module);
if (moduleEditor != null) {
final OrderEntry[] entries = moduleEditor.getOrderEntries();
for (OrderEntry entry : entries) {
if (myDisposed) return;
if (condition.value(entry)) {
result.add(module.getName());
break;
}
}
}
}
}
});
return result;
}
@Nullable
private Set<String> getDependencies(final Object selectedObject) {
if (selectedObject instanceof Module) {
return getDependencies(new Condition<OrderEntry>() {
public boolean value(final OrderEntry orderEntry) {
return orderEntry instanceof ModuleOrderEntry && Comparing.equal(((ModuleOrderEntry)orderEntry).getModule(), selectedObject);
}
});
}
else if (selectedObject instanceof Library) {
Library library = (Library)selectedObject;
if (library.getTable() == null) { //module library navigation
HashSet<String> deps = new HashSet<String>();
Module module = ((LibraryImpl)library).getModule();
if (module != null) {
deps.add(module.getName());
}
return deps;
}
return getDependencies(new Condition<OrderEntry>() {
@SuppressWarnings({"SimplifiableIfStatement"})
public boolean value(final OrderEntry orderEntry) {
if (orderEntry instanceof LibraryOrderEntry){
final LibraryImpl library = (LibraryImpl)((LibraryOrderEntry)orderEntry).getLibrary();
if (Comparing.equal(library, selectedObject)) return true;
return library != null && Comparing.equal(library.getSource(), selectedObject);
}
return false;
}
});
}
else if (selectedObject instanceof Sdk) {
return getDependencies(new Condition<OrderEntry>() {
public boolean value(final OrderEntry orderEntry) {
return orderEntry instanceof JdkOrderEntry && Comparing.equal(((JdkOrderEntry)orderEntry).getJdk(), selectedObject);
}
});
}
return null;
public ProjectStructureDaemonAnalyzer getDaemonAnalyzer() {
return myDaemonAnalyzer;
}
public void dispose() {
clearCaches();
myDisposed = true;
}
public void invalidateModules(final Set<String> modules) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (modules != null) {
for (String module : modules) {
myValidityCache.remove(myModuleManager.findModuleByName(module));
}
}
}
});
}
public void invalidateModuleName(final Module module) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final Map<String, Set<String>> problems = myValidityCache.remove(module);
if (problems != null) {
fireOnCacheChanged();
}
}
});
}
public ModulesConfigurator getModulesConfigurator() {
return myModulesConfigurator;
}
public void clearCaches() {
myJdkDependencyCache.clear();
myLibraryDependencyCache.clear();
myValidityCache.clear();
myLibraryPathValidityCache.clear();
myModulesDependencyCache.clear();
myCacheUpdaters.clear();
}
public void clearCaches(final Module module, final List<Library> chosen) {
LOG.assertTrue(ApplicationManager.getApplication().isDispatchThread());
for (Library library : chosen) {
myLibraryDependencyCache.remove(library);
}
myValidityCache.remove(module);
fireOnCacheChanged();
}
public void clearCaches(final Module module, final Sdk oldJdk, final Sdk selectedModuleJdk) {
LOG.assertTrue(ApplicationManager.getApplication().isDispatchThread());
myJdkDependencyCache.remove(oldJdk);
myJdkDependencyCache.remove(selectedModuleJdk);
myValidityCache.remove(module);
fireOnCacheChanged();
}
public void clearCaches(final OrderEntry entry) {
LOG.assertTrue(ApplicationManager.getApplication().isDispatchThread());
if (entry instanceof ModuleOrderEntry) {
final Module module = ((ModuleOrderEntry)entry).getModule();
myValidityCache.remove(module);
myModulesDependencyCache.remove(module);
} else if (entry instanceof JdkOrderEntry) {
invalidateModules(myJdkDependencyCache.remove(((JdkOrderEntry)entry).getJdk()));
} else if (entry instanceof LibraryOrderEntry) {
invalidateModules(myLibraryDependencyCache.remove(((LibraryOrderEntry)entry).getLibrary()));
}
fireOnCacheChanged();
}
public ValidityLevel isInvalid(final Object object) {
if (object instanceof Module){
final Module module = (Module)object;
if (myValidityCache.containsKey(module)) {
boolean valid = myValidityCache.get(module) == null;
return valid ? ValidityLevel.VALID : ValidityLevel.ERROR;
}
myUpdateDependenciesAlarm.addRequest(new Runnable(){
public void run() {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
updateModuleValidityCache(module);
}
});
}
}, 100);
} else if (object instanceof LibraryEx) {
final LibraryEx library = (LibraryEx)object;
if (myLibraryPathValidityCache.containsKey(library)) return myLibraryPathValidityCache.get(library);
myUpdateDependenciesAlarm.addRequest(new Runnable(){
public void run() {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
updateLibraryValidityCache(library);
}
});
}
}, 100);
}
return ValidityLevel.VALID;
}
private void updateLibraryValidityCache(final LibraryEx library) {
if (myLibraryPathValidityCache.containsKey(library)) return; //do not check twice
ValidityLevel level = ValidityLevel.VALID;
if (!(library.allPathsValid(JavadocOrderRootType.getInstance()) && library.allPathsValid(OrderRootType.SOURCES))) {
level = ValidityLevel.WARNING;
}
if (!library.allPathsValid(OrderRootType.CLASSES)) {
level = ValidityLevel.ERROR;
}
final ValidityLevel finalLevel = level;
SwingUtilities.invokeLater(new Runnable(){
public void run() {
if (!myDisposed){
myLibraryPathValidityCache.put(library, finalLevel);
fireOnCacheChanged();
}
}
});
}
private void updateModuleValidityCache(final Module module) {
if (myValidityCache.containsKey(module)) return; //do not check twice
if (myDisposed) return;
Map<String, Set<String>> problems = null;
final ModifiableModuleModel moduleModel = myModulesConfigurator.getModuleModel();
final Module[] all = moduleModel.getModules();
for (Module each : all) {
if (each != module && getRealName(each).equals(getRealName(module))) {
problems = new HashMap<String, Set<String>>();
problems.put(DUPLICATE_MODULE_NAME, null);
break;
}
}
final ModuleRootModel rootModel = myModulesConfigurator.getRootModel(module);
if (rootModel == null) return; //already disposed
final OrderEntry[] entries = rootModel.getOrderEntries();
for (OrderEntry entry : entries) {
if (myDisposed) return;
if (!entry.isValid()){
if (problems == null) {
problems = new HashMap<String, Set<String>>();
}
if (entry instanceof JdkOrderEntry && ((JdkOrderEntry)entry).getJdkName() == null) {
problems.put(NO_JDK, null);
} else {
Set<String> deletedLibraries = problems.get(DELETED_LIBRARIES);
if (deletedLibraries == null){
deletedLibraries = new HashSet<String>();
problems.put(DELETED_LIBRARIES, deletedLibraries);
}
deletedLibraries.add(entry.getPresentableName());
}
}
}
final Map<String, Set<String>> finalProblems = problems;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (!myDisposed) {
myValidityCache.put(module, finalProblems);
fireOnCacheChanged();
}
}
});
}
public Module[] getModules() {
return myModulesConfigurator.getModules();
}
@@ -382,38 +75,6 @@ public class StructureConfigurableContext implements Disposable {
return newName != null ? newName : module.getName();
}
public boolean isUnused(final Object object) {
if (object == null) return false;
if (object instanceof Module){
getCachedDependencies(object, false);
return false;
}
if (object instanceof Sdk) {
return false;
}
if (object instanceof Library) {
final LibraryTable libraryTable = ((Library)object).getTable();
if (libraryTable == null || !libraryTable.getTableLevel().equals(LibraryTablesRegistrar.PROJECT_LEVEL)) {
return false;
}
}
final Set<String> dependencies = getCachedDependencies(object, false);
return dependencies != null && dependencies.isEmpty();
}
private void fireOnCacheChanged() {
final Runnable[] all = myCacheUpdaters.toArray(new Runnable[myCacheUpdaters.size()]);
for (Runnable each : all) {
each.run();
}
}
public void addReloadProjectRequest(final Runnable runnable) {
myReloadProjectAlarm.cancelAllRequests();
myReloadProjectAlarm.addRequest(runnable, 300, ModalityState.NON_MODAL);
}
public void resetLibraries() {
final LibraryTablesRegistrar tablesRegistrar = LibraryTablesRegistrar.getInstance();
@@ -488,12 +149,8 @@ public class StructureConfigurableContext implements Disposable {
public void reset() {
myDisposed = false;
myDaemonAnalyzer.reset();
resetLibraries();
myModulesConfigurator.resetModuleEditors();
}
public void addCacheUpdateListener(Runnable runnable) {
myCacheUpdaters.add(runnable);
}
}
@@ -0,0 +1,78 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.JavadocOrderRootType;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.impl.libraries.LibraryEx;
import com.intellij.openapi.roots.impl.libraries.LibraryImpl;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* @author nik
*/
public class LibraryProjectStructureElement extends ProjectStructureElement {
private final Library myLibrary;
public LibraryProjectStructureElement(@NotNull StructureConfigurableContext context, @NotNull Library library) {
super(context);
myLibrary = library;
}
public Library getLibrary() {
return myLibrary;
}
@Override
public void check(ProjectStructureProblemsHolder problemsHolder) {
final LibraryEx library = (LibraryEx)myLibrary;
final String libraryName = myLibrary.getName();//todo[nik] get modified name?
if (!library.allPathsValid(OrderRootType.CLASSES)) {
problemsHolder.addError(ProjectBundle.message("project.roots.tooltip.library.misconfigured", libraryName));
}
else if (!library.allPathsValid(JavadocOrderRootType.getInstance()) || !library.allPathsValid(OrderRootType.SOURCES)) {
problemsHolder.addWarning(ProjectBundle.message("project.roots.tooltip.library.misconfigured", libraryName));
}
}
@Override
public List<ProjectStructureElementUsage> getUsagesInElement() {
return Collections.emptyList();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof LibraryProjectStructureElement)) return false;
return getSourceOrThis().equals(((LibraryProjectStructureElement)o).getSourceOrThis());
}
@NotNull
private Library getSourceOrThis() {
final Library source = ((LibraryImpl)myLibrary).getSource();
return source != null ? source : myLibrary;
}
@Override
public int hashCode() {
return getSourceOrThis().hashCode();
}
@Override
public String toString() {
return "library:" + myLibrary.getName();
}
@Override
public boolean highlightIfUnused() {
final LibraryTable libraryTable = myLibrary.getTable();
return libraryTable != null && LibraryTablesRegistrar.PROJECT_LEVEL.equals(libraryTable.getTableLevel());
}
}
@@ -0,0 +1,108 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import com.intellij.openapi.module.ModifiableModuleModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.ui.configuration.ModuleEditor;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* @author nik
*/
public class ModuleProjectStructureElement extends ProjectStructureElement {
private Module myModule;
public ModuleProjectStructureElement(@NotNull StructureConfigurableContext context, @NotNull Module module) {
super(context);
myModule = module;
}
public Module getModule() {
return myModule;
}
@Override
public void check(ProjectStructureProblemsHolder problemsHolder) {
final ModifiableModuleModel moduleModel = myContext.getModulesConfigurator().getModuleModel();
final Module[] all = moduleModel.getModules();
for (Module each : all) {
if (each != myModule && myContext.getRealName(each).equals(myContext.getRealName(myModule))) {
problemsHolder.addError(ProjectBundle.message("project.roots.module.duplicate.name.message"));
break;
}
}
final ModuleRootModel rootModel = myContext.getModulesConfigurator().getRootModel(myModule);
if (rootModel == null) return; //already disposed
final OrderEntry[] entries = rootModel.getOrderEntries();
for (OrderEntry entry : entries) {
if (!entry.isValid()){
if (entry instanceof JdkOrderEntry && ((JdkOrderEntry)entry).getJdkName() == null) {
problemsHolder.addError(ProjectBundle.message("project.roots.module.jdk.problem.message"));
} else {
problemsHolder.addError(ProjectBundle.message("project.roots.library.problem.message", entry.getPresentableName()));
}
}
}
}
@Override
public List<ProjectStructureElementUsage> getUsagesInElement() {
final List<ProjectStructureElementUsage> usages = new ArrayList<ProjectStructureElementUsage>();
final ModuleEditor moduleEditor = myContext.getModulesConfigurator().getModuleEditor(myModule);
if (moduleEditor != null) {
for (OrderEntry entry : moduleEditor.getOrderEntries()) {
if (entry instanceof ModuleOrderEntry) {
final Module module = ((ModuleOrderEntry)entry).getModule();
if (module != null) {
usages.add(new UsageInModuleClasspath(myContext, this, new ModuleProjectStructureElement(myContext, module)));
}
}
else if (entry instanceof LibraryOrderEntry) {
final Library library = ((LibraryOrderEntry)entry).getLibrary();
if (library != null) {
usages.add(new UsageInModuleClasspath(myContext, this, new LibraryProjectStructureElement(myContext, library)));
}
}
else if (entry instanceof JdkOrderEntry) {
final Sdk jdk = ((JdkOrderEntry)entry).getJdk();
if (jdk != null) {
usages.add(new UsageInModuleClasspath(myContext, this, new SdkProjectStructureElement(myContext, jdk)));
}
}
}
}
return usages;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ModuleProjectStructureElement)) return false;
return myModule.equals(((ModuleProjectStructureElement)o).myModule);
}
@Override
public int hashCode() {
return myModule.hashCode();
}
@Override
public String toString() {
return "module:" + myModule.getName();
}
@Override
public boolean highlightIfUnused() {
return false;
}
}
@@ -0,0 +1,243 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.MultiValuesMap;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.*;
/**
* @author nik
*/
public class ProjectStructureDaemonAnalyzer implements Disposable {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.validation.ProjectStructureDaemonAnalyzer");
private Map<ProjectStructureElement, ProjectStructureProblemsHolder> myProblemHolders = new HashMap<ProjectStructureElement, ProjectStructureProblemsHolder>();
private MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage> mySourceElement2Usages = new MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage>();
private MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage> myContainingElement2Usages = new MultiValuesMap<ProjectStructureElement, ProjectStructureElementUsage>();
private Set<ProjectStructureElement> myElementWithNotCalculatedUsages = new HashSet<ProjectStructureElement>();
private MergingUpdateQueue myAnalyzerQueue;
private List<Runnable> myListeners = new ArrayList<Runnable>();
private boolean myDisposed;
public ProjectStructureDaemonAnalyzer(StructureConfigurableContext context) {
Disposer.register(context, this);
myAnalyzerQueue = new MergingUpdateQueue("Project Structure Daemon Analyzer", 300, false, null, this, null, false);
}
private void doUpdate(final ProjectStructureElement element, final boolean check, final boolean collectUsages) {
if (myDisposed) return;
if (check) {
doCheck(element);
}
if (collectUsages) {
doCollectUsages(element);
}
}
private void doCheck(final ProjectStructureElement element) {
final ProjectStructureProblemsHolder problemsHolder = new ProjectStructureProblemsHolder();
new ReadAction() {
protected void run(final Result result) {
if (LOG.isDebugEnabled()) {
LOG.debug("checking " + element);
}
element.check(problemsHolder);
}
}.execute();
invokeLater(new Runnable() {
public void run() {
if (LOG.isDebugEnabled()) {
LOG.debug("updating problems for " + element);
}
myProblemHolders.put(element, problemsHolder);
fireProblemsUpdated();
}
});
}
private void fireProblemsUpdated() {
Runnable[] listeners = myListeners.toArray(new Runnable[myListeners.size()]);
for (Runnable listener : listeners) {
listener.run();
}
}
private void doCollectUsages(final ProjectStructureElement element) {
final List<ProjectStructureElementUsage> usages = new ReadAction<List<ProjectStructureElementUsage>>() {
protected void run(final Result<List<ProjectStructureElementUsage>> result) {
if (LOG.isDebugEnabled()) {
LOG.debug("collecting usages in " + element);
}
result.setResult(element.getUsagesInElement());
}
}.execute().getResultObject();
invokeLater(new Runnable() {
public void run() {
if (LOG.isDebugEnabled()) {
LOG.debug("updating usages for " + element);
}
updateUsages(element, usages);
fireProblemsUpdated();
}
});
}
private void updateUsages(ProjectStructureElement element, List<ProjectStructureElementUsage> usages) {
removeUsagesInElement(element);
for (ProjectStructureElementUsage usage : usages) {
addUsage(usage);
}
myElementWithNotCalculatedUsages.remove(element);
}
private static void invokeLater(Runnable runnable) {
SwingUtilities.invokeLater(runnable);
}
public void queueUpdate(@NotNull final ProjectStructureElement element) {
queueUpdate(element, true, true);
}
public void queueUpdate(@NotNull final ProjectStructureElement element, final boolean check, final boolean collectUsages) {
if (LOG.isDebugEnabled()) {
LOG.debug("start " + (check ? "checking " : "") + (collectUsages ? "collecting usages " : "") + "for " + element);
}
if (collectUsages) {
myElementWithNotCalculatedUsages.add(element);
}
myAnalyzerQueue.queue(new AnalyzeElementUpdate(element, check, collectUsages));
}
public void removeElement(ProjectStructureElement element) {
myElementWithNotCalculatedUsages.remove(element);
myProblemHolders.remove(element);
final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.removeAll(element);
if (usages != null) {
for (ProjectStructureElementUsage usage : usages) {
myProblemHolders.remove(usage.getContainingElement());
}
}
removeUsagesInElement(element);
fireProblemsUpdated();
}
public boolean isUnused(ProjectStructureElement element) {
if (!element.highlightIfUnused()) {
return false;
}
if (!myElementWithNotCalculatedUsages.isEmpty()) {
return false;
}
final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.get(element);
return usages == null || usages.isEmpty();
}
private void removeUsagesInElement(ProjectStructureElement element) {
final Collection<ProjectStructureElementUsage> usages = myContainingElement2Usages.removeAll(element);
if (usages != null) {
for (ProjectStructureElementUsage usage : usages) {
mySourceElement2Usages.remove(usage.getSourceElement(), usage);
}
}
}
private void addUsage(@NotNull ProjectStructureElementUsage usage) {
mySourceElement2Usages.put(usage.getSourceElement(), usage);
myContainingElement2Usages.put(usage.getContainingElement(), usage);
}
public void stop() {
LOG.debug("analyzer stopped");
myAnalyzerQueue.cancelAllUpdates();
clearCaches();
myAnalyzerQueue.deactivate();
}
public void clearCaches() {
LOG.debug("clear caches");
myProblemHolders.clear();
mySourceElement2Usages.clear();
myContainingElement2Usages.clear();
myElementWithNotCalculatedUsages.clear();
}
public void clearAllProblems() {
myProblemHolders.clear();
fireProblemsUpdated();
}
public void dispose() {
myDisposed = true;
myAnalyzerQueue.cancelAllUpdates();
}
public ProjectStructureProblemsHolder getProblemsHolder(ProjectStructureElement element) {
return myProblemHolders.get(element);
}
public Collection<ProjectStructureElementUsage> getUsages(ProjectStructureElement selected) {
ProjectStructureElement[] elements = myElementWithNotCalculatedUsages.toArray(new ProjectStructureElement[myElementWithNotCalculatedUsages.size()]);
for (ProjectStructureElement element : elements) {
updateUsages(element, element.getUsagesInElement());
}
fireProblemsUpdated();
final Collection<ProjectStructureElementUsage> usages = mySourceElement2Usages.get(selected);
return usages != null ? usages : Collections.<ProjectStructureElementUsage>emptyList();
}
public void addListener(Runnable runnable) {
LOG.debug("listener added " + runnable);
myListeners.add(runnable);
}
public void reset() {
LOG.debug("analyzer started");
myAnalyzerQueue.activate();
myAnalyzerQueue.queue(new Update("reset") {
public void run() {
myDisposed = false;
}
});
}
private class AnalyzeElementUpdate extends Update {
private final ProjectStructureElement myElement;
private final boolean myCheck;
private final boolean myCollectUsages;
private final Object[] myEqualityObjects;
public AnalyzeElementUpdate(ProjectStructureElement element, boolean check, boolean collectUsages) {
super(element);
myElement = element;
myCheck = check;
myCollectUsages = collectUsages;
myEqualityObjects = new Object[]{myElement, myCheck, myCollectUsages};
}
@Override
public boolean canEat(Update update) {
if (!(update instanceof AnalyzeElementUpdate)) return false;
final AnalyzeElementUpdate other = (AnalyzeElementUpdate)update;
return myElement.equals(other.myElement) && (!other.myCheck || myCheck) && (!other.myCollectUsages || myCollectUsages);
}
@Override
public Object[] getEqualityObjects() {
return myEqualityObjects;
}
public void run() {
doUpdate(myElement, myCheck, myCollectUsages);
}
}
}
@@ -0,0 +1,29 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author nik
*/
public abstract class ProjectStructureElement {
protected final StructureConfigurableContext myContext;
protected ProjectStructureElement(@NotNull StructureConfigurableContext context) {
myContext = context;
}
public abstract void check(ProjectStructureProblemsHolder problemsHolder);
public abstract List<ProjectStructureElementUsage> getUsagesInElement();
public abstract boolean highlightIfUnused();
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
}
@@ -0,0 +1,24 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import javax.swing.*;
/**
* @author nik
*/
public abstract class ProjectStructureElementUsage {
public abstract ProjectStructureElement getSourceElement();
public abstract ProjectStructureElement getContainingElement();
public abstract String getPresentableName();
public abstract void navigate();
@Override
public abstract int hashCode();
@Override
public abstract boolean equals(Object obj);
public abstract Icon getIcon();
}
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
/**
* @author nik
*/
public class ProjectStructureProblemDescription {
private String myMessage;
private final Severity mySeverity;
public ProjectStructureProblemDescription(String message, Severity severity) {
myMessage = message;
mySeverity = severity;
}
public String getMessage() {
return myMessage;
}
public Severity getSeverity() {
return mySeverity;
}
public enum Severity { ERROR, WARNING }
}
@@ -0,0 +1,57 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import com.intellij.util.SmartList;
import com.intellij.util.StringBuilderSpinAllocator;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author nik
*/
public class ProjectStructureProblemsHolder {
private List<ProjectStructureProblemDescription> myProblemDescriptions;
public void addError(String message) {
addProblem(message, ProjectStructureProblemDescription.Severity.ERROR);
}
public void addWarning(String message) {
addProblem(message, ProjectStructureProblemDescription.Severity.WARNING);
}
public void addProblem(String description, ProjectStructureProblemDescription.Severity severity) {
if (myProblemDescriptions == null) {
myProblemDescriptions = new SmartList<ProjectStructureProblemDescription>();
}
myProblemDescriptions.add(new ProjectStructureProblemDescription(description, severity));
}
@Nullable
public ProjectStructureProblemDescription.Severity getSeverity() {
if (myProblemDescriptions == null || myProblemDescriptions.isEmpty()) {
return null;
}
for (ProjectStructureProblemDescription description : myProblemDescriptions) {
if (description.getSeverity() == ProjectStructureProblemDescription.Severity.ERROR) {
return ProjectStructureProblemDescription.Severity.ERROR;
}
}
return ProjectStructureProblemDescription.Severity.WARNING;
}
public String composeTooltipMessage() {
final StringBuilder buf = StringBuilderSpinAllocator.alloc();
try {
if (myProblemDescriptions != null) {
for (ProjectStructureProblemDescription problemDescription : myProblemDescriptions) {
buf.append(problemDescription.getMessage()).append("\n");
}
}
return buf.toString();
}
finally {
StringBuilderSpinAllocator.dispose(buf);
}
}
}
@@ -0,0 +1,55 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import java.util.Collections;
import java.util.List;
/**
* @author nik
*/
public class SdkProjectStructureElement extends ProjectStructureElement {
private Sdk mySdk;
public SdkProjectStructureElement(StructureConfigurableContext context, Sdk sdk) {
super(context);
mySdk = sdk;
}
public Sdk getSdk() {
return mySdk;
}
@Override
public void check(ProjectStructureProblemsHolder problemsHolder) {
}
@Override
public List<ProjectStructureElementUsage> getUsagesInElement() {
return Collections.emptyList();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SdkProjectStructureElement)) return false;
return mySdk.equals(((SdkProjectStructureElement)o).mySdk);
}
@Override
public int hashCode() {
return mySdk.hashCode();
}
@Override
public String toString() {
return "sdk:" + mySdk.getName();
}
@Override
public boolean highlightIfUnused() {
return false;
}
}
@@ -0,0 +1,86 @@
package com.intellij.openapi.roots.ui.configuration.projectRoot.daemon;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ModuleRootModel;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.impl.OrderEntryUtil;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ModuleStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.projectRoot.StructureConfigurableContext;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* @author nik
*/
public class UsageInModuleClasspath extends ProjectStructureElementUsage {
private final StructureConfigurableContext myContext;
private final ModuleProjectStructureElement myContainingElement;
private ProjectStructureElement mySourceElement;
private Module myModule;
public UsageInModuleClasspath(@NotNull StructureConfigurableContext context, @NotNull ModuleProjectStructureElement containingElement, ProjectStructureElement sourceElement) {
myContext = context;
myContainingElement = containingElement;
myModule = containingElement.getModule();
mySourceElement = sourceElement;
}
@Override
public ProjectStructureElement getSourceElement() {
return mySourceElement;
}
@Override
public ProjectStructureElement getContainingElement() {
return myContainingElement;
}
@Override
public String getPresentableName() {
return myModule.getName();
}
public String getModuleName() {
return myModule.getName();
}
@Override
public void navigate() {
ModulesConfigurator modulesConfigurator = myContext.getModulesConfigurator();
ModuleRootModel rootModel = modulesConfigurator.getRootModel(myModule);
OrderEntry entry;
if (mySourceElement instanceof LibraryProjectStructureElement) {
entry = OrderEntryUtil.findLibraryOrderEntry(rootModel, ((LibraryProjectStructureElement)mySourceElement).getLibrary());
}
else if (mySourceElement instanceof ModuleProjectStructureElement) {
entry = OrderEntryUtil.findModuleOrderEntry(rootModel, ((ModuleProjectStructureElement)mySourceElement).getModule());
}
else if (mySourceElement instanceof SdkProjectStructureElement) {
entry = OrderEntryUtil.findJdkOrderEntry(rootModel, ((SdkProjectStructureElement)mySourceElement).getSdk());
}
else {
entry = null;
}
ModuleStructureConfigurable.getInstance(myContext.getProject()).selectOrderEntry(myModule, entry);
}
@Override
public int hashCode() {
return myModule.hashCode()*31 + mySourceElement.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof UsageInModuleClasspath && myModule.equals(((UsageInModuleClasspath)obj).myModule)
&& mySourceElement.equals(((UsageInModuleClasspath)obj).mySourceElement);
}
@Override
public Icon getIcon() {
return myModule.getModuleType().getNodeIcon(false);
}
}
@@ -461,14 +461,14 @@ public abstract class MasterDetailsComponent implements Configurable, Persistent
}
public void fireItemsChangeListener(final Object editableObject) {
for (ItemsChangeListener listner : myListners) {
listner.itemChanged(editableObject);
for (ItemsChangeListener listener : myListners) {
listener.itemChanged(editableObject);
}
}
private void fireItemsChangedExternally() {
for (ItemsChangeListener listner : myListners) {
listner.itemsExternallyChanged();
for (ItemsChangeListener listener : myListners) {
listener.itemsExternallyChanged();
}
}
@@ -338,7 +338,7 @@ choose.modules.dialog.title=Choose modules
choose.modules.dialog.description=<html>Library '' {0}'' will be added to the selected modules.</html>
project.roots.module.jdk.problem.message=Module JDK is not defined.
project.roots.module.duplicate.name.message=Duplicate module name
project.roots.library.problem.message=Classpath {0, choice, 1#entry is invalid: |2#entries are invalid: \n}
project.roots.library.problem.message=Classpath entry is invalid: {0}
project.roots.project.display.name=Project
project.roots.global.resources.display.name=Global Resources
project.roots.modules.display.name=Modules