001package com.avaje.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  public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
030    this.callStack = callStack;
031    this.zeroHash = enc(zeroHash);
032    this.pathHash = enc(pathHash);
033  }
034
035  public int hashCode() {
036    int hc = 0;
037    for (int i = 0; i < callStack.length; i++) {
038      hc = 31 * hc + callStack[i].hashCode();
039    }
040    return hc;
041  }
042  
043  public boolean equals(Object obj) {
044    if (obj == this) {
045      return true;
046    }
047    if (!(obj instanceof CallStack)) {
048      return false;
049    }
050    CallStack e = (CallStack) obj;
051    return Arrays.equals(callStack, e.callStack);
052  }
053
054  /**
055   * Return the first element of the call stack.
056   */
057  public StackTraceElement getFirstStackTraceElement() {
058    return callStack[0];
059  }
060
061  /**
062   * Return the call stack.
063   */
064  public StackTraceElement[] getCallStack() {
065    return callStack;
066  }
067
068  /**
069   * Return the hash for the first stack element.
070   */
071  public String getZeroHash() {
072    return zeroHash;
073  }
074
075  /**
076   * Return the hash for the stack elements (excluding first stack element).
077   */
078  public String getPathHash() {
079    return pathHash;
080  }
081
082  public String toString() {
083    return zeroHash + ":" + pathHash + ":" + callStack[0];
084  }
085
086  /**
087   * Return the call stack lines appended with the given newLine string.
088   */
089  public String description(String newLine) {
090    StringBuilder sb = new StringBuilder(400);
091    for (int i = 0; i < callStack.length; i++) {
092      sb.append(callStack[i].toString()).append(newLine);
093    }
094    return sb.toString();
095  }
096
097  public String getOriginKey(int queryHash) {
098    return enc(queryHash)+ "." + zeroHash + "." + pathHash;
099  }
100
101  private static final int radix = 1 << 6;
102  private static final int mask = radix - 1;
103
104  /**
105   * Convert the integer to unsigned base 64.
106   */
107  public static String enc(int i) {
108    char[] buf = new char[32];
109    int charPos = 32;
110    do {
111      buf[--charPos] = intToBase64[i & mask];
112      i >>>= 6;
113    } while (i != 0);
114
115    return new String(buf, charPos, (32 - charPos));
116  }
117
118  private static final char intToBase64[] = {
119      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
120      'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
121      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
122      'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
123      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
124  };
125}