001package io.ebean.bean;
002
003import java.io.Serializable;
004import java.util.Arrays;
005
006/**
007 * Represent the call stack (stack trace elements).
008 * <p>
009 * Used with a query to identify a CallStackQuery for AutoTune automatic query
010 * tuning.
011 * </p>
012 * <p>
013 * This is used so that a single query called from different methods can be
014 * tuned for each different call stack.
015 * </p>
016 * <p>
017 * Note the call stack is trimmed to remove the common ebean internal elements.
018 * </p>
019 */
020public final class CallStack implements Serializable {
021
022  private static final long serialVersionUID = -8590644046907438579L;
023
024  private final String zeroHash;
025  private final String pathHash;
026
027  private final StackTraceElement[] callStack;
028
029  private final int hc;
030
031  public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
032    this.callStack = callStack;
033    this.zeroHash = enc(zeroHash);
034    this.pathHash = enc(pathHash);
035    this.hc = computeHashCode();
036  }
037
038  private int computeHashCode() {
039    int hc = 0;
040    for (StackTraceElement aCallStack : callStack) {
041      hc = 92821 * hc + aCallStack.hashCode();
042    }
043    return hc;
044  }
045
046  @Override
047  public int hashCode() {
048    return hc;
049  }
050
051  @Override
052  public boolean equals(Object obj) {
053    if (obj == this) {
054      return true;
055    }
056    if (!(obj instanceof CallStack)) {
057      return false;
058    }
059    CallStack e = (CallStack) obj;
060    return Arrays.equals(callStack, e.callStack);
061  }
062
063  /**
064   * Return the first element of the call stack.
065   */
066  public StackTraceElement getFirstStackTraceElement() {
067    return callStack[0];
068  }
069
070  /**
071   * Return the call stack.
072   */
073  public StackTraceElement[] getCallStack() {
074    return callStack;
075  }
076
077  /**
078   * Return the hash for the first stack element.
079   */
080  public String getZeroHash() {
081    return zeroHash;
082  }
083
084  /**
085   * Return the hash for the stack elements (excluding first stack element).
086   */
087  public String getPathHash() {
088    return pathHash;
089  }
090
091  @Override
092  public String toString() {
093    return zeroHash + ":" + pathHash + ":" + callStack[0];
094  }
095
096  /**
097   * Return the call stack lines appended with the given newLine string.
098   */
099  public String description(String newLine) {
100    StringBuilder sb = new StringBuilder(400);
101    for (StackTraceElement aCallStack : callStack) {
102      sb.append(aCallStack.toString()).append(newLine);
103    }
104    return sb.toString();
105  }
106
107  public String getOriginKey(int queryHash) {
108    return enc(queryHash) + "." + zeroHash + "." + pathHash;
109  }
110
111  private static final int radix = 1 << 6;
112  private static final int mask = radix - 1;
113
114  /**
115   * Convert the integer to unsigned base 64.
116   */
117  public static String enc(int i) {
118    char[] buf = new char[32];
119    int charPos = 32;
120    do {
121      buf[--charPos] = intToBase64[i & mask];
122      i >>>= 6;
123    } while (i != 0);
124
125    return new String(buf, charPos, (32 - charPos));
126  }
127
128  private static final char intToBase64[] = {
129    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
130    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
131    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
132    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
133    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
134  };
135}