View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.spi;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Objects;
24  
25  import org.apache.logging.log4j.ThreadContext.ContextStack;
26  import org.apache.logging.log4j.util.StringBuilderFormattable;
27  
28  /**
29   * TODO
30   */
31  public class MutableThreadContextStack implements ThreadContextStack, StringBuilderFormattable {
32  
33      private static final long serialVersionUID = 50505011L;
34  
35      /**
36       * The underlying list (never null).
37       */
38      private final List<String> list;
39      private boolean frozen;
40  
41      /**
42       * Constructs an empty MutableThreadContextStack.
43       */
44      public MutableThreadContextStack() {
45          this(new ArrayList<String>());
46      }
47  
48      /**
49       * Constructs a new instance.
50       * @param list
51       */
52      public MutableThreadContextStack(final List<String> list) {
53          this.list = new ArrayList<>(list);
54      }
55  
56      private MutableThreadContextStack(final MutableThreadContextStack stack) {
57          this.list = new ArrayList<>(stack.list);
58      }
59  
60      private void checkInvariants() {
61          if (frozen) {
62              throw new UnsupportedOperationException("context stack has been frozen");
63          }
64      }
65  
66      @Override
67      public String pop() {
68          checkInvariants();
69          if (list.isEmpty()) {
70              return null;
71          }
72          final int last = list.size() - 1;
73          final String result = list.remove(last);
74          return result;
75      }
76  
77      @Override
78      public String peek() {
79          if (list.isEmpty()) {
80              return null;
81          }
82          final int last = list.size() - 1;
83          return list.get(last);
84      }
85  
86      @Override
87      public void push(final String message) {
88          checkInvariants();
89          list.add(message);
90      }
91  
92      @Override
93      public int getDepth() {
94          return list.size();
95      }
96  
97      @Override
98      public List<String> asList() {
99          return list;
100     }
101 
102     @Override
103     public void trim(final int depth) {
104         checkInvariants();
105         if (depth < 0) {
106             throw new IllegalArgumentException("Maximum stack depth cannot be negative");
107         }
108         if (list == null) {
109             return;
110         }
111         final List<String> copy = new ArrayList<>(list.size());
112         final int count = Math.min(depth, list.size());
113         for (int i = 0; i < count; i++) {
114             copy.add(list.get(i));
115         }
116         list.clear();
117         list.addAll(copy);
118     }
119 
120     @Override
121     public ThreadContextStack copy() {
122         return new MutableThreadContextStack(this);
123     }
124 
125     @Override
126     public void clear() {
127         checkInvariants();
128         list.clear();
129     }
130 
131     @Override
132     public int size() {
133         return list.size();
134     }
135 
136     @Override
137     public boolean isEmpty() {
138         return list.isEmpty();
139     }
140 
141     @Override
142     public boolean contains(final Object o) {
143         return list.contains(o);
144     }
145 
146     @Override
147     public Iterator<String> iterator() {
148         return list.iterator();
149     }
150 
151     @Override
152     public Object[] toArray() {
153         return list.toArray();
154     }
155 
156     @Override
157     public <T> T[] toArray(final T[] ts) {
158         return list.toArray(ts);
159     }
160 
161     @Override
162     public boolean add(final String s) {
163         checkInvariants();
164         return list.add(s);
165     }
166 
167     @Override
168     public boolean remove(final Object o) {
169         checkInvariants();
170         return list.remove(o);
171     }
172 
173     @Override
174     public boolean containsAll(final Collection<?> objects) {
175         return list.containsAll(objects);
176     }
177 
178     @Override
179     public boolean addAll(final Collection<? extends String> strings) {
180         checkInvariants();
181         return list.addAll(strings);
182     }
183 
184     @Override
185     public boolean removeAll(final Collection<?> objects) {
186         checkInvariants();
187         return list.removeAll(objects);
188     }
189 
190     @Override
191     public boolean retainAll(final Collection<?> objects) {
192         checkInvariants();
193         return list.retainAll(objects);
194     }
195 
196     @Override
197     public String toString() {
198         return String.valueOf(list);
199     }
200 
201     @Override
202     public void formatTo(final StringBuilder buffer) {
203         buffer.append('[');
204         for (int i = 0; i < list.size(); i++) {
205             if (i > 0) {
206                 buffer.append(',').append(' ');
207             }
208             buffer.append(list.get(i));
209         }
210         buffer.append(']');
211     }
212 
213     @Override
214     public int hashCode() {
215         return 31 + Objects.hashCode(list);
216     }
217 
218     @Override
219     public boolean equals(final Object obj) {
220         if (this == obj) {
221             return true;
222         }
223         if (obj == null) {
224             return false;
225         }
226         if (!(obj instanceof ThreadContextStack)) {
227             return false;
228         }
229         final ThreadContextStack other = (ThreadContextStack) obj;
230         final List<String> otherAsList = other.asList();
231         return Objects.equals(this.list, otherAsList);
232     }
233 
234     @Override
235     public ContextStack getImmutableStackOrNull() {
236         return copy();
237     }
238 
239     /**
240      * "Freezes" this context stack so it becomes immutable: all mutator methods will throw an exception from now on.
241      */
242     public void freeze() {
243         frozen = true;
244     }
245 
246     /**
247      * Returns whether this context stack is frozen.
248      * @return whether this context stack is frozen.
249      */
250     public boolean isFrozen() {
251         return frozen;
252     }
253 }