/* * 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.ProjectTopics; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import consulo.annotations.RequiredReadAction; import consulo.annotations.RequiredWriteAction; import consulo.module.extension.ModuleExtension; import consulo.module.extension.ModuleExtensionWithSdk; import consulo.roots.ModifiableModuleRootLayer; import consulo.roots.ModuleRootLayer; import consulo.roots.ModuleRootLayerListener; import consulo.roots.impl.ModuleRootLayerImpl; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; /** * @author dsl */ public class RootModelImpl extends RootModelBase implements ModifiableRootModel { public static final Logger LOGGER = Logger.getInstance(RootModelImpl.class); protected final ModuleRootManagerImpl myModuleRootManager; private boolean myWritable; private boolean myDisposed; private final RootConfigurationAccessor myConfigurationAccessor; private String myCurrentLayerName; private ModuleRootLayerImpl myCachedCurrentLayer; private final SortedMap<String, ModuleRootLayerImpl> myLayers = new TreeMap<String, ModuleRootLayerImpl>(); RootModelImpl(@NotNull ModuleRootManagerImpl moduleRootManager) { myModuleRootManager = moduleRootManager; myWritable = false; myConfigurationAccessor = new RootConfigurationAccessor(); try { initDefaultLayer(null); } catch (InvalidDataException e) { // } } @RequiredReadAction RootModelImpl(@NotNull Element element, @Nullable ProgressIndicator progressIndicator, @NotNull ModuleRootManagerImpl moduleRootManager, boolean writable) throws InvalidDataException { myModuleRootManager = moduleRootManager; myConfigurationAccessor = new RootConfigurationAccessor(); loadState(element, progressIndicator); myWritable = writable; } //creates modifiable model RootModelImpl(@NotNull RootModelImpl rootModel, @NotNull ModuleRootManagerImpl moduleRootManager, @NotNull RootConfigurationAccessor rootConfigurationAccessor) { myModuleRootManager = moduleRootManager; myWritable = true; myConfigurationAccessor = rootConfigurationAccessor; myLayers.clear(); for (Map.Entry<String, ModuleRootLayerImpl> entry : rootModel.myLayers.entrySet()) { ModuleRootLayerImpl moduleRootLayer = new ModuleRootLayerImpl(entry.getValue(), this); myLayers.put(entry.getKey(), moduleRootLayer); } setCurrentLayerSafe(rootModel.myCurrentLayerName); } private void initDefaultLayer(Element element) throws InvalidDataException { setCurrentLayerSafe(DEFAULT_LAYER_NAME); ModuleRootLayerImpl moduleRootLayer = new ModuleRootLayerImpl(null, this); myLayers.put(myCurrentLayerName, moduleRootLayer); if (element != null) { moduleRootLayer.loadState(element, null); } } private void loadState(Element element, @Nullable ProgressIndicator progressIndicator) throws InvalidDataException { String currentLayer = element.getAttributeValue("current-layer"); if (currentLayer != null) { setCurrentLayerSafe(currentLayer); for (Element moduleLayerElement : element.getChildren("module-layer")) { String name = moduleLayerElement.getAttributeValue("name"); ModuleRootLayerImpl moduleRootLayer = new ModuleRootLayerImpl(null, this); moduleRootLayer.loadState(moduleLayerElement, progressIndicator); myLayers.put(name, moduleRootLayer); } } // old format - create default profile and load it if (myLayers.isEmpty()) { initDefaultLayer(element); } } public void putState(Element parent) { parent.setAttribute("current-layer", myCurrentLayerName); for (Map.Entry<String, ModuleRootLayerImpl> entry : myLayers.entrySet()) { Element element = new Element("module-layer"); element.setAttribute("name", entry.getKey()); entry.getValue().writeExternal(element); parent.addContent(element); } } @Override public boolean isWritable() { return myWritable; } @NotNull public RootConfigurationAccessor getConfigurationAccessor() { return myConfigurationAccessor; } @Override public void removeContentEntry(@NotNull ContentEntry entry) { assertWritable(); getCurrentLayer().removeContentEntry(entry); } @Override public void addOrderEntry(@NotNull OrderEntry entry) { assertWritable(); getCurrentLayer().addOrderEntry(entry); } @NotNull @Override public LibraryOrderEntry addLibraryEntry(@NotNull Library library) { assertWritable(); return getCurrentLayer().addLibraryEntry(library); } @NotNull @Override public ModuleExtensionWithSdkOrderEntry addModuleExtensionSdkEntry(@NotNull ModuleExtensionWithSdk<?> moduleExtension) { assertWritable(); return getCurrentLayer().addModuleExtensionSdkEntry(moduleExtension); } @NotNull @Override public LibraryOrderEntry addInvalidLibrary(@NotNull String name, @NotNull String level) { assertWritable(); return getCurrentLayer().addInvalidLibrary(name, level); } @NotNull @Override public ModuleOrderEntry addModuleOrderEntry(@NotNull Module module) { assertWritable(); return getCurrentLayer().addModuleOrderEntry(module); } @NotNull @Override public ModuleOrderEntry addInvalidModuleEntry(@NotNull String name) { assertWritable(); return getCurrentLayer().addInvalidModuleEntry(name); } @Nullable @Override public LibraryOrderEntry findLibraryOrderEntry(@NotNull Library library) { return getCurrentLayer().findLibraryOrderEntry(library); } @Override public ModuleExtensionWithSdkOrderEntry findModuleExtensionSdkEntry(@NotNull ModuleExtension extension) { return getCurrentLayer().findModuleExtensionSdkEntry(extension); } @Override public void removeOrderEntry(@NotNull OrderEntry entry) { assertWritable(); getCurrentLayer().removeOrderEntry(entry); } @Override public void rearrangeOrderEntries(@NotNull OrderEntry[] newEntries) { assertWritable(); getCurrentLayer().rearrangeOrderEntries(newEntries); } @Override public void clear() { disposeLayers(); try { initDefaultLayer(null); } catch (InvalidDataException e) { // } } private void disposeLayers() { for (ModuleRootLayerImpl moduleRootLayer : myLayers.values()) { Disposer.dispose(moduleRootLayer); } myLayers.clear(); } @Override @RequiredWriteAction public void commit() { myModuleRootManager.commitModel(this); myWritable = false; } @SuppressWarnings("unchecked") public void doCommitAndDispose(boolean notifyEvents) { assert isWritable(); RootModelImpl sourceModel = getSourceModel(); ModuleRootLayerListener layerListener = getModule().getProject().getMessageBus().syncPublisher(ProjectTopics.MODULE_LAYERS); boolean extensionListenerAlreadyCalled = false; if (!Comparing.equal(sourceModel.myCurrentLayerName, myCurrentLayerName)) { ModuleRootLayerImpl oldLayer = sourceModel.getCurrentLayer(); String oldName = sourceModel.getCurrentLayerName(); sourceModel.setCurrentLayerSafe(myCurrentLayerName); if (notifyEvents) { layerListener.currentLayerChanged(getModule(), oldName, oldLayer, myCurrentLayerName, getCurrentLayer()); extensionListenerAlreadyCalled = true; } } // first we commit changed and new layers for (Map.Entry<String, ModuleRootLayerImpl> entry : myLayers.entrySet()) { final ModuleRootLayerImpl sourceModuleLayer = sourceModel.myLayers.get(entry.getKey()); ModuleRootLayerImpl toCommit = sourceModuleLayer; // if layer exists if (toCommit == null) { toCommit = new ModuleRootLayerImpl(null, sourceModel); sourceModel.myLayers.put(entry.getKey(), toCommit); } if (entry.getValue().copy(toCommit, notifyEvents && !extensionListenerAlreadyCalled && myCurrentLayerName.equals(entry.getKey())) && notifyEvents) { if (sourceModuleLayer == null) { layerListener.layerAdded(getModule(), toCommit); } else { layerListener.layerChanged(getModule(), toCommit); } } } List<String> toRemove = new SmartList<String>(); // second remove non existed layers for (String layerName : sourceModel.myLayers.keySet()) { ModuleRootLayerImpl moduleRootLayer = myLayers.get(layerName); if (moduleRootLayer == null) { toRemove.add(layerName); } } for (String layerName : toRemove) { ModuleRootLayerImpl removed = sourceModel.myLayers.remove(layerName); assert removed != null; if (notifyEvents) { layerListener.layerRemove(getModule(), removed); } Disposer.dispose(removed); } dispose(); } @Override @NotNull public LibraryTable getModuleLibraryTable() { return getCurrentLayer().getModuleLibraryTable(); } @NotNull @Override public Project getProject() { return myModuleRootManager.getModule().getProject(); } @Override @NotNull public ContentEntry addContentEntry(@NotNull VirtualFile file) { return getCurrentLayer().addContentEntry(file); } @Override @NotNull public ContentEntry addContentEntry(@NotNull String url) { return getCurrentLayer().addContentEntry(url); } @Override public boolean isDisposed() { return myDisposed; } @Override public <T extends OrderEntry> void replaceEntryOfType(@NotNull Class<T> entryClass, @Nullable final T entry) { assertWritable(); getCurrentLayer().replaceEntryOfType(entryClass, entry); } public void assertWritable() { LOGGER.assertTrue(myWritable); } public boolean isDependsOn(final Module module) { for (OrderEntry entry : getOrderEntries()) { if (entry instanceof ModuleOrderEntry) { final Module module1 = ((ModuleOrderEntry)entry).getModule(); if (module1 == module) { return true; } } } return false; } @Override @NotNull public Module getModule() { return myModuleRootManager.getModule(); } @Override @SuppressWarnings("unchecked") public boolean isChanged() { if (!myWritable) return false; RootModelImpl sourceModel = getSourceModel(); if (!Comparing.equal(myCurrentLayerName, sourceModel.myCurrentLayerName)) { return true; } for (Map.Entry<String, ModuleRootLayerImpl> entry : myLayers.entrySet()) { ModuleRootLayerImpl sourceLayer = sourceModel.myLayers.get(entry.getKey()); // new layer if (sourceLayer == null) { return true; } if (entry.getValue().isChanged(sourceLayer)) { return true; } } // check for deleted layers for (String layerName : sourceModel.myLayers.keySet()) { ModuleRootLayerImpl layer = myLayers.get(layerName); if (layer == null) { return true; } } return false; } void makeExternalChange(@NotNull Runnable runnable) { if (myWritable || myDisposed) return; myModuleRootManager.makeRootsChange(runnable); } @Override public void dispose() { assert !myDisposed; disposeLayers(); myWritable = false; myDisposed = true; } private RootModelImpl getSourceModel() { assertWritable(); return myModuleRootManager.getRootModel(); } @NotNull @Override public ModuleRootLayerImpl getCurrentLayer() { if (myCachedCurrentLayer != null) { return myCachedCurrentLayer; } ModuleRootLayerImpl moduleRootLayer = myLayers.get(myCurrentLayerName); LOGGER.assertTrue(moduleRootLayer != null); return myCachedCurrentLayer = moduleRootLayer; } @NotNull @Override public ModifiableModuleRootLayer addLayer(@NotNull String name, @Nullable String nameForCopy, boolean activate) { ModuleRootLayerImpl moduleRootLayer = myLayers.get(name); if (moduleRootLayer != null) { return moduleRootLayer; } ModuleRootLayerImpl layer = new ModuleRootLayerImpl(null, this); if (nameForCopy != null) { ModuleRootLayerImpl original = myLayers.get(nameForCopy); if (original != null) { original.copy(layer, false); } } myLayers.put(name, layer); if (activate) { setCurrentLayerSafe(name); } return layer; } public void setCurrentLayerSafe(@Nullable String name) { myCurrentLayerName = name; myCachedCurrentLayer = null; } @Nullable @Override public ModifiableModuleRootLayer setCurrentLayer(@NotNull String name) { assertWritable(); ModuleRootLayerImpl moduleRootLayer = myLayers.get(name); if (moduleRootLayer == null) { return null; } myCurrentLayerName = name; myCachedCurrentLayer = moduleRootLayer; return moduleRootLayer; } @Override public boolean removeLayer(@NotNull String name, boolean initDefault) { assertWritable(); ModuleRootLayerImpl removedLayer = myLayers.remove(name); if (removedLayer != null) { Disposer.dispose(removedLayer); if (initDefault && myLayers.isEmpty()) { try { initDefaultLayer(null); } catch (InvalidDataException e) { // } } if (Comparing.equal(myCurrentLayerName, name)) { setCurrentLayerSafe(myLayers.isEmpty() ? null : ContainerUtil.getFirstItem(myLayers.keySet())); } } return removedLayer != null; } @Override public void removeAllLayers(boolean initDefault) { assertWritable(); for (ModuleRootLayerImpl layer : myLayers.values()) { Disposer.dispose(layer); } myLayers.clear(); if (initDefault) { try { initDefaultLayer(null); } catch (InvalidDataException ignored) { // } } else { setCurrentLayerSafe(null); } } @NotNull @Override public String getCurrentLayerName() { LOGGER.assertTrue(myCurrentLayerName != null); return myCurrentLayerName; } @NotNull @Override public Map<String, ModuleRootLayer> getLayers() { return Collections.<String, ModuleRootLayer>unmodifiableSortedMap(myLayers); } @Nullable @Override public ModuleRootLayer findLayerByName(@NotNull String name) { return myLayers.get(name); } }