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; 025import org.apache.logging.log4j.util.StringBuilderFormattable; 026 027/** 028 * TODO 029 */ 030public class MutableThreadContextStack implements ThreadContextStack, StringBuilderFormattable { 031 032 private static final long serialVersionUID = 50505011L; 033 034 /** 035 * The underlying list (never null). 036 */ 037 private final List<String> list; 038 private boolean frozen; 039 040 /** 041 * Constructs an empty MutableThreadContextStack. 042 */ 043 public MutableThreadContextStack() { 044 this(new ArrayList<String>()); 045 } 046 047 /** 048 * Constructs a new instance. 049 * @param list 050 */ 051 public MutableThreadContextStack(final List<String> list) { 052 this.list = new ArrayList<>(list); 053 } 054 055 private MutableThreadContextStack(final MutableThreadContextStack stack) { 056 this.list = new ArrayList<>(stack.list); 057 } 058 059 private void checkInvariants() { 060 if (frozen) { 061 throw new UnsupportedOperationException("context stack has been frozen"); 062 } 063 } 064 065 @Override 066 public String pop() { 067 checkInvariants(); 068 if (list.isEmpty()) { 069 return null; 070 } 071 final int last = list.size() - 1; 072 final String result = list.remove(last); 073 return result; 074 } 075 076 @Override 077 public String peek() { 078 if (list.isEmpty()) { 079 return null; 080 } 081 final int last = list.size() - 1; 082 return list.get(last); 083 } 084 085 @Override 086 public void push(final String message) { 087 checkInvariants(); 088 list.add(message); 089 } 090 091 @Override 092 public int getDepth() { 093 return list.size(); 094 } 095 096 @Override 097 public List<String> asList() { 098 return list; 099 } 100 101 @Override 102 public void trim(final int depth) { 103 checkInvariants(); 104 if (depth < 0) { 105 throw new IllegalArgumentException("Maximum stack depth cannot be negative"); 106 } 107 if (list == null) { 108 return; 109 } 110 final List<String> copy = new ArrayList<>(list.size()); 111 final int count = Math.min(depth, list.size()); 112 for (int i = 0; i < count; i++) { 113 copy.add(list.get(i)); 114 } 115 list.clear(); 116 list.addAll(copy); 117 } 118 119 @Override 120 public ThreadContextStack copy() { 121 return new MutableThreadContextStack(this); 122 } 123 124 @Override 125 public void clear() { 126 checkInvariants(); 127 list.clear(); 128 } 129 130 @Override 131 public int size() { 132 return list.size(); 133 } 134 135 @Override 136 public boolean isEmpty() { 137 return list.isEmpty(); 138 } 139 140 @Override 141 public boolean contains(final Object o) { 142 return list.contains(o); 143 } 144 145 @Override 146 public Iterator<String> iterator() { 147 return list.iterator(); 148 } 149 150 @Override 151 public Object[] toArray() { 152 return list.toArray(); 153 } 154 155 @Override 156 public <T> T[] toArray(final T[] ts) { 157 return list.toArray(ts); 158 } 159 160 @Override 161 public boolean add(final String s) { 162 checkInvariants(); 163 return list.add(s); 164 } 165 166 @Override 167 public boolean remove(final Object o) { 168 checkInvariants(); 169 return list.remove(o); 170 } 171 172 @Override 173 public boolean containsAll(final Collection<?> objects) { 174 return list.containsAll(objects); 175 } 176 177 @Override 178 public boolean addAll(final Collection<? extends String> strings) { 179 checkInvariants(); 180 return list.addAll(strings); 181 } 182 183 @Override 184 public boolean removeAll(final Collection<?> objects) { 185 checkInvariants(); 186 return list.removeAll(objects); 187 } 188 189 @Override 190 public boolean retainAll(final Collection<?> objects) { 191 checkInvariants(); 192 return list.retainAll(objects); 193 } 194 195 @Override 196 public String toString() { 197 return String.valueOf(list); 198 } 199 200 @Override 201 public void formatTo(final StringBuilder buffer) { 202 buffer.append('['); 203 for (int i = 0; i < list.size(); i++) { 204 if (i > 0) { 205 buffer.append(',').append(' '); 206 } 207 buffer.append(list.get(i)); 208 } 209 buffer.append(']'); 210 } 211 212 @Override 213 public int hashCode() { 214 final int prime = 31; 215 int result = 1; 216 result = prime * result + ((this.list == null) ? 0 : this.list.hashCode()); 217 return result; 218 } 219 220 @Override 221 public boolean equals(final Object obj) { 222 if (this == obj) { 223 return true; 224 } 225 if (obj == null) { 226 return false; 227 } 228 if (!(obj instanceof ThreadContextStack)) { 229 return false; 230 } 231 final ThreadContextStack other = (ThreadContextStack) obj; 232 final List<String> otherAsList = other.asList(); 233 if (this.list == null) { 234 if (otherAsList != null) { 235 return false; 236 } 237 } else if (!this.list.equals(otherAsList)) { 238 return false; 239 } 240 return true; 241 } 242 243 @Override 244 public ContextStack getImmutableStackOrNull() { 245 return copy(); 246 } 247 248 /** 249 * "Freezes" this context stack so it becomes immutable: all mutator methods will throw an exception from now on. 250 */ 251 public void freeze() { 252 frozen = true; 253 } 254 255 /** 256 * Returns whether this context stack is frozen. 257 * @return whether this context stack is frozen. 258 */ 259 public boolean isFrozen() { 260 return frozen; 261 } 262}