org.apache.myfaces.trinidad.bean.util.ValueMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of trinidad-api Show documentation
Show all versions of trinidad-api Show documentation
Public API for the Apache MyFaces Trinidad project
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.myfaces.trinidad.bean.util;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.myfaces.trinidad.bean.FacesBean;
import org.apache.myfaces.trinidad.bean.PropertyKey;
import org.apache.myfaces.trinidad.util.CollectionUtils;
/**
* Map implementation that exposes the properties of a FacesBean
* as a Map. This Map supports Iterator.remove(), treats
* putting a null value as identical to removing the value,
* but does not support null keys. The keys may be either
* Strings or {@link PropertyKey}s, but all Map.Entry objects
* will return String keys.
*
*/
public class ValueMap extends AbstractMap
{
public ValueMap(FacesBean bean)
{
_bean = bean;
}
@Override
public Object get(Object key)
{
if (key == null)
throw new NullPointerException();
PropertyKey propertyKey = _getPropertyKey(key);
// Support attribute transparency for list-based
// properties
if (propertyKey.isList())
{
Class type = propertyKey.getType();
if (type.isArray())
type = type.getComponentType();
return _bean.getEntries(propertyKey, type);
}
else
{
Object val = _bean.getProperty(propertyKey);
return (val != null) ? val : propertyKey.getDefault();
}
}
@Override
public Object put(String key, Object value)
{
if (key == null)
throw new NullPointerException();
return _putInternal(_getPropertyKey(key), value);
}
// TODO Should remove just remove values, or also remove bindings?
@Override
public Object remove(Object key)
{
PropertyKey propertyKey = _getPropertyKey(key);
Object oldValue = _bean.getProperty(propertyKey);
_bean.setProperty(propertyKey, null);
return oldValue;
}
/**
* Override for better performance
* @param key
* @return
*/
@Override
public boolean containsKey(Object key)
{
if (key == null)
throw new NullPointerException();
PropertyKey propertyKey = _getPropertyKey(key);
// Some notes:
//
// 1. We could optimize this further by exposing containsLocalKey()
// and containsValueExpression() methods on FacesBean. This would
// allow FacesBean implementations to avoid retrieving the
// actual property value. (For example, FacesBean implememtations
// that delegate to FlaggedPropertyMap would be able to short-circuit
// after checking flags, rather than have to look up the value in
// the underlying PropertyMap.) However, since this requires an
// API change to the FacesBean interface, we are skipping this for
// now. We should re-evaluate once we can rely on Java 8 (and take
// advantage of defender methods).
//
// 2. ValueMap.containsKey() has always checked for the presence of either
// a local property value or a value expression. This differs from
// the corresponding code in Mojarra and MyFaces. Both Mojarra and
// MyFaces implement UIComponent.getAttributes().containsKey() by
// only checking for the presence of a local propery. Thus, to be
// consistent, we could call _bean.getLocalProperty() here. However,
// given that ValueMap has behaved this way forever, we may have
// users that depend on the current behavior. As such, leaving
// this as is.
//
// 3. The "!= null" comparison that we perform here relies on the assumption
// that FacesBean does not allow null property values. While this is not
// obvious from the FacesBean documentation, this is true in practice.
return (_bean.getRawProperty(propertyKey) != null);
}
@Override
public Set> entrySet()
{
return CollectionUtils.overlappingCompositeSet(
new MakeEntries(_bean.keySet()),
new MakeEntries(_bean.bindingKeySet()));
}
private Object _putInternal(PropertyKey propertyKey, Object value)
{
assert propertyKey != null;
Object oldValue = _bean.getProperty(propertyKey);
_bean.setProperty(propertyKey, value);
return oldValue;
}
private class MakeEntries extends AbstractSet>
{
public MakeEntries(Set keys)
{
_keys = keys;
}
@Override
public int size()
{
return _keys.size();
}
@Override
public Iterator> iterator()
{
final Iterator base = _keys.iterator();
return new Iterator>()
{
public boolean hasNext()
{
return base.hasNext();
}
public Map.Entry next()
{
_lastEntry = new EntryImpl(base.next());
return _lastEntry;
}
public void remove()
{
if (_lastEntry == null)
throw new IllegalStateException();
base.remove();
_lastEntry = null;
}
private EntryImpl _lastEntry;
};
}
private final Set _keys;
}
private class EntryImpl implements Entry
{
public EntryImpl(PropertyKey key)
{
assert key != null;
_key = key;
}
public String getKey()
{
return _key.getName();
}
public Object getValue()
{
return get(_key);
}
public Object setValue(Object value)
{
return _putInternal(_key, value);
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object o)
{
if (o == this)
return true;
if (!(o instanceof Map.Entry))
return false;
Map.Entry