1 16 package org.apache.cocoon.transformation; 17 18 import org.apache.avalon.framework.activity.Disposable; 19 import org.apache.avalon.framework.configuration.Configurable; 20 import org.apache.avalon.framework.configuration.Configuration; 21 import org.apache.avalon.framework.configuration.ConfigurationException; 22 import org.apache.avalon.framework.parameters.Parameters; 23 import org.apache.avalon.framework.service.ServiceException; 24 import org.apache.avalon.framework.service.ServiceManager; 25 import org.apache.avalon.framework.service.Serviceable; 26 27 import org.apache.cocoon.ProcessingException; 28 import org.apache.cocoon.caching.CacheableProcessingComponent; 29 import org.apache.cocoon.components.treeprocessor.variables.VariableExpressionTokenizer; 30 import org.apache.cocoon.components.treeprocessor.variables.VariableResolver; 31 import org.apache.cocoon.components.treeprocessor.variables.VariableResolverFactory; 32 import org.apache.cocoon.environment.SourceResolver; 33 import org.apache.cocoon.i18n.Bundle; 34 import org.apache.cocoon.i18n.BundleFactory; 35 import org.apache.cocoon.i18n.I18nUtils; 36 import org.apache.cocoon.sitemap.PatternException; 37 import org.apache.cocoon.xml.ParamSaxBuffer; 38 import org.apache.cocoon.xml.SaxBuffer; 39 40 import org.apache.excalibur.source.SourceValidity; 41 import org.xml.sax.Attributes ; 42 import org.xml.sax.SAXException ; 43 import org.xml.sax.helpers.AttributesImpl ; 44 45 import java.io.IOException ; 46 import java.text.DateFormat ; 47 import java.text.DecimalFormat ; 48 import java.text.DecimalFormatSymbols ; 49 import java.text.NumberFormat ; 50 import java.text.ParseException ; 51 import java.text.SimpleDateFormat ; 52 import java.util.Collections ; 53 import java.util.Date ; 54 import java.util.HashMap ; 55 import java.util.HashSet ; 56 import java.util.Iterator ; 57 import java.util.Locale ; 58 import java.util.Map ; 59 import java.util.MissingResourceException ; 60 import java.util.Set ; 61 import java.util.StringTokenizer ; 62 63 270 public class I18nTransformer extends AbstractTransformer 271 implements CacheableProcessingComponent, 272 Serviceable, Configurable, Disposable { 273 274 277 public static final String I18N_NAMESPACE_URI = 278 "http://apache.org/cocoon/i18n/2.1"; 279 280 283 public static final String I18N_OLD_NAMESPACE_URI = 284 "http://apache.org/cocoon/i18n/2.0"; 285 286 290 private static boolean deprecationFound = false; 291 292 296 305 public static final String I18N_TEXT_ELEMENT = "text"; 306 307 323 public static final String I18N_TRANSLATE_ELEMENT = "translate"; 324 325 356 public static final String I18N_CHOOSE_ELEMENT = "choose"; 357 358 374 public static final String I18N_WHEN_ELEMENT = "when"; 375 376 390 public static final String I18N_IF_ELEMENT = "if"; 391 392 401 public static final String I18N_OTHERWISE_ELEMENT = "otherwise"; 402 403 434 public static final String I18N_PARAM_ELEMENT = "param"; 435 436 442 public static final String I18N_PARAM_NAME_ATTRIBUTE = "name"; 443 444 468 public static final String I18N_DATE_ELEMENT = "date"; 469 470 495 public static final String I18N_DATE_TIME_ELEMENT = "date-time"; 496 497 521 public static final String I18N_TIME_ELEMENT = "time"; 522 523 542 public static final String I18N_NUMBER_ELEMENT = "number"; 543 544 547 public static final String I18N_CURRENCY_ELEMENT = "currency"; 548 549 552 public static final String I18N_PERCENT_ELEMENT = "percent"; 553 554 557 public static final String I18N_INT_CURRENCY_ELEMENT = "int-currency"; 558 559 562 public static final String I18N_CURRENCY_NO_UNIT_ELEMENT = "currency-no-unit"; 563 564 567 public static final String I18N_INT_CURRENCY_NO_UNIT_ELEMENT = "int-currency-no-unit"; 568 569 573 581 public static final String I18N_KEY_ATTRIBUTE = "key"; 582 583 592 public static final String I18N_ATTR_ATTRIBUTE = "attr"; 593 594 604 public static final String I18N_EXPR_ATTRIBUTE = "expr"; 605 606 610 620 public static final String I18N_SRC_PATTERN_ATTRIBUTE = "src-pattern"; 621 622 632 public static final String I18N_PATTERN_ATTRIBUTE = "pattern"; 633 634 646 public static final String I18N_LOCALE_ATTRIBUTE = "locale"; 647 648 658 public static final String I18N_SRC_LOCALE_ATTRIBUTE = "src-locale"; 659 660 671 public static final String I18N_VALUE_ATTRIBUTE = "value"; 672 673 683 public static final String I18N_TYPE_ATTRIBUTE = "type"; 684 685 694 public static final String CURRENCY_LOCALE_ATTRIBUTE = "currency"; 695 696 701 public static final String I18N_CATALOGUE_ATTRIBUTE = "catalogue"; 702 703 707 710 public static final String I18N_LOCALE = "locale"; 711 712 717 public static final String I18N_DEFAULT_CATALOGUE_ID = "default-catalogue-id"; 718 719 723 public static final String I18N_UNTRANSLATED = "untranslated-text"; 724 725 729 public static final String I18N_CACHE_STARTUP = "cache-at-startup"; 730 731 736 public static final String I18N_FRACTION_DIGITS_ATTRIBUTE = "fraction-digits"; 737 738 742 private static final int STATE_OUTSIDE = 0; 743 private static final int STATE_INSIDE_TEXT = 10; 744 private static final int STATE_INSIDE_PARAM = 20; 745 private static final int STATE_INSIDE_TRANSLATE = 30; 746 private static final int STATE_INSIDE_CHOOSE = 50; 747 private static final int STATE_INSIDE_WHEN = 51; 748 private static final int STATE_INSIDE_OTHERWISE = 52; 749 private static final int STATE_INSIDE_DATE = 60; 750 private static final int STATE_INSIDE_DATE_TIME = 61; 751 private static final int STATE_INSIDE_TIME = 62; 752 private static final int STATE_INSIDE_NUMBER = 63; 753 754 private static final Set dateTypes; 756 757 private static final Set numberTypes; 759 760 private static final Map datePatterns; 762 763 static { 764 HashSet set = new HashSet (5); 766 set.add(I18N_DATE_ELEMENT); 767 set.add(I18N_TIME_ELEMENT); 768 set.add(I18N_DATE_TIME_ELEMENT); 769 dateTypes = Collections.unmodifiableSet(set); 770 771 set = new HashSet (9); 773 set.add(I18N_NUMBER_ELEMENT); 774 set.add(I18N_PERCENT_ELEMENT); 775 set.add(I18N_CURRENCY_ELEMENT); 776 set.add(I18N_INT_CURRENCY_ELEMENT); 777 set.add(I18N_CURRENCY_NO_UNIT_ELEMENT); 778 set.add(I18N_INT_CURRENCY_NO_UNIT_ELEMENT); 779 numberTypes = Collections.unmodifiableSet(set); 780 781 Map map = new HashMap (7); 783 map.put("SHORT", new Integer (DateFormat.SHORT)); 784 map.put("MEDIUM", new Integer (DateFormat.MEDIUM)); 785 map.put("LONG", new Integer (DateFormat.LONG)); 786 map.put("FULL", new Integer (DateFormat.FULL)); 787 datePatterns = Collections.unmodifiableMap(map); 788 } 789 790 791 795 798 protected ServiceManager manager; 799 800 803 protected BundleFactory factory; 804 805 809 private Map catalogues; 810 811 814 private CatalogueInfo defaultCatalogue; 815 816 819 private String defaultUntranslated; 820 821 825 protected Map objectModel; 826 827 830 protected Locale locale; 831 832 835 private CatalogueInfo catalogue; 836 837 840 private String untranslated; 841 842 845 private ParamSaxBuffer untranslatedRecorder; 846 847 851 854 private int current_state; 855 856 860 private int prev_state; 861 862 867 private String currentKey; 868 869 873 private String currentCatalogueId; 874 875 878 private StringBuffer strBuffer; 879 880 883 private boolean translate_copy; 884 885 private boolean translate_end; 888 889 private ParamSaxBuffer tr_text_recorder; 891 892 private ParamSaxBuffer text_recorder; 894 895 private SaxBuffer param_recorder; 897 898 private int param_count; 900 901 private String param_name; 903 904 private HashMap indexedParams; 906 907 private String param_value; 909 910 private HashMap formattingParams; 912 913 914 918 public Locale getLocale() { 919 return this.locale; 920 } 921 922 926 public java.io.Serializable getKey() { 927 StringBuffer key = new StringBuffer (); 930 if (catalogue != null) { 931 key.append(catalogue.getLocation()[0]); 932 } 933 key.append("?"); 934 if (locale != null) { 935 key.append(locale.getLanguage()); 936 key.append("_"); 937 key.append(locale.getCountry()); 938 key.append("_"); 939 key.append(locale.getVariant()); 940 } 941 return key.toString(); 942 } 943 944 949 public SourceValidity getValidity() { 950 return org.apache.excalibur.source.impl.validity.NOPValidity.SHARED_INSTANCE; 953 } 954 955 958 public void service(ServiceManager manager) throws ServiceException { 959 this.manager = manager; 960 try { 961 this.factory = (BundleFactory) manager.lookup(BundleFactory.ROLE); 962 } catch (ServiceException e) { 963 getLogger().debug("Failed to lookup <" + BundleFactory.ROLE + ">", e); 964 throw e; 965 } 966 } 967 968 972 public void configure(Configuration conf) throws ConfigurationException { 973 Configuration cataloguesConf = conf.getChild("catalogues", false); 975 if (cataloguesConf == null) { 976 throw new ConfigurationException("I18nTransformer requires <catalogues> configuration at " + 977 conf.getLocation()); 978 } 979 980 Configuration[] catalogueConfs = cataloguesConf.getChildren("catalogue"); 982 catalogues = new HashMap (catalogueConfs.length + 3); 983 for (int i = 0; i < catalogueConfs.length; i++) { 984 String id = catalogueConfs[i].getAttribute("id"); 985 String name = catalogueConfs[i].getAttribute("name"); 986 987 String [] locations; 988 String location = catalogueConfs[i].getAttribute("location", null); 989 Configuration[] locationConf = 990 catalogueConfs[i].getChildren("location"); 991 if (location != null) { 992 if (locationConf.length > 0) { 993 String msg = "I18nTransformer: Location attribute cannot be " + 994 "specified with location elements"; 995 getLogger().error(msg); 996 throw new ConfigurationException(msg); 997 } 998 999 if (getLogger().isDebugEnabled()) { 1000 getLogger().debug("name=" + name + ", location=" + 1001 location); 1002 } 1003 locations = new String [1]; 1004 locations[0] = location; 1005 } else { 1006 if (locationConf.length == 0) { 1007 String msg = "I18nTransformer: A location attribute or location " + 1008 "elements must be specified"; 1009 getLogger().error(msg); 1010 throw new ConfigurationException(msg); 1011 } 1012 1013 locations = new String [locationConf.length]; 1014 for (int j=0; j < locationConf.length; ++j) { 1015 locations[j] = locationConf[j].getValue(); 1016 if (getLogger().isDebugEnabled()) { 1017 getLogger().debug("name=" + name + ", location=" + 1018 locations[j]); 1019 } 1020 } 1021 } 1022 1023 CatalogueInfo catalogueInfo; 1024 try { 1025 catalogueInfo = new CatalogueInfo(name, locations); 1026 } catch (PatternException e) { 1027 throw new ConfigurationException("I18nTransformer: Error in name or location " + 1028 "attribute on catalogue element with id " + id, e); 1029 } 1030 catalogues.put(id, catalogueInfo); 1031 } 1032 1033 String defaultCatalogueId = cataloguesConf.getAttribute("default"); 1034 defaultCatalogue = (CatalogueInfo) catalogues.get(defaultCatalogueId); 1035 if (defaultCatalogue == null) { 1036 throw new ConfigurationException("I18nTransformer: Default catalogue id '" + 1037 defaultCatalogueId + "' denotes a nonexisting catalogue"); 1038 } 1039 1040 defaultUntranslated = conf.getChild(I18N_UNTRANSLATED).getValue(null); 1042 if (getLogger().isDebugEnabled()) { 1043 getLogger().debug("Default untranslated text is '" + defaultUntranslated + "'"); 1044 } 1045 } 1046 1047 1050 public void setup(SourceResolver resolver, Map objectModel, String source, 1051 Parameters parameters) 1052 throws ProcessingException, SAXException , IOException { 1053 1054 this.objectModel = objectModel; 1055 1056 untranslated = parameters.getParameter(I18N_UNTRANSLATED, defaultUntranslated); 1057 if (untranslated != null) { 1058 untranslatedRecorder = new ParamSaxBuffer(); 1059 untranslatedRecorder.characters(untranslated.toCharArray(), 0, untranslated.length()); 1060 } 1061 1062 String lc = parameters.getParameter(I18N_LOCALE, null); 1064 Locale locale = I18nUtils.parseLocale(lc); 1065 if (getLogger().isDebugEnabled()) { 1066 getLogger().debug("Using locale '" + locale + "'"); 1067 } 1068 1069 this.locale = locale; 1071 this.current_state = STATE_OUTSIDE; 1072 this.prev_state = STATE_OUTSIDE; 1073 this.currentKey = null; 1074 this.currentCatalogueId = null; 1075 this.translate_copy = false; 1076 this.tr_text_recorder = null; 1077 this.text_recorder = new ParamSaxBuffer(); 1078 this.param_count = 0; 1079 this.param_name = null; 1080 this.param_value = null; 1081 this.param_recorder = null; 1082 this.indexedParams = new HashMap (3); 1083 this.formattingParams = null; 1084 this.strBuffer = null; 1085 1086 String catalogueId = parameters.getParameter(I18N_DEFAULT_CATALOGUE_ID, null); 1089 if (catalogueId != null) { 1090 CatalogueInfo catalogueInfo = (CatalogueInfo) catalogues.get(catalogueId); 1091 if (catalogueInfo == null) { 1092 throw new ProcessingException("I18nTransformer: '" + 1093 catalogueId + 1094 "' is not an existing catalogue id."); 1095 } 1096 catalogue = catalogueInfo; 1097 } else { 1098 catalogue = defaultCatalogue; 1099 } 1100 1101 if (getLogger().isDebugEnabled()) { 1102 getLogger().debug("Default catalogue is " + catalogue.getName()); 1103 } 1104 } 1105 1106 1107 1111 public void startElement(String uri, String name, String raw, 1112 Attributes attr) 1113 throws SAXException { 1114 1115 if (current_state != STATE_OUTSIDE && strBuffer != null) { 1117 i18nCharacters(strBuffer.toString()); 1118 strBuffer = null; 1119 } 1120 1121 if (I18N_NAMESPACE_URI.equals(uri)) { 1123 if (getLogger().isDebugEnabled()) { 1124 getLogger().debug("Starting i18n element: " + name); 1125 } 1126 startI18NElement(name, attr); 1127 1128 } else if (I18N_OLD_NAMESPACE_URI.equals(uri)) { 1129 if (!deprecationFound) { 1130 deprecationFound = true; 1131 getLogger().warn("The namespace <" + I18N_OLD_NAMESPACE_URI + 1132 "> is deprecated, use: <" + I18N_NAMESPACE_URI + ">"); 1133 } 1134 if (getLogger().isDebugEnabled()) { 1135 getLogger().debug("Starting deprecated i18n element: " + name); 1136 } 1137 startI18NElement(name, attr); 1138 1139 } else { 1140 if (current_state == STATE_OUTSIDE) { 1142 super.startElement(uri, name, raw, 1143 translateAttributes(name, attr)); 1144 } else if (current_state == STATE_INSIDE_PARAM) { 1145 param_recorder.startElement(uri, name, raw, attr); 1146 } else if (current_state == STATE_INSIDE_TEXT) { 1147 text_recorder.startElement(uri, name, raw, attr); 1148 } else if ((current_state == STATE_INSIDE_WHEN || 1149 current_state == STATE_INSIDE_OTHERWISE) 1150 && translate_copy) { 1151 1152 super.startElement(uri, name, raw, attr); 1153 } 1154 } 1155 } 1156 1157 public void endElement(String uri, String name, String raw) 1158 throws SAXException { 1159 1160 if (current_state != STATE_OUTSIDE && strBuffer != null) { 1162 i18nCharacters(strBuffer.toString()); 1163 strBuffer = null; 1164 } 1165 1166 if (I18N_NAMESPACE_URI.equals(uri) || I18N_OLD_NAMESPACE_URI.equals(uri)) { 1167 endI18NElement(name); 1168 } else if (current_state == STATE_INSIDE_PARAM) { 1169 param_recorder.endElement(uri, name, raw); 1170 } else if (current_state == STATE_INSIDE_TEXT) { 1171 text_recorder.endElement(uri, name, raw); 1172 } else if (current_state == STATE_INSIDE_CHOOSE || 1173 (current_state == STATE_INSIDE_WHEN || 1174 current_state == STATE_INSIDE_OTHERWISE) 1175 && !translate_copy) { 1176 1177 } else { 1179 super.endElement(uri, name, raw); 1180 } 1181 } 1182 1183 public void characters(char[] ch, int start, int len) 1184 throws SAXException { 1185 1186 if (current_state == STATE_OUTSIDE || 1187 ((current_state == STATE_INSIDE_WHEN || 1188 current_state == STATE_INSIDE_OTHERWISE) && translate_copy)) { 1189 1190 super.characters(ch, start, len); 1191 } else { 1192 if (strBuffer == null) { 1194 strBuffer = new StringBuffer (); 1195 } 1196 strBuffer.append(ch, start, len); 1197 } 1198 } 1199 1200 1204 private void startI18NElement(String name, Attributes attr) 1205 throws SAXException { 1206 1207 if (getLogger().isDebugEnabled()) { 1208 getLogger().debug("Start i18n element: " + name); 1209 } 1210 1211 if (I18N_TEXT_ELEMENT.equals(name)) { 1212 if (current_state != STATE_OUTSIDE 1213 && current_state != STATE_INSIDE_PARAM 1214 && current_state != STATE_INSIDE_TRANSLATE) { 1215 1216 throw new SAXException ( 1217 getClass().getName() 1218 + ": nested i18n:text elements are not allowed." 1219 + " Current state: " + current_state); 1220 } 1221 1222 prev_state = current_state; 1223 current_state = STATE_INSIDE_TEXT; 1224 1225 currentKey = attr.getValue("", I18N_KEY_ATTRIBUTE); 1226 if (currentKey == null) { 1227 currentKey = attr.getValue(I18N_NAMESPACE_URI, I18N_KEY_ATTRIBUTE); 1229 if (currentKey == null) { 1230 currentKey = attr.getValue(I18N_OLD_NAMESPACE_URI, I18N_KEY_ATTRIBUTE); 1232 } 1233 } 1234 1235 currentCatalogueId = attr.getValue("", I18N_CATALOGUE_ATTRIBUTE); 1236 if (currentCatalogueId == null) { 1237 currentCatalogueId = attr.getValue(I18N_NAMESPACE_URI, I18N_CATALOGUE_ATTRIBUTE); 1239 } 1240 1241 if (prev_state != STATE_INSIDE_PARAM) { 1242 tr_text_recorder = null; 1243 } 1244 1245 if (currentKey != null) { 1246 tr_text_recorder = getMessage(currentKey, (ParamSaxBuffer)null); 1247 } 1248 1249 } else if (I18N_TRANSLATE_ELEMENT.equals(name)) { 1250 if (current_state != STATE_OUTSIDE) { 1251 throw new SAXException ( 1252 getClass().getName() 1253 + ": i18n:translate element must be used " 1254 + "outside of other i18n elements. Current state: " 1255 + current_state); 1256 } 1257 1258 prev_state = current_state; 1259 current_state = STATE_INSIDE_TRANSLATE; 1260 } else if (I18N_PARAM_ELEMENT.equals(name)) { 1261 if (current_state != STATE_INSIDE_TRANSLATE) { 1262 throw new SAXException ( 1263 getClass().getName() 1264 + ": i18n:param element can be used only inside " 1265 + "i18n:translate element. Current state: " 1266 + current_state); 1267 } 1268 1269 param_name = attr.getValue(I18N_PARAM_NAME_ATTRIBUTE); 1270 if (param_name == null) { 1271 param_name = String.valueOf(param_count++); 1272 } 1273 1274 param_recorder = new SaxBuffer(); 1275 setFormattingParams(attr); 1276 current_state = STATE_INSIDE_PARAM; 1277 } else if (I18N_CHOOSE_ELEMENT.equals(name)) { 1278 if (current_state != STATE_OUTSIDE) { 1279 throw new SAXException ( 1280 getClass().getName() 1281 + ": i18n:choose elements cannot be used" 1282 + "inside of other i18n elements."); 1283 } 1284 1285 translate_copy = false; 1286 translate_end = false; 1287 prev_state = current_state; 1288 current_state = STATE_INSIDE_CHOOSE; 1289 } else if (I18N_WHEN_ELEMENT.equals(name) || 1290 I18N_IF_ELEMENT.equals(name)) { 1291 1292 if (I18N_WHEN_ELEMENT.equals(name) && 1293 current_state != STATE_INSIDE_CHOOSE) { 1294 throw new SAXException ( 1295 getClass().getName() 1296 + ": i18n:when elements are can be used only" 1297 + "inside of i18n:choose elements."); 1298 } 1299 1300 if (I18N_IF_ELEMENT.equals(name) && 1301 current_state != STATE_OUTSIDE) { 1302 throw new SAXException ( 1303 getClass().getName() 1304 + ": i18n:if elements cannot be nested."); 1305 } 1306 1307 String locale = attr.getValue(I18N_LOCALE_ATTRIBUTE); 1308 if (locale == null) 1309 throw new SAXException ( 1310 getClass().getName() 1311 + ": i18n:" + name 1312 + " element cannot be used without 'locale' attribute."); 1313 1314 if ((!translate_end && current_state == STATE_INSIDE_CHOOSE) 1315 || current_state == STATE_OUTSIDE) { 1316 1317 if (this.locale.toString().startsWith(locale)) { 1319 if (getLogger().isDebugEnabled()) { 1320 getLogger().debug("Locale matching: " + locale); 1321 } 1322 translate_copy = true; 1323 } 1324 } 1325 1326 prev_state = current_state; 1327 current_state = STATE_INSIDE_WHEN; 1328 1329 } else if (I18N_OTHERWISE_ELEMENT.equals(name)) { 1330 if (current_state != STATE_INSIDE_CHOOSE) { 1331 throw new SAXException ( 1332 getClass().getName() 1333 + ": i18n:otherwise elements are not allowed " 1334 + "only inside i18n:choose."); 1335 } 1336 1337 getLogger().debug("Matching any locale"); 1338 if (!translate_end) { 1339 translate_copy = true; 1340 } 1341 1342 prev_state = current_state; 1343 current_state = STATE_INSIDE_OTHERWISE; 1344 1345 } else if (I18N_DATE_ELEMENT.equals(name)) { 1346 if (current_state != STATE_OUTSIDE 1347 && current_state != STATE_INSIDE_TEXT 1348 && current_state != STATE_INSIDE_PARAM) { 1349 throw new SAXException ( 1350 getClass().getName() 1351 + ": i18n:date elements are not allowed " 1352 + "inside of other i18n elements."); 1353 } 1354 1355 setFormattingParams(attr); 1356 prev_state = current_state; 1357 current_state = STATE_INSIDE_DATE; 1358 } else if (I18N_DATE_TIME_ELEMENT.equals(name)) { 1359 if (current_state != STATE_OUTSIDE 1360 && current_state != STATE_INSIDE_TEXT 1361 && current_state != STATE_INSIDE_PARAM) { 1362 throw new SAXException ( 1363 getClass().getName() 1364 + ": i18n:date-time elements are not allowed " 1365 + "inside of other i18n elements."); 1366 } 1367 1368 setFormattingParams(attr); 1369 prev_state = current_state; 1370 current_state = STATE_INSIDE_DATE_TIME; 1371 } else if (I18N_TIME_ELEMENT.equals(name)) { 1372 if (current_state != STATE_OUTSIDE 1373 && current_state != STATE_INSIDE_TEXT 1374 && current_state != STATE_INSIDE_PARAM) { 1375 throw new SAXException ( 1376 getClass().getName() 1377 + ": i18n:date elements are not allowed " 1378 + "inside of other i18n elements."); 1379 } 1380 1381 setFormattingParams(attr); 1382 prev_state = current_state; 1383 current_state = STATE_INSIDE_TIME; 1384 } else if (I18N_NUMBER_ELEMENT.equals(name)) { 1385 if (current_state != STATE_OUTSIDE 1386 && current_state != STATE_INSIDE_TEXT 1387 && current_state != STATE_INSIDE_PARAM) { 1388 throw new SAXException ( 1389 getClass().getName() 1390 + ": i18n:number elements are not allowed " 1391 + "inside of other i18n elements."); 1392 } 1393 1394 setFormattingParams(attr); 1395 prev_state = current_state; 1396 current_state = STATE_INSIDE_NUMBER; 1397 } 1398 } 1399 1400 private void setFormattingParams(Attributes attr) { 1402 formattingParams = new HashMap (3); 1404 1405 String attr_value = attr.getValue(I18N_SRC_PATTERN_ATTRIBUTE); 1406 if (attr_value != null) { 1407 formattingParams.put(I18N_SRC_PATTERN_ATTRIBUTE, attr_value); 1408 } 1409 1410 attr_value = attr.getValue(I18N_PATTERN_ATTRIBUTE); 1411 if (attr_value != null) { 1412 formattingParams.put(I18N_PATTERN_ATTRIBUTE, attr_value); 1413 } 1414 1415 attr_value = attr.getValue(I18N_VALUE_ATTRIBUTE); 1416 if (attr_value != null) { 1417 formattingParams.put(I18N_VALUE_ATTRIBUTE, attr_value); 1418 } 1419 1420 attr_value = attr.getValue(I18N_LOCALE_ATTRIBUTE); 1421 if (attr_value != null) { 1422 formattingParams.put(I18N_LOCALE_ATTRIBUTE, attr_value); 1423 } 1424 1425 attr_value = attr.getValue(CURRENCY_LOCALE_ATTRIBUTE); 1426 if (attr_value != null) { 1427 formattingParams.put(CURRENCY_LOCALE_ATTRIBUTE, attr_value); 1428 } 1429 1430 attr_value = attr.getValue(I18N_SRC_LOCALE_ATTRIBUTE); 1431 if (attr_value != null) { 1432 formattingParams.put(I18N_SRC_LOCALE_ATTRIBUTE, attr_value); 1433 } 1434 1435 attr_value = attr.getValue(I18N_TYPE_ATTRIBUTE); 1436 if (attr_value != null) { 1437 formattingParams.put(I18N_TYPE_ATTRIBUTE, attr_value); 1438 } 1439 1440 attr_value = attr.getValue(I18N_FRACTION_DIGITS_ATTRIBUTE); 1441 if (attr_value != null) { 1442 formattingParams.put(I18N_FRACTION_DIGITS_ATTRIBUTE, attr_value); 1443 } 1444 } 1445 1446 private void endI18NElement(String name) throws SAXException { 1447 if (getLogger().isDebugEnabled()) { 1448 getLogger().debug("End i18n element: " + name); 1449 } 1450 1451 switch (current_state) { 1452 case STATE_INSIDE_TEXT: 1453 endTextElement(); 1454 break; 1455 1456 case STATE_INSIDE_TRANSLATE: 1457 endTranslateElement(); 1458 break; 1459 1460 case STATE_INSIDE_CHOOSE: 1461 endChooseElement(); 1462 break; 1463 1464 case STATE_INSIDE_WHEN: 1465 case STATE_INSIDE_OTHERWISE: 1466 endWhenElement(); 1467 break; 1468 1469 case STATE_INSIDE_PARAM: 1470 endParamElement(); 1471 break; 1472 1473 case STATE_INSIDE_DATE: 1474 case STATE_INSIDE_DATE_TIME: 1475 case STATE_INSIDE_TIME: 1476 endDate_TimeElement(); 1477 break; 1478 1479 case STATE_INSIDE_NUMBER: 1480 endNumberElement(); 1481 break; 1482 } 1483 } 1484 1485 private void i18nCharacters(String textValue) throws SAXException { 1486 textValue = textValue.trim(); 1488 if (textValue.length() == 0) { 1489 return; 1490 } 1491 1492 if (getLogger().isDebugEnabled()) { 1493 getLogger().debug( "i18n message text = '" + textValue + "'" ); 1494 } 1495 1496 char[] ch = textValue.toCharArray(); 1497 switch (current_state) { 1498 case STATE_INSIDE_TEXT: 1499 text_recorder.characters(ch, 0, ch.length); 1500 break; 1501 1502 case STATE_INSIDE_PARAM: 1503 param_recorder.characters(ch, 0, ch.length); 1504 break; 1505 1506 case STATE_INSIDE_WHEN: 1507 case STATE_INSIDE_OTHERWISE: 1508 break; 1510 1511 case STATE_INSIDE_TRANSLATE: 1512 if(tr_text_recorder == null) { 1513 tr_text_recorder = new ParamSaxBuffer(); 1514 } 1515 tr_text_recorder.characters(ch, 0, ch.length); 1516 break; 1517 1518 case STATE_INSIDE_CHOOSE: 1519 getLogger().debug("No characters allowed inside <i18n:choose> tags"); 1521 break; 1522 1523 case STATE_INSIDE_DATE: 1524 case STATE_INSIDE_DATE_TIME: 1525 case STATE_INSIDE_TIME: 1526 case STATE_INSIDE_NUMBER: 1527 if (formattingParams.get(I18N_VALUE_ATTRIBUTE) == null) { 1528 formattingParams.put(I18N_VALUE_ATTRIBUTE, textValue); 1529 } else { 1530 } 1532 break; 1533 1534 default: 1535 throw new IllegalStateException (getClass().getName() + 1536 " developer's fault: characters not handled. " + 1537 "Current state: " + current_state); 1538 } 1539 } 1540 1541 private Attributes translateAttributes(final String element, Attributes attr) 1543 throws SAXException { 1544 if (attr == null) { 1545 return null; 1546 } 1547 1548 AttributesImpl tempAttr = null; 1549 1550 int attrIndex = attr.getIndex(I18N_NAMESPACE_URI, I18N_ATTR_ATTRIBUTE); 1553 if (attrIndex == -1) { 1554 attrIndex = attr.getIndex(I18N_OLD_NAMESPACE_URI, I18N_ATTR_ATTRIBUTE); 1556 } 1557 1558 if (attrIndex != -1) { 1559 StringTokenizer st = new StringTokenizer (attr.getValue(attrIndex)); 1560 1561 tempAttr = new AttributesImpl (attr); 1563 tempAttr.removeAttribute(attrIndex); 1565 1566 while (st.hasMoreElements()) { 1568 final String name = st.nextToken(); 1569 1570 int index = tempAttr.getIndex(name); 1571 if (index == -1) { 1572 getLogger().warn("Attribute " + 1573 name + " not found in element <" + element + ">"); 1574 continue; 1575 } 1576 1577 String value = translateAttribute(element, name, tempAttr.getValue(index)); 1578 if (value != null) { 1579 tempAttr.setValue(index, value); 1581 } 1582 } 1583 1584 attr = tempAttr; 1585 } 1586 1587 attrIndex = attr.getIndex(I18N_NAMESPACE_URI, I18N_EXPR_ATTRIBUTE); 1590 if (attrIndex != -1) { 1591 StringTokenizer st = new StringTokenizer (attr.getValue(attrIndex)); 1592 1593 if (tempAttr == null) { 1594 tempAttr = new AttributesImpl (attr); 1595 } 1596 tempAttr.removeAttribute(attrIndex); 1597 1598 while (st.hasMoreElements()) { 1600 final String name = st.nextToken(); 1601 1602 int index = tempAttr.getIndex(name); 1603 if (index == -1) { 1604 getLogger().warn("Attribute " + 1605 name + " not found in element <" + element + ">"); 1606 continue; 1607 } 1608 1609 final StringBuffer translated = new StringBuffer (); 1610 1611 VariableExpressionTokenizer.TokenReciever tr = new VariableExpressionTokenizer.TokenReciever () { 1613 private String catalogueName; 1614 1615 public void addToken(int type, String value) { 1616 if (type == MODULE) { 1617 this.catalogueName = value; 1618 } else if (type == VARIABLE) { 1619 translated.append(translateAttribute(element, name, value)); 1620 } else if (type == TEXT) { 1621 if (this.catalogueName != null) { 1622 translated.append(translateAttribute(element, 1623 name, 1624 this.catalogueName + ":" + value)); 1625 this.catalogueName = null; 1626 } else if (value != null) { 1627 translated.append(value); 1628 } 1629 } 1630 } 1631 }; 1632 1633 try { 1634 VariableExpressionTokenizer.tokenize(tempAttr.getValue(index), tr); 1635 } catch (PatternException e) { 1636 throw new SAXException (e); 1637 } 1638 1639 tempAttr.setValue(index, translated.toString()); 1641 } 1642 1643 attr = tempAttr; 1644 } 1645 1646 return attr; 1648 } 1649 1650 1655 private String translateAttribute(String element, String name, String key) { 1656 int colonPos = key.indexOf(":"); 1659 String catalogueID = null; 1660 if (colonPos != -1) { 1661 catalogueID = key.substring(0, colonPos); 1662 key = key.substring(colonPos + 1, key.length()); 1663 } 1664 1665 final SaxBuffer text = getMessage(catalogueID, key); 1666 if (text == null) { 1667 getLogger().warn("Translation not found for attribute " + 1668 name + " in element <" + element + ">"); 1669 return untranslated; 1670 } 1671 return text.toString(); 1672 } 1673 1674 private void endTextElement() throws SAXException { 1675 switch (prev_state) { 1676 case STATE_OUTSIDE: 1677 if (tr_text_recorder == null) { 1678 if (currentKey == null) { 1679 tr_text_recorder = getMessage(text_recorder.toString(), text_recorder); 1682 } else { 1683 if (getLogger().isDebugEnabled()) { 1685 getLogger().debug("Translation not found for key '" + currentKey + "'"); 1686 } 1687 1688 if (text_recorder.isEmpty() && untranslatedRecorder != null) { 1691 tr_text_recorder = untranslatedRecorder; 1692 } else { 1693 tr_text_recorder = text_recorder; 1694 } 1695 } 1696 } 1697 1698 if (tr_text_recorder != null) { 1699 tr_text_recorder.toSAX(this.contentHandler); 1700 } 1701 1702 text_recorder.recycle(); 1703 tr_text_recorder = null; 1704 currentKey = null; 1705 currentCatalogueId = null; 1706 break; 1707 1708 case STATE_INSIDE_TRANSLATE: 1709 if (tr_text_recorder == null) { 1710 if (!text_recorder.isEmpty()) { 1711 tr_text_recorder = getMessage(text_recorder.toString(), text_recorder); 1712 if (tr_text_recorder == text_recorder) { 1713 tr_text_recorder = new ParamSaxBuffer(text_recorder); 1715 } 1716 } 1717 } 1718 1719 text_recorder.recycle(); 1720 break; 1721 1722 case STATE_INSIDE_PARAM: 1723 if (!text_recorder.isEmpty()) { 1727 getMessage(text_recorder.toString(), text_recorder).toSAX(param_recorder); 1728 text_recorder.recycle(); 1729 } 1730 break; 1731 } 1732 1733 current_state = prev_state; 1734 prev_state = STATE_OUTSIDE; 1735 } 1736 1737 private void endParamElement() throws SAXException { 1739 String paramType = (String )formattingParams.get(I18N_TYPE_ATTRIBUTE); 1740 if (paramType != null) { 1741 1743 if (getLogger().isDebugEnabled()) { 1744 getLogger().debug("Param type: " + paramType); 1745 } 1746 if (formattingParams.get(I18N_VALUE_ATTRIBUTE) == null && param_value != null) { 1747 if (getLogger().isDebugEnabled()) { 1748 getLogger().debug("Put param value: " + param_value); 1749 } 1750 formattingParams.put(I18N_VALUE_ATTRIBUTE, param_value); 1751 } 1752 1753 if (dateTypes.contains(paramType)) { 1755 if (getLogger().isDebugEnabled()) { 1756 getLogger().debug("Formatting date_time param: " + formattingParams); 1757 } 1758 param_value = formatDate_Time(formattingParams); 1759 } else if (numberTypes.contains(paramType)) { 1760 if (getLogger().isDebugEnabled()) { 1761 getLogger().debug("Formatting number param: " + formattingParams); 1762 } 1763 param_value = formatNumber(formattingParams); 1764 } 1765 if (getLogger().isDebugEnabled()) { 1766 getLogger().debug("Added substitution param: " + param_value); 1767 } 1768 } 1769 1770 param_value = null; 1771 current_state = STATE_INSIDE_TRANSLATE; 1772 1773 if(param_recorder == null) { 1774 return; 1775 } 1776 1777 indexedParams.put(param_name, param_recorder); 1778 param_recorder = null; 1779 } 1780 1781 private void endTranslateElement() throws SAXException { 1782 if (tr_text_recorder != null) { 1783 if (getLogger().isDebugEnabled()) { 1784 getLogger().debug("End of translate with params. " + 1785 "Fragment for substitution : " + tr_text_recorder); 1786 } 1787 tr_text_recorder.toSAX(super.contentHandler, indexedParams); 1788 tr_text_recorder = null; 1789 text_recorder.recycle(); 1790 } 1791 1792 indexedParams.clear(); 1793 param_count = 0; 1794 current_state = STATE_OUTSIDE; 1795 } 1796 1797 private void endChooseElement() { 1798 current_state = STATE_OUTSIDE; 1799 } 1800 1801 private void endWhenElement() { 1802 current_state = prev_state; 1803 if (translate_copy) { 1804 translate_copy = false; 1805 translate_end = true; 1806 } 1807 } 1808 1809 private void endDate_TimeElement() throws SAXException { 1810 String result = formatDate_Time(formattingParams); 1811 switch(prev_state) { 1812 case STATE_OUTSIDE: 1813 super.contentHandler.characters(result.toCharArray(), 0, 1814 result.length()); 1815 break; 1816 case STATE_INSIDE_PARAM: 1817 param_recorder.characters(result.toCharArray(), 0, result.length()); 1818 break; 1819 case STATE_INSIDE_TEXT: 1820 text_recorder.characters(result.toCharArray(), 0, result.length()); 1821 break; 1822 } 1823 current_state = prev_state; 1824 } 1825 1826 private Locale getLocale(Map params, String attribute) { 1828 String lc = (String )params.get(attribute); 1830 return I18nUtils.parseLocale(lc, this.locale); 1831 } 1832 1833 private String formatDate_Time(Map params) throws SAXException { 1834 if (params == null) { 1836 throw new IllegalArgumentException ("Nothing to format"); 1837 } 1838 1839 SimpleDateFormat to_fmt; 1841 SimpleDateFormat from_fmt; 1842 1843 int srcStyle = DateFormat.DEFAULT; 1845 int style = DateFormat.DEFAULT; 1846 1847 boolean realPattern = false; 1849 boolean realSrcPattern = false; 1850 1851 Locale srcLoc = getLocale(params, I18N_SRC_LOCALE_ATTRIBUTE); 1853 Locale loc = getLocale(params, I18N_LOCALE_ATTRIBUTE); 1855 1856 String srcPattern = (String )params.get(I18N_SRC_PATTERN_ATTRIBUTE); 1858 String pattern = (String )params.get(I18N_PATTERN_ATTRIBUTE); 1860 String value = (String )params.get(I18N_VALUE_ATTRIBUTE); 1862 1863 if (srcPattern != null) { 1865 Integer patternValue = (Integer )datePatterns.get(srcPattern.toUpperCase()); 1867 if (patternValue != null) { 1868 srcStyle = patternValue.intValue(); 1869 } else { 1870 realSrcPattern = true; 1871 } 1872 } 1873 1874 if (pattern != null) { 1876 Integer patternValue = (Integer )datePatterns.get(pattern.toUpperCase()); 1877 if (patternValue != null) { 1878 style = patternValue.intValue(); 1879 } else { 1880 realPattern = true; 1881 } 1882 } 1883 1884 String paramType = (String )formattingParams.get(I18N_TYPE_ATTRIBUTE); 1886 1887 if (current_state == STATE_INSIDE_DATE || 1889 I18N_DATE_ELEMENT.equals(paramType)) { 1890 1891 to_fmt = (SimpleDateFormat )DateFormat.getDateInstance(style, loc); 1892 from_fmt = (SimpleDateFormat )DateFormat.getDateInstance( 1893 srcStyle, 1894 srcLoc 1895 ); 1896 } else if (current_state == STATE_INSIDE_DATE_TIME || 1897 I18N_DATE_TIME_ELEMENT.equals(paramType)) { 1898 to_fmt = (SimpleDateFormat )DateFormat.getDateTimeInstance( 1899 style, 1900 style, 1901 loc 1902 ); 1903 from_fmt = (SimpleDateFormat )DateFormat.getDateTimeInstance( 1904 srcStyle, 1905 srcStyle, 1906 srcLoc 1907 ); 1908 } else { 1909 to_fmt = (SimpleDateFormat )DateFormat.getTimeInstance(style, loc); 1911 from_fmt = (SimpleDateFormat )DateFormat.getTimeInstance( 1912 srcStyle, 1913 srcLoc 1914 ); 1915 } 1916 1917 Date dateValue; 1919 1920 if (realSrcPattern) { 1922 from_fmt.applyPattern(srcPattern); 1923 } 1924 1925 if (realPattern) { 1926 to_fmt.applyPattern(pattern); 1927 } 1928 1929 if (value == null) { 1931 dateValue = new Date (); 1932 } else { 1933 try { 1934 dateValue = from_fmt.parse(value); 1935 } catch (ParseException pe) { 1936 throw new SAXException ( 1937 this.getClass().getName() 1938 + "i18n:date - parsing error.", pe 1939 ); 1940 } 1941 } 1942 1943 if (getLogger().isDebugEnabled()) { 1945 getLogger().debug("### Formatting date: " + dateValue + " with localized pattern " + 1946 to_fmt.toLocalizedPattern() + " for locale: " + locale); 1947 } 1948 return to_fmt.format(dateValue); 1949 } 1950 1951 private void endNumberElement() throws SAXException { 1952 String result = formatNumber(formattingParams); 1953 switch(prev_state) { 1954 case STATE_OUTSIDE: 1955 super.contentHandler.characters(result.toCharArray(), 0, result.length()); 1956 break; 1957 case STATE_INSIDE_PARAM: 1958 param_recorder.characters(result.toCharArray(), 0, result.length()); 1959 break; 1960 case STATE_INSIDE_TEXT: 1961 text_recorder.characters(result.toCharArray(), 0, result.length()); 1962 break; 1963 } 1964 current_state = prev_state; 1965 } 1966 1967 private String formatNumber(Map params) throws SAXException { 1968 if (params == null) { 1969 throw new SAXException ( 1970 this.getClass().getName() 1971 + ": i18n:number - error in element attributes." 1972 ); 1973 } 1974 1975 String srcPattern = (String )params.get(I18N_SRC_PATTERN_ATTRIBUTE); 1977 String pattern = (String )params.get(I18N_PATTERN_ATTRIBUTE); 1979 String value = (String )params.get(I18N_VALUE_ATTRIBUTE); 1981 1982 if (value == null) return ""; 1983 String type = (String )params.get(I18N_TYPE_ATTRIBUTE); 1985 1986 int fractionDigits = -1; 1988 try { 1989 String fd = (String )params.get(I18N_FRACTION_DIGITS_ATTRIBUTE); 1990 if (fd != null) 1991 fractionDigits = Integer.parseInt(fd); 1992 } catch (NumberFormatException nfe) { 1993 getLogger().warn("Error in number format with fraction-digits", nfe); 1994 } 1995 1996 Number numberValue; 1998 1999 Locale loc = getLocale(params, I18N_LOCALE_ATTRIBUTE); 2001 Locale srcLoc = getLocale(params, I18N_SRC_LOCALE_ATTRIBUTE); 2002 Locale currencyLoc = getLocale(params, CURRENCY_LOCALE_ATTRIBUTE); 2004 Locale dgLoc = null; 2006 if (currencyLoc != null) { 2007 dgLoc = loc; 2011 loc = currencyLoc; 2012 } 2013 2014 DecimalFormat from_fmt = (DecimalFormat )NumberFormat.getInstance(srcLoc); 2016 int int_currency = 0; 2017 2018 if (srcPattern != null) { 2020 from_fmt.applyPattern(srcPattern); 2021 } 2022 2023 DecimalFormat to_fmt; 2025 char dec = from_fmt.getDecimalFormatSymbols().getDecimalSeparator(); 2026 int decAt = 0; 2027 boolean appendDec = false; 2028 2029 if (type == null || type.equals( I18N_NUMBER_ELEMENT )) { 2030 to_fmt = (DecimalFormat )NumberFormat.getInstance(loc); 2031 to_fmt.setMaximumFractionDigits(309); 2032 for (int i = value.length() - 1; 2033 i >= 0 && value.charAt(i) != dec; i--, decAt++) { 2034 } 2035 2036 if (decAt < value.length())to_fmt.setMinimumFractionDigits(decAt); 2037 decAt = 0; 2038 for (int i = 0; i < value.length() && value.charAt(i) != dec; i++) { 2039 if (Character.isDigit(value.charAt(i))) { 2040 decAt++; 2041 } 2042 } 2043 2044 to_fmt.setMinimumIntegerDigits(decAt); 2045 if (value.charAt(value.length() - 1) == dec) { 2046 appendDec = true; 2047 } 2048 } else if (type.equals( I18N_CURRENCY_ELEMENT )) { 2049 to_fmt = (DecimalFormat )NumberFormat.getCurrencyInstance(loc); 2050 } else if (type.equals( I18N_INT_CURRENCY_ELEMENT )) { 2051 to_fmt = (DecimalFormat )NumberFormat.getCurrencyInstance(loc); 2052 int_currency = 1; 2053 for (int i = 0; i < to_fmt.getMaximumFractionDigits(); i++) { 2054 int_currency *= 10; 2055 } 2056 } else if ( type.equals( I18N_CURRENCY_NO_UNIT_ELEMENT ) ) { 2057 DecimalFormat tmp = (DecimalFormat ) NumberFormat.getCurrencyInstance( loc ); 2058 to_fmt = (DecimalFormat ) NumberFormat.getInstance( loc ); 2059 to_fmt.setMinimumFractionDigits(tmp.getMinimumFractionDigits()); 2060 to_fmt.setMaximumFractionDigits(tmp.getMaximumFractionDigits()); 2061 } else if ( type.equals( I18N_INT_CURRENCY_NO_UNIT_ELEMENT ) ) { 2062 DecimalFormat tmp = (DecimalFormat ) NumberFormat.getCurrencyInstance( loc ); 2063 int_currency = 1; 2064 for ( int i = 0; i < tmp.getMaximumFractionDigits(); i++ ) 2065 int_currency *= 10; 2066 to_fmt = (DecimalFormat ) NumberFormat.getInstance( loc ); 2067 to_fmt.setMinimumFractionDigits(tmp.getMinimumFractionDigits()); 2068 to_fmt.setMaximumFractionDigits(tmp.getMaximumFractionDigits()); 2069 } else if (type.equals( I18N_PERCENT_ELEMENT )) { 2070 to_fmt = (DecimalFormat )NumberFormat.getPercentInstance(loc); 2071 } else { 2072 throw new SAXException ("<i18n:number>: unknown type: " + type); 2073 } 2074 2075 if(fractionDigits > -1) { 2076 to_fmt.setMinimumFractionDigits(fractionDigits); 2077 to_fmt.setMaximumFractionDigits(fractionDigits); 2078 } 2079 2080 if(dgLoc != null) { 2081 DecimalFormat df = (DecimalFormat )NumberFormat.getCurrencyInstance(dgLoc); 2082 DecimalFormatSymbols dfsNew = df.getDecimalFormatSymbols(); 2083 DecimalFormatSymbols dfsOrig = to_fmt.getDecimalFormatSymbols(); 2084 dfsOrig.setDecimalSeparator(dfsNew.getDecimalSeparator()); 2085 dfsOrig.setMonetaryDecimalSeparator(dfsNew.getMonetaryDecimalSeparator()); 2086 dfsOrig.setGroupingSeparator(dfsNew.getGroupingSeparator()); 2087 to_fmt.setDecimalFormatSymbols(dfsOrig); 2088 } 2089 2090 if (pattern != null) { 2092 to_fmt.applyPattern(pattern); 2093 } 2094 2095 if (value == null) { 2096 numberValue = new Long (0); 2097 } else { 2098 try { 2099 numberValue = from_fmt.parse(value); 2100 if (int_currency > 0) { 2101 numberValue = new Double (numberValue.doubleValue() / 2102 int_currency); 2103 } else { 2104 } 2106 } catch (ParseException pe) { 2107 throw new SAXException ( 2108 this.getClass().getName() 2109 + "i18n:number - parsing error.", pe 2110 ); 2111 } 2112 } 2113 2114 String result = to_fmt.format(numberValue); 2116 if (appendDec) result = result + dec; 2117 if (getLogger().isDebugEnabled()) { 2118 getLogger().debug("i18n:number result: " + result); 2119 } 2120 return result; 2121 } 2122 2123 2125 2131 private ParamSaxBuffer getMessage(String catalogueID, String key) { 2132 if (getLogger().isDebugEnabled()) { 2133 getLogger().debug("Getting key " + key + " from catalogue " + catalogueID); 2134 } 2135 2136 CatalogueInfo catalogue = this.catalogue; 2137 if (catalogueID != null) { 2138 catalogue = (CatalogueInfo)catalogues.get(catalogueID); 2139 if (catalogue == null) { 2140 if (getLogger().isWarnEnabled()) { 2141 getLogger().warn("Catalogue not found: " + catalogueID + 2142 ", will not translate key " + key); 2143 } 2144 return null; 2145 } 2146 } 2147 2148 Bundle bundle = catalogue.getCatalogue(); 2149 if (bundle == null) { 2150 getLogger().debug("Untranslated key: '" + key + "'"); 2152 return null; 2153 } 2154 2155 try { 2156 return (ParamSaxBuffer) bundle.getObject(key); 2157 } catch (MissingResourceException e) { 2158 getLogger().debug("Untranslated key: '" + key + "'"); 2159 } 2160 2161 return null; 2162 } 2163 2164 2170 private ParamSaxBuffer getMessage(String key, ParamSaxBuffer defaultValue) { 2171 SaxBuffer value = getMessage(currentCatalogueId, key); 2172 if (value == null) { 2173 return defaultValue; 2174 } 2175 2176 return new ParamSaxBuffer(value); 2177 } 2178 2179 public void recycle() { 2180 this.untranslatedRecorder = null; 2181 this.catalogue = null; 2182 this.objectModel = null; 2183 2184 Iterator i = catalogues.values().iterator(); 2186 while (i.hasNext()) { 2187 CatalogueInfo catalogueInfo = (CatalogueInfo) i.next(); 2188 catalogueInfo.releaseCatalog(); 2189 } 2190 2191 super.recycle(); 2192 } 2193 2194 public void dispose() { 2195 if (manager != null) { 2196 manager.release(factory); 2197 } 2198 factory = null; 2199 manager = null; 2200 catalogues = null; 2201 } 2202 2203 2204 2209 private final class CatalogueInfo { 2210 VariableResolver name; 2211 VariableResolver[] locations; 2212 String resolvedName; 2213 String [] resolvedLocations; 2214 Bundle catalogue; 2215 2216 public CatalogueInfo(String name, String [] locations) throws PatternException { 2217 this.name = VariableResolverFactory.getResolver(name, manager); 2218 this.locations = new VariableResolver[locations.length]; 2219 for (int i=0; i < locations.length; ++i) { 2220 this.locations[i] = VariableResolverFactory.getResolver(locations[i], manager); 2221 } 2222 } 2223 2224 public String getName() { 2225 try { 2226 if (resolvedName == null) { 2227 resolve(); 2228 } 2229 } catch (Exception e) { 2230 } 2232 return resolvedName; 2233 } 2234 2235 public String [] getLocation() { 2236 try { 2237 if (resolvedName == null) { 2238 resolve(); 2239 } 2240 } catch (Exception e) { 2241 } 2243 return resolvedLocations; 2244 } 2245 2246 private void resolve() throws Exception { 2247 if (resolvedLocations == null) { 2248 resolvedLocations = new String [locations.length]; 2249 for (int i=0; i < resolvedLocations.length; ++i) { 2250 resolvedLocations[i] = locations[i].resolve(null, objectModel); 2251 } 2252 } 2253 if (resolvedName == null) { 2254 resolvedName = name.resolve(null, objectModel); 2255 } 2256 } 2257 2258 public Bundle getCatalogue() { 2259 if (catalogue == null) { 2260 try { 2261 resolve(); 2262 catalogue = factory.select(resolvedLocations, resolvedName, locale); 2263 } catch (Exception e) { 2264 getLogger().error("Error obtaining catalogue '" + getName() + 2265 "' from <" + getLocation() + "> for locale " + 2266 locale, e); 2267 } 2268 } 2269 2270 return catalogue; 2271 } 2272 2273 public void releaseCatalog() { 2274 if (catalogue != null) { 2275 factory.release(catalogue); 2276 } 2277 catalogue = null; 2278 resolvedName = null; 2279 resolvedLocations = null; 2280 } 2281 } 2282} 2283 | Popular Tags |