001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.internal; 018 019import static org.apache.juneau.common.utils.Utils.*; 020 021import java.util.*; 022import java.util.function.*; 023 024import org.apache.juneau.*; 025import org.apache.juneau.collections.*; 026 027/** 028 * Represents a wrapped {@link BeanMap} where property values can be overridden, removed, or reordered without 029 * affecting the underlying bean. 030 * 031 * <p> 032 * Provides the {@link #filterKeys(List)} method for specifying the keys to keep in the bean map and in what order 033 * they should appear. 034 * 035 * <h5 class='section'>See Also:</h5><ul> 036 037 * </ul> 038 * 039 * @param <T> The class type of the wrapped bean. 040 */ 041public class DelegateBeanMap<T> extends BeanMap<T> { 042 043 private Set<String> keys = set(); 044 private JsonMap overrideValues = new JsonMap(); 045 046 /** 047 * Constructor. 048 * 049 * @param bean The bean being wrapped. 050 * @param session The bean session that created this bean map. 051 */ 052 @SuppressWarnings("unchecked") 053 public 054 DelegateBeanMap(T bean, BeanSession session) { 055 super(session, bean, session.getBeanMeta((Class<T>)bean.getClass())); 056 } 057 058 /** 059 * Add a key in the next position. 060 * 061 * @param key The key to add. 062 */ 063 public void addKey(String key) { 064 this.keys.add(key); 065 } 066 067 @Override /* Map */ 068 public Object put(String key, Object val) { 069 this.overrideValues.put(key, val); 070 this.keys.add(key); 071 return null; 072 } 073 074 @Override /* Map */ 075 public Object get(Object key) { 076 if (overrideValues.containsKey(key)) 077 return overrideValues.get(key); 078 return super.get(key); 079 } 080 081 @Override /* Map */ 082 public Set<String> keySet() { 083 return keys; 084 } 085 086 /** 087 * Remove all but the specified properties from this bean map. 088 * 089 * <p> 090 * This does not affect the underlying bean. 091 * 092 * @param keys The remaining keys in the bean map (in the specified order). 093 * @return This object. 094 */ 095 public DelegateBeanMap<T> filterKeys(List<String> keys) { 096 this.keys.clear(); 097 this.keys.addAll(keys); 098 return this; 099 } 100 101 @Override /* Map */ 102 public Object remove(Object key) { 103 keys.remove(key); 104 return null; 105 } 106 107 @Override /* BeanMap */ 108 public BeanMeta<T> getMeta() { 109 return new BeanMetaFiltered<>(super.getMeta(), keys); 110 } 111 112 @Override /* Map */ 113 public synchronized Set<Entry<String,Object>> entrySet() { 114 Set<Entry<String,Object>> s = set(); 115 keys.forEach(k -> { 116 BeanMapEntry bme; 117 if (overrideValues.containsKey(k)) 118 bme = new BeanMapEntryOverride(this, this.getPropertyMeta(k), overrideValues.get(k)); 119 else 120 bme = this.getProperty(k); 121 if (bme == null) 122 throw new BeanRuntimeException(super.getClassMeta().getInnerClass(), "Property ''{0}'' not found on class.", k); 123 s.add(bme); 124 }); 125 return s; 126 } 127 128 @Override /* BeanMap */ 129 public Collection<BeanPropertyMeta> getProperties() { 130 List<BeanPropertyMeta> l = new ArrayList<>(keys.size()); 131 keys.forEach(k -> { 132 BeanPropertyMeta p = this.getPropertyMeta(k); 133 if (overrideValues.containsKey(k)) 134 p = BeanPropertyMeta.builder(this.meta, k).overrideValue(overrideValues.get(k)).delegateFor(p).build(); 135 if (p == null) 136 p = BeanPropertyMeta.builder(this.meta, k).overrideValue(null).delegateFor(p).build(); 137 l.add(p); 138 }); 139 return l; 140 } 141 142 @Override 143 public BeanMap<T> forEachProperty(Predicate<BeanPropertyMeta> filter, Consumer<BeanPropertyMeta> action) { 144 getProperties().forEach(x -> { 145 if (filter == null || filter.test(x)) 146 action.accept(x); 147 }); 148 return this; 149 } 150 151 class BeanMapEntryOverride extends BeanMapEntry { 152 Object value; 153 154 BeanMapEntryOverride(BeanMap<?> bm, BeanPropertyMeta bpm, Object value) { 155 super(bm, bpm, bpm.getName()); 156 this.value = value; 157 } 158 159 @Override /* Map.Entry */ 160 public Object getValue() { 161 return value; 162 } 163 } 164}