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