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}