Updating syntax.xml to attempt fix issues reported in ticket #761
[phpeclipse.git] / net.sourceforge.phpeclipse.externaltools / src / net / sourceforge / phpdt / externaltools / model / ToolUtil.java
1 package net.sourceforge.phpdt.externaltools.model;
2
3 /**********************************************************************
4  Copyright (c) 2002 IBM Corp. and others. All rights reserved.
5  This file is made available under the terms of the Common Public License v1.0
6  which accompanies this distribution, and is available at
7  http://www.eclipse.org/legal/cpl-v10.html
8  �
9  Contributors:
10  **********************************************************************/
11
12 import java.util.ArrayList;
13
14 import net.sourceforge.phpdt.externaltools.internal.model.ExternalToolsModelMessages;
15 import net.sourceforge.phpdt.externaltools.internal.registry.ArgumentVariable;
16 import net.sourceforge.phpdt.externaltools.internal.registry.ArgumentVariableRegistry;
17 import net.sourceforge.phpdt.externaltools.internal.registry.PathLocationVariable;
18 import net.sourceforge.phpdt.externaltools.internal.registry.PathLocationVariableRegistry;
19 import net.sourceforge.phpdt.externaltools.variable.ExpandVariableContext;
20 import net.sourceforge.phpeclipse.externaltools.ExternalToolsPlugin;
21
22 import org.eclipse.core.runtime.IPath;
23 import org.eclipse.core.runtime.MultiStatus;
24
25 /**
26  * General utility class dealing with external tools
27  */
28 public final class ToolUtil {
29         /**
30          * Argument parsing constants
31          */
32         private static final char ARG_DELIMITER = ' '; //$NON-NLS-1$
33
34         private static final char ARG_DBL_QUOTE = '"'; //$NON-NLS-1$
35
36         /**
37          * Variable tag indentifiers
38          */
39         private static final char VAR_TAG_START_CHAR1 = '$'; //$NON-NLS-1$
40
41         private static final char VAR_TAG_START_CHAR2 = '{'; //$NON-NLS-1$
42
43         private static final char VAR_TAG_END_CHAR1 = '}'; //$NON-NLS-1$
44
45         private static final String VAR_TAG_START = "${"; //$NON-NLS-1$
46
47         private static final String VAR_TAG_END = "}"; //$NON-NLS-1$
48
49         private static final String VAR_TAG_SEP = ":"; //$NON-NLS-1$
50
51         /**
52          * No instances allowed
53          */
54         private ToolUtil() {
55                 super();
56         }
57
58         /**
59          * Builds a variable tag that will be auto-expanded before the tool is run.
60          * 
61          * @param varName
62          *            the name of a known variable (one of the VAR_* constants for
63          *            instance)
64          * @param varArgument
65          *            an optional argument for the variable, <code>null</code> if
66          *            none
67          */
68         public static String buildVariableTag(String varName, String varArgument) {
69                 StringBuffer buf = new StringBuffer();
70                 buildVariableTag(varName, varArgument, buf);
71                 return buf.toString();
72         }
73
74         /**
75          * Builds a variable tag that will be auto-expanded before the tool is run.
76          * 
77          * @param varName
78          *            the name of a known variable (one of the VAR_* constants for
79          *            instance)
80          * @param varArgument
81          *            an optional argument for the variable, <code>null</code> if
82          *            none
83          * @param buffer
84          *            the buffer to write the constructed variable tag
85          */
86         public static void buildVariableTag(String varName, String varArgument,
87                         StringBuffer buffer) {
88                 buffer.append(VAR_TAG_START);
89                 buffer.append(varName);
90                 if (varArgument != null && varArgument.length() > 0) {
91                         buffer.append(VAR_TAG_SEP);
92                         buffer.append(varArgument);
93                 }
94                 buffer.append(VAR_TAG_END);
95         }
96
97         /**
98          * Expands all the variables found in an individual argument text.
99          * 
100          * @param argument
101          *            one of the argument text in the list of arguments
102          * @param context
103          *            the context to use for expanding variables
104          * @param status
105          *            multi status to report any problems expanding variables
106          * @return the argument text with all variables expanded, or
107          *         <code>null</code> if not possible
108          */
109         public static String expandArgument(String argument,
110                         ExpandVariableContext context, MultiStatus status) {
111                 StringBuffer buffer = new StringBuffer();
112
113                 int start = 0;
114                 while (true) {
115                         VariableDefinition varDef = extractVariableTag(argument, start);
116
117                         // No more variables found...
118                         if (varDef.start == -1) {
119                                 if (start == 0)
120                                         buffer.append(argument);
121                                 else
122                                         buffer.append(argument.substring(start));
123                                 break;
124                         }
125
126                         // Invalid variable format
127                         if (varDef.end == -1 || varDef.name == null
128                                         || varDef.name.length() == 0) {
129                                 String msg = ExternalToolsModelMessages
130                                                 .getString("ToolUtil.argumentVarFormatWrong"); //$NON-NLS-1$
131                                 status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
132                                 return null;
133                         }
134
135                         // Copy text between start and variable.
136                         if (varDef.start > start)
137                                 buffer.append(argument.substring(start, varDef.start));
138                         start = varDef.end;
139
140                         // Lookup the variable if it exist
141                         ArgumentVariableRegistry registry;
142                         registry = ExternalToolsPlugin.getDefault()
143                                         .getArgumentVariableRegistry();
144                         ArgumentVariable variable = registry
145                                         .getArgumentVariable(varDef.name);
146                         if (variable == null) {
147                                 String msg = ExternalToolsModelMessages
148                                                 .format(
149                                                                 "ToolUtil.argumentVarMissing", new Object[] { varDef.name }); //$NON-NLS-1$
150                                 status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
151                                 return null;
152                         }
153
154                         // Expand the variable as text if possible
155                         String text = variable.getExpander().getText(varDef.name,
156                                         varDef.argument, context);
157                         if (text == null) {
158                                 String msg = ExternalToolsModelMessages
159                                                 .format(
160                                                                 "ToolUtil.argumentVarExpandFailed", new Object[] { varDef.name }); //$NON-NLS-1$
161                                 status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
162                                 return null;
163                         }
164                         buffer.append(text);
165                 }
166
167                 return buffer.toString();
168         }
169
170         /**
171          * Returns a list of individual arguments where all variables have been
172          * expanded.
173          * 
174          * @param arguments
175          *            the arguments with leading and trailing spaces already
176          *            removed.
177          * @param context
178          *            the context used to expand the variable(s)
179          * @param status
180          *            multi status to report any problems expanding variables
181          * @return the list of individual arguments where some elements in the list
182          *         maybe <code>null</code> if problems expanding variable(s).
183          */
184         public static String[] expandArguments(String arguments,
185                         ExpandVariableContext context, MultiStatus status) {
186                 if (arguments == null || arguments.length() == 0)
187                         return new String[0];
188
189                 String[] argList = parseArgumentsIntoList(arguments);
190                 for (int i = 0; i < argList.length; i++)
191                         argList[i] = expandArgument(argList[i], context, status);
192
193                 return argList;
194         }
195
196         /**
197          * Returns the expanded directory location if represented by a directory
198          * variable. Otherwise, the directory location given is return unless an
199          * unknown variable was detected.
200          * 
201          * @param dirLocation
202          *            a directory location either as a path or a variable with
203          *            leading and trailing spaces already removed.
204          * @param context
205          *            the context used to expand the variable
206          * @param status
207          *            multi status to report any problems expanding variables
208          * @return the directory location as a string or <code>null</code> if not
209          *         possible
210          */
211         public static String expandDirectoryLocation(String dirLocation,
212                         ExpandVariableContext context, MultiStatus status) {
213                 if (dirLocation == null || dirLocation.length() == 0)
214                         return ""; //$NON-NLS-1$
215
216                 VariableDefinition varDef = extractVariableTag(dirLocation, 0);
217                 // Return if no variable found
218                 if (varDef.start < 0)
219                         return dirLocation;
220
221                 // Disallow text before/after variable
222                 if (varDef.start != 0
223                                 || (varDef.end < dirLocation.length() && varDef.end != -1)) {
224                         String msg = ExternalToolsModelMessages
225                                         .getString("ToolUtil.dirLocVarBetweenText"); //$NON-NLS-1$
226                         status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
227                         return null;
228                 }
229
230                 // Invalid variable format
231                 if (varDef.name == null || varDef.name.length() == 0
232                                 || varDef.end == -1) {
233                         String msg = ExternalToolsModelMessages
234                                         .getString("ToolUtil.dirLocVarFormatWrong"); //$NON-NLS-1$
235                         status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
236                         return null;
237                 }
238
239                 // Lookup the variable if it exist
240                 PathLocationVariableRegistry registry;
241                 registry = ExternalToolsPlugin.getDefault()
242                                 .getDirectoryLocationVariableRegistry();
243                 PathLocationVariable variable = registry
244                                 .getPathLocationVariable(varDef.name);
245                 if (variable == null) {
246                         String msg = ExternalToolsModelMessages.format(
247                                         "ToolUtil.dirLocVarMissing", new Object[] { varDef.name }); //$NON-NLS-1$
248                         status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
249                         return null;
250                 }
251
252                 // Expand the variable into a IPath if possible
253                 IPath path = variable.getExpander().getPath(varDef.name,
254                                 varDef.argument, context);
255                 if (path == null) {
256                         String msg = ExternalToolsModelMessages
257                                         .format(
258                                                         "ToolUtil.dirLocVarExpandFailed", new Object[] { varDef.name }); //$NON-NLS-1$
259                         status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
260                         return null;
261                 }
262
263                 return path.toOSString();
264         }
265
266         /**
267          * Returns the expanded file location if represented by a file variable.
268          * Otherwise, the file location given is return unless an unknown variable
269          * was detected.
270          * 
271          * @param fileLocation
272          *            a file location either as a path or a variable with leading
273          *            and trailing spaces already removed.
274          * @param context
275          *            the context used to expand the variable
276          * @param status
277          *            multi status to report any problems expanding variables
278          * @return the file location as a string or <code>null</code> if not
279          *         possible
280          */
281         public static String expandFileLocation(String fileLocation,
282                         ExpandVariableContext context, MultiStatus status) {
283                 if (fileLocation == null || fileLocation.length() == 0)
284                         return ""; //$NON-NLS-1$
285
286                 VariableDefinition varDef = extractVariableTag(fileLocation, 0);
287                 // Return if no variable found
288                 if (varDef.start < 0)
289                         return fileLocation;
290
291                 // Disallow text before/after variable
292                 if (varDef.start != 0
293                                 || (varDef.end < fileLocation.length() && varDef.end != -1)) {
294                         String msg = ExternalToolsModelMessages
295                                         .getString("ToolUtil.fileLocVarBetweenText"); //$NON-NLS-1$
296                         status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
297                         return null;
298                 }
299
300                 // Invalid variable format
301                 if (varDef.name == null || varDef.name.length() == 0
302                                 || varDef.end == -1) {
303                         String msg = ExternalToolsModelMessages
304                                         .getString("ToolUtil.fileLocVarFormatWrong"); //$NON-NLS-1$
305                         status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
306                         return null;
307                 }
308
309                 // Lookup the variable if it exist
310                 PathLocationVariableRegistry registry;
311                 registry = ExternalToolsPlugin.getDefault()
312                                 .getFileLocationVariableRegistry();
313                 PathLocationVariable variable = registry
314                                 .getPathLocationVariable(varDef.name);
315                 if (variable == null) {
316                         String msg = ExternalToolsModelMessages.format(
317                                         "ToolUtil.fileLocVarMissing", new Object[] { varDef.name }); //$NON-NLS-1$
318                         status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
319                         return null;
320                 }
321
322                 // Expand the variable into a IPath if possible
323                 IPath path = variable.getExpander().getPath(varDef.name,
324                                 varDef.argument, context);
325                 if (path == null) {
326                         String msg = ExternalToolsModelMessages
327                                         .format(
328                                                         "The variable {0} with argument {1} could not be expanded to a valid path.",
329                                                         new Object[] { varDef.name, varDef.argument });
330                         status.merge(ExternalToolsPlugin.newErrorStatus(msg, null));
331                         return null;
332                 }
333
334                 return path.toString();
335         }
336
337         /**
338          * Extracts from the source text the variable tag's name and argument.
339          * 
340          * @param text
341          *            the source text to parse for a variable tag
342          * @param start
343          *            the index in the string to start the search
344          * @return the variable definition
345          */
346         public static VariableDefinition extractVariableTag(String text, int start) {
347                 VariableDefinition varDef = new VariableDefinition();
348
349                 varDef.start = text.indexOf(VAR_TAG_START, start);
350                 if (varDef.start < 0)
351                         return varDef;
352                 start = varDef.start + VAR_TAG_START.length();
353
354                 int end = text.indexOf(VAR_TAG_END, start);
355                 if (end < 0)
356                         return varDef;
357                 varDef.end = end + VAR_TAG_END.length();
358                 if (end == start)
359                         return varDef;
360
361                 int mid = text.indexOf(VAR_TAG_SEP, start);
362                 if (mid < 0 || mid > end) {
363                         varDef.name = text.substring(start, end);
364                 } else {
365                         if (mid > start)
366                                 varDef.name = text.substring(start, mid);
367                         mid = mid + VAR_TAG_SEP.length();
368                         if (mid < end)
369                                 varDef.argument = text.substring(mid, end);
370                 }
371
372                 return varDef;
373         }
374
375         /**
376          * Parses the argument text into an array of individual arguments using the
377          * space character as the delimiter. An individual argument containing
378          * spaces must have a double quote (") at the start and end. Two double
379          * quotes together is taken to mean an embedded double quote in the argument
380          * text. Variables are treated as a single unit and therefore spaces and
381          * double quotes inside a variable are copied as is and not parsed.
382          * 
383          * @param arguments
384          *            the arguments as one string
385          * @return the array of arguments
386          */
387         public static String[] parseArgumentsIntoList(String arguments) {
388                 if (arguments == null || arguments.length() == 0)
389                         return new String[0];
390
391                 ArrayList list = new ArrayList(10);
392                 boolean inQuotes = false;
393                 boolean inVar = false;
394                 int start = 0;
395                 int end = arguments.length();
396                 StringBuffer buffer = new StringBuffer(end);
397
398                 while (start < end) {
399                         char ch = arguments.charAt(start);
400                         start++;
401
402                         switch (ch) {
403                         case ARG_DELIMITER:
404                                 if (inQuotes || inVar) {
405                                         buffer.append(ch);
406                                 } else {
407                                         if (buffer.length() > 0) {
408                                                 list.add(buffer.toString());
409                                                 buffer.setLength(0);
410                                         }
411                                 }
412                                 break;
413
414                         case ARG_DBL_QUOTE:
415                                 if (inVar) {
416                                         buffer.append(ch);
417                                 } else {
418                                         if (start < end) {
419                                                 if (arguments.charAt(start) == ARG_DBL_QUOTE) {
420                                                         // Two quotes together represents one quote
421                                                         buffer.append(ch);
422                                                         start++;
423                                                 } else {
424                                                         inQuotes = !inQuotes;
425                                                 }
426                                         } else {
427                                                 // A lone quote at the end, just drop it.
428                                                 inQuotes = false;
429                                         }
430                                 }
431                                 break;
432
433                         case VAR_TAG_START_CHAR1:
434                                 buffer.append(ch);
435                                 if (!inVar && start < end) {
436                                         if (arguments.charAt(start) == VAR_TAG_START_CHAR2) {
437                                                 buffer.append(VAR_TAG_START_CHAR2);
438                                                 inVar = true;
439                                                 start++;
440                                         }
441                                 }
442                                 break;
443
444                         case VAR_TAG_END_CHAR1:
445                                 buffer.append(ch);
446                                 inVar = false;
447                                 break;
448
449                         default:
450                                 buffer.append(ch);
451                                 break;
452                         }
453
454                 }
455
456                 if (buffer.length() > 0)
457                         list.add(buffer.toString());
458
459                 String[] results = new String[list.size()];
460                 list.toArray(results);
461                 return results;
462         }
463
464         /**
465          * Structure to represent a variable definition within a source string.
466          */
467         public static final class VariableDefinition {
468                 /**
469                  * Index in the source text where the variable started or
470                  * <code>-1</code> if no valid variable start tag identifier found.
471                  */
472                 public int start = -1;
473
474                 /**
475                  * Index in the source text of the character following the end of the
476                  * variable or <code>-1</code> if no valid variable end tag found.
477                  */
478                 public int end = -1;
479
480                 /**
481                  * The variable's name found in the source text, or <code>null</code>
482                  * if no valid variable found.
483                  */
484                 public String name = null;
485
486                 /**
487                  * The variable's argument found in the source text, or
488                  * <code>null</code> if no valid variable found or if the variable did
489                  * not specify an argument
490                  */
491                 public String argument = null;
492
493                 /**
494                  * Create an initialized variable definition.
495                  */
496                 private VariableDefinition() {
497                         super();
498                 }
499
500                 /**
501                  * Create an initialized variable definition.
502                  */
503                 private VariableDefinition(int start, int end) {
504                         super();
505                         this.start = start;
506                         this.end = end;
507                 }
508         }
509 }