Having the code
// umbraco.cms.businesslogic.language.Language
static string GetUmbracoDictionaryValueOrNull(string key, Language lang)
{
try
{
if (Dictionary.DictionaryItem.hasKey(key))
return new Dictionary.DictionaryItem(key).Value(lang.id);
else
return null;
}
catch
{
return null;
}
}
Is it possible, having a key, say "ui.mySelect" to obtain all available/existing subkeys, say "ui.mySelect.optionOne", "ui.mySelect.optionTwo",... , "ui.mySelect.optionLast".
It should be something like this:
public static List<string> GetDictionarySubValues(string key)
{
var dic = new Dictionary.DictionaryItem(key);
if (dic == null || !dic.hasChildren) return new List<string>();
return dic.Children
.Select(c => c.Value())
.ToList();
}
Related
I am trying to check if a booking record exists, then show its details. Otherwise return to Bookparking page but else part isn't working and shows Object reference not set to an instance of an object because there is no such field with the Session[""]
Controller:
public ActionResult Viewparking()
{
if (IsUserLoggedIn(Session, Request) == false)
{
return RedirectToAction("login");
}
else
{
String id = Session["username"].ToString();
ViewBag.userid = id;
var checkbooking = db.tb_booking.Where(s => s.username == id).FirstOrDefault();
if (checkbooking != null)
{
var show = db.tb_booking.Where(e => e.username == id).FirstOrDefault();
}
else
{ //ViewBag.f = "You have no booking yet!!";
return RedirectToAction("Bookparking", "user");
}
return View();
}
}
As Gabriel noted, you have not null checked the value from the session. Code would be something like this:
public ActionResult Viewparking()
{
if (IsUserLoggedIn(Session, Request) == false)
{
return RedirectToAction("login");
}
else
{
String id = Session["username"]?.ToString();
if (id != null)
{
ViewBag.userid = id;
var checkbooking = db.tb_booking.FirstOrDefault(s => s.username == id);
if (checkbooking != null)
{ // TODO: checkbooking is currently unused, except to check if you can fetch it.
return View();
}
}
// If you reach this code, then either id is null, or the booking was not found
return RedirectToAction("Bookparking", "user");
}
}
I got an MVC 5 application that i'm porting to asp.net Core.
In the MVC application call to controller we're made using AngularJS $resource (sending JSON) and we we're POSTing data doing :
ressource.save({ entries: vm.entries, projectId: vm.project.id }).$promise...
that will send a JSON body like:
{
entries:
[
{
// lots of fields
}
],
projectId:12
}
the MVC controller looked like this :
[HttpPost]
public JsonResult Save(List<EntryViewModel> entries, int projectId) {
// code here
}
How can I replicate the same behaviour with .NET Core since we can't have multiple [FromBody]
you cannot have multiple parameter with the FromBody attibute in an action method. If that is need, use a complex type such as a class with properties equivalent to the parameter or dynamic type like that
[HttpPost("save/{projectId}")]
public JsonResult Save(int projectId, [FromBody] dynamic entries) {
// code here
}
As pointed out in the comment, one possible solution is to unify the properties you're posting onto a single model class.
Something like the following should do the trick:
public class SaveModel
{
public List<EntryViewModel> Entries{get;set;}
public int ProjectId {get;set;}
}
Don't forget to decorate the model with the [FromBody] attribute:
[HttpPost]
public JsonResult Save([FromBody]SaveViewModel model)
{
// code here
}
Hope this helps!
It's still rough but I made a Filter to mimic the feature.
public class OldMVCFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Method != "GET")
{
var body = context.HttpContext.Request.Body;
JToken token = null;
var param = context.ActionDescriptor.Parameters;
using (var reader = new StreamReader(body))
using (var jsonReader = new JsonTextReader(reader))
{
jsonReader.CloseInput = false;
token = JToken.Load(jsonReader);
}
if (token != null)
{
var serializer = new JsonSerializer();
serializer.DefaultValueHandling = DefaultValueHandling.Populate;
serializer.FloatFormatHandling = FloatFormatHandling.DefaultValue;
foreach (var item in param)
{
JToken model = token[item.Name];
if (model == null)
{
// try to cast the full body as the current object
model = token.Root;
}
if (model != null)
{
model = this.RemoveEmptyChildren(model, item.ParameterType);
var res = model.ToObject(item.ParameterType, serializer);
context.ActionArguments[item.Name] = res;
}
}
}
}
}
private JToken RemoveEmptyChildren(JToken token, Type type)
{
var HasBaseType = type.GenericTypeArguments.Count() > 0;
List<PropertyInfo> PIList = new List<PropertyInfo>();
if (HasBaseType)
{
PIList.AddRange(type.GenericTypeArguments.FirstOrDefault().GetProperties().ToList());
}
else
{
PIList.AddRange(type.GetTypeInfo().GetProperties().ToList());
}
if (token != null)
{
if (token.Type == JTokenType.Object)
{
JObject copy = new JObject();
foreach (JProperty jProp in token.Children<JProperty>())
{
var pi = PIList.FirstOrDefault(p => p.Name == jProp.Name);
if (pi != null) // If destination type dont have this property we ignore it
{
JToken child = jProp.Value;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, pi.PropertyType);
}
if (!IsEmpty(child))
{
if (child.Type == JTokenType.Object || child.Type == JTokenType.Array)
{
// nested value has been checked, we add the object
copy.Add(jProp.Name, child);
}
else
{
if (!pi.Name.ToLowerInvariant().Contains("string"))
{
// ignore empty value when type is not string
var Val = (string)child;
if (!string.IsNullOrWhiteSpace(Val))
{
// we add the property only if it contain meningfull data
copy.Add(jProp.Name, child);
}
}
}
}
}
}
return copy;
}
else if (token.Type == JTokenType.Array)
{
JArray copy = new JArray();
foreach (JToken item in token.Children())
{
JToken child = item;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, type);
}
if (!IsEmpty(child))
{
copy.Add(child);
}
}
return copy;
}
return token;
}
return null;
}
private bool IsEmpty(JToken token)
{
return (token.Type == JTokenType.Null || token.Type == JTokenType.Undefined);
}
}
I got this simple model class:
public class PrvProduct
{
[Key]
public Int32 ProductId
{
get; set;
}
public Int64 ProductLineId;
public String MfgPartNumber;
public String ProductName;
public String ProductDescription;
}
I'm trying to call a stored proc, using .net core, it works fine, returns a list of PrvProduct objects. problem is: their fields are empty, unless I fill them up myself in code. ProductId is always there, not sure why (maybe because i typed there the [key] attribute?) but the rest are not.
is there a simple way to map class fields to results sets, like in ado.net (i would just do SQLDataAdapter.Fill(MyDataTable) and the MyDataTable fields will have the values by field name)... or do i have to do option 2 below every time?
Many thanks!
string sqlQuery = "EXEC Maint.GetProductList '" + sNameFilter + "'";
//option 1: this gets no value in the fields of each PrvProduct (ProductId gets value maybe because its [key], the others don't)
IQueryable results = _context.Products.FromSql(sqlQuery).AsNoTracking();
//option 2: this works, but... do i have to do this for every stored proc i call, every field, or is there a beter way to map class fields to returned results fields?
List<PrvProduct> oList = new List<PrvProduct>();
using (var command = _context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = sqlQuery;
command.CommandType = CommandType.Text;
_context.Database.OpenConnection();
using (var result = command.ExecuteReader())
{
while (result.Read())
{
// Map to your entity
oList.Add(new PrvProduct
{
ProductId = result.GetInt32(0),
ProductName = result.GetString(1)
});
}
}
}
In EF Core, if you execute a stored procedure using one of your DbSet entities then it will map it automatically. The problem is that in many case you need to map a stored procedure to a DTO, for example, and the DTO is not part of your DbSet entities. In those cases you need to go back in time and map it manually which is a waste of time.
In order to avoid mapping the data reader manually, I added a bunch of extension methods that do it for you. The code is not perfect and I'm still improving it but it's good enough in most of the cases.
Once you add the extensions methods I'm gonna describe below, you can use it like this:
return dbContext.Database.SqlQuery<SalesReportDTO>("spGetSalesReport",
SqlParameterBuilder.Build("customerId", customerId),
SqlParameterBuilder.Build("dateFrom", from),
SqlParameterBuilder.Build("dateTo", to)).ToList();
DatabaseFacadeExtensions: adds extensions methods to DatabaseFacade class, allowing you to call the method SqlQuery from dbContext.Database just like we used to do with Entity Framework 6.
public static class DatabaseFacadeExtensions
{
public static List<T> SqlQuery<T>(this DatabaseFacade database, string query, params SqlParameter[] parameters)
{
return SqlQuery<T>(database, query, null, CommandType.StoredProcedure, parameters);
}
public static List<T> SqlQuery<T>(this DatabaseFacade database, string query, CommandType commandType, params SqlParameter[] parameters)
{
return SqlQuery<T>(database, query, null, commandType, parameters);
}
public static List<T> SqlQuery<T>(this DatabaseFacade database, string query, int? commandTimeout, params SqlParameter[] parameters)
{
return SqlQuery<T>(database, query, commandTimeout, CommandType.StoredProcedure, parameters);
}
public static List<T> SqlQuery<T>(this DatabaseFacade database, string query, int? commandTimeout, CommandType commandType, params SqlParameter[] parameters)
{
using (var cmd = database.GetDbConnection().CreateCommand())
{
cmd.CommandText = query;
cmd.CommandType = commandType;
if (commandTimeout.HasValue)
{
cmd.CommandTimeout = commandTimeout.Value;
}
cmd.Parameters.AddRange(parameters);
if (cmd.Connection.State == System.Data.ConnectionState.Closed)
{
cmd.Connection.Open();
}
try
{
using (var reader = cmd.ExecuteReader())
{
return reader.MapToList<T>();
}
}
finally
{
cmd.Connection.Close();
}
}
}
}
DbDataReaderExtensions: adds extensions methods to DbDataReader class so it can map the data reader to your own clases.
public static class DbDataReaderExtensions
{
public static List<T> MapToList<T>(this DbDataReader dr)
{
var objList = new List<T>();
if (dr.HasRows)
{
bool isSingleValue = typeof(T).IsPrimitive || typeof(T) == typeof(string);
IEnumerable<PropertyInfo> props = null;
Dictionary<string, DbColumn> colMapping = null;
if (!isSingleValue)
{
props = typeof(T).GetRuntimeProperties();
colMapping = dr.GetColumnSchema()
.Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
.ToDictionary(key => key.ColumnName.ToLower());
}
while (dr.Read())
{
T obj;
if (isSingleValue)
{
obj = (T)dr.GetValue(0);
}
else
{
obj = Activator.CreateInstance<T>();
foreach (var prop in props)
{
string propertyName = prop.Name.ToLower();
if (!colMapping.ContainsKey(propertyName))
{
continue;
}
var val = dr.GetValue(colMapping[propertyName].ColumnOrdinal.Value);
if (val != DBNull.Value)
{
// enum property
if (prop.PropertyType.IsEnum)
{
prop.SetValue(obj, Enum.ToObject(prop.PropertyType, val));
}
// nullable enum property
if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) && Nullable.GetUnderlyingType(prop.PropertyType).IsEnum)
{
prop.SetValue(obj, Enum.ToObject(Nullable.GetUnderlyingType(prop.PropertyType), val));
}
else
{
prop.SetValue(obj, val);
}
}
}
}
objList.Add(obj);
}
}
return objList;
}
public static T MapToObject<T>(this DbDataReader dr)
{
var props = typeof(T).GetRuntimeProperties();
if (dr.HasRows)
{
var colMapping = dr.GetColumnSchema()
.Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
.ToDictionary(key => key.ColumnName.ToLower());
if (dr.Read())
{
T obj = Activator.CreateInstance<T>();
foreach (var prop in props)
{
var val = dr.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
prop.SetValue(obj, val == DBNull.Value ? null : val);
}
return obj;
}
}
return default(T);
}
}
The next class is optional but I use to build parameters in a simpler way and it's needed in the example I described above:
public class SqlParameterBuilder
{
public static SqlParameter Build(string name, bool? value)
{
if (value.HasValue)
{
return new SqlParameter() { ParameterName = name, Value = value.Value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, int? value)
{
if (value.HasValue)
{
return new SqlParameter() { ParameterName = name, Value = value.Value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, string value)
{
if (value != null)
{
return new SqlParameter() { ParameterName = name, Value = value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, DateTime? value)
{
if (value != null)
{
return new SqlParameter { ParameterName = name, SqlDbType = SqlDbType.DateTime, Value = value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, Guid? value)
{
if (value.HasValue)
{
return new SqlParameter { ParameterName = name, SqlDbType = SqlDbType.UniqueIdentifier, Value = value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, int[] values)
{
SqlParameter par = new SqlParameter(name, SqlDbType.Structured);
par.TypeName = "dbo.IntParameterList";
DataTable dt = new DataTable();
dt.Columns.Add("id", typeof(int));
par.Value = dt;
if (values != null)
{
foreach (int value in values.Where(p => p != 0))
{
dt.Rows.Add(value);
}
}
return par;
}
public static SqlParameter Build(string name, string[] values, VarcharParameterListEnum varcharParameterListType = VarcharParameterListEnum.Varchar50)
{
SqlParameter par = new SqlParameter(name, SqlDbType.Structured);
switch(varcharParameterListType)
{
case VarcharParameterListEnum.Varchar15:
par.TypeName = "dbo.Varchar15ParameterList";
break;
case VarcharParameterListEnum.Varchar50:
par.TypeName = "dbo.Varchar50ParameterList";
break;
case VarcharParameterListEnum.Varchar100:
par.TypeName = "dbo.Varchar100ParameterList";
break;
case VarcharParameterListEnum.Varchar255:
par.TypeName = "dbo.Varchar255ParameterList";
break;
case VarcharParameterListEnum.Varchar510:
par.TypeName = "dbo.Varchar510ParameterList";
break;
}
DataTable dt = new DataTable();
dt.Columns.Add("textValue", typeof(string));
par.Value = dt;
if (values != null)
{
foreach (var value in values.Where(p => !string.IsNullOrWhiteSpace(p)))
{
dt.Rows.Add(value);
}
}
return par;
}
}
I have two methods for a database entity which gets the translation for selected language if it has one, or the default language translation.
public string GetName(int? LanguageId, int? DefaultLanguageId)
{
string retval = "";
var textInSelectedLanguage = this.CategoryTexts.Where(w => w.LanguageId == LanguageId).SingleOrDefault();
if (textInSelectedLanguage == null)
{
retval = this.CategoryTexts.Where(w => w.LanguageId == DefaultLanguageId).SingleOrDefault().Name;
}
else
{
retval = textInSelectedLanguage.Name;
}
return retval;
}
public string GetDescription(int? LanguageId, int? DefaultLanguageId)
{
string retval = "";
var textInSelectedLanguage = this.CategoryTexts.Where(w => w.LanguageId == LanguageId).SingleOrDefault();
if (textInSelectedLanguage == null)
{
retval = this.CategoryTexts.Where(w => w.LanguageId == DefaultLanguageId).SingleOrDefault().Description;
}
else
{
retval = textInSelectedLanguage.Description;
}
return retval;
}
As you can see they are very similar. If I have more properties to translate, this won't be a good implementation. The behavior is similar for the other translations.
How can reduce this code to one method?
I tried to use reflection but I didn't had any results.
Later...
I reduced my code to one method which return me the an entity instance with all the properties in the selected language or default language:
public CategoryText GetTranslation(int? DesiredLanguageId, int? DefaultLanguageId)
{
CategoryText retval = null;
var textInSelectedLanguage = this.CategoryTexts.Where(w => w.LanguageId == DesiredLanguageId).SingleOrDefault();
if (textInSelectedLanguage == null)
{
retval = this.CategoryTexts.Where(w => w.LanguageId == DefaultLanguageId).SingleOrDefault();
}
else
{
retval = textInSelectedLanguage;
}
return retval;
}
I think this method can be easily made generic by trying to find a way to replace my CategoryTexts Dbset with any other DbSet database entity. How can I do this?
Assuming you have an appropriate base class you could write it:-
public T GetTranslation<T>(DbSet<T> set, int? DesiredLanguageId, int? DefaultLanguageId)
where T:SomeBaseClassThatHasPropertyLanguageId
{
return
set.SingleOrDefault(w => w.LanguageId == DesiredLanguageId) ??
set.SingleOrDefault(w => w.LanguageId == DefaultLanguageId);
}
I somehow solved this.
I have an Internationalization class which with the following method:
public static T GetDbEntityTranslation<T>(ITranslatable Entity)
{
return (T)Entity.GetTranslation<T>(GetDefaultLanguage().Id, GetChosenLanguage().Id);
}
The ITranslatable interface:
public interface ITranslatable
{
T GetTranslation<T>(int? DesiredLanguageId, int? DefaultLanguageId);
}
My category class:
public partial class Category : ITranslatable
{
private LSEntities db = new LSEntities();
public T GetTranslation<T>(int? DesiredLanguageId, int? DefaultLanguageId)
{
CategoryText retval = null;
retval = db.CategoryTexts.Where(w => w.LanguageId == DesiredLanguageId && w.CategoryId == this.Id).SingleOrDefault()
?? this.CategoryTexts.Where(w => w.LanguageId == DefaultLanguageId && w.CategoryId == this.Id).SingleOrDefault()
?? this.CategoryTexts.Where(w => w.CategoryId == this.Id).FirstOrDefault();
if (retval == null)
throw new Exception("No translation found for this object");
return (T)(object)retval;
}
}
The problem was that I need to get my translation based on my category ID which is stored in Category class. My translations are searched in CategoryTexts property.
To internationalize a composite component, you have to put a .properties file that has the exact same name than the component itself and in the same folder.
From the xhtml, you can access these translations through ${cc.resourceBundleMap.key}.
Until now, all is fine and works for me. Where the problems starts is when I add more .properties files for other languages. No matter which local is my computer in, the picked language is the default one (component.properties).
This seems to be a recurrent problem since Ziletka also reports the same problem in How to localize JSF 2 composite components, but remained unanswered.
I have tried all sort of possibilities:
no default .properties file
component_fr.properties
component_fr_CA.properties
component_fr_FR.properties
component_en.properties
component_en_CA.properties
component_en_US.properties
but it results in a:
javax.el.ELException: [...] /resources/component/component.xhtml default="${cc.resourceBundleMap.key}": java.lang.NullPointerException
with default .properties file plus Language specification
component.properties
component_fr.properties
component_en.properties
only the default is loaded.
with default .properties file plus Language and Country specifications
component.properties
component_fr_CA.properties
component_fr_FR.properties
component_en_CA.properties
component_en_US.properties
And again: only the default is loaded.
I would love to avoid having to rely on the backing bean to provide the translations and can't resolve into believing that it is not supported. Can anyone help?
This feature was implemented long time ago in MyFaces Core. See: MYFACES-3308. The test case done can be found here
The locale applied to the composite component depends on the value retrieved from UIViewRoot.getLocale().
Apparently the problem is still there and its root is in the javax.faces.component.UIComponent class particularly in the findComponentResourceBundleLocaleMatch method. The snipped is below
private Resource findComponentResourceBundleLocaleMatch(FacesContext context,
String resourceName, String libraryName) {
Resource result = null;
ResourceBundle resourceBundle = null;
int i;
if (-1 != (i = resourceName.lastIndexOf("."))) {
resourceName = resourceName.substring(0, i) +
".properties"; //THE PROBLEM IS HERE
if (null != context) {
result = context.getApplication().getResourceHandler().
createResource(resourceName, libraryName);
InputStream propertiesInputStream = null;
try {
propertiesInputStream = result.getInputStream();
resourceBundle = new PropertyResourceBundle(propertiesInputStream);
} catch (IOException ex) {
Logger.getLogger(UIComponent.class.getName()).log(Level.SEVERE, null, ex);
} finally{
if(null != propertiesInputStream){
try{
propertiesInputStream.close();
} catch(IOException ioe){
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, null, ioe);
}
}
}
}
}
}
result = (null != resourceBundle) ? result : null;
return result;
}
You can see it at the line with a comment that states 'THE PROBLEM IS HERE'. Precisely when it looks for a properties file to load it does not respect any language and/or country code. It always loads a default resource.
Possible solution
The 'problematic' method is called from another method getResourceBundleMap of the same class and a part of the code you are interested in is marked with a comment (line #1000)
// Step 2: if this is a composite component, look for a
// ResourceBundle as a Resource
Which is no surprise as you need a composite component. So the solution would be to define a backing component class for your composite component and redefine resourceBundleMap loading. Below you may find the implementation that respects language only which means it would work for files like componentName_en.properties and componentName_de.properties but would not for something like componentName_en_US.properties
Your .properties files should be in the same directory as the definition of your component
testComponent.properties
testComponent_de.properties
testComponent_en.properties
testComponent_fr.properties
in your component testComponent.xhtmlspecify a definition class in componentType attribute.
<cc:interface componentType="test.component">
....
</cc:interface>
The component may look as following. I used the original code mostly with couple changes. The idea is to override the problematic method and withing the code try reading a properties file for a specified language first and if it is not found, read the default one.
#FacesComponent("test.component")
public class TestComponent extends UINamingContainer {
private static final String PROPERTIES_EXT = ".properties";
private Logger LOGGER = <use one you like>;
private Map<String, String> resourceBundleMap = null;
#Override
public Map<String, String> getResourceBundleMap() {
ResourceBundle resourceBundle = null;
if (null == resourceBundleMap) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
Locale currentLocale = null;
if (null != context) {
if (null != root) {
currentLocale = root.getLocale();
}
}
if (null == currentLocale) {
currentLocale = Locale.getDefault();
}
if (this.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY)) {
Resource ccResource = (Resource)
this.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY);
if (null != ccResource) {
InputStream propertiesInputStream = null;
try {
propertiesInputStream = ccResource.getInputStream();
resourceBundle = findComponentResourceBundleLocaleMatch(ccResource.getResourceName(),
ccResource.getLibraryName(), currentLocale.getLanguage());
} catch (IOException ex) {
LOGGER.error(null, ex);
} finally {
if (null != propertiesInputStream) {
try {
propertiesInputStream.close();
} catch (IOException ioe) {
LOGGER.error(null, ioe);
}
}
}
}
}
if (null != resourceBundle) {
final ResourceBundle bundle = resourceBundle;
resourceBundleMap =
new Map() {
// this is an immutable Map
public String toString() {
StringBuffer sb = new StringBuffer();
Iterator<Map.Entry<String, Object>> entries =
this.entrySet().iterator();
Map.Entry<String, Object> cur;
while (entries.hasNext()) {
cur = entries.next();
sb.append(cur.getKey()).append(": ").append(cur.getValue()).append('\n');
}
return sb.toString();
}
// Do not need to implement for immutable Map
public void clear() {
throw new UnsupportedOperationException();
}
public boolean containsKey(Object key) {
boolean result = false;
if (null != key) {
result = (null != bundle.getObject(key.toString()));
}
return result;
}
public boolean containsValue(Object value) {
Enumeration<String> keys = bundle.getKeys();
boolean result = false;
while (keys.hasMoreElements()) {
Object curObj = bundle.getObject(keys.nextElement());
if ((curObj == value) ||
((null != curObj) && curObj.equals(value))) {
result = true;
break;
}
}
return result;
}
public Set<Map.Entry<String, Object>> entrySet() {
HashMap<String, Object> mappings = new HashMap<String, Object>();
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
Object value = bundle.getObject(key);
mappings.put(key, value);
}
return mappings.entrySet();
}
#Override
public boolean equals(Object obj) {
return !((obj == null) || !(obj instanceof Map))
&& entrySet().equals(((Map) obj).entrySet());
}
public Object get(Object key) {
if (null == key) {
return null;
}
try {
return bundle.getObject(key.toString());
} catch (MissingResourceException e) {
return "???" + key + "???";
}
}
public int hashCode() {
return bundle.hashCode();
}
public boolean isEmpty() {
Enumeration<String> keys = bundle.getKeys();
return !keys.hasMoreElements();
}
public Set keySet() {
Set<String> keySet = new HashSet<String>();
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
keySet.add(keys.nextElement());
}
return keySet;
}
// Do not need to implement for immutable Map
public Object put(Object k, Object v) {
throw new UnsupportedOperationException();
}
// Do not need to implement for immutable Map
public void putAll(Map t) {
throw new UnsupportedOperationException();
}
// Do not need to implement for immutable Map
public Object remove(Object k) {
throw new UnsupportedOperationException();
}
public int size() {
int result = 0;
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
keys.nextElement();
result++;
}
return result;
}
public java.util.Collection values() {
ArrayList<Object> result = new ArrayList<Object>();
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
result.add(
bundle.getObject(keys.nextElement()));
}
return result;
}
};
}
if (null == resourceBundleMap) {
resourceBundleMap = Collections.EMPTY_MAP;
}
}
return resourceBundleMap;
}
private ResourceBundle findComponentResourceBundleLocaleMatch(String resourceName, String libraryName, String lng) {
FacesContext context = FacesContext.getCurrentInstance();
ResourceBundle resourceBundle = null;
int i;
if (-1 != (i = resourceName.lastIndexOf("."))) {
if (null != context) {
InputStream propertiesInputStream = null;
try {
propertiesInputStream = getResourceInputStream(context, resourceName.substring(0, i), libraryName, lng);
resourceBundle = new PropertyResourceBundle(propertiesInputStream);
} catch (IOException ex) {
LOGGER.error(null, ex);
} finally {
if (null != propertiesInputStream) {
try {
propertiesInputStream.close();
} catch (IOException ioe) {
LOGGER.error(null, ioe);
}
}
}
}
}
return resourceBundle;
}
private InputStream getResourceInputStream(FacesContext context, final String resourceName, String libraryName, String lng) throws IOException {
InputStream propertiesInputStream = null;
propertiesInputStream = getPropertiesResourceInputStream(context, String.format("%s_%s%s", resourceName, lng, PROPERTIES_EXT), libraryName);
if (null == propertiesInputStream) {
propertiesInputStream = getPropertiesResourceInputStream(context, resourceName + PROPERTIES_EXT, libraryName);
}
return propertiesInputStream;
}
private InputStream getPropertiesResourceInputStream(FacesContext context, final String resourceName, String libraryName) throws IOException {
Resource result = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);
if (null == result) {
return null;
}
return result.getInputStream();
}
}
Done.
However that is obviously a bug in the Mojarra and I hope it will be fixed soon. Closer look to the code related to the composite components reveals that the default .properties file for a component is read twice which I guess is not a very good idea too, but this is another story.
PS. You may easily adjust te code to respect country code as well if you wish.