2 * Copyright 2000-2011 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.execution.configurations;
18 import com.intellij.openapi.application.Application;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.application.PathMacros;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.util.text.StringUtil;
23 import com.intellij.util.ArrayUtil;
24 import com.intellij.util.EnvironmentUtil;
25 import com.intellij.util.StringBuilderSpinAllocator;
26 import com.intellij.util.containers.CollectionFactory;
27 import com.intellij.util.containers.ContainerUtil;
28 import gnu.trove.THashMap;
29 import org.jetbrains.annotations.NonNls;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
37 public class ParametersList implements Cloneable {
38 private static final Logger LOG = Logger.getInstance("#com.intellij.execution.configurations.ParametersList");
40 private static final Pattern PROPERTY_PATTERN = Pattern.compile("-D(\\S+?)=(.+)");
42 private List<String> myParameters = new ArrayList<String>();
43 private Map<String, String> myMacroMap = null;
44 private List<ParamsGroup> myGroups = new ArrayList<ParamsGroup>();
46 public boolean hasParameter(@NonNls final String param) {
47 return myParameters.contains(param);
50 public boolean hasProperty(@NonNls final String name) {
51 for (@NonNls String parameter : myParameters) {
52 if (StringUtil.startsWithConcatenationOf(parameter, "-D" + name, "=")) return true;
58 public String getPropertyValue(@NotNull @NonNls final String name) {
59 final String prefix = "-D" + name + "=";
60 for (String parameter : myParameters) {
61 if (parameter.startsWith(prefix)) {
62 return parameter.substring(prefix.length());
69 public Map<String, String> getProperties() {
70 Map<String, String> result = new THashMap<String, String>();
71 for (String parameter : myParameters) {
72 Matcher matcher = PROPERTY_PATTERN.matcher(parameter);
73 if (matcher.matches()) {
74 result.put(matcher.group(1), matcher.group(2));
81 public String getParametersString() {
82 return join(getList());
86 public String[] getArray() {
87 return ArrayUtil.toStringArray(getList());
91 public List<String> getList() {
92 if (myGroups.isEmpty()) {
93 return Collections.unmodifiableList(myParameters);
96 final List<String> params = new ArrayList<String>();
97 params.addAll(myParameters);
98 for (ParamsGroup group : myGroups) {
99 params.addAll(group.getParameters());
101 return Collections.unmodifiableList(params);
104 public void clearAll() {
105 myParameters.clear();
109 public void prepend(@NonNls final String parameter) {
113 public void prependAll(@NonNls final String... parameter) {
114 for (int i = parameter.length - 1; i >= 0; i--) {
115 addAt(0, parameter[i]);
119 public void addParametersString(final String parameters) {
120 if (parameters != null) {
121 final String[] split = parse(parameters);
122 for (String param : split) {
128 public void add(@NonNls final String parameter) {
129 myParameters.add(expandMacros(parameter));
132 public ParamsGroup addParamsGroup(@NotNull final String groupId) {
133 return addParamsGroup(new ParamsGroup(groupId));
136 public ParamsGroup addParamsGroup(@NotNull final ParamsGroup group) {
141 public ParamsGroup addParamsGroupAt(final int index, @NotNull final ParamsGroup group) {
142 myGroups.add(index, group);
146 public ParamsGroup addParamsGroupAt(final int index, @NotNull final String groupId) {
147 final ParamsGroup group = new ParamsGroup(groupId);
148 myGroups.add(index, group);
152 public int getParamsGroupsCount() {
153 return myGroups.size();
156 public List<String> getParameters() {
157 return Collections.unmodifiableList(myParameters);
160 public List<ParamsGroup> getParamsGroups() {
161 return Collections.unmodifiableList(myGroups);
164 public ParamsGroup getParamsGroupAt(final int index) {
165 return myGroups.get(index);
169 public ParamsGroup getParamsGroup(@NotNull final String name) {
170 for (ParamsGroup group : myGroups) {
171 if (name.equals(group.getId())) return group;
176 public ParamsGroup removeParamsGroup(final int index) {
177 return myGroups.remove(index);
180 public void addAt(final int index, @NotNull final String parameter) {
181 myParameters.add(index, expandMacros(parameter));
184 public void defineProperty(@NonNls final String propertyName, @NonNls final String propertyValue) {
185 addProperty(propertyName, propertyValue);
188 public void addProperty(@NonNls final String propertyName, @NonNls final String propertyValue) {
189 //noinspection HardCodedStringLiteral
190 myParameters.add("-D" + propertyName + "=" + propertyValue);
193 public void replaceOrAppend(final @NonNls String parameterPrefix, final @NonNls String replacement) {
194 replaceOrAdd(parameterPrefix, replacement, myParameters.size());
197 private void replaceOrAdd(final @NonNls String parameterPrefix, final @NonNls String replacement, final int position) {
198 for (ListIterator<String> iterator = myParameters.listIterator(); iterator.hasNext(); ) {
199 final String param = iterator.next();
200 if (param.startsWith(parameterPrefix)) {
201 if ("".equals(replacement)) {
205 iterator.set(replacement);
210 if (!"".equals(replacement)) {
211 myParameters.add(position, replacement);
215 public void replaceOrPrepend(final @NonNls String parameter, final @NonNls String replacement) {
216 replaceOrAdd(parameter, replacement, 0);
219 public void set(int ind, final @NonNls String value) {
220 myParameters.set(ind, value);
223 public void add(@NonNls final String name, @NonNls final String value) {
228 public void addAll(final String... parameters) {
229 ContainerUtil.addAll(myParameters, parameters);
232 public void addAll(final List<String> parameters) {
233 myParameters.addAll(parameters);
237 public ParametersList clone() {
239 final ParametersList clone = (ParametersList)super.clone();
240 clone.myParameters = new ArrayList<String>(myParameters);
241 clone.myGroups = new ArrayList<ParamsGroup>(myGroups.size() + 1);
242 for (ParamsGroup group : myGroups) {
243 clone.myGroups.add(group.clone());
247 catch (CloneNotSupportedException e) {
254 * <p>Joins list of parameters into single string, which may be then parsed back into list by {@link #parse(String)}.</p>
257 * <strong>Conversion rules:</strong>
259 * <li>double quotes are escaped by backslash (<code>\</code>);</li>
260 * <li>empty parameters parameters and parameters with spaces inside are surrounded with double quotes (<code>"</code>);</li>
261 * <li>parameters are separated by single whitespace.</li>
265 * <p><strong>Examples:</strong></p>
267 * <code>['a', 'b'] => 'a b'</code><br/>
268 * <code>['a="1 2"', 'b'] => '"a \"1 2\"" b'</code>
271 * @param parameters a list of parameters to join.
272 * @return a string with parameters.
275 public static String join(@NotNull final List<String> parameters) {
276 return ParametersTokenizer.encode(parameters);
280 public static String join(final String... parameters) {
281 return ParametersTokenizer.encode(Arrays.asList(parameters));
285 * <p>Converts single parameter string (as created by {@link #join(java.util.List)}) into list of parameters.</p>
288 * <strong>Conversion rules:</strong>
290 * <li>starting/whitespaces are trimmed;</li>
291 * <li>parameters are split by whitespaces, whitespaces itself are dropped</li>
292 * <li>parameters inside double quotes (<code>"a b"</code>) are kept as single one;</li>
293 * <li>double quotes are dropped, escaped double quotes (<code>\"</code>) are un-escaped.</li>
297 * <p><strong>Examples:</strong></p>
299 * <code>' a b ' => ['a', 'b']</code><br/>
300 * <code>'a="1 2" b' => ['a=1 2', 'b']</code><br/>
301 * <code>'a " " b' => ['a', ' ', 'b']</code><br/>
302 * <code>'"a \"1 2\"" b' => ['a="1 2"', 'b']</code>
305 * @param string parameter string to split.
306 * @return array of parameters.
309 public static String[] parse(@NotNull final String string) {
310 final List<String> params = ParametersTokenizer.decode(string);
311 return ArrayUtil.toStringArray(params);
314 public String expandMacros(String text) {
315 final Map<String, String> macroMap = getMacroMap();
316 final Set<String> set = macroMap.keySet();
317 for (final String from : set) {
318 final String to = macroMap.get(from);
319 text = StringUtil.replace(text, from, to, true);
324 private Map<String, String> getMacroMap() {
325 if (myMacroMap == null) {
326 // the insertion order is important for later iterations, so LinkedHashMap is used
327 myMacroMap = new LinkedHashMap<String, String>();
329 // ApplicationManager.getApplication() will return null if executed in ParameterListTest
330 final Application application = ApplicationManager.getApplication();
331 if (application != null) {
332 final PathMacros pathMacros = PathMacros.getInstance();
333 final Set<String> names = pathMacros.getAllMacroNames();
334 for (String name : names) {
335 myMacroMap.put("${" + name + "}", pathMacros.getValue(name));
337 final Map<String, String> env = EnvironmentUtil.getEnviromentProperties();
338 for (String name : env.keySet()) {
339 final String key = "${" + name + "}";
340 if (!myMacroMap.containsKey(key)) {
341 myMacroMap.put(key, env.get(name));
350 public String toString() {
351 return myParameters.toString();
354 private static class ParametersTokenizer {
355 private ParametersTokenizer() {
359 public static String encode(@NotNull final List<String> parameters) {
360 final StringBuilder buffer = new StringBuilder();
361 for (final String parameter : parameters) {
362 if (buffer.length() > 0) {
365 buffer.append(encode(parameter));
367 return buffer.toString();
371 public static String encode(@NotNull String parameter) {
372 final StringBuilder builder = StringBuilderSpinAllocator.alloc();
374 builder.append(parameter);
375 StringUtil.escapeQuotes(builder);
376 if (builder.length() == 0 || StringUtil.indexOf(builder, ' ') >= 0 || StringUtil.indexOf(builder, '|') >= 0) {
377 StringUtil.quote(builder);
379 return builder.toString();
382 StringBuilderSpinAllocator.dispose(builder);
387 public static List<String> decode(@NotNull String parameterString) {
388 parameterString = parameterString.trim();
390 final ArrayList<String> params = CollectionFactory.arrayList();
391 final StringBuilder token = new StringBuilder(128);
392 boolean inQuotes = false;
393 boolean escapedQuote = false;
394 boolean nonEmpty = false;
396 for (int i = 0; i < parameterString.length(); i++) {
397 final char ch = parameterString.charAt(i);
401 inQuotes = !inQuotes;
405 escapedQuote = false;
407 else if (Character.isWhitespace(ch)) {
409 if (token.length() > 0 || nonEmpty) {
410 params.add(token.toString());
417 else if (ch == '\\') {
418 if (i < parameterString.length() - 1 && parameterString.charAt(i + 1) == '"') {
427 if (token.length() > 0 || nonEmpty) {
428 params.add(token.toString());