blob: f7c39d5a53223bdb63fb69438b79bcfea284005c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2016 QNX Software Systems
* 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:
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.codan.core.param;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.cdt.codan.core.model.AbstractCheckerWithProblemPreferences;
/**
* MapProblemPreference - for checker that needs more than one preferences and
* they all differently "named".
* For example checker for parameter names shadowing would have two boolean
* options:
* "check constructors" and
* "check setters". In this case you use this type.
* {@link AbstractCheckerWithProblemPreferences} class has map as default top
* level parameter preference.
*
* @noextend This class is not intended to be extended by clients.
*/
public class MapProblemPreference extends AbstractProblemPreference
implements IProblemPreferenceCompositeValue, IProblemPreferenceCompositeDescriptor {
private Map<String, IProblemPreference> hash = Collections.synchronizedMap(new LinkedHashMap<String, IProblemPreference>());
/**
* Default constructor
*/
public MapProblemPreference() {
super();
}
/**
* @param key
* - key for itself
* @param label
* - label for this group of parameters
*/
public MapProblemPreference(String key, String label) {
setKey(key);
setLabel(label);
}
@Override
public PreferenceType getType() {
return PreferenceType.TYPE_MAP;
}
/**
* Returns parameter preference for element by key
*/
@Override
public IProblemPreference getChildDescriptor(String key) {
return hash.get(key);
}
/**
* Adds or replaces child descriptor and value for the element with the key
* equals to desc.getKey(). The desc object would be put in the map, some of
* its field may be modified.
*
* @param desc
*/
@Override
public IProblemPreference addChildDescriptor(IProblemPreference desc) {
((AbstractProblemPreference) desc).setParent(this);
hash.put(desc.getKey(), desc);
return desc;
}
/**
* Return list of child descriptors. Client should threat returned value as
* read only,
* and not assume that modifying its elements would modify actual child
* values.
*/
@Override
public IProblemPreference[] getChildDescriptors() {
return hash.values().toArray(new IProblemPreference[hash.values().size()]);
}
/**
* Returns value of the child element by its key
*/
@Override
public Object getChildValue(String key) {
IProblemPreference childInfo = getChildDescriptor(key);
return childInfo.getValue();
}
/**
* Set child value by its key
*/
@Override
public void setChildValue(String key, Object value) {
IProblemPreference pref = getChildDescriptor(key);
if (pref == null)
throw new IllegalArgumentException("Preference for " + key //$NON-NLS-1$
+ " must exists before setting its value"); //$NON-NLS-1$
pref.setValue(value);
hash.put(key, pref); // cannot assume getChildDescriptor returns shared value
}
/**
* Removes child value and descriptor by key
*/
@Override
public void removeChildValue(String key) {
hash.remove(key);
}
@Override
public Object clone() {
MapProblemPreference map = (MapProblemPreference) super.clone();
synchronized (hash) {
map.hash = Collections.synchronizedMap(new LinkedHashMap<String, IProblemPreference>(hash));
}
// now we have to clone the values too
for (Entry<String, IProblemPreference> entry : map.hash.entrySet()) {
entry.setValue((IProblemPreference) entry.getValue().clone());
}
return map;
}
@Override
public String exportValue() {
synchronized (hash) {
StringBuilder buf = new StringBuilder("{"); //$NON-NLS-1$
for (Iterator<String> iterator = hash.keySet().iterator(); iterator.hasNext();) {
String key = iterator.next();
IProblemPreference d = hash.get(key);
if (d instanceof AbstractProblemPreference) {
if (((AbstractProblemPreference) d).isDefault()) {
continue;
}
}
buf.append(key).append("=>").append(d.exportValue()); //$NON-NLS-1$
if (iterator.hasNext())
buf.append(","); //$NON-NLS-1$
}
return buf.toString() + "}"; //$NON-NLS-1$
}
}
@Override
public void importValue(String str) {
StreamTokenizer tokenizer = getImportTokenizer(str);
try {
importValue(tokenizer);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(str + ":" + e.toString(), e); //$NON-NLS-1$
}
}
/**
* @param tokenizer
*/
@Override
public void importValue(StreamTokenizer tokenizer) {
int token;
try {
token = tokenizer.nextToken();
if (token != '{') {
throw new IllegalArgumentException(String.valueOf((char) token));
}
while (true) {
token = tokenizer.nextToken();
if (token == '}')
break;
String key = tokenizer.sval;
token = tokenizer.nextToken();
if (token != '=')
throw new IllegalArgumentException(String.valueOf((char) token));
token = tokenizer.nextToken();
if (token != '>')
throw new IllegalArgumentException(String.valueOf((char) token));
importChildValue(key, tokenizer);
token = tokenizer.nextToken();
if (token == '}')
break;
if (token != ',')
throw new IllegalArgumentException(String.valueOf((char) token));
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
/**
* @param key
* @param tokenizer
* @return
* @throws IOException
* @since 2.0
*/
protected IProblemPreference importChildValue(String key, StreamTokenizer tokenizer) throws IOException {
IProblemPreference desc = getChildDescriptor(key);
if (desc != null && desc instanceof AbstractProblemPreference) {
((AbstractProblemPreference) desc).importValue(tokenizer);
setChildValue(key, desc.getValue());
}
return desc;
}
/**
* Removes child descriptor by its key
*/
@Override
public void removeChildDescriptor(IProblemPreference info) {
hash.remove(info.getKey());
}
/**
* @return size of the map
*/
public int size() {
return hash.size();
}
/**
* Clears the map
*/
public void clear() {
hash.clear();
}
@Override
public String toString() {
return hash.values().toString();
}
/**
* Value of this preference is a map key=>value of child preferences.
* Modifying this returned map would not change internal state of this
* object.
*/
@Override
public Object getValue() {
synchronized (hash) {
LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
for (Iterator<IProblemPreference> iterator = hash.values().iterator(); iterator.hasNext();) {
IProblemPreference pref = iterator.next();
map.put(pref.getKey(), pref.getValue());
}
return map;
}
}
/**
* Set values for this object child elements. Elements are not present in
* this map would be removed.
* Preference descriptors for the keys must be set before calling this
* method, unless value if instanceof {@link IProblemPreference}.
*
* @param value - must be Map<String,Object>
*/
@SuppressWarnings("unchecked")
@Override
public void setValue(Object value) {
Map<String, Object> map = (Map<String, Object>) value;
synchronized (hash) {
Map<String, IProblemPreference> hashCopy = new HashMap<>(hash);
hash.clear();
for (Iterator<String> iterator = map.keySet().iterator(); iterator.hasNext();) {
String key = iterator.next();
Object value2 = map.get(key);
if (value2 instanceof IProblemPreference) {
hash.put(key, (IProblemPreference) value2);
} else {
IProblemPreference pref = hashCopy.get(key);
addChildDescriptor(pref);
//setChildValue(key, value2);
pref.setValue(value2);
hash.put(key, pref);
}
}
}
}
}