1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.impl;
18
19 import java.util.Arrays;
20 import java.util.Comparator;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Objects;
24
25 import org.apache.logging.log4j.util.BiConsumer;
26 import org.apache.logging.log4j.util.ReadOnlyStringMap;
27 import org.apache.logging.log4j.util.StringMap;
28 import org.apache.logging.log4j.util.TriConsumer;
29
30
31
32
33 class JdkMapAdapterStringMap implements StringMap {
34 private static final long serialVersionUID = -7348247784983193612L;
35 private static final String FROZEN = "Frozen collection cannot be modified";
36 private static final Comparator<? super String> NULL_FIRST_COMPARATOR = new Comparator<String>() {
37 @Override
38 public int compare(final String left, final String right) {
39 if (left == null) {
40 return -1;
41 }
42 if (right == null) {
43 return 1;
44 }
45 return left.compareTo(right);
46 }
47 };
48
49 private final Map<String, String> map;
50 private boolean immutable = false;
51 private transient String[] sortedKeys;
52
53 public JdkMapAdapterStringMap() {
54 this(new HashMap<String, String>());
55 }
56
57 public JdkMapAdapterStringMap(final Map<String, String> map) {
58 this.map = Objects.requireNonNull(map, "map");
59 }
60
61 @Override
62 public Map<String, String> toMap() {
63 return map;
64 }
65
66 private void assertNotFrozen() {
67 if (immutable) {
68 throw new UnsupportedOperationException(FROZEN);
69 }
70 }
71
72 @Override
73 public boolean containsKey(final String key) {
74 return map.containsKey(key);
75 }
76
77 @SuppressWarnings("unchecked")
78 @Override
79 public <V> void forEach(final BiConsumer<String, ? super V> action) {
80 final String[] keys = getSortedKeys();
81 for (int i = 0; i < keys.length; i++) {
82 action.accept(keys[i], (V) map.get(keys[i]));
83 }
84 }
85
86 @SuppressWarnings("unchecked")
87 @Override
88 public <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final S state) {
89 final String[] keys = getSortedKeys();
90 for (int i = 0; i < keys.length; i++) {
91 action.accept(keys[i], (V) map.get(keys[i]), state);
92 }
93 }
94
95 private String[] getSortedKeys() {
96 if (sortedKeys == null) {
97 sortedKeys = map.keySet().toArray(new String[map.size()]);
98 Arrays.sort(sortedKeys, NULL_FIRST_COMPARATOR);
99 }
100 return sortedKeys;
101 }
102
103 @SuppressWarnings("unchecked")
104 @Override
105 public <V> V getValue(final String key) {
106 return (V) map.get(key);
107 }
108
109 @Override
110 public boolean isEmpty() {
111 return map.isEmpty();
112 }
113
114 @Override
115 public int size() {
116 return map.size();
117 }
118
119 @Override
120 public void clear() {
121 if (map.isEmpty()) {
122 return;
123 }
124 assertNotFrozen();
125 map.clear();
126 sortedKeys = null;
127 }
128
129 @Override
130 public void freeze() {
131 immutable = true;
132 }
133
134 @Override
135 public boolean isFrozen() {
136 return immutable;
137 }
138
139 @Override
140 public void putAll(final ReadOnlyStringMap source) {
141 assertNotFrozen();
142 source.forEach(PUT_ALL, map);
143 sortedKeys = null;
144 }
145
146 private static TriConsumer<String, String, Map<String, String>> PUT_ALL = new TriConsumer<String, String, Map<String, String>>() {
147 @Override
148 public void accept(final String key, final String value, final Map<String, String> stringStringMap) {
149 stringStringMap.put(key, value);
150 }
151 };
152
153 @Override
154 public void putValue(final String key, final Object value) {
155 assertNotFrozen();
156 map.put(key, value == null ? null : String.valueOf(value));
157 sortedKeys = null;
158 }
159
160 @Override
161 public void remove(final String key) {
162 if (!map.containsKey(key)) {
163 return;
164 }
165 assertNotFrozen();
166 map.remove(key);
167 sortedKeys = null;
168 }
169
170 @Override
171 public String toString() {
172 final StringBuilder result = new StringBuilder(map.size() * 13);
173 result.append('{');
174 final String[] keys = getSortedKeys();
175 for (int i = 0; i < keys.length; i++) {
176 if (i > 0) {
177 result.append(", ");
178 }
179 result.append(keys[i]).append('=').append(map.get(keys[i]));
180 }
181 result.append('}');
182 return result.toString();
183 }
184
185 @Override
186 public boolean equals(final Object object) {
187 if (object == this) {
188 return true;
189 }
190 if (!(object instanceof JdkMapAdapterStringMap)) {
191 return false;
192 }
193 final JdkMapAdapterStringMap other = (JdkMapAdapterStringMap) object;
194 return map.equals(other.map) && immutable == other.immutable;
195 }
196
197 @Override
198 public int hashCode() {
199 return map.hashCode() + (immutable ? 31 : 0);
200 }
201 }