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.commons.utils.CollectionUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021 022import java.util.*; 023import java.util.function.*; 024 025import org.apache.juneau.*; 026import org.apache.juneau.collections.*; 027 028/** 029 * Represents a wrapped {@link BeanMap} where property values can be overridden, removed, or reordered without 030 * affecting the underlying bean. 031 * 032 * <p> 033 * Provides the {@link #filterKeys(List)} method for specifying the keys to keep in the bean map and in what order 034 * they should appear. 035 * 036 * 037 * @param <T> The class type of the wrapped bean. 038 */ 039public class DelegateBeanMap<T> extends BeanMap<T> { 040 041 class BeanMapEntryOverride extends BeanMapEntry { 042 Object value; 043 044 BeanMapEntryOverride(BeanMap<?> bm, BeanPropertyMeta bpm, Object value) { 045 super(bm, bpm, bpm.getName()); 046 this.value = value; 047 } 048 049 @Override /* Overridden from Map.Entry */ 050 public Object getValue() { return value; } 051 } 052 053 private Set<String> keys = set(); 054 055 private JsonMap overrideValues = new JsonMap(); 056 057 /** 058 * Constructor. 059 * 060 * @param bean The bean being wrapped. 061 * @param session The bean session that created this bean map. 062 */ 063 @SuppressWarnings("unchecked") 064 public DelegateBeanMap(T bean, BeanSession session) { 065 super(session, bean, session.getBeanMeta((Class<T>)bean.getClass())); 066 } 067 068 /** 069 * Add a key in the next position. 070 * 071 * @param key The key to add. 072 */ 073 public void addKey(String key) { 074 this.keys.add(key); 075 } 076 077 @Override /* Overridden from Map */ 078 public synchronized Set<Entry<String,Object>> entrySet() { 079 Set<Entry<String,Object>> s = set(); 080 keys.forEach(k -> { 081 BeanMapEntry bme; 082 if (overrideValues.containsKey(k)) 083 bme = new BeanMapEntryOverride(this, this.getPropertyMeta(k), overrideValues.get(k)); 084 else 085 bme = this.getProperty(k); 086 if (bme == null) 087 throw bex(super.getClassMeta().inner(), "Property ''{0}'' not found on class.", k); 088 s.add(bme); 089 }); 090 return s; 091 } 092 093 /** 094 * Remove all but the specified properties from this bean map. 095 * 096 * <p> 097 * This does not affect the underlying bean. 098 * 099 * @param keys The remaining keys in the bean map (in the specified order). 100 * @return This object. 101 */ 102 public DelegateBeanMap<T> filterKeys(List<String> keys) { 103 this.keys.clear(); 104 this.keys.addAll(keys); 105 return this; 106 } 107 108 @Override 109 public BeanMap<T> forEachProperty(Predicate<BeanPropertyMeta> filter, Consumer<BeanPropertyMeta> action) { 110 getProperties().forEach(x -> { 111 if (filter == null || filter.test(x)) 112 action.accept(x); 113 }); 114 return this; 115 } 116 117 @Override /* Overridden from Map */ 118 public Object get(Object key) { 119 if (overrideValues.containsKey(key)) 120 return overrideValues.get(key); 121 return super.get(key); 122 } 123 124 @Override /* Overridden from BeanMap */ 125 public BeanMeta<T> getMeta() { return new BeanMetaFiltered<>(super.getMeta(), keys); } 126 127 @Override /* Overridden from BeanMap */ 128 public Collection<BeanPropertyMeta> getProperties() { 129 var l = new ArrayList<BeanPropertyMeta>(keys.size()); 130 keys.forEach(k -> { 131 var p = this.getPropertyMeta(k); 132 if (overrideValues.containsKey(k)) 133 p = BeanPropertyMeta.builder(this.meta, k).overrideValue(overrideValues.get(k)).delegateFor(p).build(); 134 if (p == null) 135 p = BeanPropertyMeta.builder(this.meta, k).overrideValue(null).build(); 136 l.add(p); 137 }); 138 return l; 139 } 140 141 @Override /* Overridden from Map */ 142 public Set<String> keySet() { 143 return keys; 144 } 145 146 @Override /* Overridden from Map */ 147 public Object put(String key, Object val) { 148 this.overrideValues.put(key, val); 149 this.keys.add(key); 150 return null; 151 } 152 153 @Override /* Overridden from Map */ 154 public Object remove(Object key) { 155 keys.remove(key); 156 return null; 157 } 158}