001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010–2011 ApexIdentity Inc. 015 * Portions Copyright 2011-2014 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.util; 019 020import java.util.AbstractMap; 021import java.util.AbstractSet; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.Map; 025import java.util.Set; 026 027/** 028 * A {@link FieldMap} that can be extended with arbitrary keys. If the key maps 029 * to a key exposed by the field map, the field map is used, otherwise the key 030 * is handled in this implementation. The backing map is a {@link HashMap} with 031 * default initial capacity and load factor. 032 */ 033public class ExtensibleFieldMap extends AbstractMap<String, Object> implements Map<String, Object> { 034 035 /** Map to store fields. */ 036 private final FieldMap fields; 037 038 /** Map to store extended keys. */ 039 private final Map<String, Object> extension = new HashMap<String, Object>(); 040 041 /** 042 * Constructs a new extensible field map, using this object's field members 043 * as keys. This is only useful in the case where a class subclasses 044 * {@code ExtensibleFieldMap}. 045 */ 046 public ExtensibleFieldMap() { 047 fields = new FieldMap(this); 048 } 049 050 /** The Map entrySet view. */ 051 private final Set<Entry<String, Object>> entrySet = new AbstractSet<Entry<String, Object>>() { 052 @Override 053 public void clear() { 054 ExtensibleFieldMap.this.clear(); 055 } 056 057 @Override 058 public boolean contains(final Object o) { 059 return (o instanceof Entry) 060 && ExtensibleFieldMap.this.containsKey(((Entry<?, ?>) o).getKey()); 061 } 062 063 @Override 064 public boolean isEmpty() { 065 return ExtensibleFieldMap.this.isEmpty(); 066 } 067 068 @Override 069 public Iterator<Entry<String, Object>> iterator() { 070 return new Iterator<Entry<String, Object>>() { 071 final Iterator<Entry<String, Object>> fieldIterator = fields.entrySet().iterator(); 072 final Iterator<Entry<String, Object>> extensionIterator = extension.entrySet() 073 .iterator(); 074 Iterator<Entry<String, Object>> currentIterator = fieldIterator; 075 076 @Override 077 public boolean hasNext() { 078 return fieldIterator.hasNext() || extensionIterator.hasNext(); 079 } 080 081 @Override 082 public Entry<String, Object> next() { 083 if (!currentIterator.hasNext() && currentIterator != extensionIterator) { 084 currentIterator = extensionIterator; 085 } 086 return currentIterator.next(); 087 } 088 089 @Override 090 public void remove() { 091 currentIterator.remove(); 092 } 093 }; 094 } 095 096 @Override 097 public boolean remove(final Object o) { 098 return (o instanceof Entry) 099 && ExtensibleFieldMap.this.remove(((Entry<?, ?>) o).getKey()) != null; 100 } 101 102 @Override 103 public int size() { 104 return ExtensibleFieldMap.this.size(); 105 } 106 }; 107 108 /** 109 * Constructs a new extensible field map, using the specified object's field 110 * members as keys. 111 * 112 * @param object the object whose field members are to be exposed in the map. 113 */ 114 public ExtensibleFieldMap(Object object) { 115 fields = new FieldMap(object); 116 } 117 118 @Override 119 public Object get(Object key) { 120 return fields.containsKey(key) ? fields.get(key) : extension.get(key); 121 } 122 123 @Override 124 public boolean containsKey(Object key) { 125 return fields.containsKey(key) || extension.containsKey(key); 126 } 127 128 @Override 129 public Object remove(Object key) { 130 return fields.containsKey(key) ? fields.remove(key) : extension.remove(key); 131 } 132 133 @Override 134 public void clear() { 135 fields.clear(); 136 extension.clear(); 137 } 138 139 @Override 140 public int size() { 141 return fields.size() + extension.size(); 142 } 143 144 @Override 145 public Set<Map.Entry<String, Object>> entrySet() { 146 return entrySet; 147 } 148 149 @Override 150 public boolean isEmpty() { 151 return fields.isEmpty() && extension.isEmpty(); 152 } 153 154 @Override 155 public Object put(String key, Object value) { 156 return fields.containsKey(key) ? fields.put(key, value) : extension.put(key, value); 157 } 158}