/*******************************************************************************
* Copyright (c) 2007, 2016 Intel Corporation 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:
* Intel Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.core.settings.model.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
import org.eclipse.cdt.core.settings.model.ICMacroEntry;
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
public class SettingsSet {
public static final int READ_ONLY = 1;
public static final int WRITABLE = 1 << 1;
private SettingLevel[] fLevels;
public class SettingLevel {
private int fFlagsToSet;
private int fFlagsToClear;
private boolean fIsReadOnly;
private boolean fIsOverrideSupported;
private LinkedHashMap<EntryNameKey, EntryInfo> fEntries;
HashSet<String> fOverrideSet;
private Object fContext;
private SettingLevel() {
fEntries = new LinkedHashMap<EntryNameKey, EntryInfo>();
}
public boolean isReadOnly() {
return fIsReadOnly;
}
public void setReadOnly(boolean readOnly) {
fIsReadOnly = readOnly;
}
public boolean isOverrideSupported() {
return fIsOverrideSupported;
}
public void setOverrideSupported(boolean supported) {
fIsOverrideSupported = supported;
}
public void setFlagsToSet(int flags) {
fFlagsToSet = flags;
}
public boolean containsOverrideInfo() {
return fOverrideSet != null;
}
public void setFlagsToClear(int flags) {
fFlagsToClear = flags;
}
public int getFlagsToSet() {
return fFlagsToSet;
}
public int getFlagsToClear() {
return fFlagsToClear;
}
public int getFlags(int baseFlags) {
return (baseFlags | fFlagsToSet) & (~fFlagsToClear);
}
@SuppressWarnings("unchecked")
public Set<String> getOverrideSet() {
if (fOverrideSet != null)
return (HashSet<String>)fOverrideSet.clone();
return new HashSet<String>();
}
public void addEntries(ICLanguageSettingEntry entries[]) {
if (entries != null) {
for (int i = 0; i < entries.length; i++) {
addEntry(entries[i]);
}
}
}
public void addEntries(List<ICLanguageSettingEntry> list) {
for (ICLanguageSettingEntry se : list)
addEntry(se);
}
public void addEntry(ICLanguageSettingEntry entry) {
addEntry(entry, null);
}
public void addEntry(ICLanguageSettingEntry entry, Object customInfo) {
entry = CDataUtil.createEntry(entry, fFlagsToSet, fFlagsToClear);
EntryInfo info = new EntryInfo(entry, customInfo);
fEntries.put(info.getContentsKey(), info);
}
public void addOverrideName(String name) {
if (fOverrideSet == null)
fOverrideSet = new HashSet<String>();
fOverrideSet.add(name);
}
public void addOverrideNameSet(Set<String> set) {
if (set == null)
return;
if (fOverrideSet != null) {
fOverrideSet.addAll(set);
} else if (set.size() != 0) {
fOverrideSet = new HashSet<String>(set);
}
}
public void removeOverrideName(String name) {
if (fOverrideSet == null)
return;
fOverrideSet.remove(name);
if (fOverrideSet.size() == 0)
fOverrideSet = null;
}
public void clear() {
fEntries.clear();
fOverrideSet = null;
}
public Map<EntryNameKey, EntryInfo> clearAndGetMap() {
Map<EntryNameKey, EntryInfo> map = fEntries;
fEntries = new LinkedHashMap<EntryNameKey, EntryInfo>();
fOverrideSet = null;
return map;
}
public EntryInfo[] getInfos() {
return fEntries.values().toArray(new EntryInfo[fEntries.size()]);
}
public ICLanguageSettingEntry[] getEntries() {
List<ICLanguageSettingEntry> list = getEntriesList(false);
return list.toArray(new ICLanguageSettingEntry[list.size()]);
}
public ICLanguageSettingEntry[] getEntries(boolean includeOverridden) {
List<ICLanguageSettingEntry> list = getEntriesList(includeOverridden);
return list.toArray(new ICLanguageSettingEntry[list.size()]);
}
public List<ICLanguageSettingEntry> getEntriesList(boolean includeOverridden) {
List<ICLanguageSettingEntry> list = new ArrayList<ICLanguageSettingEntry>();
EntryInfo infos[] = getInfos();
for (EntryInfo info : infos) {
if (includeOverridden || !info.isOverridden())
list.add(info.getEntry());
}
return list;
}
public Object getContext() {
return fContext;
}
public void setContext(Object context) {
fContext = context;
}
}
public static class EntryInfo {
private ICLanguageSettingEntry fEntry;
private EntryNameKey fNameKey;
private boolean fIsOverRidden;
private Object fCustomInfo;
private EntryInfo(ICLanguageSettingEntry entry, Object customInfo) {
fEntry = entry;
fCustomInfo = customInfo;
}
public EntryNameKey getContentsKey() {
if (fNameKey == null) {
fNameKey = new EntryNameKey(fEntry);
}
return fNameKey;
}
private void makeOverridden(boolean overridden) {
fIsOverRidden = overridden;
}
public ICLanguageSettingEntry getEntry() {
return fEntry;
}
public boolean isOverridden() {
return fIsOverRidden;
}
public Object getCustomInfo() {
return fCustomInfo;
}
}
public SettingsSet(int num) {
fLevels = new SettingLevel[num];
for (int i = 0; i < num; i++) {
fLevels[i] = new SettingLevel();
}
}
public SettingLevel[] getLevels() {
return fLevels.clone();
}
public void adjustOverrideState() {
Set<String> set = new HashSet<String>();
SettingLevel level;
for (int i = 0; i < fLevels.length; i++) {
level = fLevels[i];
if (level.isOverrideSupported() && level.fOverrideSet != null)
set.addAll(level.fOverrideSet);
adjustOverrideState(fLevels[i], set);
}
}
private void adjustOverrideState(SettingLevel level, Set<String> overridenSet) {
for (EntryInfo info : level.getInfos()) {
if (overridenSet.add(info.getEntry().getName())) {
info.makeOverridden(false);
} else {
info.makeOverridden(true);
}
}
}
public ICLanguageSettingEntry[] getEntries() {
return getEntries(READ_ONLY | WRITABLE);
}
public ICLanguageSettingEntry[] getEntries(int types) {
adjustOverrideState();
List<ICLanguageSettingEntry> entries = new ArrayList<ICLanguageSettingEntry>();
for (SettingLevel sl : fLevels) {
if (isCompatible(sl, types))
getEntries(sl, entries);
}
return entries.toArray(new ICLanguageSettingEntry[entries.size()]);
}
private void getEntries(SettingLevel level, List<ICLanguageSettingEntry> list) {
for (EntryInfo info : level.getInfos())
if (!info.isOverridden())
list.add(info.getEntry());
}
private boolean isCompatible(SettingLevel level, int types) {
if ((types & READ_ONLY) == 0 && level.isReadOnly())
return false;
if ((types & WRITABLE) == 0 && !level.isReadOnly())
return false;
return true;
}
private int getWritableLevelNum() {
for (int i = 0; i <fLevels.length; i++) {
if (!fLevels[i].isReadOnly())
return i;
}
return -1;
}
private int getOverrideLevelNum() {
for (int i = 0; i <fLevels.length; i++) {
if (fLevels[i].isOverrideSupported())
return i;
}
return -1;
}
@SuppressWarnings("unchecked")
public void applyEntries(ICLanguageSettingEntry[] entries) {
HashMap<EntryNameKey, Object[]> map = getEntryLevelMap(WRITABLE | READ_ONLY);
Map<EntryNameKey, Object[]> mapCopy = (HashMap<EntryNameKey, Object[]>)map.clone();
Map<EntryNameKey, EntryInfo>[] clearedInfos = new Map [fLevels.length];
for (int i = 0; i < fLevels.length; i++) {
if (!fLevels[i].isReadOnly())
clearedInfos[i] = fLevels[i].clearAndGetMap();
}
Integer levelInteger;
int levelNum;
ICLanguageSettingEntry entry;
int writableLevel = getWritableLevelNum();
SettingLevel level;
for (int i = 0; i < entries.length; i++) {
entry = entries[i];
EntryNameKey key = new EntryNameKey(entry);
Object[] o = map.get(key);
if (o != null && valueMatches(entry, o[1])) {
mapCopy.remove(key);
levelInteger = (Integer)o[0];
if (! entry.isBuiltIn()) // allow overwrite existing entry,
levelInteger = null; // even with the same value
} else {
levelInteger = null;
}
levelNum = levelInteger != null ? levelInteger.intValue() : writableLevel;
if (levelNum >= 0) {
level = fLevels[levelNum];
if (!level.isReadOnly()) {
Map<EntryNameKey, EntryInfo> clearedInfo = clearedInfos[levelNum];
Object customInfo = null;
if (clearedInfo != null) {
EntryInfo info = clearedInfo.get(key);
if (info != null && entry.equalsByContents(info.getEntry()))
customInfo = info.getCustomInfo();
}
level.addEntry(entry, customInfo);
}
}
}
int overrideLevel = getOverrideLevelNum();
if (overrideLevel >= 0) {
level = fLevels[overrideLevel];
if (level.isOverrideSupported() && !mapCopy.isEmpty()) {
for (EntryNameKey enk : mapCopy.keySet()) {
ICSettingEntry e = enk.getEntry();
if ((e.getFlags() & ICSettingEntry.BUILTIN) == 0)
continue;
String str = e.getName();
if (str != null)
level.addOverrideName(str);
}
}
}
adjustOverrideState();
}
public HashMap<EntryNameKey, Object[]> getEntryLevelMap(int types) {
HashMap<EntryNameKey, Object[]> map = new HashMap<EntryNameKey, Object[]>();
for (int i = 0; i < fLevels.length; i++) {
if (isCompatible(fLevels[i], types))
addLevelInfoToMap(fLevels[i], i, map);
}
return map;
}
private void addLevelInfoToMap(SettingLevel level, int l, Map<EntryNameKey, Object[]> map) {
for (EntryInfo info : level.getInfos()) {
EntryNameKey key = info.getContentsKey();
if (!map.containsKey(key))
map.put(key, new Object[]{Integer.valueOf(l), info.getEntry()});
}
}
private static boolean valueMatches(ICLanguageSettingEntry e, Object o) {
if (!(e instanceof ICMacroEntry))
return true; // ignore values for other entries
if (!(o instanceof ICMacroEntry))
return false; // cannot compare different entries
String s1 = e.getValue();
String s2 = ((ICMacroEntry) o).getValue();
return s1 == null ? s2 == null : s1.equals(s2);
}
}