blob: aa793d5a3ea591826465d8d7d8971afe3f1e0028 [file] [log] [blame]
/*
* 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 flex.messaging.io;
import flex.messaging.MessageException;
import flex.messaging.log.Log;
import flex.messaging.log.Logger;
import flex.messaging.util.ClassUtil;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Proxies serialization of a Map and considers all keys as String based property
* names. Additionally, bean properties from the instance are also included and
* override any Map entries with the same key name.
*/
public class MapProxy extends BeanProxy {
static final long serialVersionUID = 7857999941099335210L;
private static final int NULL_KEY_ERROR = 10026;
/**
* Constructor
*/
public MapProxy() {
super();
//dynamic = true;
}
/**
* Construct with a default instance type.
*
* @param defaultInstance defines the alias if provided
*/
public MapProxy(Object defaultInstance) {
super(defaultInstance);
//dynamic = true;
}
/**
* {@inheritDoc}
*/
@Override
public List getPropertyNames(Object instance) {
if (instance == null)
return null;
List propertyNames = null;
List excludes = null;
if (descriptor != null) {
excludes = descriptor.getExcludesForInstance(instance);
if (excludes == null) // For compatibility with older implementations
excludes = descriptor.getExcludes();
}
// Add all Map keys as properties
if (instance instanceof Map) {
Map map = (Map) instance;
if (map.size() > 0) {
propertyNames = new ArrayList(map.size());
SerializationContext context = getSerializationContext();
Iterator it = map.keySet().iterator();
while (it.hasNext()) {
Object key = it.next();
if (key != null) {
if (excludes != null && excludes.contains(key))
continue;
propertyNames.add(key.toString());
} else {
// Log null key errors
if (Log.isWarn() && context.logPropertyErrors) {
Logger log = Log.getLogger(LOG_CATEGORY);
log.warn("Cannot send a null Map key for type {0}.",
new Object[]{map.getClass().getName()});
}
if (!context.ignorePropertyErrors) {
// Cannot send a null Map key for type {0}.
MessageException ex = new MessageException();
ex.setMessage(NULL_KEY_ERROR, new Object[]{map.getClass().getName()});
throw ex;
}
}
}
}
}
// Then, check for bean properties
List beanProperties = super.getPropertyNames(instance);
if (beanProperties != null) {
if (propertyNames == null)
propertyNames = beanProperties;
else
propertyNames.addAll(beanProperties);
}
return propertyNames;
}
/**
* {@inheritDoc}
*/
@Override
public Object getValue(Object instance, String propertyName) {
if (instance == null || propertyName == null)
return null;
Object value = null;
// First, check for bean property
BeanProperty bp = getBeanProperty(instance, propertyName);
if (bp != null)
value = super.getBeanValue(instance, bp);
// Then check for Map entry
if (value == null && instance instanceof Map)
value = getMapValue((Map) instance, propertyName);
return value;
}
/**
* {@inheritDoc}
*/
@Override
public void setValue(Object instance, String propertyName, Object value) {
if (instance == null || propertyName == null)
return;
Map props = getBeanProperties(instance);
if (props.containsKey(propertyName)) {
super.setValue(instance, propertyName, value);
} else if (instance instanceof Map) {
ClassUtil.validateAssignment(instance, propertyName, value);
((Map) instance).put(propertyName, value);
}
}
/**
* {@inheritDoc}
*/
@Override
protected boolean ignorePropertyErrors(SerializationContext context) {
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean logPropertyErrors(SerializationContext context) {
return false;
}
/**
* Return the classname of the instance, including ASObject types.
* If the instance is a Map and is in the java.util package, we return null.
*
* @param instance the object to find the class name of
* @return the class name of the object.
*/
@Override
protected String getClassName(Object instance) {
return (instance != null && instance instanceof Map
&& instance.getClass().getName().startsWith("java.util.")) ?
null : super.getClassName(instance);
}
/**
* Given a map and a property name, returns the value keyed under that property
* name but instead of depending on {@link Map#get(Object)}, propertyName
* is compared against key#toString. This is due to the fact that propertyNames
* are always stored as Strings.
*
* @param map The Map to check against.
* @param propertyName The property name to check for.
* @return The value keyed under property name or null if it does not exist.
*/
protected Object getMapValue(Map map, String propertyName) {
for (Object entry : map.entrySet()) {
Object key = ((Map.Entry) entry).getKey();
if (key.toString().equals(propertyName))
return ((Map.Entry) entry).getValue();
}
return null;
}
}