| /* |
| * 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.impl; |
| |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.roots.ContentEntry; |
| import com.intellij.openapi.roots.ContentFolder; |
| import com.intellij.openapi.roots.ExcludeFolder; |
| import com.intellij.openapi.roots.SourceFolder; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.InvalidDataException; |
| import com.intellij.openapi.util.WriteExternalException; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.pointers.VirtualFilePointer; |
| import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.SmartList; |
| import com.intellij.util.containers.ContainerUtil; |
| import org.jdom.Element; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.jps.model.JpsElement; |
| import org.jetbrains.jps.model.JpsElementFactory; |
| import org.jetbrains.jps.model.java.JavaSourceRootType; |
| import org.jetbrains.jps.model.module.JpsModuleSourceRoot; |
| import org.jetbrains.jps.model.module.JpsModuleSourceRootType; |
| import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer; |
| |
| import java.util.*; |
| |
| /** |
| * @author dsl |
| */ |
| public class ContentEntryImpl extends RootModelComponentBase implements ContentEntry, ClonableContentEntry, Comparable<ContentEntryImpl> { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.SimpleContentEntryImpl"); |
| @NotNull private final VirtualFilePointer myRoot; |
| @NonNls public static final String ELEMENT_NAME = JpsModuleRootModelSerializer.CONTENT_TAG; |
| private final Set<SourceFolder> mySourceFolders = new LinkedHashSet<SourceFolder>(); |
| private final Set<ExcludeFolder> myExcludeFolders = new TreeSet<ExcludeFolder>(ContentFolderComparator.INSTANCE); |
| @NonNls public static final String URL_ATTRIBUTE = JpsModuleRootModelSerializer.URL_ATTRIBUTE; |
| |
| ContentEntryImpl(@NotNull VirtualFile file, @NotNull RootModelImpl m) { |
| this(file.getUrl(), m); |
| } |
| |
| ContentEntryImpl(@NotNull String url, @NotNull RootModelImpl m) { |
| super(m); |
| myRoot = VirtualFilePointerManager.getInstance().create(url, this, null); |
| } |
| |
| ContentEntryImpl(@NotNull Element e, @NotNull RootModelImpl m) throws InvalidDataException { |
| this(getUrlFrom(e), m); |
| initSourceFolders(e); |
| initExcludeFolders(e); |
| } |
| |
| private static String getUrlFrom(@NotNull Element e) throws InvalidDataException { |
| LOG.assertTrue(ELEMENT_NAME.equals(e.getName())); |
| |
| String url = e.getAttributeValue(URL_ATTRIBUTE); |
| if (url == null) throw new InvalidDataException(); |
| return url; |
| } |
| |
| private void initSourceFolders(@NotNull Element e) throws InvalidDataException { |
| for (Object child : e.getChildren(SourceFolderImpl.ELEMENT_NAME)) { |
| addSourceFolder(new SourceFolderImpl((Element)child, this)); |
| } |
| } |
| |
| private void initExcludeFolders(@NotNull Element e) throws InvalidDataException { |
| for (Object child : e.getChildren(ExcludeFolderImpl.ELEMENT_NAME)) { |
| ExcludeFolderImpl excludeFolder = new ExcludeFolderImpl((Element)child, this); |
| addExcludeFolder(excludeFolder); |
| } |
| } |
| |
| @Override |
| public VirtualFile getFile() { |
| //assert !isDisposed(); |
| return myRoot.getFile(); |
| } |
| |
| @Override |
| @NotNull |
| public String getUrl() { |
| return myRoot.getUrl(); |
| } |
| |
| @NotNull |
| @Override |
| public SourceFolder[] getSourceFolders() { |
| return mySourceFolders.toArray(new SourceFolder[mySourceFolders.size()]); |
| } |
| |
| @NotNull |
| @Override |
| public List<SourceFolder> getSourceFolders(@NotNull JpsModuleSourceRootType<?> rootType) { |
| return getSourceFolders(Collections.singleton(rootType)); |
| } |
| |
| @NotNull |
| @Override |
| public List<SourceFolder> getSourceFolders(@NotNull Set<? extends JpsModuleSourceRootType<?>> rootTypes) { |
| SmartList<SourceFolder> folders = new SmartList<SourceFolder>(); |
| for (SourceFolder folder : mySourceFolders) { |
| if (rootTypes.contains(folder.getRootType())) { |
| folders.add(folder); |
| } |
| } |
| return folders; |
| } |
| |
| @Override |
| @NotNull |
| public VirtualFile[] getSourceFolderFiles() { |
| assert !isDisposed(); |
| final SourceFolder[] sourceFolders = getSourceFolders(); |
| ArrayList<VirtualFile> result = new ArrayList<VirtualFile>(sourceFolders.length); |
| for (SourceFolder sourceFolder : sourceFolders) { |
| final VirtualFile file = sourceFolder.getFile(); |
| if (file != null) { |
| result.add(file); |
| } |
| } |
| return VfsUtilCore.toVirtualFileArray(result); |
| } |
| |
| @NotNull |
| @Override |
| public ExcludeFolder[] getExcludeFolders() { |
| //assert !isDisposed(); |
| return myExcludeFolders.toArray(new ExcludeFolder[myExcludeFolders.size()]); |
| } |
| |
| @NotNull |
| @Override |
| public List<String> getExcludeFolderUrls() { |
| List<String> excluded = new ArrayList<String>(); |
| for (ExcludeFolder folder : myExcludeFolders) { |
| excluded.add(folder.getUrl()); |
| } |
| for (DirectoryIndexExcludePolicy excludePolicy : Extensions.getExtensions(DirectoryIndexExcludePolicy.EP_NAME, getRootModel().getProject())) { |
| for (VirtualFilePointer pointer : excludePolicy.getExcludeRootsForModule(getRootModel())) { |
| excluded.add(pointer.getUrl()); |
| } |
| } |
| return excluded; |
| } |
| |
| @Override |
| @NotNull |
| public VirtualFile[] getExcludeFolderFiles() { |
| assert !isDisposed(); |
| ArrayList<VirtualFile> result = new ArrayList<VirtualFile>(); |
| for (ExcludeFolder excludeFolder : getExcludeFolders()) { |
| ContainerUtil.addIfNotNull(result, excludeFolder.getFile()); |
| } |
| for (DirectoryIndexExcludePolicy excludePolicy : Extensions.getExtensions(DirectoryIndexExcludePolicy.EP_NAME, getRootModel().getProject())) { |
| for (VirtualFilePointer pointer : excludePolicy.getExcludeRootsForModule(getRootModel())) { |
| ContainerUtil.addIfNotNull(result, pointer.getFile()); |
| } |
| } |
| return VfsUtilCore.toVirtualFileArray(result); |
| } |
| |
| @NotNull |
| @Override |
| public SourceFolder addSourceFolder(@NotNull VirtualFile file, boolean isTestSource) { |
| return addSourceFolder(file, isTestSource, SourceFolderImpl.DEFAULT_PACKAGE_PREFIX); |
| } |
| |
| @NotNull |
| @Override |
| public SourceFolder addSourceFolder(@NotNull VirtualFile file, boolean isTestSource, @NotNull String packagePrefix) { |
| JavaSourceRootType type = isTestSource ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE; |
| return addSourceFolder(file, type); |
| } |
| |
| @Override |
| @NotNull |
| public <P extends JpsElement> SourceFolder addSourceFolder(@NotNull VirtualFile file, @NotNull JpsModuleSourceRootType<P> type, |
| @NotNull P properties) { |
| assertCanAddFolder(file); |
| return addSourceFolder(new SourceFolderImpl(file, JpsElementFactory.getInstance().createModuleSourceRoot(file.getUrl(), type, properties), this)); |
| } |
| |
| @NotNull |
| @Override |
| public <P extends JpsElement> SourceFolder addSourceFolder(@NotNull VirtualFile file, @NotNull JpsModuleSourceRootType<P> type) { |
| return addSourceFolder(file, type, type.createDefaultProperties()); |
| } |
| |
| @NotNull |
| @Override |
| public SourceFolder addSourceFolder(@NotNull String url, boolean isTestSource) { |
| return addSourceFolder(url, isTestSource ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE); |
| } |
| |
| @NotNull |
| @Override |
| public <P extends JpsElement> SourceFolder addSourceFolder(@NotNull String url, @NotNull JpsModuleSourceRootType<P> type) { |
| return addSourceFolder(url, type, type.createDefaultProperties()); |
| } |
| |
| @NotNull |
| @Override |
| public <P extends JpsElement> SourceFolder addSourceFolder(@NotNull String url, |
| @NotNull JpsModuleSourceRootType<P> type, |
| @NotNull P properties) { |
| assertFolderUnderMe(url); |
| JpsModuleSourceRoot sourceRoot = JpsElementFactory.getInstance().createModuleSourceRoot(url, type, properties); |
| return addSourceFolder(new SourceFolderImpl(sourceRoot, this)); |
| } |
| |
| @NotNull |
| private SourceFolder addSourceFolder(@NotNull SourceFolderImpl f) { |
| mySourceFolders.add(f); |
| Disposer.register(this, f); //rewire source folder dispose parent from rootmodel to this content root |
| return f; |
| } |
| |
| @Override |
| public void removeSourceFolder(@NotNull SourceFolder sourceFolder) { |
| assert !isDisposed(); |
| assertCanRemoveFrom(sourceFolder, mySourceFolders); |
| doRemove(sourceFolder); |
| } |
| |
| private void doRemove(SourceFolder sourceFolder) { |
| mySourceFolders.remove(sourceFolder); |
| Disposer.dispose((Disposable)sourceFolder); |
| } |
| |
| @Override |
| public void clearSourceFolders() { |
| assert !isDisposed(); |
| getRootModel().assertWritable(); |
| for (SourceFolder folder : mySourceFolders) { |
| Disposer.dispose((Disposable)folder); |
| } |
| mySourceFolders.clear(); |
| } |
| |
| @Override |
| public ExcludeFolder addExcludeFolder(@NotNull VirtualFile file) { |
| assert !isDisposed(); |
| assertCanAddFolder(file); |
| return addExcludeFolder(new ExcludeFolderImpl(file, this)); |
| } |
| |
| @Override |
| public ExcludeFolder addExcludeFolder(@NotNull String url) { |
| assert !isDisposed(); |
| assertCanAddFolder(url); |
| return addExcludeFolder(new ExcludeFolderImpl(url, this)); |
| } |
| |
| private void assertCanAddFolder(@NotNull VirtualFile file) { |
| assertCanAddFolder(file.getUrl()); |
| } |
| |
| private void assertCanAddFolder(@NotNull String url) { |
| getRootModel().assertWritable(); |
| assertFolderUnderMe(url); |
| } |
| |
| @Override |
| public void removeExcludeFolder(@NotNull ExcludeFolder excludeFolder) { |
| assert !isDisposed(); |
| assertCanRemoveFrom(excludeFolder, myExcludeFolders); |
| myExcludeFolders.remove(excludeFolder); |
| Disposer.dispose((Disposable)excludeFolder); |
| } |
| |
| @Override |
| public boolean removeExcludeFolder(@NotNull String url) { |
| for (ExcludeFolder folder : myExcludeFolders) { |
| if (folder.getUrl().equals(url)) { |
| myExcludeFolders.remove(folder); |
| Disposer.dispose((Disposable)folder); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public void clearExcludeFolders() { |
| assert !isDisposed(); |
| getRootModel().assertWritable(); |
| for (ExcludeFolder excludeFolder : myExcludeFolders) { |
| Disposer.dispose((Disposable)excludeFolder); |
| } |
| myExcludeFolders.clear(); |
| } |
| |
| private ExcludeFolder addExcludeFolder(ExcludeFolder f) { |
| Disposer.register(this, (Disposable)f); |
| myExcludeFolders.add(f); |
| return f; |
| } |
| |
| private <T extends ContentFolder> void assertCanRemoveFrom(T f, @NotNull Set<T> ff) { |
| getRootModel().assertWritable(); |
| LOG.assertTrue(ff.contains(f)); |
| } |
| |
| private void assertFolderUnderMe(@NotNull String url) { |
| final String path = VfsUtilCore.urlToPath(url); |
| final String rootPath = VfsUtilCore.urlToPath(getUrl()); |
| if (!FileUtil.isAncestor(rootPath, path, false)) { |
| LOG.error("The file '" + path + "' is not under content entry root '" + rootPath + "'"); |
| } |
| } |
| |
| @Override |
| public boolean isSynthetic() { |
| return false; |
| } |
| |
| @Override |
| @NotNull |
| public ContentEntry cloneEntry(@NotNull RootModelImpl rootModel) { |
| assert !isDisposed(); |
| ContentEntryImpl cloned = new ContentEntryImpl(myRoot.getUrl(), rootModel); |
| for (final SourceFolder sourceFolder : mySourceFolders) { |
| if (sourceFolder instanceof ClonableContentFolder) { |
| ContentFolder folder = ((ClonableContentFolder)sourceFolder).cloneFolder(cloned); |
| cloned.addSourceFolder((SourceFolderImpl)folder); |
| } |
| } |
| |
| for (final ExcludeFolder excludeFolder : myExcludeFolders) { |
| if (excludeFolder instanceof ClonableContentFolder) { |
| ContentFolder folder = ((ClonableContentFolder)excludeFolder).cloneFolder(cloned); |
| cloned.addExcludeFolder((ExcludeFolder)folder); |
| } |
| } |
| |
| return cloned; |
| } |
| |
| public void writeExternal(@NotNull Element element) throws WriteExternalException { |
| assert !isDisposed(); |
| LOG.assertTrue(ELEMENT_NAME.equals(element.getName())); |
| element.setAttribute(URL_ATTRIBUTE, myRoot.getUrl()); |
| for (final SourceFolder sourceFolder : mySourceFolders) { |
| if (sourceFolder instanceof SourceFolderImpl) { |
| JpsModuleRootModelSerializer.saveSourceRoot(element, sourceFolder.getUrl(), sourceFolder.getJpsElement().asTyped()); |
| } |
| } |
| |
| for (final ExcludeFolder excludeFolder : myExcludeFolders) { |
| if (excludeFolder instanceof ExcludeFolderImpl) { |
| final Element subElement = new Element(ExcludeFolderImpl.ELEMENT_NAME); |
| ((ExcludeFolderImpl)excludeFolder).writeExternal(subElement); |
| element.addContent(subElement); |
| } |
| } |
| } |
| |
| private static final class ContentFolderComparator implements Comparator<ContentFolder> { |
| public static final ContentFolderComparator INSTANCE = new ContentFolderComparator(); |
| |
| @Override |
| public int compare(@NotNull ContentFolder o1, @NotNull ContentFolder o2) { |
| return o1.getUrl().compareTo(o2.getUrl()); |
| } |
| } |
| |
| @Override |
| public int compareTo(@NotNull ContentEntryImpl other) { |
| int i = getUrl().compareTo(other.getUrl()); |
| if (i != 0) return i; |
| i = ArrayUtil.lexicographicCompare(getSourceFolders(), other.getSourceFolders()); |
| if (i != 0) return i; |
| return ArrayUtil.lexicographicCompare(getExcludeFolders(), other.getExcludeFolders()); |
| } |
| } |