blob: 073f0500a82f789d9386994add974663b98e54f6 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.i18n.server;
import com.google.gwt.i18n.client.Messages.Example;
import com.google.gwt.i18n.client.Messages.PluralCount;
import com.google.gwt.i18n.client.Messages.Select;
import com.google.gwt.i18n.server.MessageFormatUtils.MessageStyle;
import com.google.gwt.i18n.shared.GwtLocale;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
/**
* Factory for GWT properties file format.
*/
public class PropertyCatalogFactory implements MessageCatalogFactory {
// @VisibleForTesting
static final String SELECTOR_BOILERPLATE_1 = "# Lines of the form"
+ " key[form|form]=msg are for alternate forms of";
static final String SELECTOR_BOILERPLATE_2 = "# the message according to"
+ " Plural Count and Selector entries above.";
static final String STRINGMAP_BOILERPLATE_1 = "# Lines of the form"
+ " key[entry]=msg are for individual entries, and";
static final String STRINGMAP_BOILERPLATE_2 = "# the line without [] lists"
+ " the entries, separated by commas.";
private static class PropertiesWriter extends DefaultVisitor
implements Writer {
private static String stringJoin(String joiner, String... values) {
StringBuilder buf = new StringBuilder();
boolean needsJoiner = false;
for (String value : values) {
if (needsJoiner) {
buf.append(joiner);
} else {
needsJoiner = true;
}
buf.append(value);
}
return buf.toString();
}
private final PrintWriter writer;
private String baseKey;
public PropertiesWriter(PrintWriter writer) {
this.writer = writer;
}
public void close() throws IOException {
writer.close();
}
@Override
public void endMessage(Message msg, MessageTranslation trans) {
baseKey = null;
}
public MessageInterfaceVisitor visitClass() {
return this;
}
@Override
public MessageVisitor visitMessage(Message msg, MessageTranslation trans) {
writer.println();
String description = msg.getDescription();
if (description != null) {
writer.println("# Description: " + description);
}
String meaning = msg.getMeaning();
if (meaning != null) {
writer.println("# Meaning: " + meaning);
}
List<Parameter> params = msg.getParameters();
for (int i = 0; i < params.size(); ++i) {
Parameter param = params.get(i);
writer.print("# " + i + " - " + param.getName());
if (param.isAnnotationPresent(PluralCount.class)) {
writer.print(", Plural Count");
}
if (param.isAnnotationPresent(Select.class)) {
writer.print(", Selector");
}
if (param.isAnnotationPresent(Example.class)) {
Example exampleAnnot = param.getAnnotation(Example.class);
writer.print(", Example: " + exampleAnnot.value());
}
writer.println();
}
int[] selectorIndices = msg.getSelectorParameterIndices();
if (selectorIndices.length > 0) {
if (selectorIndices[0] >= 0) {
writer.println(SELECTOR_BOILERPLATE_1);
writer.println(SELECTOR_BOILERPLATE_2);
} else {
writer.println(STRINGMAP_BOILERPLATE_1);
writer.println(STRINGMAP_BOILERPLATE_2);
}
}
baseKey = quoteKey(msg.getKey());
writer.println(baseKey + "=" + propertiesMessage(msg.getMessageStyle(),
msg.getDefaultMessage()));
return this;
}
@Override
public void visitMessageInterface(MessageInterface msgIntf, GwtLocale sourceLocale) {
writer.println("# Messages from " + msgIntf.getQualifiedName());
writer.println("# Source locale " + sourceLocale);
}
@Override
public void visitTranslation(String[] formNames, boolean isDefault,
MessageStyle style, String msg) {
if (isDefault) {
// default message is processed in processDefaultMessageBefore
return;
}
if (msg == null) {
msg = "";
}
String key = baseKey;
key += "[" + stringJoin("|", formNames) + "]";
writer.println(key + "=" + propertiesMessage(style, msg));
}
private String propertiesMessage(MessageStyle style, String msg) {
if (msg == null) {
// TODO(jat): is this the right thing to do if no translation was found?
return "";
}
// TODO(jat): translate so property files have consistent quoting rules?
return quoteValue(msg);
}
/**
* Quote keys for use in a properties file.
*
* In addition to the usual quoting, all spaces are backslash-quoted.
*
* @param str key to quote
* @return quoted key
*/
private String quoteKey(String str) {
str = str.replace("\\", "\\\\");
str = str.replace(" ", "\\ ");
return quoteSpecial(str);
}
/**
* Quote strings for use in a properties file.
*
* @param str string to quote
* @return quoted string
*/
private String quoteSpecial(String str) {
return str.replaceAll("([\f\t\n\r$!=:#])", "\\\\$1");
}
/**
* Quote values for use in a properties file.
*
* In addition to the usual quoting, leading spaces are backslash-quoted.
*
* @param str value to quote
* @return quoted value
*/
private String quoteValue(String str) {
str = str.replace("\\", "\\\\");
if (str.startsWith(" ")) {
int n = 0;
while (n < str.length() && str.charAt(n) == ' ') {
n++;
}
str = str.substring(n);
while (n-- > 0) {
str = "\\ " + str;
}
}
return quoteSpecial(str);
}
}
public String getExtension() {
return ".properties";
}
public Writer getWriter(Context context,
String fileName) {
PrintWriter pw = context.createTextFile(fileName, "UTF-8");
if (pw == null) {
return null;
}
PropertiesWriter writer = new PropertiesWriter(pw);
return writer;
}
}