library root detection: allow to detect several root types in one detector

This commit is contained in:
nik
2012-03-30 15:30:04 +04:00
parent e614b0aabc
commit a0552e9d11
11 changed files with 200 additions and 77 deletions
@@ -45,7 +45,7 @@ public class MarkLibraryRootAction extends AnAction {
final List<VirtualFile> jars = getRoots(e);
if (jars.isEmpty()) return;
final List<OrderRoot> roots = RootDetectionUtil.detectRoots(jars, null, project, new DefaultLibraryRootsComponentDescriptor().getRootDetectors(), true);
final List<OrderRoot> roots = RootDetectionUtil.detectRoots(jars, null, project, new DefaultLibraryRootsComponentDescriptor());
new CreateLibraryFromFilesDialog(project, roots).show();
}
@@ -19,6 +19,7 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.ui.OrderRoot;
import com.intellij.openapi.roots.libraries.ui.RootDetector;
import com.intellij.openapi.roots.libraries.ui.impl.LibraryRootsDetectorImpl;
import com.intellij.openapi.roots.libraries.ui.impl.RootDetectionUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -56,7 +57,8 @@ public class PathUIUtils {
*/
public static VirtualFile[] scanAndSelectDetectedJavaSourceRoots(Component parentComponent, final VirtualFile[] rootCandidates) {
final List<OrderRoot> orderRoots = RootDetectionUtil.detectRoots(Arrays.asList(rootCandidates), parentComponent, null,
Collections.singletonList(JAVA_SOURCE_ROOT_DETECTOR), false);
new LibraryRootsDetectorImpl(Collections.singletonList(JAVA_SOURCE_ROOT_DETECTOR)),
new OrderRootType[0]);
final List<VirtualFile> result = new ArrayList<VirtualFile>();
for (OrderRoot root : orderRoots) {
result.add(root.getFile());
@@ -168,7 +168,7 @@ public class CreateModuleLibraryChooser implements ClasspathElementChooser<Libra
rootsComponentDescriptor = myDefaultDescriptor;
}
List<OrderRoot> chosenRoots = RootDetectionUtil.detectRoots(Arrays.asList(files), myParentComponent, myModule.getProject(),
rootsComponentDescriptor.getRootDetectors(), true);
rootsComponentDescriptor);
final List<OrderRoot> roots = filterAlreadyAdded(chosenRoots);
if (roots.isEmpty()) {
@@ -173,7 +173,6 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent
}
});
final List<? extends RootDetector> detectors = myDescriptor.getRootDetectors();
toolbarDecorator.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
@@ -188,9 +187,7 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent
private AnAction[] getActions() {
List<AnAction> actions = new ArrayList<AnAction>();
if (!detectors.isEmpty()) {
actions.add(new AttachFilesAction(detectors, ProjectBundle.message("button.text.attach.files")));
}
actions.add(new AttachFilesAction(ProjectBundle.message("button.text.attach.files")));
for (AttachRootButtonDescriptor descriptor : myDescriptor.createAttachButtons()) {
actions.add(new AttachItemAction(descriptor, descriptor.getButtonText()));
}
@@ -348,11 +345,8 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent
}
private class AttachFilesAction extends AttachItemActionBase {
private final List<? extends RootDetector> myDetectors;
public AttachFilesAction(List<? extends RootDetector> detectors, String title) {
public AttachFilesAction(String title) {
super(title);
myDetectors = detectors;
}
@Override
@@ -368,7 +362,7 @@ public class LibraryRootsComponent implements Disposable, LibraryEditorComponent
final VirtualFile[] files = FileChooser.chooseFiles(myPanel, chooserDescriptor, initialSelection);
if (files.length == 0) return Collections.emptyList();
return RootDetectionUtil.detectRoots(Arrays.asList(files), myPanel, myProject, myDetectors, true);
return RootDetectionUtil.detectRoots(Arrays.asList(files), myPanel, myProject, myDescriptor);
}
}
@@ -24,7 +24,6 @@ import com.intellij.openapi.roots.libraries.LibraryTypeService;
import com.intellij.openapi.roots.libraries.NewLibraryConfiguration;
import com.intellij.openapi.roots.libraries.ui.LibraryRootsComponentDescriptor;
import com.intellij.openapi.roots.libraries.ui.OrderRoot;
import com.intellij.openapi.roots.libraries.ui.RootDetector;
import com.intellij.openapi.roots.libraries.ui.impl.RootDetectionUtil;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.LibraryEditor;
import com.intellij.openapi.util.io.FileUtil;
@@ -35,7 +34,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
@@ -60,22 +58,15 @@ public class LibraryTypeServiceImpl extends LibraryTypeService {
@Nullable VirtualFile contextDirectory,
LibraryType<?> type,
final Project project) {
final List<? extends RootDetector> rootDetectors = descriptor.getRootDetectors();
final List<OrderRoot> roots;
if (!rootDetectors.isEmpty()) {
final FileChooserDescriptor chooserDescriptor = descriptor.createAttachFilesChooserDescriptor();
chooserDescriptor.setTitle("Select Library Files");
final VirtualFile[] rootCandidates = FileChooser.chooseFiles(parentComponent, chooserDescriptor, contextDirectory);
if (rootCandidates.length == 0) {
return null;
}
final FileChooserDescriptor chooserDescriptor = descriptor.createAttachFilesChooserDescriptor();
chooserDescriptor.setTitle("Select Library Files");
final VirtualFile[] rootCandidates = FileChooser.chooseFiles(parentComponent, chooserDescriptor, contextDirectory);
if (rootCandidates.length == 0) {
return null;
}
roots = RootDetectionUtil.detectRoots(Arrays.asList(rootCandidates), parentComponent, project, rootDetectors, true);
if (roots.isEmpty()) return null;
}
else {
roots = Collections.emptyList();
}
final List<OrderRoot> roots = RootDetectionUtil.detectRoots(Arrays.asList(rootCandidates), parentComponent, project, descriptor);
if (roots.isEmpty()) return null;
String name = suggestLibraryName(roots);
return doCreate(type, name, roots);
}
@@ -15,6 +15,7 @@ package com.intellij.openapi.roots.libraries.ui;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.ui.impl.LibraryRootsDetectorImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,14 +37,29 @@ public abstract class LibraryRootsComponentDescriptor {
public abstract OrderRootTypePresentation getRootTypePresentation(@NotNull OrderRootType type);
/**
* Provides root detectors for 'Attach Files' button. They will be used to automatically assign {@link OrderRootType}s for selected files.
* Also these detectors are used when a new library is created so the list must not be empty.
* Provides separate detectors for root types supported by the library type.
*
* @return non-empty list of {@link RootDetector}'s implementations
*/
@NotNull
public abstract List<? extends RootDetector> getRootDetectors();
/**
* Provides root detector for 'Attach Files' button. It will be used to automatically assign {@link OrderRootType}s for selected files.
* Also this detector is used when a new library is created
*
* @return {@link LibraryRootsDetector}'s implementation
*/
@NotNull
public LibraryRootsDetector getRootsDetector() {
final List<? extends RootDetector> detectors = getRootDetectors();
if (detectors.isEmpty()) {
throw new IllegalStateException("Detectors list is empty for " + this);
}
return new LibraryRootsDetectorImpl(detectors);
}
/**
* @return descriptor for the file chooser which will be shown when 'Attach Files' button is pressed
*/
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.roots.libraries.ui;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* @author nik
*/
public abstract class LibraryRootsDetector {
/**
* Find suitable roots in {@code rootCandidate} or its descendants.
* @param rootCandidate file selected in the file chooser by user
* @param progressIndicator can be used to show information about the progress and to abort searching if process is cancelled
* @return suitable roots
*/
public abstract Collection<OrderRoot> detectRoots(@NotNull VirtualFile rootCandidate, @NotNull ProgressIndicator progressIndicator);
/**
* @return presentable name for the root type or {@code null} if the root type isn't supported by this detector
*/
@Nullable
public abstract String getRootTypeName(@NotNull OrderRootType rootType, boolean isJarDirectory);
}
@@ -141,7 +141,7 @@ public class DetectedRootsChooserDialog extends DialogWrapper {
private static CheckedTreeNode createTree(List<SuggestedChildRootInfo> suggestedRoots) {
TObjectIntHashMap<VirtualFile> rootTypesCount = new TObjectIntHashMap<VirtualFile>();
for (SuggestedChildRootInfo suggestedRoot : suggestedRoots) {
final VirtualFile root = suggestedRoot.getSuggestedRoot();
final VirtualFile root = suggestedRoot.getSuggestedRoot().getFile();
if (!rootTypesCount.containsKey(root)) {
rootTypesCount.put(root, 0);
}
@@ -158,7 +158,7 @@ public class DetectedRootsChooserDialog extends DialogWrapper {
rootCandidateNodes.put(rootCandidate, parent);
root.add(parent);
}
final String rootType = rootTypesCount.get(rootInfo.getSuggestedRoot()) > 1 ? rootInfo.getDetector().getPresentableRootTypeName() : null;
final String rootType = rootTypesCount.get(rootInfo.getSuggestedRoot().getFile()) > 1 ? rootInfo.getRootTypeName() : null;
parent.add(new VirtualFileCheckedTreeNode(rootInfo, rootType));
}
return root;
@@ -197,7 +197,7 @@ public class DetectedRootsChooserDialog extends DialogWrapper {
public VirtualFileCheckedTreeNode(SuggestedChildRootInfo rootInfo, String rootType) {
super(rootInfo);
myFile = rootInfo.getSuggestedRoot();
myFile = rootInfo.getSuggestedRoot().getFile();
myRootType = rootType;
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.roots.libraries.ui.impl;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.ui.LibraryRootsDetector;
import com.intellij.openapi.roots.libraries.ui.OrderRoot;
import com.intellij.openapi.roots.libraries.ui.RootDetector;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author nik
*/
public class LibraryRootsDetectorImpl extends LibraryRootsDetector {
private List<? extends RootDetector> myDetectors;
public LibraryRootsDetectorImpl(List<? extends RootDetector> detectors) {
myDetectors = detectors;
}
@Override
public Collection<OrderRoot> detectRoots(@NotNull VirtualFile rootCandidate, @NotNull ProgressIndicator progressIndicator) {
List<OrderRoot> result = new ArrayList<OrderRoot>();
for (RootDetector detector : myDetectors) {
final Collection<VirtualFile> files = detector.detectRoots(rootCandidate, progressIndicator);
for (VirtualFile file : files) {
result.add(new OrderRoot(file, detector.getRootType(), detector.isJarDirectory()));
}
}
return result;
}
@Override
public String getRootTypeName(@NotNull OrderRootType rootType, boolean isJarDirectory) {
for (RootDetector detector : myDetectors) {
if (detector.getRootType().equals(rootType) && detector.isJarDirectory() == isJarDirectory) {
return detector.getPresentableRootTypeName();
}
}
return null;
}
}
@@ -20,12 +20,14 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.ui.LibraryRootsComponentDescriptor;
import com.intellij.openapi.roots.libraries.ui.LibraryRootsDetector;
import com.intellij.openapi.roots.libraries.ui.OrderRoot;
import com.intellij.openapi.roots.libraries.ui.RootDetector;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -46,26 +48,31 @@ public class RootDetectionUtil {
public static List<OrderRoot> detectRoots(@NotNull final Collection<VirtualFile> rootCandidates,
@Nullable Component parentComponent,
@Nullable Project project,
@NotNull final List<? extends RootDetector> detectors,
boolean allowUserToSelectRootTypeIfNothingIsDetected) {
LOG.assertTrue(!detectors.isEmpty());
@NotNull final LibraryRootsComponentDescriptor rootsComponentDescriptor) {
return detectRoots(rootCandidates, parentComponent, project, rootsComponentDescriptor.getRootsDetector(),
rootsComponentDescriptor.getRootTypes());
}
@NotNull
public static List<OrderRoot> detectRoots(@NotNull final Collection<VirtualFile> rootCandidates, @Nullable Component parentComponent,
@Nullable Project project, @NotNull final LibraryRootsDetector detector,
@NotNull OrderRootType[] rootTypesAllowedToBeSelectedByUserIfNothingIsDetected) {
final List<OrderRoot> result = new ArrayList<OrderRoot>();
final List<SuggestedChildRootInfo> suggestedRoots = new ArrayList<SuggestedChildRootInfo>();
new Task.Modal(project, "Scanning for Roots", true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
try {
for (RootDetector detector : detectors) {
for (VirtualFile rootCandidate : rootCandidates) {
final Collection<VirtualFile> roots = detector.detectRoots(rootCandidate, indicator);
final VirtualFile first = ContainerUtil.getFirstItem(roots);
if (first != null && roots.size() == 1 && first.equals(rootCandidate)) {
result.add(new OrderRoot(first, detector.getRootType(), detector.isJarDirectory()));
}
else {
for (VirtualFile root : roots) {
suggestedRoots.add(new SuggestedChildRootInfo(detector, rootCandidate, root));
}
for (VirtualFile rootCandidate : rootCandidates) {
final Collection<OrderRoot> roots = detector.detectRoots(rootCandidate, indicator);
if (!roots.isEmpty() && allRootsEqualTo(roots, rootCandidate)) {
result.addAll(roots);
}
else {
for (OrderRoot root : roots) {
final String typeName = detector.getRootTypeName(root.getType(), root.isJarDirectory());
LOG.assertTrue(typeName != null, "Unexpected root type " + root.getType().name() + (root.isJarDirectory() ? " (jar directory)" : "") + ", detectors: " + detector);
suggestedRoots.add(new SuggestedChildRootInfo(rootCandidate, root, typeName));
}
}
}
@@ -84,33 +91,42 @@ public class RootDetectionUtil {
return Collections.emptyList();
}
for (SuggestedChildRootInfo rootInfo : dialog.getChosenRoots()) {
result
.add(new OrderRoot(rootInfo.getSuggestedRoot(), rootInfo.getDetector().getRootType(), rootInfo.getDetector().isJarDirectory()));
result.add(rootInfo.getSuggestedRoot());
}
}
if (result.isEmpty() && allowUserToSelectRootTypeIfNothingIsDetected) {
List<RootDetector> sortedDetectors = new ArrayList<RootDetector>(detectors);
Collections.sort(sortedDetectors, new Comparator<RootDetector>() {
@Override
public int compare(final RootDetector o1, final RootDetector o2) {
return o1.getPresentableRootTypeName().compareToIgnoreCase(o2.getPresentableRootTypeName());
if (result.isEmpty() && rootTypesAllowedToBeSelectedByUserIfNothingIsDetected.length > 0) {
Map<String, Pair<OrderRootType, Boolean>> types = new HashMap<String, Pair<OrderRootType, Boolean>>();
for (OrderRootType type : rootTypesAllowedToBeSelectedByUserIfNothingIsDetected) {
for (boolean isDirectory : new boolean[]{false, true}) {
final String typeName = detector.getRootTypeName(type, isDirectory);
if (typeName != null) {
types.put(typeName, Pair.create(type, isDirectory));
}
}
});
List<String> names = new ArrayList<String>();
for (RootDetector detector : sortedDetectors) {
names.add(detector.getPresentableRootTypeName());
}
LOG.assertTrue(!types.isEmpty(), "No allowed root types found for " + detector);
List<String> sortedNames = new ArrayList<String>(types.keySet());
Collections.sort(sortedNames, String.CASE_INSENSITIVE_ORDER);
final int i = Messages.showChooseDialog("Choose category for selected files:", "Attach Files",
ArrayUtil.toStringArray(names), names.get(0), null);
ArrayUtil.toStringArray(sortedNames), sortedNames.get(0), null);
if (i != -1) {
final RootDetector detector = sortedDetectors.get(i);
final Pair<OrderRootType, Boolean> pair = types.get(sortedNames.get(i));
for (VirtualFile candidate : rootCandidates) {
result.add(new OrderRoot(candidate, detector.getRootType(), detector.isJarDirectory()));
result.add(new OrderRoot(candidate, pair.getFirst(), pair.getSecond()));
}
}
}
return result;
}
private static boolean allRootsEqualTo(Collection<OrderRoot> roots, VirtualFile candidate) {
for (OrderRoot root : roots) {
if (!root.getFile().equals(candidate)) {
return false;
}
}
return true;
}
}
@@ -15,32 +15,32 @@
*/
package com.intellij.openapi.roots.libraries.ui.impl;
import com.intellij.openapi.roots.libraries.ui.RootDetector;
import com.intellij.openapi.roots.libraries.ui.OrderRoot;
import com.intellij.openapi.vfs.VirtualFile;
/**
* @author nik
*/
class SuggestedChildRootInfo {
private RootDetector myDetector;
private VirtualFile myRootCandidate;
private VirtualFile mySuggestedRoot;
private final VirtualFile myRootCandidate;
private final OrderRoot mySuggestedRoot;
private final String myRootTypeName;
SuggestedChildRootInfo(RootDetector detector, VirtualFile rootCandidate, VirtualFile suggestedRoot) {
myDetector = detector;
SuggestedChildRootInfo(VirtualFile rootCandidate, OrderRoot suggestedRoot, String rootTypeName) {
myRootCandidate = rootCandidate;
mySuggestedRoot = suggestedRoot;
}
public RootDetector getDetector() {
return myDetector;
myRootTypeName = rootTypeName;
}
public VirtualFile getRootCandidate() {
return myRootCandidate;
}
public VirtualFile getSuggestedRoot() {
public OrderRoot getSuggestedRoot() {
return mySuggestedRoot;
}
public String getRootTypeName() {
return myRootTypeName;
}
}