/* * 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.projectRoots.impl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.projectRoots.ProjectRootListener; import com.intellij.openapi.projectRoots.ex.SdkRoot; import com.intellij.openapi.projectRoots.ex.SdkRootContainer; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.ArchiveCopyingFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFileSystem; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; import consulo.vfs.ArchiveFileSystem; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Map; /** * @author mike */ public class SdkRootContainerImpl implements PersistentStateComponent<Element>, SdkRootContainer { public static final Logger LOGGER = Logger.getInstance(SdkRootContainerImpl.class); private final Map<OrderRootType, CompositeSdkRoot> myRoots = new HashMap<OrderRootType, CompositeSdkRoot>(); private Map<OrderRootType, VirtualFile[]> myFiles = new HashMap<OrderRootType, VirtualFile[]>(); private boolean myInsideChange = false; private final List<ProjectRootListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private boolean myNoCopyArchive = false; public SdkRootContainerImpl(boolean noCopyArchive) { myNoCopyArchive = noCopyArchive; for (OrderRootType rootType : OrderRootType.getAllTypes()) { myRoots.put(rootType, new CompositeSdkRoot()); myFiles.put(rootType, VirtualFile.EMPTY_ARRAY); } } @Override @NotNull public VirtualFile[] getRootFiles(@NotNull OrderRootType type) { return myFiles.get(type); } @Override @NotNull public SdkRoot[] getRoots(@NotNull OrderRootType type) { return myRoots.get(type).getProjectRoots(); } @Override public void startChange() { LOGGER.assertTrue(!myInsideChange); myInsideChange = true; } @Override public void finishChange() { LOGGER.assertTrue(myInsideChange); HashMap<OrderRootType, VirtualFile[]> oldRoots = new HashMap<OrderRootType, VirtualFile[]>(myFiles); for (OrderRootType orderRootType : OrderRootType.getAllTypes()) { final VirtualFile[] roots = myRoots.get(orderRootType).getVirtualFiles(); final boolean same = Comparing.equal(roots, oldRoots.get(orderRootType)); myFiles.put(orderRootType, myRoots.get(orderRootType).getVirtualFiles()); if (!same) { fireRootsChanged(); } } myInsideChange = false; } public void addProjectRootContainerListener(ProjectRootListener listener) { myListeners.add(listener); } public void removeProjectRootContainerListener(ProjectRootListener listener) { myListeners.remove(listener); } private void fireRootsChanged() { /* ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { LOG.info("roots changed: type='" + type + "'\n oldRoots='" + Arrays.asList(oldRoots) + "'\n newRoots='" + Arrays.asList(newRoots) + "' "); } }); */ for (final ProjectRootListener listener : myListeners) { listener.rootsChanged(); } } @Override public void removeRoot(@NotNull SdkRoot root, @NotNull OrderRootType type) { LOGGER.assertTrue(myInsideChange); myRoots.get(type).remove(root); } @Override @NotNull public SdkRoot addRoot(@NotNull VirtualFile virtualFile, @NotNull OrderRootType type) { LOGGER.assertTrue(myInsideChange); return myRoots.get(type).add(virtualFile); } @Override public void addRoot(@NotNull SdkRoot root, @NotNull OrderRootType type) { LOGGER.assertTrue(myInsideChange); myRoots.get(type).add(root); } @Override public void removeAllRoots(@NotNull OrderRootType type) { LOGGER.assertTrue(myInsideChange); myRoots.get(type).clear(); } @Override public void removeRoot(@NotNull VirtualFile root, @NotNull OrderRootType type) { LOGGER.assertTrue(myInsideChange); myRoots.get(type).remove(root); } @Override public void removeAllRoots() { LOGGER.assertTrue(myInsideChange); for (CompositeSdkRoot myRoot : myRoots.values()) { myRoot.clear(); } } @Override public void update() { LOGGER.assertTrue(myInsideChange); for (CompositeSdkRoot myRoot : myRoots.values()) { myRoot.update(); } } @Override public void loadState(Element element) { for (OrderRootType type : OrderRootType.getAllTypes()) { read(element, type); } ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { myFiles = new HashMap<OrderRootType, VirtualFile[]>(); for (OrderRootType rootType : myRoots.keySet()) { CompositeSdkRoot root = myRoots.get(rootType); if (myNoCopyArchive) { addNoCopyArchiveForPaths(root); } myFiles.put(rootType, root.getVirtualFiles()); } } }); for (OrderRootType type : OrderRootType.getAllTypes()) { final VirtualFile[] newRoots = getRootFiles(type); final VirtualFile[] oldRoots = VirtualFile.EMPTY_ARRAY; if (!Comparing.equal(oldRoots, newRoots)) { fireRootsChanged(); } } } @NotNull @Override public Element getState() { Element element = new Element("state"); OrderRootType[] allTypes = OrderRootType.getSortedRootTypes(); for (OrderRootType type : allTypes) { write(element, type); } return element; } private static void addNoCopyArchiveForPaths(SdkRoot root) { if (root instanceof SimpleSdkRoot) { String url = ((SimpleSdkRoot)root).getUrl(); String protocolId = VirtualFileManager.extractProtocol(url); VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocolId); if (fileSystem instanceof ArchiveFileSystem) { String path = VirtualFileManager.extractPath(url); ((ArchiveCopyingFileSystem)fileSystem).addNoCopyArchiveForPath(path); } } else if (root instanceof CompositeSdkRoot) { SdkRoot[] roots = ((CompositeSdkRoot)root).getProjectRoots(); for (SdkRoot root1 : roots) { addNoCopyArchiveForPaths(root1); } } } private void read(Element element, OrderRootType type) { Element child = element.getChild(type.getName()); if (child == null) { myRoots.put(type, new CompositeSdkRoot()); return; } List<Element> children = child.getChildren(); LOGGER.assertTrue(children.size() == 1); CompositeSdkRoot root = (CompositeSdkRoot)SdkRootStateUtil.readRoot(children.get(0)); myRoots.put(type, root); } private void write(Element roots, OrderRootType type) { Element e = new Element(type.getName()); roots.addContent(e); e.addContent(SdkRootStateUtil.writeRoot(myRoots.get(type))); } }