/*
* Copyright 2016 NAVER Corp.
*
* Licensed 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 com.navercorp.pinpoint.web.calltree.span;
import com.navercorp.pinpoint.common.server.bo.SpanBo;
import com.navercorp.pinpoint.common.server.bo.SpanEventBo;
/**
* @author jaehong.kim
*/
public class CallStackMock {
private static final int STACK_SIZE = 8;
private static final int DEFAULT_INDEX = 0;
private static long currentTime = 1;
private SpanEventBo[] stack = new SpanEventBo[STACK_SIZE];
private final SpanBo spanBo;
private int index = DEFAULT_INDEX;
private short sequence;
private int latestStackIndex = 0;
private boolean async = false;
private int asyncId = 0;
private CallTree callTree;
public CallStackMock() {
this(false, -1);
}
public CallStackMock(boolean async, int asyncId) {
this.spanBo = new SpanBo();
this.spanBo.setStartTime(currentTime);
currentTime++;
this.async = async;
if (this.async) {
callTree = new SpanAsyncCallTree(new SpanAlign(spanBo));
} else {
callTree = new SpanCallTree(new SpanAlign(spanBo));
}
this.asyncId = asyncId;
}
public void push() {
final SpanEventBo spanEvent = new SpanEventBo();
final int startElapsed = (int) (currentTime - spanBo.getStartTime());
currentTime++;
spanEvent.setStartElapsed(startElapsed);
spanEvent.setSequence(sequence++);
if (this.async) {
spanEvent.setAsyncId(asyncId);
}
checkExtend(index + 1);
stack[index++] = spanEvent;
if (this.latestStackIndex != this.index) {
this.latestStackIndex = this.index;
spanEvent.setDepth(this.latestStackIndex);
}
callTree.add(spanEvent.getDepth(), new SpanAlign(spanBo, spanEvent));
}
private void checkExtend(final int size) {
final SpanEventBo[] originalStack = this.stack;
if (size >= originalStack.length) {
final int copyStackSize = originalStack.length << 1;
final SpanEventBo[] copyStack = new SpanEventBo[copyStackSize];
System.arraycopy(originalStack, 0, copyStack, 0, originalStack.length);
this.stack = copyStack;
}
}
public SpanEventBo pop() {
final SpanEventBo spanEvent = peek();
if (spanEvent != null) {
stack[index - 1] = null;
index--;
final int endElapsed = (int) (currentTime - (spanBo.getStartTime() + spanEvent.getStartElapsed()));
spanEvent.setEndElapsed(endElapsed);
}
return spanEvent;
}
public SpanEventBo peek() {
if (index == DEFAULT_INDEX) {
return null;
}
return stack[index - 1];
}
public boolean empty() {
if (this.async) {
return index == DEFAULT_INDEX + 1;
}
return index == DEFAULT_INDEX;
}
public int getIndex() {
return index;
}
public void append(CallTree subCallTree) {
if (peek() == null) {
callTree.add(0, subCallTree);
} else {
callTree.add(peek().getDepth(), subCallTree);
}
}
public CallTree close() {
if (this.async) {
pop();
}
final int after = (int) (currentTime - spanBo.getStartTime());
spanBo.setElapsed(after);
return callTree;
}
}