/******************************************************************************* * 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); } } } } }