Date getEndTime() {
   Set<Date> endTimes = new HashSet<Date>();
   for (ExprSpec es : exprSpecs) {
     if (es.getEndTime() != null) {
       endTimes.add(es.getEndTime());
     }
   }
   if (!endTimes.isEmpty()) {
     return Collections.min(endTimes);
   }
   return null;
 }
 Date getStartTime() {
   Set<Date> startTimes = new HashSet<Date>();
   for (ExprSpec es : exprSpecs) {
     if (es.getStartTime() != null) {
       startTimes.add(es.getStartTime());
     }
   }
   if (!startTimes.isEmpty()) {
     return Collections.max(startTimes);
   }
   return null;
 }
 private void addAllNestedExpressions(
     CubeQueryContext cubeql,
     ExprSpecContext baseEsc,
     AbstractBaseTable baseTable,
     Set<ExprSpecContext> nestedExpressions,
     Set<String> exprCols)
     throws SemanticException {
   for (String col : exprCols) {
     Set<ExprSpecContext> replacedExpressions = new LinkedHashSet<ExprSpecContext>();
     for (ExprSpec es : baseTable.getExpressionByName(col).getExpressionSpecs()) {
       ASTNode finalAST = HQLParser.copyAST(baseEsc.getFinalAST());
       replaceColumnInAST(finalAST, col, es.getASTNode());
       ExprSpecContext replacedESC = new ExprSpecContext(baseEsc, es, finalAST, cubeql);
       nestedExpressions.add(replacedESC);
       replacedExpressions.add(replacedESC);
     }
     Set<String> remaining = new LinkedHashSet<String>(exprCols);
     remaining.remove(col);
     for (ExprSpecContext replacedESC : replacedExpressions) {
       addAllNestedExpressions(cubeql, replacedESC, baseTable, nestedExpressions, remaining);
     }
   }
 }
 ExprSpecContext(ExprSpec exprSpec, CubeQueryContext cubeql) throws SemanticException {
   // replaces table names in expression with aliases in the query
   finalAST = replaceAlias(exprSpec.getASTNode(), cubeql);
   exprSpecs.add(exprSpec);
 }