package restx.i18n; import com.google.common.base.Charsets; import com.google.common.hash.HashFunction; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import restx.*; import restx.factory.Component; import restx.http.CacheControl; import restx.http.ETag; import restx.http.HttpStatus; import javax.inject.Named; import java.io.IOException; import java.io.PrintWriter; import java.util.Locale; import java.util.Map.Entry; /** * Date: 2/2/14 * Time: 08:41 */ @Component public class MessagesRouter extends RestxRouter { public MessagesRouter(@Named("Messages") Messages messages, @Named("restx.i18n.labelsJsTemplate") String labelsJsTemplate, @Named("CurrentLocaleResolver") CurrentLocaleResolver currentLocaleResolver) { super("MessagesRouter", new JsonLabelsRoute(messages, currentLocaleResolver), new JsLabelsRoute(messages, labelsJsTemplate, currentLocaleResolver)); } private static class JsonLabelsRoute extends StdRoute { private final Messages messages; private CurrentLocaleResolver currentLocaleResolver; public JsonLabelsRoute(Messages messages, CurrentLocaleResolver currentLocaleResolver) { super("labels.json", new StdRestxRequestMatcher("GET", "/i18n/labels.json")); this.messages = messages; this.currentLocaleResolver = currentLocaleResolver; } @Override public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx) throws IOException { Iterable<Entry<String, String>> entries = messages.entries(currentLocaleResolver.guessLocale(req)); handleETagFor(req, resp, entries); resp.setStatus(HttpStatus.OK); resp.setContentType("application/json"); PrintWriter writer = resp.getWriter(); writeLabelsJson(writer, entries); } } private static class JsLabelsRoute extends StdRoute { private final Messages messages; private final CurrentLocaleResolver currentLocaleResolver; private final String labelsJsBefore; private final String labelsJsAfter; public JsLabelsRoute(Messages messages, String labelsJsTemplate, CurrentLocaleResolver currentLocaleResolver) { super("labels.js", new StdRestxRequestMatcher("GET", "/i18n/labels.js")); this.messages = messages; this.currentLocaleResolver = currentLocaleResolver; int i = labelsJsTemplate.indexOf("{LABELS}"); if (i == -1) { throw new IllegalArgumentException("invalid labels js template. It must have {LABELS} token inside." + "\n\tIt was :'" + labelsJsTemplate + "'"); } labelsJsBefore = labelsJsTemplate.substring(0, i); labelsJsAfter = labelsJsTemplate.substring(i + "{LABELS}".length()); } @Override public void handle(RestxRequestMatch match, RestxRequest req, RestxResponse resp, RestxContext ctx) throws IOException { Iterable<Entry<String, String>> entries = messages.entries(currentLocaleResolver.guessLocale(req)); handleETagFor(req, resp, entries); resp.setStatus(HttpStatus.OK); resp.setContentType("application/javascript"); PrintWriter writer = resp.getWriter(); writer.print(labelsJsBefore); writeLabelsJson(writer, entries); writer.print(labelsJsAfter); } } protected static void handleETagFor(RestxRequest req, RestxResponse resp, Iterable<Entry<String, String>> entries) { Hasher hasher = Hashing.sha1().newHasher(); for (Entry<String, String> entry : entries) { hasher.putString(entry.getKey(), Charsets.UTF_8).putString(entry.getValue(), Charsets.UTF_8); } new ETag(hasher.hash().toString(), CacheControl.MUST_REVALIDATE).handleIn(req, resp); } protected static void writeLabelsJson(PrintWriter writer, Iterable<Entry<String, String>> labels) { writer.println("{"); boolean firstKey = true; for (Entry<String, String> entry : labels) { if (firstKey) { firstKey = false; } else { writer.print(",\n"); } writer.print(String.format(" \"%s\" : \"%s\"", entry.getKey(), escape(entry.getValue()))); } writer.print("\n}"); } private static String escape(String str) { return str.replace("\"", "\\\"").replace("\n", "\\n"); } }