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