blob: c493c07638311486f3169d4b51380d566239ae1f [file] [log] [blame]
/*******************************************************************************
* 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);
}
}
}
}