| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.brooklyn.util.collections; |
| |
| import com.google.common.collect.Iterables; |
| import org.apache.brooklyn.util.guava.Maybe; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.Objects; |
| import java.util.stream.Stream; |
| |
| public class ThreadLocalStack<T> implements Iterable<T> { |
| |
| private final boolean acceptDuplicates; |
| |
| /** if duplicates not accepted, the call to push will return false */ |
| public ThreadLocalStack(boolean acceptDuplicates) { |
| this.acceptDuplicates = acceptDuplicates; |
| } |
| public ThreadLocalStack() { this.acceptDuplicates = true; } |
| |
| final ThreadLocal<Collection<T>> set = new ThreadLocal<>(); |
| |
| public Collection<T> getAll(boolean forceInitialized) { |
| Collection<T> result = set.get(); |
| if (forceInitialized && result==null) { |
| result = acceptDuplicates ? MutableList.of() : MutableSet.of(); |
| set.set(result); |
| } |
| return result; |
| } |
| |
| public T pop() { |
| Collection<T> resultS = getAll(true); |
| T last = Iterables.getLast(resultS); |
| resultS.remove(last); |
| if (resultS.isEmpty()) set.remove(); |
| return last; |
| } |
| |
| /** returns true unless duplicates are not accepted, in which case it returns false iff the object supplied is equal to one already present */ |
| public boolean push(T object) { |
| return getAll(true).add(object); |
| } |
| |
| /** top of stack first */ |
| @Override |
| public Iterator<T> iterator() { |
| return stream().iterator(); |
| } |
| |
| /** top of stack first */ |
| public Stream<T> stream() { |
| MutableList<T> l = MutableList.copyOf(getAll(false)); |
| Collections.reverse(l); |
| return l.stream(); |
| } |
| |
| public Maybe<T> peek() { |
| Collection<T> resultS = getAll(false); |
| if (resultS==null || resultS.isEmpty()) return Maybe.absent("Nothing in local stack"); |
| return Maybe.of( Iterables.getLast(resultS) ); |
| } |
| |
| public Maybe<T> peekPenultimate() { |
| Collection<T> resultS = getAll(false); |
| if (resultS==null) return Maybe.absent(); |
| int size = resultS.size(); |
| if (size<=1) return Maybe.absent(); |
| return Maybe.of( Iterables.get(resultS, size-2) ); |
| } |
| |
| public void pop(T entry) { |
| Maybe<T> popped = peek(); |
| if (popped.isAbsent()) throw new IllegalStateException("Nothing to pop; cannot pop "+entry); |
| if (!Objects.equals(entry, popped.get())) throw new IllegalStateException("Stack mismatch, expected to pop "+entry+" but instead would have popped "+popped.get()); |
| pop(); |
| } |
| |
| public int size() { |
| Collection<T> v = getAll(false); |
| if (v==null) return 0; |
| return v.size(); |
| } |
| } |