| /* |
| * Copyright 2000-2011 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.ide.util.importProject; |
| |
| import com.intellij.ide.util.projectWizard.importSources.DetectedContentRoot; |
| import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot; |
| import com.intellij.ide.util.projectWizard.importSources.DetectedSourceRoot; |
| import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.fileTypes.FileTypeManager; |
| import com.intellij.openapi.module.ModuleType; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.SmartList; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.MultiMap; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.*; |
| |
| /** |
| * @author nik |
| */ |
| public class RootDetectionProcessor { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.importProject.RootDetectionProcessor"); |
| private final File myBaseDir; |
| private final ProjectStructureDetector[] myDetectors; |
| private final List<DetectedProjectRoot>[] myDetectedRoots; |
| private final FileTypeManager myTypeManager; |
| private final ProgressIndicator myProgressIndicator; |
| |
| @NotNull |
| public static List<DetectedRootData> detectRoots(@NotNull File baseProjectFile) { |
| return new RootDetectionProcessor(baseProjectFile, ProjectStructureDetector.EP_NAME.getExtensions()).detectRoots(); |
| } |
| |
| public RootDetectionProcessor(File baseDir, final ProjectStructureDetector[] detectors) { |
| myBaseDir = getCanonicalDir(baseDir); |
| myDetectors = detectors; |
| //noinspection unchecked |
| myDetectedRoots = new List[myDetectors.length]; |
| myTypeManager = FileTypeManager.getInstance(); |
| myProgressIndicator = ProgressManager.getInstance().getProgressIndicator(); |
| } |
| |
| private static File getCanonicalDir(File baseDir) { |
| try { |
| return new File(FileUtil.resolveShortWindowsName(baseDir.getAbsolutePath())); |
| } |
| catch (IOException e) { |
| LOG.info(e); |
| return baseDir; |
| } |
| } |
| |
| public static MultiMap<ProjectStructureDetector, DetectedProjectRoot> createRootsMap(List<DetectedRootData> list) { |
| MultiMap<ProjectStructureDetector, DetectedProjectRoot> roots = new MultiMap<ProjectStructureDetector, DetectedProjectRoot>(); |
| for (final DetectedRootData rootData : list) { |
| for (ProjectStructureDetector detector : rootData.getSelectedDetectors()) { |
| roots.putValue(detector, rootData.getSelectedRoot()); |
| } |
| } |
| return roots; |
| } |
| |
| |
| public Map<ProjectStructureDetector, List<DetectedProjectRoot>> runDetectors() { |
| if (!myBaseDir.isDirectory()) { |
| return Collections.emptyMap(); |
| } |
| |
| BitSet enabledDetectors = new BitSet(myDetectors.length); |
| enabledDetectors.set(0, myDetectors.length); |
| for (int i = 0; i < myDetectors.length; i++) { |
| myDetectedRoots[i] = new ArrayList<DetectedProjectRoot>(); |
| } |
| processRecursively(myBaseDir, enabledDetectors); |
| |
| final Map<ProjectStructureDetector, List<DetectedProjectRoot>> result = new LinkedHashMap<ProjectStructureDetector, List<DetectedProjectRoot>>(); |
| for (int i = 0; i < myDetectors.length; i++) { |
| if (!myDetectedRoots[i].isEmpty()) { |
| result.put(myDetectors[i], myDetectedRoots[i]); |
| } |
| } |
| return result; |
| } |
| |
| private List<Pair<File, Integer>> processRecursively(File dir, BitSet enabledDetectors) { |
| List<Pair<File, Integer>> parentsToSkip = new SmartList<Pair<File, Integer>>(); |
| |
| if (myTypeManager.isFileIgnored(dir.getName())) { |
| return parentsToSkip; |
| } |
| if (myProgressIndicator != null) { |
| if (myProgressIndicator.isCanceled()) { |
| return parentsToSkip; |
| } |
| myProgressIndicator.setText2(dir.getPath()); |
| } |
| |
| File[] children = dir.listFiles(); |
| |
| if (children == null) { |
| children = ArrayUtil.EMPTY_FILE_ARRAY; |
| } |
| |
| BitSet enabledForChildren = enabledDetectors; |
| for (int i = 0, detectorsLength = myDetectors.length; i < detectorsLength; i++) { |
| if (!enabledDetectors.get(i)) continue; |
| |
| final ProjectStructureDetector.DirectoryProcessingResult result = myDetectors[i].detectRoots(dir, children, myBaseDir, myDetectedRoots[i]); |
| |
| if (!result.isProcessChildren()) { |
| if (enabledForChildren == enabledDetectors) { |
| enabledForChildren = new BitSet(); |
| enabledForChildren.or(enabledDetectors); |
| } |
| enabledForChildren.set(i, false); |
| } |
| |
| final File parentToSkip = result.getParentToSkip(); |
| if (parentToSkip != null && !parentToSkip.equals(dir)) { |
| parentsToSkip.add(Pair.create(parentToSkip, i)); |
| } |
| } |
| |
| if (!enabledForChildren.isEmpty()) { |
| for (File child : children) { |
| if (child.isDirectory()) { |
| final List<Pair<File, Integer>> toSkip = processRecursively(child, enabledForChildren); |
| if (!toSkip.isEmpty()) { |
| if (enabledForChildren == enabledDetectors) { |
| enabledForChildren = new BitSet(); |
| enabledForChildren.or(enabledDetectors); |
| } |
| for (Pair<File, Integer> pair : toSkip) { |
| enabledForChildren.set(pair.getSecond(), false); |
| if (!pair.getFirst().equals(dir)) { |
| parentsToSkip.add(pair); |
| } |
| } |
| if (enabledForChildren.isEmpty()) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| return parentsToSkip; |
| } |
| |
| private static void removeIncompatibleRoots(DetectedProjectRoot root, Map<File, DetectedRootData> rootData) { |
| DetectedRootData[] allRoots = rootData.values().toArray(new DetectedRootData[rootData.values().size()]); |
| for (DetectedRootData child : allRoots) { |
| final File childDirectory = child.getDirectory(); |
| if (FileUtil.isAncestor(root.getDirectory(), childDirectory, true)) { |
| for (DetectedProjectRoot projectRoot : child.getAllRoots()) { |
| if (!root.canContainRoot(projectRoot)) { |
| child.removeRoot(projectRoot); |
| } |
| } |
| if (child.isEmpty()) { |
| rootData.remove(childDirectory); |
| } |
| } |
| } |
| } |
| |
| private static boolean isUnderIncompatibleRoot(DetectedProjectRoot root, Map<File, DetectedRootData> rootData) { |
| File directory = root.getDirectory().getParentFile(); |
| while (directory != null) { |
| final DetectedRootData data = rootData.get(directory); |
| if (data != null) { |
| for (DetectedProjectRoot parentRoot : data.getAllRoots()) { |
| if (!parentRoot.canContainRoot(root)) { |
| return true; |
| } |
| } |
| } |
| directory = directory.getParentFile(); |
| } |
| return false; |
| } |
| |
| private List<DetectedRootData> detectRoots() { |
| Map<ProjectStructureDetector, List<DetectedProjectRoot>> roots = runDetectors(); |
| if (myProgressIndicator != null) { |
| myProgressIndicator.setText2("Processing " + roots.values().size() + " project roots..."); |
| } |
| |
| Map<File, DetectedRootData> rootData = new LinkedHashMap<File, DetectedRootData>(); |
| for (ProjectStructureDetector detector : roots.keySet()) { |
| for (DetectedProjectRoot detectedRoot : roots.get(detector)) { |
| if (isUnderIncompatibleRoot(detectedRoot, rootData)) { |
| continue; |
| } |
| |
| final DetectedRootData data = rootData.get(detectedRoot.getDirectory()); |
| if (data == null) { |
| rootData.put(detectedRoot.getDirectory(), new DetectedRootData(detector, detectedRoot)); |
| } |
| else { |
| detectedRoot = data.addRoot(detector, detectedRoot); |
| } |
| removeIncompatibleRoots(detectedRoot, rootData); |
| } |
| } |
| |
| List<DetectedRootData> dataCollection = mergeContentRoots(rootData); |
| if (myProgressIndicator != null) { |
| myProgressIndicator.setText2(""); |
| } |
| return dataCollection; |
| } |
| |
| private List<DetectedRootData> mergeContentRoots(Map<File, DetectedRootData> rootData) { |
| LOG.debug(rootData.size() + " roots found, merging content roots"); |
| boolean hasSourceRoots = false; |
| Set<ModuleType> typesToReplace = new HashSet<ModuleType>(); |
| Set<ModuleType> moduleTypes = new HashSet<ModuleType>(); |
| for (DetectedRootData data : rootData.values()) { |
| for (DetectedProjectRoot root : data.getAllRoots()) { |
| if (root instanceof DetectedContentRoot) { |
| Collections.addAll(typesToReplace, ((DetectedContentRoot)root).getTypesToReplace()); |
| moduleTypes.add(((DetectedContentRoot)root).getModuleType()); |
| } |
| else if (root instanceof DetectedSourceRoot) { |
| LOG.debug("Source root found: " + root.getDirectory() + ", content roots will be ignored"); |
| hasSourceRoots = true; |
| break; |
| } |
| } |
| } |
| moduleTypes.removeAll(typesToReplace); |
| |
| if (hasSourceRoots || moduleTypes.size() <= 1) { |
| Iterator<DetectedRootData> iterator = rootData.values().iterator(); |
| DetectedContentRoot firstRoot = null; |
| ProjectStructureDetector firstDetector = null; |
| while (iterator.hasNext()) { |
| DetectedRootData data = iterator.next(); |
| for (DetectedProjectRoot root : data.getAllRoots()) { |
| if (root instanceof DetectedContentRoot) { |
| LOG.debug("Removed detected " + root.getRootTypeName() + " content root: " + root.getDirectory()); |
| Collection<ProjectStructureDetector> detectors = data.removeRoot(root); |
| if ((firstRoot == null || firstDetector == null) && moduleTypes.contains(((DetectedContentRoot)root).getModuleType())) { |
| firstRoot = (DetectedContentRoot)root; |
| firstDetector = ContainerUtil.getFirstItem(detectors); |
| } |
| } |
| } |
| if (data.isEmpty()) { |
| iterator.remove(); |
| } |
| } |
| if (!hasSourceRoots && firstRoot != null && firstDetector != null) { |
| DetectedContentRoot baseRoot = new DetectedContentRoot(myBaseDir, firstRoot.getRootTypeName(), firstRoot.getModuleType()); |
| DetectedRootData data = rootData.get(myBaseDir); |
| if (data == null) { |
| rootData.put(myBaseDir, new DetectedRootData(firstDetector, baseRoot)); |
| } |
| else { |
| data.addRoot(firstDetector, baseRoot); |
| } |
| LOG.debug("Added " + firstRoot.getRootTypeName() + " content root for " + myBaseDir); |
| } |
| } |
| return new ArrayList<DetectedRootData>(rootData.values()); |
| } |
| } |