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; 023import java.util.Objects; 024 025import org.apache.logging.log4j.ThreadContext.ContextStack; 026import org.apache.logging.log4j.util.StringBuilderFormattable; 027 028/** 029 * TODO 030 */ 031public class MutableThreadContextStack implements ThreadContextStack, StringBuilderFormattable { 032 033 private static final long serialVersionUID = 50505011L; 034 035 /** 036 * The underlying list (never null). 037 */ 038 private final List<String> list; 039 private boolean frozen; 040 041 /** 042 * Constructs an empty MutableThreadContextStack. 043 */ 044 public MutableThreadContextStack() { 045 this(new ArrayList<String>()); 046 } 047 048 /** 049 * Constructs a new instance. 050 * @param list 051 */ 052 public MutableThreadContextStack(final List<String> list) { 053 this.list = new ArrayList<>(list); 054 } 055 056 private MutableThreadContextStack(final MutableThreadContextStack stack) { 057 this.list = new ArrayList<>(stack.list); 058 } 059 060 private void checkInvariants() { 061 if (frozen) { 062 throw new UnsupportedOperationException("context stack has been frozen"); 063 } 064 } 065 066 @Override 067 public String pop() { 068 checkInvariants(); 069 if (list.isEmpty()) { 070 return null; 071 } 072 final int last = list.size() - 1; 073 final String result = list.remove(last); 074 return result; 075 } 076 077 @Override 078 public String peek() { 079 if (list.isEmpty()) { 080 return null; 081 } 082 final int last = list.size() - 1; 083 return list.get(last); 084 } 085 086 @Override 087 public void push(final String message) { 088 checkInvariants(); 089 list.add(message); 090 } 091 092 @Override 093 public int getDepth() { 094 return list.size(); 095 } 096 097 @Override 098 public List<String> asList() { 099 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}