import java.util.HashSet;
import java.util.Properties;
import java.util.TreeSet;
+import java.util.regex.Pattern;
/**
* This class scans Java source files for calls to MessageManager and reports
public class MessageBundleChecker
{
/*
+ * regex ^"[^"]*"$
+ * opening quote, closing quote, no quotes in between
+ */
+ static Pattern STRING_PATTERN = Pattern.compile("^\"[^\"]*\"$");
+
+ /*
* number of text lines to read at a time in order to parse
* code that is split over several lines
*/
static int bufferSize = 3;
+ /*
+ * resource bundle key is arg0 for these methods
+ */
static final String METHOD1 = "MessageManager.getString(";
- static final String METHOD2 = "MessageManager.getStringOrReturn(";
+ static final String METHOD2 = "MessageManager.formatMessage(";
- static final String METHOD3 = "MessageManager.formatMessage(";
+ static final String METHOD3 = "MessageManager.getStringOrReturn(";
- static final String[] METHODS = { METHOD1, METHOD2, METHOD3 };
+ /*
+ * resource bundle key is arg1 for this method
+ */
+ static final String JVINIT = "JvSwingUtils.jvInitComponent(";
+
+ static final String[] METHODS = { METHOD1, METHOD2, METHOD3, JVINIT };
/*
* root of the Java source folders we want to scan
+ " possibly invalid parameter calls");
System.out.println(messageKeys.size()
- + " keys not found, possibly unused");
+ + " keys not found, either unused or constructed dynamically");
for (String key : messageKeys)
{
System.out.println(" " + key);
}
javaCount++;
+ /*
+ * skip class with designed dynamic lookup call
+ */
+ if (path.endsWith("gui/JvSwingUtils.java"))
+ {
+ return;
+ }
+
String[] lines = new String[bufferSize];
BufferedReader br = new BufferedReader(new FileReader(f));
for (int i = 0; i < bufferSize; i++)
{
continue;
}
- String methodArgs = combined.substring(pos + method.length());
+
+ /*
+ * extract what follows the opening bracket of the method call
+ */
+ String methodArgs = combined.substring(pos + method.length()).trim();
if ("".equals(methodArgs))
{
/*
- * continues on next line - catch in the next read loop iteration
+ * arguments are on next line - catch in the next read loop iteration
*/
continue;
}
- if (!methodArgs.startsWith("\""))
+ if (methodArgs.indexOf(",") == -1 && methodArgs.indexOf(")") == -1)
{
- System.out.println(String.format(
- "Possible dynamic key at %s line %s %s",
+ /*
+ * arguments continue on next line - catch in the next read loop iteration
+ */
+ continue;
+ }
+
+ if (JVINIT == method && methodArgs.indexOf(",") == -1)
+ {
+ /*
+ * not interested in 1-arg calls to jvInitComponent
+ */
+ continue;
+ }
+
+ if (METHOD3 == method)
+ {
+ System.out.println(String.format("Dynamic key at %s line %s %s",
path.substring(sourcePath.length()), lineNos, combined));
continue;
}
- methodArgs = methodArgs.substring(1);
- int quotePos = methodArgs.indexOf("\"");
- if (quotePos == -1)
+
+ String messageKey = getMessageKey(method, methodArgs);
+ if (messageKey == null)
{
System.out.println(String.format("Trouble parsing %s line %s %s",
path.substring(sourcePath.length()), lineNos, combined));
continue;
}
- String messageKey = methodArgs.substring(0, quotePos);
+
+ if (!(STRING_PATTERN.matcher(messageKey).matches()))
+ {
+ System.out.println(String.format("Dynamic key at %s line %s %s",
+ path.substring(sourcePath.length()), lineNos, combined));
+ continue;
+ }
+
+ /*
+ * strip leading and trailing quote
+ */
+ messageKey = messageKey.substring(1, messageKey.length() - 1);
+
if (!this.messages.containsKey(messageKey))
{
System.out.println(String.format(
}
}
+ /**
+ * Helper method to parse out the resource bundle key parameter of a method
+ * call
+ *
+ * @param method
+ * @param methodArgs
+ * the rest of the source line starting with arguments to method
+ * @return
+ */
+ private String getMessageKey(String method, String methodArgs)
+ {
+ String key = methodArgs;
+
+ /*
+ * locate second argument if calling jvInitComponent()
+ */
+ if (method == JVINIT)
+ {
+ int commaLoc = methodArgs.indexOf(",");
+ if (commaLoc == -1)
+ {
+ return null;
+ }
+ key = key.substring(commaLoc + 1).trim();
+ }
+
+ /*
+ * take up to next comma or ) or end of line
+ */
+ int commaPos = key.indexOf(",");
+ int bracePos = key.indexOf(")");
+ int endPos = commaPos == -1 ? bracePos : (bracePos == -1 ? commaPos
+ : Math.min(commaPos, bracePos));
+ if (endPos == -1 && key.length() > 1 && key.endsWith("\""))
+ {
+ endPos = key.length();
+ }
+
+ return endPos == -1 ? null : key.substring(0, endPos);
+ }
+
private String combineLines(String[] lines)
{
String combined = "";