blob: 4b8a8b3b031830a9a096607cdf23a08e184ba60e [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 org.apache.juneau.internal;
import java.util.*;
import org.apache.juneau.*;
/**
* Represents a wrapped {@link BeanMap} where property values can be overridden, removed, or reordered without
* affecting the underlying bean.
*
* <p>
* Provides the {@link #filterKeys(List)} method for specifying the keys to keep in the bean map and in what order
* they should appear.
*
* @param <T> The class type of the wrapped bean.
*/
public class DelegateBeanMap<T> extends BeanMap<T> {
private Set<String> keys = new LinkedHashSet<>();
private ObjectMap overrideValues = new ObjectMap();
/**
* Constructor.
*
* @param bean The bean being wrapped.
* @param session The bean session that created this bean map.
*/
@SuppressWarnings("unchecked")
public
DelegateBeanMap(T bean, BeanSession session) {
super(session, bean, session.getBeanMeta((Class<T>)bean.getClass()));
}
/**
* Add a key in the next position.
*
* @param key The key to add.
*/
public void addKey(String key) {
this.keys.add(key);
}
@Override /* Map */
public Object put(String key, Object val) {
this.overrideValues.put(key, val);
this.keys.add(key);
return null;
}
@Override /* Map */
public Object get(Object key) {
if (overrideValues.containsKey(key))
return overrideValues.get(key);
return super.get(key);
}
@Override /* Map */
public Set<String> keySet() {
return keys;
}
/**
* Remove all but the specified properties from this bean map.
*
* <p>
* This does not affect the underlying bean.
*
* @param keys The remaining keys in the bean map (in the specified order).
* @return This object (for method chaining).
*/
public DelegateBeanMap<T> filterKeys(List<String> keys) {
this.keys.clear();
this.keys.addAll(keys);
return this;
}
@Override /* Map */
public Object remove(Object key) {
keys.remove(key);
return null;
}
@Override /* BeanMap */
public BeanMeta<T> getMeta() {
return new BeanMetaFiltered<>(super.getMeta(), keys);
}
@Override /* Map */
public Set<Entry<String,Object>> entrySet() {
Set<Entry<String,Object>> s = Collections.newSetFromMap(new LinkedHashMap<Map.Entry<String,Object>,Boolean>());
for (final String key : keys) {
BeanMapEntry bme;
if (overrideValues.containsKey(key))
bme = new BeanMapEntryOverride(this, this.getPropertyMeta(key), overrideValues.get(key));
else
bme = this.getProperty(key);
if (bme == null)
throw new BeanRuntimeException(super.getClassMeta().getInnerClass(), "Property ''{0}'' not found on class.", key);
s.add(bme);
}
return s;
}
@Override /* BeanMap */
public Collection<BeanPropertyMeta> getProperties() {
List<BeanPropertyMeta> l = new ArrayList<>(keys.size());
for (final String key : keys) {
BeanPropertyMeta p = this.getPropertyMeta(key);
if (overrideValues.containsKey(key))
p = BeanPropertyMeta.builder(this.meta, key).overrideValue(overrideValues.get(key)).delegateFor(p).build();
if (p == null)
p = BeanPropertyMeta.builder(this.meta, key).overrideValue(null).delegateFor(p).build();
l.add(p);
}
return l;
}
final class BeanMapEntryOverride extends BeanMapEntry {
Object value;
BeanMapEntryOverride(BeanMap<?> bm, BeanPropertyMeta bpm, Object value) {
super(bm, bpm, bpm.getName());
this.value = value;
}
@Override /* Map.Entry */
public Object getValue() {
return value;
}
}
}