/* * 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.roots.ContentEntry; import com.intellij.openapi.roots.ContentFolder; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; import com.intellij.util.containers.HashMap; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import consulo.roots.ContentFolderPropertyProvider; import consulo.roots.ContentFolderTypeProvider; import consulo.roots.impl.UnknownContentFolderTypeProvider; import java.util.Collections; import java.util.List; import java.util.Map; /** * @author dsl */ public class ContentFolderImpl extends BaseModuleRootLayerChild implements ContentFolder, ClonableContentFolder, Comparable<ContentFolderImpl> { @NonNls public static final String URL_ATTRIBUTE = "url"; @NonNls public static final String TYPE_ATTRIBUTE = "type"; @NonNls public static final String ELEMENT_NAME = "content-folder"; private final VirtualFilePointer myFilePointer; protected final ContentEntryImpl myContentEntry; private final ContentFolderTypeProvider myContentFolderType; private Map<String, Object> myProperties; private Map<Key, Object> myPropertiesByKeyCache; ContentFolderImpl(@NotNull VirtualFile file, @NotNull ContentFolderTypeProvider contentFolderType, @NotNull ContentEntryImpl contentEntry) { super(contentEntry.getModuleRootLayer()); myContentEntry = contentEntry; myContentFolderType = contentFolderType; myFilePointer = VirtualFilePointerManager.getInstance().create(file, this, null); } ContentFolderImpl(@NotNull String url, @NotNull ContentFolderTypeProvider contentFolderType, @Nullable Map<String, Object> map, @NotNull ContentEntryImpl contentEntry) { super(contentEntry.getModuleRootLayer()); myContentEntry = contentEntry; myContentFolderType = contentFolderType; myProperties = map == null ? null : new HashMap<String, Object>(map); myFilePointer = VirtualFilePointerManager.getInstance().create(url, this, null); } protected ContentFolderImpl(@NotNull ContentFolderImpl that, @NotNull ContentEntryImpl contentEntry) { this(that.myFilePointer, that.myProperties, that.getType(), contentEntry); } ContentFolderImpl(@NotNull Element element, @NotNull ContentEntryImpl contentEntry) throws InvalidDataException { this(getUrlFrom(element), getType(element), readProperties(element), contentEntry); } protected ContentFolderImpl(@NotNull VirtualFilePointer filePointer, @Nullable Map<String, Object> properties, @NotNull ContentFolderTypeProvider contentFolderType, @NotNull ContentEntryImpl contentEntry) { super(contentEntry.getModuleRootLayer()); myContentEntry = contentEntry; myContentFolderType = contentFolderType; myProperties = properties; myFilePointer = VirtualFilePointerManager.getInstance().duplicate(filePointer, this, null); } private static String getUrlFrom(Element element) throws InvalidDataException { String url = element.getAttributeValue(URL_ATTRIBUTE); if (url == null) { throw new InvalidDataException(); } return url; } @Nullable private static Map<String, Object> readProperties(Element element) throws InvalidDataException { List<Element> elementChildren = element.getChildren("property"); if (elementChildren.isEmpty()) { return null; } Map<String, Object> map = new HashMap<String, Object>(); for (Element elementChild : elementChildren) { String key = elementChild.getAttributeValue("key"); String value = elementChild.getAttributeValue("value"); ContentFolderPropertyProvider propertyProvider = ContentFolderPropertyProvider.findProvider(key); if(propertyProvider != null) { Object b = propertyProvider.fromString(value); map.put(key, b); } else { map.put(key, value); } } return map; } private static ContentFolderTypeProvider getType(Element element) throws InvalidDataException { String type = element.getAttributeValue(TYPE_ATTRIBUTE); if (type == null) { throw new InvalidDataException(); } for (ContentFolderTypeProvider contentFolderTypeProvider : ContentFolderTypeProvider.EP_NAME.getExtensions()) { if (Comparing.equal(contentFolderTypeProvider.getId(), type)) { return contentFolderTypeProvider; } } return new UnknownContentFolderTypeProvider(type); } @Override public VirtualFile getFile() { if (!myFilePointer.isValid()) { return null; } return myFilePointer.getFile(); } @Override @NotNull public ContentEntry getContentEntry() { return myContentEntry; } @SuppressWarnings("unchecked") protected void writeExternal(Element element) { element.setAttribute(TYPE_ATTRIBUTE, myContentFolderType.getId()); element.setAttribute(URL_ATTRIBUTE, myFilePointer.getUrl()); if(myProperties == null) { return; } for (Map.Entry<String, Object> entry : myProperties.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); Element child = new Element("property"); child.setAttribute("key", key); ContentFolderPropertyProvider propertiesProvider = ContentFolderPropertyProvider.findProvider(key); if(propertiesProvider != null) { child.setAttribute("value", propertiesProvider.toString(value)); } else { child.setAttribute("value", (String)value); } element.addContent(child); } } @Override @NotNull public String getUrl() { return myFilePointer.getUrl(); } @NotNull @Override public Map<Key, Object> getProperties() { initPropertiesByKeyCache(); return myPropertiesByKeyCache; } private void initPropertiesByKeyCache() { if (myPropertiesByKeyCache != null) { return; } if(myProperties == null) { myPropertiesByKeyCache = Collections.emptyMap(); } else { myPropertiesByKeyCache = new HashMap<Key, Object>(myProperties.size()); for (Map.Entry<String, Object> entry : myProperties.entrySet()) { ContentFolderPropertyProvider<?> provider = ContentFolderPropertyProvider.findProvider(entry.getKey()); if(provider == null) { continue; } myPropertiesByKeyCache.put(provider.getKey(), entry.getValue()); } } } @Nullable @Override @SuppressWarnings("unchecked") public <T> T getPropertyValue(@NotNull Key<T> key) { if (myProperties == null) { return null; } return (T)myProperties.get(key.toString()); } @Override public <T> void setPropertyValue(@NotNull Key<T> key, @Nullable T value) { myPropertiesByKeyCache = null; if (value == null && myProperties == null) { return; } if (value == null) { myProperties.remove(key.toString()); } else { if (myProperties == null) { myProperties = new HashMap<String, Object>(); } myProperties.put(key.toString(), value); } } @Override public boolean isSynthetic() { return false; } @NotNull @Override public ContentFolderTypeProvider getType() { return myContentFolderType; } @Override public int compareTo(@NotNull ContentFolderImpl folder) { int typeCompare = getType().getId().compareToIgnoreCase(folder.getType().getId()); if (typeCompare != 0) { return typeCompare; } int diff = (myProperties == null ? 0 : myProperties.hashCode()) - (folder.myProperties == null ? 0 : folder.myProperties.hashCode()); if (diff != 0) { return diff > 0 ? 1 : -1; } return getUrl().compareTo(folder.getUrl()); } @Override public boolean equals(Object obj) { if (!(obj instanceof ContentFolderImpl)) return false; return compareTo((ContentFolderImpl)obj) == 0; } @Override public int hashCode() { return getUrl().hashCode(); } @Nullable @Override public String toString() { return myFilePointer == null ? null : getUrl(); } @Override public ContentFolder cloneFolder(ContentEntry contentEntry) { assert !((ContentEntryImpl)contentEntry).isDisposed() : "target entry already disposed: " + contentEntry; assert !isDisposed() : "Already disposed: " + this; return new ContentFolderImpl(this, (ContentEntryImpl)contentEntry); } }