001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements. See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache license, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License. You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the license for the specific language governing permissions and
015     * limitations under the license.
016     */
017    package org.apache.logging.log4j.spi;
018    
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.Iterator;
022    import java.util.List;
023    
024    import org.apache.logging.log4j.ThreadContext.ContextStack;
025    
026    /**
027     *
028     */
029    public class MutableThreadContextStack implements ThreadContextStack {
030    
031        private static final long serialVersionUID = 50505011L;
032    
033        /**
034         * The underlying list (never null).
035         */
036        private final List<String> list;
037        private boolean frozen;
038    
039        /**
040         * Constructs an empty MutableThreadContextStack.
041         */
042        public MutableThreadContextStack() {
043            this(new ArrayList<String>());
044        }
045    
046        public MutableThreadContextStack(final List<String> list) {
047            this.list = new ArrayList<String>(list);
048        }
049    
050        private MutableThreadContextStack(final MutableThreadContextStack stack) {
051            this.list = new ArrayList<String>(stack.list);
052        }
053    
054        private void checkInvariants() {
055            if (frozen) {
056                throw new UnsupportedOperationException("context stack has been frozen");
057            }
058        }
059    
060        @Override
061        public String pop() {
062            checkInvariants();
063            if (list.isEmpty()) {
064                return null;
065            }
066            final int last = list.size() - 1;
067            final String result = list.remove(last);
068            return result;
069        }
070    
071        @Override
072        public String peek() {
073            if (list.isEmpty()) {
074                return null;
075            }
076            final int last = list.size() - 1;
077            return list.get(last);
078        }
079    
080        @Override
081        public void push(final String message) {
082            checkInvariants();
083            list.add(message);
084        }
085    
086        @Override
087        public int getDepth() {
088            return list.size();
089        }
090    
091        @Override
092        public List<String> asList() {
093            return list;
094        }
095    
096        @Override
097        public void trim(final int depth) {
098            checkInvariants();
099            if (depth < 0) {
100                throw new IllegalArgumentException("Maximum stack depth cannot be negative");
101            }
102            if (list == null) {
103                return;
104            }
105            final List<String> copy = new ArrayList<String>(list.size());
106            final int count = Math.min(depth, list.size());
107            for (int i = 0; i < count; i++) {
108                copy.add(list.get(i));
109            }
110            list.clear();
111            list.addAll(copy);
112        }
113    
114        @Override
115        public ThreadContextStack copy() {
116            return new MutableThreadContextStack(this);
117        }
118    
119        @Override
120        public void clear() {
121            checkInvariants();
122            list.clear();
123        }
124    
125        @Override
126        public int size() {
127            return list.size();
128        }
129    
130        @Override
131        public boolean isEmpty() {
132            return list.isEmpty();
133        }
134    
135        @Override
136        public boolean contains(final Object o) {
137            return list.contains(o);
138        }
139    
140        @Override
141        public Iterator<String> iterator() {
142            return list.iterator();
143        }
144    
145        @Override
146        public Object[] toArray() {
147            return list.toArray();
148        }
149    
150        @Override
151        public <T> T[] toArray(final T[] ts) {
152            return list.toArray(ts);
153        }
154    
155        @Override
156        public boolean add(final String s) {
157            checkInvariants();
158            return list.add(s);
159        }
160    
161        @Override
162        public boolean remove(final Object o) {
163            checkInvariants();
164            return list.remove(o);
165        }
166    
167        @Override
168        public boolean containsAll(final Collection<?> objects) {
169            return list.containsAll(objects);
170        }
171    
172        @Override
173        public boolean addAll(final Collection<? extends String> strings) {
174            checkInvariants();
175            return list.addAll(strings);
176        }
177    
178        @Override
179        public boolean removeAll(final Collection<?> objects) {
180            checkInvariants();
181            return list.removeAll(objects);
182        }
183    
184        @Override
185        public boolean retainAll(final Collection<?> objects) {
186            checkInvariants();
187            return list.retainAll(objects);
188        }
189    
190        @Override
191        public String toString() {
192            return String.valueOf(list);
193        }
194    
195        @Override
196        public int hashCode() {
197            final int prime = 31;
198            int result = 1;
199            result = prime * result + ((this.list == null) ? 0 : this.list.hashCode());
200            return result;
201        }
202    
203        @Override
204        public boolean equals(final Object obj) {
205            if (this == obj) {
206                return true;
207            }
208            if (obj == null) {
209                return false;
210            }
211            if (!(obj instanceof ThreadContextStack)) {
212                return false;
213            }
214            final ThreadContextStack other = (ThreadContextStack) obj;
215            final List<String> otherAsList = other.asList();
216            if (this.list == null) {
217                if (otherAsList != null) {
218                    return false;
219                }
220            } else if (!this.list.equals(otherAsList)) {
221                return false;
222            }
223            return true;
224        }
225    
226        @Override
227        public ContextStack getImmutableStackOrNull() {
228            return copy();
229        }
230    
231        /**
232         * "Freezes" this context stack so it becomes immutable: all mutator methods will throw an exception from now on.
233         */
234        public void freeze() {
235            frozen = true;
236        }
237        
238        public boolean isFrozen() {
239            return frozen;
240        }
241    }