1 /*******************************************************************************
2  * Copyright (c) 2005, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Landmark Graphics Corporation - bug 397183
14  *******************************************************************************/
15 package org.eclipse.equinox.internal.p2.engine;
16 
17 import java.util.*;
18 import java.util.Map.Entry;
19 import org.eclipse.core.runtime.IStatus;
20 import org.eclipse.equinox.p2.engine.spi.*;
21 
22 public class ParameterizedProvisioningAction extends ProvisioningAction {
23 	private ProvisioningAction action;
24 	private Map<String, String> actionParameters;
25 	//ActualParameter is used to keep values to which variables have been resolved.
26 	//This is especially useful when undoing in the presence of variables that change (e.g. lastResult)
27 	private Map<String, Object> actualParameters;
28 	private String actionText;
29 
ParameterizedProvisioningAction(ProvisioningAction action, Map<String, String> actionParameters, String actionText)30 	public ParameterizedProvisioningAction(ProvisioningAction action, Map<String, String> actionParameters, String actionText) {
31 		if (action == null || actionParameters == null)
32 			throw new IllegalArgumentException(Messages.ParameterizedProvisioningAction_action_or_parameters_null);
33 		this.action = action;
34 		this.actionParameters = actionParameters;
35 		this.actualParameters = new HashMap<>(actionParameters.size());
36 		this.actionText = actionText;
37 	}
38 
39 	@Override
execute(Map<String, Object> parameters)40 	public IStatus execute(Map<String, Object> parameters) {
41 		parameters = processActionParameters(parameters);
42 		return action.execute(parameters);
43 	}
44 
45 	@Override
undo(Map<String, Object> parameters)46 	public IStatus undo(Map<String, Object> parameters) {
47 		parameters = processActionParameters(parameters);
48 		return action.undo(parameters);
49 	}
50 
processActionParameters(Map<String, Object> parameters)51 	private Map<String, Object> processActionParameters(Map<String, Object> parameters) {
52 		Map<String, Object> result = new HashMap<>(parameters);
53 		for (Entry<String, String> entry : actionParameters.entrySet()) {
54 			String name = entry.getKey();
55 			Object value = processVariables(entry.getValue(), parameters, false);
56 			result.put(name, value);
57 		}
58 		return Collections.unmodifiableMap(result);
59 	}
60 
61 	//allowInfixReplacement triggers the replacement of the variables found in the middle of a string (e.g. abc${var}def)
processVariables(String parameterValue, Map<String, Object> parameters, boolean allowInfixReplacement)62 	private Object processVariables(String parameterValue, Map<String, Object> parameters, boolean allowInfixReplacement) {
63 		int variableBeginIndex = parameterValue.indexOf("${"); //$NON-NLS-1$
64 		if (variableBeginIndex == -1)
65 			return parameterValue;
66 
67 		int variableEndIndex = parameterValue.indexOf('}', variableBeginIndex + 2);
68 		if (variableEndIndex == -1)
69 			return parameterValue;
70 
71 		String preVariable = parameterValue.substring(0, variableBeginIndex);
72 		String variableName = parameterValue.substring(variableBeginIndex + 2, variableEndIndex);
73 
74 		//replace the internal name by the user visible name
75 		if (Phase.LAST_RESULT_PUBLIC_NAME.equals(variableName)) {
76 			variableName = Phase.LAST_RESULT_INTERNAL_NAME;
77 		}
78 		Object valueUsed = actualParameters.get(variableName);
79 		Object value = valueUsed == null ? parameters.get(variableName) : valueUsed;
80 		actualParameters.put(variableName, value);
81 
82 		if (value instanceof Value) {
83 			if (allowInfixReplacement == false && variableBeginIndex == 0 && variableEndIndex == parameterValue.length() - 1) {
84 				return ((Value<?>) value).getValue();
85 			}
86 
87 			Value<?> result = (Value<?>) value;
88 			if (result.getClazz() == String.class) {
89 				value = result.getValue();
90 			} else
91 				throw new RuntimeException("The type of the variable is expected to be a String"); //$NON-NLS-1$
92 		}
93 
94 		// try to replace this parameter with a character
95 		if (value == null && variableName.charAt(0) == '#') {
96 			try {
97 				int code = Integer.parseInt(variableName.substring(1));
98 				if (code >= 0 && code < 65536)
99 					value = Character.toString((char) code);
100 			} catch (Throwable t) {
101 				// ignore and leave value as null
102 			}
103 		}
104 
105 		String variableValue = value == null ? "" : value.toString(); //$NON-NLS-1$			//TODO This is where we replace the values
106 		String postVariable = (String) processVariables(parameterValue.substring(variableEndIndex + 1), parameters, true);
107 		return preVariable + variableValue + postVariable;
108 	}
109 
getAction()110 	public ProvisioningAction getAction() {
111 		return action;
112 	}
113 
getParameters()114 	public Map<String, String> getParameters() {
115 		return actionParameters;
116 	}
117 
getActionText()118 	public String getActionText() {
119 		return actionText;
120 	}
121 
122 	@Override
getTouchpoint()123 	public Touchpoint getTouchpoint() {
124 		return action.getTouchpoint();
125 	}
126 
127 	@Override
setTouchpoint(Touchpoint touchpoint)128 	public void setTouchpoint(Touchpoint touchpoint) {
129 		throw new UnsupportedOperationException();
130 	}
131 
132 	@Override
getResult()133 	public Value<?> getResult() {
134 		return action.getResult();
135 	}
136 
137 }
138