| /******************************************************************************* |
| * Copyright (c) 2011 University of Illinois 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: |
| * Albert L. Rossi - design and implementation |
| ******************************************************************************/ |
| package org.eclipse.ptp.rm.jaxb.control.ui.variables; |
| |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import java.util.TreeSet; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.variables.VariablesPlugin; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| import org.eclipse.ptp.core.IPTPLaunchConfigurationConstants; |
| import org.eclipse.ptp.rm.jaxb.control.JAXBControlConstants; |
| import org.eclipse.ptp.rm.jaxb.control.internal.variables.RMVariableMap; |
| import org.eclipse.ptp.rm.jaxb.control.ui.JAXBControlUIConstants; |
| import org.eclipse.ptp.rm.jaxb.control.ui.messages.Messages; |
| import org.eclipse.ptp.rm.jaxb.core.IVariableMap; |
| import org.eclipse.ptp.rm.jaxb.core.JAXBCorePlugin; |
| import org.eclipse.ptp.rm.jaxb.core.data.AttributeType; |
| import org.eclipse.ptp.rm.jaxb.core.data.PropertyType; |
| import org.eclipse.ptp.rm.jaxb.ui.JAXBUIConstants; |
| |
| /** |
| * A wrapper for the LaunchConfiguration accessed through the IVariableMap |
| * interface.<br> |
| * <br> |
| * <br> |
| * Unlike the RMVariableMap, the internal map here is largely flat in the sense |
| * that it holds only name-value pairs instead of the Property or Attribute |
| * objects.<br> |
| * <br> |
| * When this map is loaded from its parent (see |
| * {@link #initialize(IVariableMap)}), the full set of Properties and Attributes |
| * are maintained in a global map, which remains unaltered; a second, volatile |
| * map can be swapped in and out by the caller to view the tab-specific |
| * environment.<br> |
| * <br> |
| * This object also maintains the default values defined from the parent in a |
| * separate map, and a map for invisible properties (i.e., those not exported to |
| * widgets). Finally, it also holds aside linked properties for reevaluation at |
| * update. |
| * |
| * @see org.eclipse.debug.core.ILaunchConfigurationWorkingCopy |
| * @see org.eclipse.ptp.rm.jaxb.core.IVariableMap |
| * |
| * @author arossi |
| */ |
| public class LCVariableMap implements IVariableMap { |
| private static final Object monitor = new Object(); |
| |
| private final Map<String, Object> linkedTo; |
| private final Map<String, Object> excluded; |
| private final Map<String, Object> values; |
| private final Map<String, String> defaultValues; |
| private final Map<String, Object> temp; |
| private final Set<String> hidden; |
| |
| private String rmPrefix; |
| |
| public LCVariableMap() { |
| this.values = Collections.synchronizedMap(new TreeMap<String, Object>()); |
| this.excluded = Collections.synchronizedMap(new TreeMap<String, Object>()); |
| this.defaultValues = Collections.synchronizedMap(new TreeMap<String, String>()); |
| this.linkedTo = Collections.synchronizedMap(new TreeMap<String, Object>()); |
| this.temp = Collections.synchronizedMap(new TreeMap<String, Object>()); |
| this.hidden = new HashSet<String>(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ptp.rm.jaxb.core.IVariableMap#clear() |
| */ |
| public void clear() { |
| values.clear(); |
| defaultValues.clear(); |
| linkedTo.clear(); |
| temp.clear(); |
| excluded.clear(); |
| hidden.clear(); |
| } |
| |
| /** |
| * Merge the attribute map on the configuration with the current (volatile) |
| * map.<br> |
| * <br> |
| * |
| * Note that if any |
| * <code>null<code> values go into the working copy, they are not subsequently written to the original. |
| * Thus to maintain consistency, we do no allow <code>null</code> values in |
| * the map. |
| * |
| * @param configuration |
| * working copy of Launch Tab's current configuration |
| * @throws CoreException |
| */ |
| @SuppressWarnings("rawtypes") |
| public void flush(ILaunchConfigurationWorkingCopy configuration) throws CoreException { |
| for (String name : values.keySet()) { |
| Object value = values.get(name); |
| if (value instanceof Boolean) { |
| configuration.setAttribute(name, (Boolean) value); |
| } else if (value instanceof Integer) { |
| configuration.setAttribute(name, (Integer) value); |
| } else if (value instanceof List) { |
| configuration.setAttribute(name, (List) value); |
| } else if (value instanceof Set) { |
| configuration.setAttribute(name, (Set) value); |
| } else if (value instanceof Map) { |
| configuration.setAttribute(name, (Map) value); |
| } else { |
| configuration.setAttribute(name, (String) value); |
| } |
| } |
| } |
| |
| /** |
| * @param name |
| * of tab or viewer for which to find qualifying widgets or |
| * checked rows |
| * @param statePrefix |
| * the tag for the control state |
| * @return set of variable names in this control state |
| */ |
| public Set<String> forControlState(String name, String statePrefix) { |
| Set<String> set = new TreeSet<String>(); |
| String state = (String) get(statePrefix + name); |
| if (state != null) { |
| String[] split = state.split(JAXBControlUIConstants.SP); |
| for (String s : split) { |
| set.add(s); |
| } |
| } |
| return set; |
| } |
| |
| /** |
| * @param name |
| * of widget, bound to a Property or Attribute |
| * @return value of the Property or Attribute, or <code>null</code> if none |
| */ |
| public Object get(String name) { |
| if (name == null) { |
| return null; |
| } |
| return values.get(rmPrefix + name); |
| } |
| |
| /** |
| * @param viewerName |
| * of viewer for which to find checked rows |
| * @return set of checked row model names |
| */ |
| public Set<String> getChecked(String viewerName) { |
| return forControlState(viewerName, JAXBControlUIConstants.CHECKED_ATTRIBUTES); |
| } |
| |
| /** |
| * @param name |
| * of widget, bound to a Property or Attribute |
| * @return default value of the Property or Attribute, or <code>null</code> |
| * if none |
| */ |
| public String getDefault(String name) { |
| if (name == null) { |
| return null; |
| } |
| return defaultValues.get(name); |
| } |
| |
| /* |
| * Unsupported (non-Javadoc) |
| * |
| * @see org.eclipse.ptp.rm.jaxb.core.IVariableMap#getDiscovered() |
| */ |
| public Map<String, Object> getDiscovered() { |
| return null; |
| } |
| |
| /** |
| * @return hiddenDiscovered |
| */ |
| public Map<String, Object> getExcluded() { |
| return excluded; |
| } |
| |
| /** |
| * @return the set of properties and attributes marked not visible |
| */ |
| public Set<String> getHidden() { |
| return hidden; |
| } |
| |
| /** |
| * The ${rm: prefix points to the RMVariableResolver, ${lc: to the |
| * LCVariableResolver, so we substitute the latter and pass off the |
| * substitution to the resolver for resolution. |
| * |
| * @param value |
| * expression to be resolved |
| * @return resolved expression |
| */ |
| public String getString(String value) { |
| try { |
| value = value.replaceAll(JAXBControlUIConstants.VRM, JAXBControlUIConstants.VLC); |
| return dereference(value); |
| } catch (CoreException t) { |
| JAXBCorePlugin.log(t); |
| } |
| return value; |
| } |
| |
| /** |
| * Interface method. Not called in this UI class. |
| * |
| * @param jobId |
| * is irrelevant |
| * @param value |
| * expression to be resolved |
| * @return resolved expression |
| */ |
| public String getString(String jobId, String value) { |
| return getString(value); |
| } |
| |
| /* |
| * Not supported. (non-Javadoc) |
| * |
| * @see org.eclipse.ptp.rm.jaxb.core.IVariableMap#getVariables() |
| */ |
| public Map<String, Object> getVariables() { |
| return null; |
| } |
| |
| /** |
| * Initialize this map from the resource manager environment instance. |
| * |
| * @param rmVars |
| * resource manager environment map |
| * @param rmId |
| * IPTPLaunchConfigurationConstants. |
| * ATTR_RESOURCE_MANAGER_UNIQUENAME |
| * @throws Throwable |
| */ |
| public void initialize(IVariableMap rmVars, String rmId) throws Throwable { |
| clear(); |
| this.rmPrefix = rmId + JAXBUIConstants.DOT; |
| for (String s : rmVars.getVariables().keySet()) { |
| loadValues(s, rmVars.getVariables().get(s), false); |
| } |
| for (String s : rmVars.getDiscovered().keySet()) { |
| loadValues(s, rmVars.getDiscovered().get(s), true); |
| } |
| } |
| |
| /** |
| * @param name |
| * of widget, bound to a Property or Attribute |
| * @param value |
| * of Property or Attribute |
| */ |
| public void put(String name, Object value) { |
| if (name == null || JAXBUIConstants.ZEROSTR.equals(name)) { |
| return; |
| } |
| if (value != null) { |
| values.put(rmPrefix + name, value); |
| } |
| } |
| |
| /** |
| * Relink ptp, debug, directory, executable and arguments variables. Note |
| * that the externally defined variables do not get the rmId prefix. |
| * |
| * @param configuration |
| * current launch settings |
| * @throws CoreException |
| */ |
| public void relinkConfigurationProperties(ILaunchConfiguration configuration) throws CoreException { |
| for (Iterator<String> key = values.keySet().iterator(); key.hasNext();) { |
| String name = key.next(); |
| if (!name.startsWith(rmPrefix)) { |
| key.remove(); |
| } |
| } |
| |
| Map<?, ?> attributes = configuration.getAttributes(); |
| for (Object o : attributes.keySet()) { |
| String key = (String) o; |
| if (RMVariableMap.isExternal(key)) { |
| values.put(key, attributes.get(key)); |
| } |
| } |
| |
| String cdir = (String) get(JAXBControlConstants.CONTROL_WORKING_DIR_VAR); |
| String dir = (String) get(JAXBControlConstants.DIRECTORY); |
| if (dir == null || JAXBControlConstants.ZEROSTR.equals(dir)) { |
| dir = (cdir == null ? JAXBControlConstants.ZEROSTR : cdir); |
| } |
| put(JAXBControlConstants.DIRECTORY, configuration.getAttribute(IPTPLaunchConfigurationConstants.ATTR_WORKING_DIR, dir)); |
| put(JAXBControlConstants.EXEC_PATH, |
| configuration.getAttribute(IPTPLaunchConfigurationConstants.ATTR_EXECUTABLE_PATH, JAXBControlConstants.ZEROSTR)); |
| put(JAXBControlConstants.PROG_ARGS, |
| configuration.getAttribute(IPTPLaunchConfigurationConstants.ATTR_ARGUMENTS, JAXBControlConstants.ZEROSTR)); |
| } |
| |
| /** |
| * Relink the hidden variables in the current environment. (Note that the |
| * presence of a symbolic link overrides the default value only if the |
| * linked value is not <code>null</code>). |
| * |
| */ |
| public void relinkHidden(String controller) { |
| Set<String> valid = forControlState(controller, JAXBUIConstants.VALID); |
| for (String name : linkedTo.keySet()) { |
| Object value = null; |
| Object o = linkedTo.get(name); |
| String link = null; |
| if (o instanceof PropertyType) { |
| PropertyType p = (PropertyType) o; |
| link = p.getLinkValueTo(); |
| } else if (o instanceof AttributeType) { |
| AttributeType a = (AttributeType) o; |
| link = a.getLinkValueTo(); |
| } |
| if (link != null) { |
| if (valid.contains(link) || RMVariableMap.isExternal(link) || RMVariableMap.isFixedValid(link)) { |
| value = get(link); |
| } |
| } |
| if (value == null || JAXBUIConstants.ZEROSTR.equals(value)) { |
| value = defaultValues.get(name); |
| } |
| if (value == null) { |
| value = JAXBUIConstants.ZEROSTR; |
| } |
| put(name, value); |
| } |
| } |
| |
| /** |
| * Not allowed on the LCVariableMap |
| */ |
| public Object remove(String name) { |
| return null; |
| } |
| |
| /** |
| * Restores the value map from the temp map. |
| */ |
| public void restoreGlobal() { |
| values.clear(); |
| values.putAll(temp); |
| temp.clear(); |
| } |
| |
| /** |
| * Only here do we remove values. |
| * |
| * @param name |
| * @param defaultv |
| */ |
| public void setDefault(String name, String defaultv) { |
| if (defaultv == null) { |
| values.remove(rmPrefix + name); |
| } else { |
| put(name, defaultv); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ptp.rm.jaxb.core.IVariableMap#setInitialized(boolean) |
| */ |
| public void setInitialized(boolean initialized) { |
| // does nothing |
| } |
| |
| /** |
| * Sets the internal map to the currently valid variables. |
| */ |
| public void shiftToCurrent(String controller) { |
| Set<String> valid = forControlState(controller, JAXBUIConstants.VALID); |
| temp.putAll(values); |
| values.clear(); |
| for (String var : temp.keySet()) { |
| Object value = temp.get(var); |
| if (JAXBUIConstants.ZEROSTR.equals(value)) { |
| continue; |
| } |
| if (var != null) { |
| if (var.startsWith(rmPrefix)) { |
| var = var.substring(rmPrefix.length()); |
| if (valid.contains(var) || RMVariableMap.isFixedValid(var)) { |
| put(var, value); |
| } |
| } else if (RMVariableMap.isExternal(var)) { |
| values.put(var, value); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Update the loaded map with the most recent values from the configuration. |
| * |
| * @throws CoreException |
| */ |
| @SuppressWarnings({ "unchecked" }) |
| public void updateFromConfiguration(ILaunchConfiguration configuration) throws CoreException { |
| Map<String, Object> attr = configuration.getAttributes(); |
| for (String key : attr.keySet()) { |
| if (key.startsWith(rmPrefix) || RMVariableMap.isExternal(key)) { |
| values.put(key, attr.get(key)); |
| } |
| } |
| } |
| |
| /** |
| * Calls the string substitution method on the variable manager. Under |
| * synchronization, sets the variable resolver's map reference to this |
| * instance. |
| * |
| * @param expression |
| * to be resolved (recursively dereferenced from the map). |
| * @return the resolved expression |
| * @throws CoreException |
| */ |
| private String dereference(String expression) throws CoreException { |
| if (expression == null) { |
| return null; |
| } |
| synchronized (monitor) { |
| LCVariableResolver.setActive(this); |
| return VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(expression); |
| } |
| } |
| |
| /** |
| * If the value of the Property or Attribute is <code>null</code> and it has |
| * a defined default, the value is set to the default. |
| * |
| * @param key |
| * from the original RMVariableMap |
| * @param value |
| * the Property or Attribute |
| * @param discovered |
| * property was discovered at run time |
| * @throws Throwable |
| */ |
| private void loadValues(String key, Object value, boolean discovered) throws Throwable { |
| String name = null; |
| String defVal = null; |
| boolean linked = false; |
| boolean visible = true; |
| Object o = null; |
| if (value instanceof PropertyType) { |
| PropertyType p = (PropertyType) value; |
| name = p.getName(); |
| if (name == null) { |
| return; |
| } |
| defVal = p.getDefault(); |
| visible = p.isVisible(); |
| if (!visible) { |
| hidden.add(name); |
| if (p.getLinkValueTo() != null) { |
| linked = true; |
| linkedTo.put(name, p); |
| } else { |
| o = p.getValue(); |
| } |
| } else { |
| o = p.getValue(); |
| } |
| } else if (value instanceof AttributeType) { |
| AttributeType ja = (AttributeType) value; |
| name = ja.getName(); |
| if (name == null) { |
| return; |
| } |
| defVal = ja.getDefault(); |
| visible = ja.isVisible(); |
| if (!visible) { |
| hidden.add(name); |
| if (ja.getLinkValueTo() != null) { |
| linked = true; |
| linkedTo.put(name, ja); |
| } else { |
| o = ja.getValue(); |
| } |
| } else { |
| o = ja.getValue(); |
| } |
| } else { |
| throw new ArrayStoreException(Messages.IllegalVariableValueType + value); |
| } |
| |
| if (!discovered) { |
| defaultValues.put(name, defVal); |
| if (!linked) { |
| if (o == null) { |
| setDefault(name, defVal); |
| } else { |
| put(name, o); |
| } |
| } |
| } else { |
| if (!visible) { |
| hidden.add(name); |
| excluded.put(name, o); |
| } else { |
| put(name, o); |
| } |
| } |
| } |
| } |