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 */
017package org.apache.logging.log4j.spi;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Iterator;
022import java.util.List;
023
024import org.apache.logging.log4j.ThreadContext.ContextStack;
025
026/**
027 *
028 */
029public 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}