001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2005-2010 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.i18n; 011 012import mondrian.olap.MondrianProperties; 013import mondrian.olap.Util; 014import mondrian.spi.DynamicSchemaProcessor; 015import mondrian.spi.impl.FilterDynamicSchemaProcessor; 016 017import org.apache.log4j.Logger; 018 019import java.io.InputStream; 020import java.util.MissingResourceException; 021import java.util.ResourceBundle; 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025/** 026 * Schema processor which helps localize data and metadata. 027 * 028 * @author arosselet 029 * @since August 26, 2005 030 */ 031public class LocalizingDynamicSchemaProcessor 032 extends FilterDynamicSchemaProcessor 033 implements DynamicSchemaProcessor 034{ 035 private static final Logger LOGGER = 036 Logger.getLogger(LocalizingDynamicSchemaProcessor.class); 037 038 /** Creates a new instance of LocalizingDynamicSchemaProcessor */ 039 public LocalizingDynamicSchemaProcessor() { 040 } 041 042 private ResourceBundle bundle; 043 044 /** 045 * Regular expression for variables. 046 */ 047 private static final Pattern pattern = Pattern.compile("(%\\{.*?\\})"); 048 049 /** 050 * Populates the bundle with the given resource. 051 * 052 * <p>The name of the property file is typically the name of a class, as 053 * per {@link ResourceBundle#getBundle(String)}. However, for backwards 054 * compatibility, the name can contain slashes (which are converted to 055 * dots) and end with ".properties" (which is removed). Therefore 056 * "com/acme/MyResource.properties" is equivalent to 057 * "com.acme.MyResource". 058 * 059 * @see MondrianProperties#LocalePropFile 060 * 061 * @param propFile The name of the property file 062 */ 063 void populate(String propFile) { 064 if (propFile.endsWith(".properties")) { 065 propFile = 066 propFile.substring( 067 0, 068 propFile.length() - ".properties".length()); 069 } 070 try { 071 bundle = ResourceBundle.getBundle( 072 propFile, 073 Util.parseLocale(locale), 074 getClass().getClassLoader()); 075 } catch (Exception e) { 076 LOGGER.warn( 077 "Mondrian: Warning: no suitable locale file found for locale '" 078 + locale 079 + "'", 080 e); 081 } 082 } 083 084 private void loadProperties() { 085 String propFile = MondrianProperties.instance().LocalePropFile.get(); 086 if (propFile != null) { 087 populate(propFile); 088 } 089 } 090 091 public String filter( 092 String schemaUrl, 093 Util.PropertyList connectInfo, 094 InputStream stream) throws Exception 095 { 096 setLocale(connectInfo.get("Locale")); 097 098 loadProperties(); 099 100 String schema = super.filter(schemaUrl, connectInfo, stream); 101 if (bundle != null) { 102 schema = doRegExReplacements(schema); 103 } 104 LOGGER.debug(schema); 105 return schema; 106 } 107 108 private String doRegExReplacements(String schema) { 109 // As of JDK 1.5, cannot use StringBuilder - appendReplacement requires 110 // the antediluvian StringBuffer. 111 StringBuffer intlSchema = new StringBuffer(); 112 Matcher match = pattern.matcher(schema); 113 String key; 114 while (match.find()) { 115 key = extractKey(match.group()); 116 int start = match.start(); 117 int end = match.end(); 118 119 try { 120 String intlProperty = bundle.getString(key); 121 if (intlProperty != null) { 122 match.appendReplacement(intlSchema, intlProperty); 123 } 124 } catch (MissingResourceException e) { 125 LOGGER.error("Missing resource for key [" + key + "]", e); 126 } catch (NullPointerException e) { 127 LOGGER.error( 128 "missing resource key at substring(" + start + "," + end 129 + ")", 130 e); 131 } 132 } 133 match.appendTail(intlSchema); 134 return intlSchema.toString(); 135 } 136 137 private String extractKey(String group) { 138 // removes leading '%{' and tailing '%' from the matched string 139 // to obtain the required key 140 return group.substring(2, group.length() - 1); 141 } 142 143 /** 144 * Property locale. 145 */ 146 private String locale; 147 148 /** 149 * Returns the property locale. 150 * 151 * @return Value of property locale. 152 */ 153 public String getLocale() { 154 return this.locale; 155 } 156 157 /** 158 * Sets the property locale. 159 * 160 * @param locale New value of property locale. 161 */ 162 public void setLocale(String locale) { 163 this.locale = locale; 164 } 165} 166 167// End LocalizingDynamicSchemaProcessor.java