/*******************************************************************************
* Copyright (c) 2014 Ericsson and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc Khouzam (Ericsson) - Support Dynamic Printf (Bug 400628)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.breakpoints;
import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.osgi.util.NLS;
/**
* Utility methods to help deal with Dynamic Printf logic.
*
* @since 4.4
*/
public class GDBDynamicPrintfUtils {
@Immutable
public static class GDBDynamicPrintfString {
private boolean fValid;
private String fStringSection;
private String[] fArgs;
private String fErrorMessage;
public GDBDynamicPrintfString(String str) {
if (str == null) {
fErrorMessage = Messages.DynamicPrintf_Invalid_string;
return;
}
str = str.trim();
// No point in having an empty string, just disable the dprintf instead
if (str.isEmpty()) {
fErrorMessage = Messages.DynamicPrintf_Invalid_string;
return;
}
// First character must be a double-quote
if (str.charAt(0) != '"') {
fErrorMessage = Messages.DynamicPrintf_Printf_must_start_with_quote;
return;
}
// Now go through the string and look for two things:
// 1- count the number of % (but ignore any %%)
// 2- find the closing double-quote (but ignore any \")
char[] chars = str.toCharArray();
int closingQuoteIndex = 0;
int numArgExpected = 0;
for (int i=1; i<chars.length; i++) {
switch (chars[i]) {
case '\\':
// Next char can be ignored
i++;
break;
case '"':
closingQuoteIndex = i;
break;
case '%':
if (chars.length > i+1 && chars[i+1] != '%') {
// Not a %% so we have found an expected argument.
numArgExpected++;
}
// We either found a second %, which we must skip to
// avoid parsing it again, or we didn't and we know
// our next character must be part of the format.
// In both cases we can and should skip the next character.
// Note that we are not going to make sure the format is
// correct as that becomes really complex.
i++;
break;
}
// If we found the closing double-quote, there is no need to keep counting
if (closingQuoteIndex > 0) break;
}
if (closingQuoteIndex < 1) {
// Didn't find a closing double-quote!
fErrorMessage = Messages.DynamicPrintf_Printf_missing_closing_quote;
return;
}
// We extract the string part of the printf string leaving the arguments
fStringSection = str.substring(0, closingQuoteIndex+1);
int numArgPresent = 0;
if (closingQuoteIndex + 1 >= str.length()) {
// No more characters after the string part
fArgs = new String[0];
numArgPresent = 0;
} else {
String argString = str.substring(closingQuoteIndex+1).trim();
if (argString.charAt(0) != ',') {
fErrorMessage = Messages.DynamicPrintf_Missing_comma;
return;
}
// Remove the first , to avoid an empty element after the split.
// Then split the string but keep any empty results
String[] args = argString.substring(1).split(",", -1); //$NON-NLS-1$
for (String argument : args) {
if (argument.trim().isEmpty()) {
fErrorMessage = Messages.DynamicPrintf_Empty_arg;
return;
}
}
fArgs = args;
numArgPresent = fArgs.length;
}
if (numArgPresent != numArgExpected) {
if (numArgPresent > numArgExpected) {
fErrorMessage = NLS.bind(Messages.DynamicPrintf_Extra_arg, numArgPresent - numArgExpected);
} else {
fErrorMessage = NLS.bind(Messages.DynamicPrintf_Missing_arg, numArgExpected - numArgPresent);
}
return;
}
// Everything is ok!
fValid = true;
}
public boolean isValid() {
return fValid;
}
public String getString() {
if (!isValid()) return ""; //$NON-NLS-1$
return fStringSection;
}
public String[] getArguments() {
if (!isValid()) return new String[0];
return fArgs;
}
public String getErrorMessage() {
return fErrorMessage;
}
}
}