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 */ ToolUtil()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 */ buildVariableTag(String varName, String varArgument)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 */ buildVariableTag(String varName, String varArgument, StringBuffer buffer)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 */ expandArgument(String argument, ExpandVariableContext context, MultiStatus status)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 */ expandArguments(String arguments, ExpandVariableContext context, MultiStatus status)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 */ expandDirectoryLocation(String dirLocation, ExpandVariableContext context, MultiStatus status)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 */ expandFileLocation(String fileLocation, ExpandVariableContext context, MultiStatus status)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 */ extractVariableTag(String text, int start)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 */ parseArgumentsIntoList(String arguments)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 */ VariableDefinition()496 private VariableDefinition() { 497 super(); 498 } 499 500 /** 501 * Create an initialized variable definition. 502 */ VariableDefinition(int start, int end)503 private VariableDefinition(int start, int end) { 504 super(); 505 this.start = start; 506 this.end = end; 507 } 508 } 509 } 510