package net.jhorstmann.i18n.tools.xgettext; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MessageFunction { private static final Pattern SIGNATURE_PATTERN; static { String name = "\\w+"; String packagePrefix = "(?:" + name + "\\.)*"; String methodName = packagePrefix + "(?:" + name + "|<init>)"; String typeName = "(?:" + packagePrefix + name + "(?:\\[\\])*(?:\\.\\.\\.)?)"; String sp = "\\s+"; String param = typeName + "(?:" + sp + name + ")?"; String paramList = param + "(?:\\s*,\\s*" + param + ")*"; SIGNATURE_PATTERN = Pattern.compile("(?:(" + typeName + ")" + sp + ")?" + "(" + methodName + ")\\(((?:" + paramList + ")?)\\)"); } private String namespace; private String name; private String description; private int parameterCount; private int messageIndex; private int contextIndex; private int pluralIndex; private static int validateParamIdx(String signature) { int idx = signature.indexOf('('); if (idx < 0) { throw new IllegalArgumentException("Invalid function signature"); } else if (idx == 0) { throw new IllegalArgumentException("Missing function name"); } if (!signature.endsWith(")")) { throw new IllegalArgumentException("Invalid function signature"); } return idx; } public static MessageFunction fromEL(String signature) { String namespace; if (signature.charAt(0) == '{') { int idx = signature.indexOf('}', 1); namespace = signature.substring(1, idx); signature = signature.substring(idx+1); } else { namespace = ""; } return fromEL(namespace, signature); } public static MessageFunction fromEL(String namespace, String signature) { int idx = validateParamIdx(signature); String methodName = signature.substring(0, idx).trim(); String methodParams = signature.substring(idx + 1, signature.length() - 1); int messageIndex = -1; int contextIndex = -1; int pluralIndex = -1; String[] params = methodParams.split("\\s*,\\s*"); int length = params.length; for (int i = 0; i < length; i++) { String name = params[i]; if ("context".equals(name)) { contextIndex = i; } else if ("message".equals(name)) { messageIndex = i; } else if ("plural".equals(name)) { pluralIndex = i; } } return new MessageFunction(namespace, methodName, null, messageIndex, contextIndex, pluralIndex, length); } public static MessageFunction fromJava(String signature) { Matcher matcher = SIGNATURE_PATTERN.matcher(signature); if (matcher.matches()) { String returnType = matcher.group(1); String methodName = matcher.group(2); String parameters = matcher.group(3); String className; int idx = methodName.lastIndexOf('.'); if (idx >= 0) { className = methodName.substring(0, idx); methodName = methodName.substring(idx+1); } else { className = ""; } if (returnType == null) { returnType = "void"; } return fromJava(className, returnType, methodName, parameters); } else { throw new IllegalArgumentException("Invalid java method signature '" + signature + "'"); } } public static MessageFunction fromJava(String className, String signature) { MessageFunction fn = fromJava(signature); fn.setNamespace(className.replace('.', '/')); return fn; } private static void appendInternalName(StringBuilder desc, String javaType) { if (javaType.endsWith("...")) { desc.append("["); javaType = javaType.substring(0, javaType.length() - 3); } while (javaType.endsWith("[]")) { desc.append("["); javaType = javaType.substring(0, javaType.length() - 2); } if ("void".equals(javaType)) { desc.append("V"); } else if ("boolean".equals(javaType)) { desc.append("Z"); } else if ("byte".equals(javaType)) { desc.append("B"); } else if ("char".equals(javaType)) { desc.append("C"); } else if ("short".equals(javaType)) { desc.append("S"); } else if ("int".equals(javaType)) { desc.append("I"); } else if ("long".equals(javaType)) { desc.append("J"); } else if ("float".equals(javaType)) { desc.append("F"); } else if ("double".equals(javaType)) { desc.append("D"); } else if ("Object".equals(javaType)) { desc.append("Ljava/lang/Object;"); } else if ("String".equals(javaType)) { desc.append("Ljava/lang/String;"); } else if ("Locale".equals(javaType)) { desc.append("Ljava/util/Locale;"); } else if ("ResourceBundle".equals(javaType)) { desc.append("Ljava/util/ResourceBundle;"); } else { desc.append("L"); desc.append(javaType.replace('.', '/')); desc.append(";"); } } private static MessageFunction fromJava(String className, String returnType, String methodName, String methodParams) { String namespace = className.replace('.', '/'); int messageIndex = -1; int contextIndex = -1; int pluralIndex = -1; boolean isConstructor = "<init>".equals(methodName); String[] params = methodParams.split("\\s*,\\s*"); StringBuilder desc = new StringBuilder(); desc.append("("); int length = params.length; for (int i = 0; i < length; i++) { String[] typeAndName = params[i].split("\\s+"); if (typeAndName.length > 1) { String name = typeAndName[1]; if ("context".equals(name)) { contextIndex = isConstructor ? i+1 : i; } else if ("message".equals(name)) { messageIndex = isConstructor ? i+1 : i; } else if ("plural".equals(name)) { pluralIndex = isConstructor ? i+1 : i; } } if (typeAndName[0].length() > 0) { appendInternalName(desc, typeAndName[0]); } } desc.append(")"); appendInternalName(desc, returnType); return new MessageFunction(namespace, methodName, desc.toString(), messageIndex, contextIndex, pluralIndex, length); } private MessageFunction(String namespace, String name, String description, int messageIndex, int contextIndex, int pluralIndex, int parameterCount) { this.namespace = namespace; this.name = name; this.description = description; this.messageIndex = messageIndex; this.contextIndex = contextIndex; this.pluralIndex = pluralIndex; this.parameterCount = parameterCount; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public int getContextIndex() { return contextIndex; } public void setContextIndex(int contextIndex) { this.contextIndex = contextIndex; } public int getMessageIndex() { return messageIndex; } public void setMessageIndex(int messageIndex) { this.messageIndex = messageIndex; } public int getPluralIndex() { return pluralIndex; } public void setPluralIndex(int pluralIndex) { this.pluralIndex = pluralIndex; } public int getParameterCount() { return parameterCount; } public void setParameterCount(int parameterCount) { this.parameterCount = parameterCount; } }