| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.core.internal.variables; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.UnsupportedEncodingException; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.FactoryConfigurationError; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.OutputKeys; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Preferences; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.variables.IDynamicVariable; |
| import org.eclipse.core.variables.IStringVariable; |
| import org.eclipse.core.variables.IStringVariableManager; |
| import org.eclipse.core.variables.IValueVariable; |
| import org.eclipse.core.variables.IValueVariableListener; |
| import org.eclipse.core.variables.VariablesPlugin; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Singleton string variable manager. |
| */ |
| public class StringVariableManager implements IStringVariableManager { |
| |
| /** |
| * Dynamic variables - maps variable names to variables. |
| */ |
| private Map fDynamicVariables; |
| |
| /** |
| * Value varialbes - maps variable names to variables. |
| */ |
| private Map fValueVariables; |
| |
| /** |
| * Variable listeners |
| */ |
| private ListenerList fListeners; |
| |
| // notifications |
| private static final int ADDED = 0; |
| private static final int CHANGED = 1; |
| private static final int REMOVED = 2; |
| |
| /** |
| * Singleton variable manager. |
| */ |
| private static StringVariableManager fgManager; |
| |
| // true during initialization code - supress change notification |
| private boolean fInitializing = false; |
| |
| // Variable extension point constants |
| private static final String ATTR_NAME= "name"; //$NON-NLS-1$ |
| private static final String ATTR_DESCRIPTION="description"; //$NON-NLS-1$ |
| // Persisted variable XML constants |
| private static final String VALUE_VARIABLES_TAG= "valueVariables"; //$NON-NLS-1$ |
| private static final String VALUE_VARIABLE_TAG= "valueVariable"; //$NON-NLS-1$ |
| private static final String NAME_TAG= "name"; //$NON-NLS-1$ |
| private static final String VALUE_TAG= "value"; //$NON-NLS-1$ |
| private static final String DESCRIPTION_TAG="description"; //$NON-NLS-1$ |
| private static final String INITIALIZED_TAG="contributed"; //$NON-NLS-1$ |
| // XML values |
| private static final String TRUE_VALUE= "true"; //$NON-NLS-1$ |
| private static final String FALSE_VALUE= "false"; //$NON-NLS-1$ |
| // preference store key for value variables |
| private static final String PREF_VALUE_VARIABLES= VariablesPlugin.getUniqueIdentifier() + ".valueVariables"; //$NON-NLS-1$ |
| |
| /** |
| * Notifies a string variable listener in a safe runnable to handle |
| * exceptions. |
| */ |
| class StringVariableNotifier implements ISafeRunnable { |
| |
| private IValueVariableListener fListener; |
| private int fType; |
| private IValueVariable[] fVariables; |
| |
| /** |
| * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) |
| */ |
| public void handleException(Throwable exception) { |
| IStatus status = new Status(IStatus.ERROR, VariablesPlugin.getUniqueIdentifier(), VariablesPlugin.INTERNAL_ERROR, "An exception occurred during string variable change notification", exception); //$NON-NLS-1$ |
| VariablesPlugin.log(status); |
| } |
| |
| /** |
| * @see org.eclipse.core.runtime.ISafeRunnable#run() |
| */ |
| public void run() throws Exception { |
| switch (fType) { |
| case ADDED: |
| fListener.variablesAdded(fVariables); |
| break; |
| case REMOVED: |
| fListener.variablesRemoved(fVariables); |
| break; |
| case CHANGED: |
| fListener.variablesChanged(fVariables); |
| break; |
| } |
| } |
| |
| /** |
| * Notifies the given listener of the add/change/remove |
| * |
| * @param listener the listener to notify |
| * @param launch the launch that has changed |
| * @param update the type of change |
| */ |
| public void notify(IValueVariable[] variables, int update) { |
| fVariables = variables; |
| fType = update; |
| Object[] copiedListeners= fListeners.getListeners(); |
| for (int i= 0; i < copiedListeners.length; i++) { |
| fListener = (IValueVariableListener)copiedListeners[i]; |
| Platform.run(this); |
| } |
| fVariables = null; |
| fListener = null; |
| // persist variables whenever there is an add/change/remove |
| storeValueVariables(); |
| } |
| } |
| |
| /** |
| * Returns a new notifier. |
| * |
| * @return a new notifier |
| */ |
| private StringVariableNotifier getNotifier() { |
| return new StringVariableNotifier(); |
| } |
| |
| /** |
| * Returns the default string variable manager |
| * |
| * @return string variable manager |
| */ |
| public static StringVariableManager getDefault() { |
| if (fgManager == null) { |
| fgManager = new StringVariableManager(); |
| } |
| return fgManager; |
| } |
| |
| /** |
| * Constructs a new string variable manager. |
| */ |
| private StringVariableManager() { |
| fListeners = new ListenerList(5); |
| } |
| |
| /** |
| * Load contributed variables and persisted variables |
| */ |
| private void initialize() { |
| if (fDynamicVariables == null) { |
| fInitializing = true; |
| fDynamicVariables = new HashMap(5); |
| fValueVariables = new HashMap(5); |
| loadPersistedValueVariables(); |
| loadContributedValueVariables(); |
| loadDynamicVariables(); |
| fInitializing = false; |
| } |
| } |
| |
| /** |
| * Loads contributed dynamic variables |
| */ |
| private void loadDynamicVariables() { |
| IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(VariablesPlugin.PI_CORE_VARIABLES, EXTENSION_POINT_DYNAMIC_VARIABLES); |
| IConfigurationElement elements[]= point.getConfigurationElements(); |
| for (int i = 0; i < elements.length; i++) { |
| IConfigurationElement element = elements[i]; |
| String name= element.getAttribute(ATTR_NAME); |
| if (name == null) { |
| VariablesPlugin.logMessage(MessageFormat.format("Variable extension missing required 'name' attribute: {0}", new String[] {element.getDeclaringExtension().getLabel()}), null); //$NON-NLS-1$ |
| continue; |
| } |
| String description= element.getAttribute(ATTR_DESCRIPTION); |
| DynamicVariable variable= new DynamicVariable(name, description, element); |
| fDynamicVariables.put(variable.getName(), variable); |
| } |
| } |
| |
| /** |
| * Loads any persisted value varialbes from the preference store. |
| */ |
| private void loadPersistedValueVariables() { |
| String variablesString= VariablesPlugin.getDefault().getPluginPreferences().getString(PREF_VALUE_VARIABLES); |
| if (variablesString.length() == 0) { |
| return; |
| } |
| Element root= null; |
| Throwable ex = null; |
| try { |
| ByteArrayInputStream stream = new ByteArrayInputStream(variablesString.getBytes("UTF-8")); //$NON-NLS-1$ |
| DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| root = parser.parse(stream).getDocumentElement(); |
| } catch (UnsupportedEncodingException e) { |
| ex = e; |
| } catch (ParserConfigurationException e) { |
| ex = e; |
| } catch (FactoryConfigurationError e) { |
| ex = e; |
| } catch (SAXException e) { |
| ex = e; |
| } catch (IOException e) { |
| ex = e; |
| } |
| if (ex != null) { |
| VariablesPlugin.logMessage("An exception occurred while loading persisted value variables.", ex); //$NON-NLS-1$ |
| return; |
| } |
| if (!root.getNodeName().equals(VALUE_VARIABLES_TAG)) { |
| VariablesPlugin.logMessage("Invalid format encountered while loading persisted value variables.", null); //$NON-NLS-1$ |
| return; |
| } |
| NodeList list= root.getChildNodes(); |
| for (int i= 0, numItems= list.getLength(); i < numItems; i++) { |
| Node node= list.item(i); |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| Element element= (Element) node; |
| if (!element.getNodeName().equals(VALUE_VARIABLE_TAG)) { |
| VariablesPlugin.logMessage(MessageFormat.format("Invalid XML element encountered while loading value variables: {0}", new String[] {node.getNodeName()}), null); //$NON-NLS-1$ |
| continue; |
| } |
| String name= element.getAttribute(NAME_TAG); |
| if (name.length() > 0) { |
| String value= element.getAttribute(VALUE_TAG); |
| String description= element.getAttribute(DESCRIPTION_TAG); |
| boolean initialized= TRUE_VALUE.equals(element.getAttribute(INITIALIZED_TAG)); |
| ValueVariable variable= new ValueVariable(name, description, null); |
| if (initialized) { |
| variable.setValue(value); |
| } |
| fValueVariables.put(name, variable); |
| } else { |
| VariablesPlugin.logMessage("Invalid variable entry encountered while loading value variables. Variable name is null.", null); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| |
| /** |
| * Loads contributed value variables. This is done after the persisted value |
| * varaibles are restored. Any contributed variables with the same name are |
| * merged with existing persisted values. |
| */ |
| private void loadContributedValueVariables() { |
| IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(VariablesPlugin.PI_CORE_VARIABLES, EXTENSION_POINT_VALUE_VARIABLES); |
| IConfigurationElement elements[]= point.getConfigurationElements(); |
| for (int i = 0; i < elements.length; i++) { |
| IConfigurationElement element = elements[i]; |
| String name= element.getAttribute(ATTR_NAME); |
| if (name == null) { |
| VariablesPlugin.logMessage(MessageFormat.format("Variable extension missing required 'name' attribute: {0}", new String[] {element.getDeclaringExtension().getLabel()}), null); //$NON-NLS-1$ |
| continue; |
| } |
| String description= element.getAttribute(ATTR_DESCRIPTION); |
| ValueVariable variable= new ValueVariable(name, description, element); |
| // if already present, merge with persisted value |
| ValueVariable existing = (ValueVariable)getValueVariable(name); |
| if (existing != null) { |
| if (existing.isInitialized()) { |
| variable.setValue(existing.getValue()); |
| } |
| } |
| fValueVariables.put(variable.getName(), variable); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#getVariables() |
| */ |
| public IStringVariable[] getVariables() { |
| initialize(); |
| List list = new ArrayList(fDynamicVariables.size() + fValueVariables.size()); |
| list.addAll(fDynamicVariables.values()); |
| list.addAll(fValueVariables.values()); |
| return (IStringVariable[]) list.toArray(new IStringVariable[list.size()]); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#getValueVariables() |
| */ |
| public IValueVariable[] getValueVariables() { |
| initialize(); |
| return (IValueVariable[]) fValueVariables.values().toArray(new IValueVariable[fValueVariables.size()]); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#getDynamicVariables() |
| */ |
| public IDynamicVariable[] getDynamicVariables() { |
| initialize(); |
| return (IDynamicVariable[]) fDynamicVariables.values().toArray(new IDynamicVariable[fDynamicVariables.size()]); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#performStringSubstitution(java.lang.String) |
| */ |
| public String performStringSubstitution(String expression) throws CoreException { |
| return performStringSubstitution(expression, true); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#newValueVariable(java.lang.String, java.lang.String) |
| */ |
| public IValueVariable newValueVariable(String name, String description) { |
| IConfigurationElement element = null; |
| ValueVariable existing = (ValueVariable)getValueVariable(name); |
| if (existing != null && existing.isContributed()) { |
| element = existing.getConfigurationElement(); |
| } |
| return new ValueVariable(name, description, element); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#addVariables(org.eclipse.debug.internal.core.stringsubstitution.IValueVariable[]) |
| */ |
| public void addVariables(IValueVariable[] variables) throws CoreException { |
| initialize(); |
| MultiStatus status = new MultiStatus(VariablesPlugin.getUniqueIdentifier(), VariablesPlugin.INTERNAL_ERROR, VariablesMessages.getString("StringVariableManager.26"), null); //$NON-NLS-1$ |
| for (int i = 0; i < variables.length; i++) { |
| IValueVariable variable = variables[i]; |
| if (getValueVariable(variable.getName()) != null) { |
| status.add(new Status(IStatus.ERROR, VariablesPlugin.getUniqueIdentifier(), VariablesPlugin.INTERNAL_ERROR, MessageFormat.format(VariablesMessages.getString("StringVariableManager.27"), new String[]{variable.getName()}), null)); //$NON-NLS-1$ |
| } |
| } |
| if (status.isOK()) { |
| for (int i = 0; i < variables.length; i++) { |
| IValueVariable variable = variables[i]; |
| fValueVariables.put(variable.getName(), variable); |
| } |
| IValueVariable[] copy = new IValueVariable[variables.length]; |
| System.arraycopy(variables, 0, copy, 0, variables.length); |
| getNotifier().notify(copy, ADDED); |
| return; |
| } |
| throw new CoreException(status); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#removeVariables(org.eclipse.debug.internal.core.stringsubstitution.IValueVariable[]) |
| */ |
| public void removeVariables(IValueVariable[] variables) { |
| initialize(); |
| List removed = new ArrayList(variables.length); |
| for (int i = 0; i < variables.length; i++) { |
| IValueVariable variable = variables[i]; |
| if (fValueVariables.remove(variable.getName()) != null) { |
| removed.add(variable); |
| } |
| } |
| if (removed.size() > 0) { |
| getNotifier().notify((IValueVariable[])removed.toArray(new IValueVariable[removed.size()]), REMOVED); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#getDynamicVariable(java.lang.String) |
| */ |
| public IDynamicVariable getDynamicVariable(String name) { |
| initialize(); |
| return (IDynamicVariable) fDynamicVariables.get(name); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#getValueVariable(java.lang.String) |
| */ |
| public IValueVariable getValueVariable(String name) { |
| initialize(); |
| return (IValueVariable) fValueVariables.get(name); |
| } |
| |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#addValueVariableListener(org.eclipse.debug.internal.core.stringsubstitution.IValueVariableListener) |
| */ |
| public void addValueVariableListener(IValueVariableListener listener) { |
| fListeners.add(listener); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#removeValueVariableListener(org.eclipse.debug.internal.core.stringsubstitution.IValueVariableListener) |
| */ |
| public void removeValueVariableListener(IValueVariableListener listener) { |
| fListeners.remove(listener); |
| } |
| |
| /** |
| * Returns a memento representing the value variables currently registered. |
| * |
| * @return memento representing the value variables currently registered |
| * @throws IOException if an I/O exception occurs while creating the XML. |
| */ |
| private String getValueVariablesAsXML() throws IOException, ParserConfigurationException, TransformerException { |
| IValueVariable[] variables = getValueVariables(); |
| |
| Document document= getDocument(); |
| Element rootElement= document.createElement(VALUE_VARIABLES_TAG); |
| document.appendChild(rootElement); |
| for (int i = 0; i < variables.length; i++) { |
| ValueVariable variable = (ValueVariable)variables[i]; |
| Element element= document.createElement(VALUE_VARIABLE_TAG); |
| element.setAttribute(NAME_TAG, variable.getName()); |
| String value= variable.getValue(); |
| if (value != null) { |
| element.setAttribute(VALUE_TAG, value); |
| } |
| String description= variable.getDescription(); |
| if (description != null) { |
| element.setAttribute(DESCRIPTION_TAG, description); |
| } |
| element.setAttribute(INITIALIZED_TAG, variable.isInitialized() ? TRUE_VALUE : FALSE_VALUE); |
| rootElement.appendChild(element); |
| } |
| return serializeDocument(document); |
| } |
| |
| private Document getDocument() throws ParserConfigurationException { |
| DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); |
| DocumentBuilder docBuilder = dfactory.newDocumentBuilder(); |
| Document doc =docBuilder.newDocument(); |
| return doc; |
| } |
| |
| /** |
| * Serializes a XML document into a string - encoded in UTF8 format, |
| * with platform line separators. |
| * |
| * @param doc document to serialize |
| * @return the document as a string |
| * @throws TransformerException if an unrecoverable error occurs during the serialization |
| * @throws IOException if the encoding attempted to be used is not supported |
| */ |
| private String serializeDocument(Document doc) throws TransformerException, UnsupportedEncodingException { |
| ByteArrayOutputStream s= new ByteArrayOutputStream(); |
| |
| TransformerFactory factory= TransformerFactory.newInstance(); |
| Transformer transformer= factory.newTransformer(); |
| transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ |
| transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ |
| |
| DOMSource source= new DOMSource(doc); |
| StreamResult outputTarget= new StreamResult(s); |
| transformer.transform(source, outputTarget); |
| |
| return s.toString("UTF8"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Saves the value variables currently registered in the |
| * preference store. |
| */ |
| private void storeValueVariables() { |
| Preferences prefs= VariablesPlugin.getDefault().getPluginPreferences(); |
| String variableString= ""; //$NON-NLS-1$ |
| if (!fValueVariables.isEmpty()) { |
| try { |
| variableString= getValueVariablesAsXML(); |
| } catch (IOException e) { |
| VariablesPlugin.log(new Status(IStatus.ERROR, VariablesPlugin.getUniqueIdentifier(), IStatus.ERROR, "An exception occurred while storing launch configuration variables.", e)); //$NON-NLS-1$ |
| return; |
| } catch (ParserConfigurationException e) { |
| VariablesPlugin.log(new Status(IStatus.ERROR, VariablesPlugin.getUniqueIdentifier(), IStatus.ERROR, "An exception occurred while storing launch configuration variables.", e)); //$NON-NLS-1$ |
| return; |
| } catch (TransformerException e) { |
| VariablesPlugin.log(new Status(IStatus.ERROR, VariablesPlugin.getUniqueIdentifier(), IStatus.ERROR, "An exception occurred while storing launch configuration variables.", e)); //$NON-NLS-1$ |
| return; |
| } |
| } |
| prefs.setValue(PREF_VALUE_VARIABLES, variableString); |
| VariablesPlugin.getDefault().savePluginPreferences(); |
| } |
| |
| /** |
| * Fire a change notification for the given variable. |
| * |
| * @param variable the variable that has changed |
| */ |
| protected void notifyChanged(ValueVariable variable) { |
| if (!fInitializing) { |
| IValueVariable existing = getValueVariable(variable.getName()); |
| if (variable.equals(existing)) { |
| // do not do change notification for unregistered variables |
| getNotifier().notify(new IValueVariable[]{variable}, CHANGED); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#generateVariableExpression(java.lang.String, java.lang.String) |
| */ |
| public String generateVariableExpression(String varName, String arg) { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("${"); //$NON-NLS-1$ |
| buffer.append(varName); |
| if (arg != null) { |
| buffer.append(":"); //$NON-NLS-1$ |
| buffer.append(arg); |
| } |
| buffer.append("}"); //$NON-NLS-1$ |
| return buffer.toString(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.internal.core.stringsubstitution.IStringVariableManager#performStringSubstitution(java.lang.String, boolean) |
| */ |
| public String performStringSubstitution(String expression, boolean reportUndefinedVariables) throws CoreException { |
| return new StringSubstitutionEngine().performStringSubstitution(expression, reportUndefinedVariables, true, this); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.variables.IStringVariableManager#validateStringVariables(java.lang.String) |
| */ |
| public void validateStringVariables(String expression) throws CoreException { |
| new StringSubstitutionEngine().validateStringVariables(expression, this); |
| } |
| } |