diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/scannerconfig/MBSLanguageSettingsProvider.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/scannerconfig/MBSLanguageSettingsProvider.java index 5f343e9382d..f324c2a6fc8 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/scannerconfig/MBSLanguageSettingsProvider.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/scannerconfig/MBSLanguageSettingsProvider.java @@ -16,6 +16,7 @@ import java.util.List; import org.eclipse.cdt.core.AbstractExecutableExtensionBase; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsBroadcastingProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICFileDescription; import org.eclipse.cdt.core.settings.model.ICFolderDescription; @@ -27,6 +28,9 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; +/** + * Implementation of language settings provider for CDT Managed Build System. + */ public class MBSLanguageSettingsProvider extends AbstractExecutableExtensionBase implements ILanguageSettingsBroadcastingProvider { @Override public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { @@ -90,7 +94,6 @@ public class MBSLanguageSettingsProvider extends AbstractExecutableExtensionBase return array; } - @Override public void setSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId, List entries) { @@ -123,4 +126,25 @@ public class MBSLanguageSettingsProvider extends AbstractExecutableExtensionBase } } + @Override + public LanguageSettingsStorage copyStorage() { + class PretendStorage extends LanguageSettingsStorage { + @Override + public boolean isEmpty() { + return false; + } + @Override + public LanguageSettingsStorage clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); +// return this; + } + @Override + public boolean equals(Object obj) { + // Note that this always triggers change event even if nothing changed in MBS + return false; + } + } + return new PretendStorage(); + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsBroadcastingProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsBroadcastingProvider.java index 8987647ad60..e4ec18493d5 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsBroadcastingProvider.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsBroadcastingProvider.java @@ -8,17 +8,19 @@ * Contributors: * Andrew Gvozdev - initial API and implementation *******************************************************************************/ - package org.eclipse.cdt.core.language.settings.providers; import java.util.List; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.internal.core.language.settings.providers.ILanguageSettingsChangeEvent; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; import org.eclipse.core.resources.IResource; /** - * TODO + * This interface is to be implemented by providers which want to broadcast the changes in their setting entries + * by {@link LanguageSettingsProvidersSerializer#notifyLanguageSettingsChangeListeners(ILanguageSettingsChangeEvent)} */ public interface ILanguageSettingsBroadcastingProvider extends ILanguageSettingsProvider { @Override @@ -28,5 +30,12 @@ public interface ILanguageSettingsBroadcastingProvider extends ILanguageSettings @Override public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId); - public void setSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId, List entries); + /** + * Return a copy of internal storage. This should be a deep copy/clone of the storage. + * It is used to calculate the delta and being kept in the last state object of configuration + * description to compare to a new state later. + * + * @return a copy of internal storage. + */ + public LanguageSettingsStorage copyStorage(); } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsEditableProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsEditableProvider.java index dc8c20c7657..2147b479101 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsEditableProvider.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsEditableProvider.java @@ -30,9 +30,29 @@ public interface ILanguageSettingsEditableProvider extends ILanguageSettingsBroa public String getName(); @Override public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId); - @Override + + /** + * Sets language settings entries for the provider. + * + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. + * @param languageId - language id. If {@code null}, then entries are considered to be defined for + * any language. + * @param entries - language settings entries to set. + */ public void setSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId, List entries); + /** + * Shallow clone of the provider. "Shallow" is defined here as the exact copy except that + * the copy will have zero language settings entries. + * + * @return shallow copy of the provider. + * @throws CloneNotSupportedException in case {@link #clone()} throws the exception. + */ public ILanguageSettingsEditableProvider cloneShallow() throws CloneNotSupportedException; + + /* + * @see Object#clone() + */ public ILanguageSettingsEditableProvider clone() throws CloneNotSupportedException; } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsBaseProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsBaseProvider.java index d1709352b7a..dad259c2584 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsBaseProvider.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsBaseProvider.java @@ -18,7 +18,6 @@ import java.util.List; import org.eclipse.cdt.core.AbstractExecutableExtensionBase; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; -import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsStorage; import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages; import org.eclipse.core.resources.IResource; diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java index bb5376ff49d..446173c43c5 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java @@ -14,10 +14,11 @@ package org.eclipse.cdt.core.language.settings.providers; import java.util.ArrayList; import java.util.List; +import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; import org.eclipse.cdt.internal.core.XmlUtil; -import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsStorage; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsSerializableStorage; import org.eclipse.core.resources.IResource; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -27,6 +28,8 @@ import org.w3c.dom.NodeList; /** * This class is the base class for language settings providers able to serialize * into XML storage. + * Although this class has setter methods, it is not editable in UI by design. + * Implement {@link ILanguageSettingsEditableProvider} interface for that. * * TODO - more JavaDoc, info and hints about class hierarchy * @@ -46,7 +49,7 @@ public class LanguageSettingsSerializableProvider extends LanguageSettingsBasePr /** Tells if language settings entries are persisted with the project or in workspace area while serializing. */ private boolean storeEntriesInProjectArea = false; - private LanguageSettingsStorage fStorage = new LanguageSettingsStorage(); + private LanguageSettingsSerializableStorage fStorage = new LanguageSettingsSerializableStorage(); /** * Default constructor. This constructor has to be always followed with setting id and name of the provider. @@ -158,7 +161,6 @@ public class LanguageSettingsSerializableProvider extends LanguageSettingsBasePr * the language scope. See {@link #getLanguageScope()} * @param entries - language settings entries to set. */ - @Override public void setSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId, List entries) { String rcProjectPath = rc!=null ? rc.getProjectRelativePath().toString() : null; fStorage.setSettingEntries(rcProjectPath, languageId, entries); @@ -321,7 +323,7 @@ public class LanguageSettingsSerializableProvider extends LanguageSettingsBasePr if (languageScope!=null) clone.languageScope = new ArrayList(languageScope); - clone.fStorage = new LanguageSettingsStorage(); + clone.fStorage = new LanguageSettingsSerializableStorage(); return clone; } @@ -339,7 +341,7 @@ public class LanguageSettingsSerializableProvider extends LanguageSettingsBasePr @Override protected LanguageSettingsSerializableProvider clone() throws CloneNotSupportedException { LanguageSettingsSerializableProvider clone = cloneShallowInternal(); - clone.fStorage = fStorage.cloneStorage(); + clone.fStorage = fStorage.clone(); return clone; } @@ -410,10 +412,13 @@ public class LanguageSettingsSerializableProvider extends LanguageSettingsBasePr return true; } - /** - * @noreference This method is not intended to be referenced by clients. - */ - public LanguageSettingsStorage getStorageInternal() { - return fStorage; + @Override + public LanguageSettingsStorage copyStorage() { + try { + return fStorage.clone(); + } catch (CloneNotSupportedException e) { + CCorePlugin.log(e); + } + return null; } } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsStorage.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsStorage.java new file mode 100644 index 00000000000..bc5f077954d --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsStorage.java @@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.internal.core.parser.util.WeakHashSet; +import org.eclipse.core.resources.IResource; + +public class LanguageSettingsStorage implements Cloneable { + /** + * Storage to keep settings entries. Note that it is not necessary to keep configuration in the maps + * as the configuration is always the one provider belongs to. + */ + protected Map>> fStorage = new HashMap>>(); + + /** + * Pool of LSE lists implemented as WeakHashSet. That allows to gain memory savings + * at the expense of CPU time. WeakHashSet handles garbage collection when a list is not + * referenced anywhere else. See JavaDoc {@link java.lang.ref.WeakReference} about weak reference objects. + */ + private static WeakHashSet> listPool = new WeakHashSet>() { + @Override + public synchronized List add(List list) { + return super.add(list); + } + + }; + + /** + * TODO + *
Note that this list is unmodifiable. + * + */ + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + List entries = null; + Map> langMap = fStorage.get(languageId); + if (langMap!=null) { + String rcProjectPath = rc!=null ? rc.getProjectRelativePath().toString() : null; + entries = langMap.get(rcProjectPath); + } + return entries; + } + + /** + * Some providers may collect entries in pretty much random order. For the purposes of + * predictability, UI usability and efficient storage the entries are sorted by kinds + * and secondary by name for kinds where the secondary order is not significant. + * + * @param entries - list of entries to sort. + * @return - sorted entries. + */ + private List sortEntries(List entries) { + List sortedEntries = new ArrayList(entries); + Collections.sort(sortedEntries, new Comparator(){ + /** + * This comparator sorts by kinds first and the macros are sorted additionally by name. + */ + @Override + public int compare(ICLanguageSettingEntry entry0, ICLanguageSettingEntry entry1) { + int kind0 = entry0.getKind(); + int kind1 = entry1.getKind(); + if (kind0==ICSettingEntry.MACRO && kind1==ICSettingEntry.MACRO) { + return entry0.getName().compareTo(entry1.getName()); + } + + return kind0 - kind1; + }}); + + return sortedEntries; + } + + /** + * + */ + public void setSettingEntries(String rcProjectPath, String languageId, List entries) { + synchronized (fStorage) { + if (entries!=null) { + Map> langMap = fStorage.get(languageId); + if (langMap==null) { + langMap = new HashMap>(); + fStorage.put(languageId, langMap); + } + List sortedEntries = getPooledList(sortEntries(entries), false); + langMap.put(rcProjectPath, sortedEntries); + } else { + // do not keep nulls in the tables + Map> langMap = fStorage.get(languageId); + if (langMap!=null) { + langMap.remove(rcProjectPath); + if (langMap.size()==0) { + fStorage.remove(languageId); + } + } + } + } + } + + /** + * @return {@code true} if the provider does not keep any settings yet or {@code false} if there are some. + */ + public boolean isEmpty() { + return fStorage.isEmpty(); + } + + /** + * Clear all the entries for all configurations, all resources and all languages. + */ + public void clear() { + synchronized (fStorage) { + fStorage.clear(); + } + } + + /** + * Returns the equal list of entries from the pool to conserve the memory. + * + * @param entries - list of entries to pool. + * @param copy - specify {@code true} to copy the list in order to prevent + * back-door modification on the original list changes. + * @return returns the list of entries from the pool. + */ + private static List getPooledList(List entries, boolean copy) { + if (entries == null) + return null; + + List pooledList = listPool.get(entries); + if (pooledList != null) { + return pooledList; + } + + if (entries.size() == 0) { + return getPooledEmptyList(); + } + + if (copy) { + entries = new ArrayList(entries); + } + pooledList = Collections.unmodifiableList(entries); + return listPool.add(pooledList); + } + + /** + * Returns the equal list of entries from the pool to conserve the memory. + * + * @param entries - list of entries to pool. + * @return returns the list of entries from the pool. + */ + public static List getPooledList(List entries) { + return getPooledList(entries, true); + } + + /** + * @return the empty immutable list which is pooled. Use this call rather than creating + * new empty array to ensure that operator '==' can be used instead of deep equals(). + */ + public static List getPooledEmptyList() { + List pooledEmptyList = Collections.emptyList(); + return listPool.add(pooledEmptyList); + } + + /** + * Clone storage for the entries. Copies references for lists of entries as a whole. + * Note that is OK as the lists kept in storage are unmodifiable. + */ + @Override + public LanguageSettingsStorage clone() throws CloneNotSupportedException { + LanguageSettingsStorage storageClone = (LanguageSettingsStorage) super.clone(); + storageClone.fStorage = new HashMap>>(); + synchronized (fStorage) { + Set>>> entrySetLang = fStorage.entrySet(); + for (Entry>> entryLang : entrySetLang) { + String langId = entryLang.getKey(); + Map> mapRc = entryLang.getValue(); + Map> mapRcClone = new HashMap>(); + Set>> entrySetRc = mapRc.entrySet(); + for (Entry> entryRc : entrySetRc) { + String rcProjectPath = entryRc.getKey(); + List lsEntries = entryRc.getValue(); + // don't need to clone entries, they are from the LSE pool + mapRcClone.put(rcProjectPath, lsEntries); + } + storageClone.fStorage.put(langId, mapRcClone); + } + } + return storageClone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((fStorage == null) ? 0 : fStorage.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LanguageSettingsStorage other = (LanguageSettingsStorage) obj; + if (fStorage == null) { + if (other.fStorage != null) + return false; + } else if (!fStorage.equals(other.fStorage)) + return false; + return true; + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsDelta.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsDelta.java index 4b88f99e6cf..317f545e14d 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsDelta.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsDelta.java @@ -12,6 +12,8 @@ package org.eclipse.cdt.internal.core.language.settings.providers; import java.util.LinkedHashMap; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; + /** * This class currently is a placeholder holding old and new states. @@ -21,11 +23,11 @@ public class LanguageSettingsDelta { // maps need to be ordered by providers @SuppressWarnings("unused") private LinkedHashMap oldLanguageSettingsState; + LanguageSettingsStorage> oldLanguageSettingsState; @SuppressWarnings("unused") private LinkedHashMap newLanguageSettingsState; - + LanguageSettingsStorage> newLanguageSettingsState; + public LanguageSettingsDelta(LinkedHashMap oldState, LinkedHashMap newState) { oldLanguageSettingsState = oldState; newLanguageSettingsState = newState; diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java index 83ecd49a078..0e237ea0d21 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java @@ -24,6 +24,7 @@ import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsEditabl import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsSerializableProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; import org.eclipse.cdt.core.language.settings.providers.ScannerDiscoveryLegacySupport; import org.eclipse.cdt.core.model.ILanguage; import org.eclipse.cdt.core.model.LanguageManager; @@ -338,7 +339,7 @@ public class LanguageSettingsProvidersSerializer { } private static List createLanguageSettingsChangeEvents(List serializableProviders) { - List events = new ArrayList(); + List events = new ArrayList(); List serializableIds = new ArrayList(); for (LanguageSettingsSerializableProvider provider : serializableProviders) { diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsSerializableStorage.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsSerializableStorage.java new file mode 100644 index 00000000000..f2e05acd297 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsSerializableStorage.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.core.settings.model.util.LanguageSettingEntriesSerializer; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class LanguageSettingsSerializableStorage extends LanguageSettingsStorage { + private static final String ELEM_LANGUAGE = "language"; //$NON-NLS-1$ + private static final String ATTR_LANGUAGE_ID = "id"; //$NON-NLS-1$ + private static final String ELEM_RESOURCE = "resource"; //$NON-NLS-1$ + private static final String ATTR_PROJECT_PATH = "project-relative-path"; //$NON-NLS-1$ + + private static final String ELEM_ENTRY = "entry"; //$NON-NLS-1$ + private static final String ATTR_KIND = "kind"; //$NON-NLS-1$ + private static final String ATTR_NAME = "name"; //$NON-NLS-1$ + private static final String ATTR_VALUE = "value"; //$NON-NLS-1$ + private static final String ELEM_FLAG = "flag"; //$NON-NLS-1$ + + /** + * Serialize the provider entries under parent XML element. + * @param elementProvider - element where to serialize the entries. + */ + public void serializeEntries(Element elementProvider) { + synchronized (fStorage) { + for (Entry>> entryLang : fStorage.entrySet()) { + serializeLanguage(elementProvider, entryLang); + } + } + } + + /** + * Serialize the provider entries for a given language list. + */ + private void serializeLanguage(Element parentElement, Entry>> entryLang) { + String langId = entryLang.getKey(); + if (langId!=null) { + Element elementLanguage = XmlUtil.appendElement(parentElement, ELEM_LANGUAGE, new String[] {ATTR_LANGUAGE_ID, langId}); + parentElement = elementLanguage; + } + for (Entry> entryRc : entryLang.getValue().entrySet()) { + serializeResource(parentElement, entryRc); + } + } + + /** + * Serialize the provider entries for a given resource list. + */ + private void serializeResource(Element parentElement, Entry> entryRc) { + String rcProjectPath = entryRc.getKey(); + if (rcProjectPath!=null) { + Element elementRc = XmlUtil.appendElement(parentElement, ELEM_RESOURCE, new String[] {ATTR_PROJECT_PATH, rcProjectPath}); + parentElement = elementRc; + } + serializeSettingEntries(parentElement, entryRc.getValue()); + } + + /** + * Serialize given settings entries. + */ + private void serializeSettingEntries(Element parentElement, List settingEntries) { + for (ICLanguageSettingEntry entry : settingEntries) { + Element elementSettingEntry = XmlUtil.appendElement(parentElement, ELEM_ENTRY, new String[] { + ATTR_KIND, LanguageSettingEntriesSerializer.kindToString(entry.getKind()), + ATTR_NAME, entry.getName(), + }); + switch(entry.getKind()) { + case ICSettingEntry.MACRO: + elementSettingEntry.setAttribute(ATTR_VALUE, entry.getValue()); + break; +// case ICLanguageSettingEntry.LIBRARY_FILE: +// // TODO: sourceAttachment fields may need to be covered +// break; + } + int flags = entry.getFlags(); + if (flags!=0) { + // Element elementFlag = + XmlUtil.appendElement(elementSettingEntry, ELEM_FLAG, new String[] { + ATTR_VALUE, LanguageSettingEntriesSerializer.composeFlagsString(entry.getFlags()) + }); + } + } + } + + /** + * Load provider entries from XML provider element. + * @param providerNode - parent XML element where entries are defined. + */ + public void loadEntries(Element providerNode) { + List settings = new ArrayList(); + NodeList nodes = providerNode.getChildNodes(); + for (int i=0;i0) { + setSettingEntries(null, null, settings); + } + } + + /** + * Load a setting entry from XML element. + */ + private ICLanguageSettingEntry loadSettingEntry(Node parentElement) { + String settingKind = XmlUtil.determineAttributeValue(parentElement, ATTR_KIND); + String settingName = XmlUtil.determineAttributeValue(parentElement, ATTR_NAME); + + NodeList flagNodes = parentElement.getChildNodes(); + int flags = 0; + for (int i=0;i settings = new ArrayList(); + NodeList nodes = parentNode.getChildNodes(); + for (int i=0;i0) { + setSettingEntries(null, langId, settings); + } + } + + /** + * Load entries defined in resource element. + */ + private void loadResourceElement(Node parentNode, String cfgId, String langId) { + String rcProjectPath = XmlUtil.determineAttributeValue(parentNode, ATTR_PROJECT_PATH); + + List settings = new ArrayList(); + NodeList nodes = parentNode.getChildNodes(); + for (int i=0;i0) { + setSettingEntries(rcProjectPath, langId, settings); + } + } + + @Override + public LanguageSettingsSerializableStorage clone() throws CloneNotSupportedException { + return (LanguageSettingsSerializableStorage) super.clone(); + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsStorage.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsStorage.java deleted file mode 100644 index ec37aa13ec6..00000000000 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsStorage.java +++ /dev/null @@ -1,419 +0,0 @@ -package org.eclipse.cdt.internal.core.language.settings.providers; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; -import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; -import org.eclipse.cdt.core.settings.model.ICSettingEntry; -import org.eclipse.cdt.core.settings.model.util.CDataUtil; -import org.eclipse.cdt.core.settings.model.util.LanguageSettingEntriesSerializer; -import org.eclipse.cdt.internal.core.XmlUtil; -import org.eclipse.cdt.internal.core.parser.util.WeakHashSet; -import org.eclipse.core.resources.IResource; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -public class LanguageSettingsStorage { - private static final String ELEM_LANGUAGE = "language"; //$NON-NLS-1$ - private static final String ATTR_LANGUAGE_ID = "id"; //$NON-NLS-1$ - private static final String ELEM_RESOURCE = "resource"; //$NON-NLS-1$ - private static final String ATTR_PROJECT_PATH = "project-relative-path"; //$NON-NLS-1$ - - private static final String ELEM_ENTRY = "entry"; //$NON-NLS-1$ - private static final String ATTR_KIND = "kind"; //$NON-NLS-1$ - private static final String ATTR_NAME = "name"; //$NON-NLS-1$ - private static final String ATTR_VALUE = "value"; //$NON-NLS-1$ - private static final String ELEM_FLAG = "flag"; //$NON-NLS-1$ - - /** - * Storage to keep settings entries. Note that it is not necessary to keep configuration in the maps - * as the configuration is always the one provider belongs to. - */ - private Map>> fStorage = new HashMap>>(); - - /** - * Pool of LSE lists implemented as WeakHashSet. That allows to gain memory savings - * at the expense of CPU time. WeakHashSet handles garbage collection when a list is not - * referenced anywhere else. See JavaDoc {@link java.lang.ref.WeakReference} about weak reference objects. - */ - private static WeakHashSet> listPool = new WeakHashSet>() { - @Override - public synchronized List add(List list) { - return super.add(list); - } - - }; - - /** - * TODO - *
Note that this list is unmodifiable. - * - */ - public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { - List entries = null; - Map> langMap = fStorage.get(languageId); - if (langMap!=null) { - String rcProjectPath = rc!=null ? rc.getProjectRelativePath().toString() : null; - entries = langMap.get(rcProjectPath); - } - return entries; - } - - /** - * Some providers may collect entries in pretty much random order. For the purposes of - * predictability, UI usability and efficient storage the entries are sorted by kinds - * and secondary by name for kinds where the secondary order is not significant. - * - * @param entries - list of entries to sort. - * @return - sorted entries. - */ - private List sortEntries(List entries) { - List sortedEntries = new ArrayList(entries); - Collections.sort(sortedEntries, new Comparator(){ - /** - * This comparator sorts by kinds first and the macros are sorted additionally by name. - */ - @Override - public int compare(ICLanguageSettingEntry entry0, ICLanguageSettingEntry entry1) { - int kind0 = entry0.getKind(); - int kind1 = entry1.getKind(); - if (kind0==ICSettingEntry.MACRO && kind1==ICSettingEntry.MACRO) { - return entry0.getName().compareTo(entry1.getName()); - } - - return kind0 - kind1; - }}); - - return sortedEntries; - } - - /** - * - */ - public void setSettingEntries(String rcProjectPath, String languageId, List entries) { - synchronized (fStorage) { - if (entries!=null) { - Map> langMap = fStorage.get(languageId); - if (langMap==null) { - langMap = new HashMap>(); - fStorage.put(languageId, langMap); - } - List sortedEntries = getPooledList(sortEntries(entries), false); - langMap.put(rcProjectPath, sortedEntries); - } else { - // do not keep nulls in the tables - Map> langMap = fStorage.get(languageId); - if (langMap!=null) { - langMap.remove(rcProjectPath); - if (langMap.size()==0) { - fStorage.remove(languageId); - } - } - } - } - } - - /** - * @return {@code true} if the provider does not keep any settings yet or {@code false} if there are some. - */ - public boolean isEmpty() { - return fStorage.isEmpty(); - } - - /** - * Clear all the entries for all configurations, all resources and all languages. - */ - public void clear() { - synchronized (fStorage) { - fStorage.clear(); - } - } - - /** - * Serialize the provider entries under parent XML element. - * @param elementProvider - element where to serialize the entries. - */ - public void serializeEntries(Element elementProvider) { - synchronized (fStorage) { - for (Entry>> entryLang : fStorage.entrySet()) { - serializeLanguage(elementProvider, entryLang); - } - } - } - - /** - * Serialize the provider entries for a given language list. - */ - private void serializeLanguage(Element parentElement, Entry>> entryLang) { - String langId = entryLang.getKey(); - if (langId!=null) { - Element elementLanguage = XmlUtil.appendElement(parentElement, ELEM_LANGUAGE, new String[] {ATTR_LANGUAGE_ID, langId}); - parentElement = elementLanguage; - } - for (Entry> entryRc : entryLang.getValue().entrySet()) { - serializeResource(parentElement, entryRc); - } - } - - /** - * Serialize the provider entries for a given resource list. - */ - private void serializeResource(Element parentElement, Entry> entryRc) { - String rcProjectPath = entryRc.getKey(); - if (rcProjectPath!=null) { - Element elementRc = XmlUtil.appendElement(parentElement, ELEM_RESOURCE, new String[] {ATTR_PROJECT_PATH, rcProjectPath}); - parentElement = elementRc; - } - serializeSettingEntries(parentElement, entryRc.getValue()); - } - - /** - * Serialize given settings entries. - */ - private void serializeSettingEntries(Element parentElement, List settingEntries) { - for (ICLanguageSettingEntry entry : settingEntries) { - Element elementSettingEntry = XmlUtil.appendElement(parentElement, ELEM_ENTRY, new String[] { - ATTR_KIND, LanguageSettingEntriesSerializer.kindToString(entry.getKind()), - ATTR_NAME, entry.getName(), - }); - switch(entry.getKind()) { - case ICSettingEntry.MACRO: - elementSettingEntry.setAttribute(ATTR_VALUE, entry.getValue()); - break; -// case ICLanguageSettingEntry.LIBRARY_FILE: -// // TODO: sourceAttachment fields may need to be covered -// break; - } - int flags = entry.getFlags(); - if (flags!=0) { - // Element elementFlag = - XmlUtil.appendElement(elementSettingEntry, ELEM_FLAG, new String[] { - ATTR_VALUE, LanguageSettingEntriesSerializer.composeFlagsString(entry.getFlags()) - }); - } - } - } - - /** - * Load provider entries from XML provider element. - * @param providerNode - parent XML element where entries are defined. - */ - public void loadEntries(Element providerNode) { - List settings = new ArrayList(); - NodeList nodes = providerNode.getChildNodes(); - for (int i=0;i0) { - setSettingEntries(null, null, settings); - } - } - - /** - * Load a setting entry from XML element. - */ - private ICLanguageSettingEntry loadSettingEntry(Node parentElement) { - String settingKind = XmlUtil.determineAttributeValue(parentElement, ATTR_KIND); - String settingName = XmlUtil.determineAttributeValue(parentElement, ATTR_NAME); - - NodeList flagNodes = parentElement.getChildNodes(); - int flags = 0; - for (int i=0;i settings = new ArrayList(); - NodeList nodes = parentNode.getChildNodes(); - for (int i=0;i0) { - setSettingEntries(null, langId, settings); - } - } - - /** - * Load entries defined in resource element. - */ - private void loadResourceElement(Node parentNode, String cfgId, String langId) { - String rcProjectPath = XmlUtil.determineAttributeValue(parentNode, ATTR_PROJECT_PATH); - - List settings = new ArrayList(); - NodeList nodes = parentNode.getChildNodes(); - for (int i=0;i0) { - setSettingEntries(rcProjectPath, langId, settings); - } - } - - /** - * Returns the equal list of entries from the pool to conserve the memory. - * - * @param entries - list of entries to pool. - * @param copy - specify {@code true} to copy the list in order to prevent - * back-door modification on the original list changes. - * @return returns the list of entries from the pool. - */ - private static List getPooledList(List entries, boolean copy) { - if (entries == null) - return null; - - List pooledList = listPool.get(entries); - if (pooledList != null) { - return pooledList; - } - - if (entries.size() == 0) { - return getPooledEmptyList(); - } - - if (copy) { - entries = new ArrayList(entries); - } - pooledList = Collections.unmodifiableList(entries); - return listPool.add(pooledList); - } - - /** - * Returns the equal list of entries from the pool to conserve the memory. - * - * @param entries - list of entries to pool. - * @return returns the list of entries from the pool. - */ - public static List getPooledList(List entries) { - return getPooledList(entries, true); - } - - /** - * @return the empty immutable list which is pooled. Use this call rather than creating - * new empty array to ensure that operator '==' can be used instead of deep equals(). - */ - public static List getPooledEmptyList() { - List pooledEmptyList = Collections.emptyList(); - return listPool.add(pooledEmptyList); - } - - /** - * Clone storage for the entries. Copies references for lists of entries as a whole. - * Note that is OK as the lists kept in storage are unmodifiable. - */ - public LanguageSettingsStorage cloneStorage() { - LanguageSettingsStorage storageClone = new LanguageSettingsStorage(); - synchronized (fStorage) { - Set>>> entrySetLang = fStorage.entrySet(); - for (Entry>> entryLang : entrySetLang) { - String langId = entryLang.getKey(); - Map> mapRc = entryLang.getValue(); - Map> mapRcClone = new HashMap>(); - Set>> entrySetRc = mapRc.entrySet(); - for (Entry> entryRc : entrySetRc) { - String rcProjectPath = entryRc.getKey(); - List lsEntries = entryRc.getValue(); - // don't need to clone entries, they are from the LSE pool - mapRcClone.put(rcProjectPath, lsEntries); - } - storageClone.fStorage.put(langId, mapRcClone); - } - } - return storageClone; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((fStorage == null) ? 0 : fStorage.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - LanguageSettingsStorage other = (LanguageSettingsStorage) obj; - if (fStorage == null) { - if (other.fStorage != null) - return false; - } else if (!fStorage.equals(other.fStorage)) - return false; - return true; - } - -} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java index a7c6d604840..6b140bba8ea 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java @@ -23,9 +23,10 @@ import java.util.Map.Entry; import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsBroadcastingProvider; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; -import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsSerializableProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; import org.eclipse.cdt.core.settings.model.CExternalSetting; import org.eclipse.cdt.core.settings.model.ICBuildSetting; import org.eclipse.cdt.core.settings.model.ICConfigExtensionReference; @@ -45,7 +46,6 @@ import org.eclipse.cdt.internal.core.cdtvariables.StorableCdtVariables; import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsDelta; import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; -import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsStorage; import org.eclipse.cdt.utils.envvar.StorableEnvironment; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.QualifiedName; @@ -193,11 +193,15 @@ public class CConfigurationSpecSettings implements ICSettingsStorage{ fOwner = base.fOwner; copyExtensionInfo(base); - + fLanguageSettingsProviders = LanguageSettingsProvidersSerializer.cloneProviders(base.getLanguageSettingProviders()); for (String providerId : base.lspPersistedState.keySet()) { - LanguageSettingsStorage clone = base.lspPersistedState.get(providerId).cloneStorage(); - lspPersistedState.put(providerId, clone); + try { + LanguageSettingsStorage clone = base.lspPersistedState.get(providerId).clone(); + lspPersistedState.put(providerId, clone); + } catch (CloneNotSupportedException e) { + CCorePlugin.log(e); + } } } @@ -997,11 +1001,11 @@ public class CConfigurationSpecSettings implements ICSettingsStorage{ public void updateExternalSettingsProviders(String[] ids){ ExtensionContainerFactory.updateReferencedProviderIds(fCfg, ids); } - + /** * Adds list of {@link ILanguageSettingsProvider} to the specs. * Note that only unique IDs are accepted. - * + * * @param providers - list of providers to keep in the specs. */ public void setLanguageSettingProviders(List providers) { @@ -1035,32 +1039,25 @@ public class CConfigurationSpecSettings implements ICSettingsStorage{ */ public LanguageSettingsDelta dropDelta() { LanguageSettingsDelta languageSettingsDelta = null; - // newState gets shallow map first - LinkedHashMap newStateShallow = new LinkedHashMap(); + LinkedHashMap newState = new LinkedHashMap(); for (ILanguageSettingsProvider provider : fLanguageSettingsProviders) { if (LanguageSettingsManager.isWorkspaceProvider(provider)) { provider = LanguageSettingsManager.getRawProvider(provider); } - if (provider instanceof LanguageSettingsSerializableProvider) { - LanguageSettingsStorage store = ((LanguageSettingsSerializableProvider) provider).getStorageInternal(); - if (!store.isEmpty()) { - newStateShallow.put(provider.getId(), store); + if (provider instanceof ILanguageSettingsBroadcastingProvider) { + LanguageSettingsStorage store = ((ILanguageSettingsBroadcastingProvider) provider).copyStorage(); + // avoid triggering event if empty provider was added + if (store != null && !store.isEmpty()) { + newState.put(provider.getId(), store); } } } - if (!newStateShallow.equals(lspPersistedState)) { - // do deep copy if the state needs to be saved - LinkedHashMap newStateDeep = new LinkedHashMap(); - for (Entry entry : newStateShallow.entrySet()) { - String providerId = entry.getKey(); - LanguageSettingsStorage store = entry.getValue(); - newStateDeep.put(providerId, store.cloneStorage()); - } - languageSettingsDelta = new LanguageSettingsDelta(lspPersistedState, newStateDeep); - lspPersistedState = newStateDeep; + if (!newState.equals(lspPersistedState)) { + languageSettingsDelta = new LanguageSettingsDelta(lspPersistedState, newState); + lspPersistedState = newState; } - + return languageSettingsDelta; } - + }