Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.lens.cube.parse; import static org.apache.lens.cube.parse.HQLParser.equalsAST; import java.util.Map; import java.util.Set; import org.apache.lens.server.api.error.LensException; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.ql.parse.ASTNode; import com.google.common.base.Objects; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import lombok.Data; import lombok.extern.slf4j.Slf4j; @Slf4j public class TestQuery { private static HiveConf conf = new HiveConf(); private ASTNode ast; private String query; private String joinQueryPart = null; private String trimmedQuery = null; private Map<JoinType, Set<String>> joinTypeStrings = Maps.newTreeMap(); private String preJoinQueryPart = null; private String postJoinQueryPart = null; private boolean processed = false; public enum JoinType { INNERJOIN, LEFTOUTERJOIN, RIGHTOUTERJOIN, FULLOUTERJOIN, UNIQUE, LEFTSEMIJOIN, JOIN } public enum Clause { WHERE, GROUPBY, HAVING, ORDEREDBY } public TestQuery(String query) { this.query = query; } public ASTNode getAST() throws LensException { if (this.ast == null) { ast = HQLParser.parseHQL(this.query, conf); } return ast; } public void processQueryAsString() { if (!processed) { processed = true; this.trimmedQuery = getTrimmedQuery(query); this.joinQueryPart = extractJoinStringFromQuery(trimmedQuery); /** * Get the join query part, pre-join query and post-join query part from the trimmed query. * */ if (StringUtils.isNotBlank(joinQueryPart)) { this.preJoinQueryPart = trimmedQuery.substring(0, trimmedQuery.indexOf(joinQueryPart)); this.postJoinQueryPart = trimmedQuery.substring(getMinIndexOfClause()); prepareJoinStrings(trimmedQuery); } else { int minIndex = getMinIndexOfClause(); this.preJoinQueryPart = trimmedQuery.substring(0, minIndex); this.postJoinQueryPart = trimmedQuery.substring(minIndex); } } } private String getTrimmedQuery(String query) { return query.toUpperCase().replaceAll("\\W", ""); } private void prepareJoinStrings(String query) { while (true) { JoinDetails joinDetails = getNextJoinTypeDetails(query); int nextJoinIndex = joinDetails.getIndex(); if (joinDetails.getJoinType() == null) { log.info("Parsing joinQuery completed"); return; } Set<String> joinStrings = joinTypeStrings.get(joinDetails.getJoinType()); if (joinStrings == null) { joinStrings = Sets.newTreeSet(); joinTypeStrings.put(joinDetails.getJoinType(), joinStrings); } joinStrings.add(joinDetails.getJoinString()); // Pass the remaining query for finding next join query query = query.substring(nextJoinIndex + joinDetails.getJoinType().name().length()); } } @Data private class JoinDetails { private JoinType joinType; private int index; private String joinString; } /** * Get the next join query details from a given query */ private JoinDetails getNextJoinTypeDetails(String query) { int nextJoinIndex = Integer.MAX_VALUE; JoinType nextJoinTypePart = null; for (JoinType joinType : JoinType.values()) { int joinIndex = StringUtils.indexOf(query, joinType.name(), 1); if (joinIndex < nextJoinIndex && joinIndex > 0) { nextJoinIndex = joinIndex; nextJoinTypePart = joinType; } } JoinDetails joinDetails = new JoinDetails(); joinDetails.setIndex(nextJoinIndex); if (nextJoinIndex != Integer.MAX_VALUE) { joinDetails.setJoinString( getJoinString(query.substring(nextJoinIndex + nextJoinTypePart.name().length()))); } joinDetails.setJoinType(nextJoinTypePart); return joinDetails; } private String getJoinString(String joinQueryStr) { int nextJoinIndex = Integer.MAX_VALUE; for (JoinType joinType : JoinType.values()) { int joinIndex = StringUtils.indexOf(joinQueryStr, joinType.name()); if (joinIndex < nextJoinIndex && joinIndex > 0) { nextJoinIndex = joinIndex; } } if (nextJoinIndex == Integer.MAX_VALUE) { int minClauseIndex = getMinIndexOfClause(joinQueryStr); // return join query completely if there is no Clause in the query return minClauseIndex == -1 ? joinQueryStr : joinQueryStr.substring(0, minClauseIndex); } return joinQueryStr.substring(0, nextJoinIndex); } private int getMinIndexOfClause() { return getMinIndexOfClause(trimmedQuery); } private int getMinIndexOfClause(String query) { int minClauseIndex = Integer.MAX_VALUE; for (Clause clause : Clause.values()) { int clauseIndex = StringUtils.indexOf(query, clause.name()); if (clauseIndex == -1) { continue; } minClauseIndex = clauseIndex < minClauseIndex ? clauseIndex : minClauseIndex; } return (minClauseIndex == Integer.MAX_VALUE) ? query.length() : minClauseIndex; } private int getMinIndexOfJoinType() { int minJoinTypeIndex = Integer.MAX_VALUE; for (JoinType joinType : JoinType.values()) { int joinIndex = StringUtils.indexOf(trimmedQuery, joinType.name()); if (joinIndex == -1) { continue; } minJoinTypeIndex = joinIndex < minJoinTypeIndex ? joinIndex : minJoinTypeIndex; } return minJoinTypeIndex == Integer.MAX_VALUE ? -1 : minJoinTypeIndex; } private String extractJoinStringFromQuery(String queryTrimmed) { int joinStartIndex = getMinIndexOfJoinType(); int joinEndIndex = getMinIndexOfClause(); if (joinStartIndex == -1 && joinEndIndex == -1) { return queryTrimmed; } return StringUtils.substring(queryTrimmed, joinStartIndex, joinEndIndex); } @Override public boolean equals(Object query) { if (!(query instanceof TestQuery)) { return false; } TestQuery expected = (TestQuery) query; if (this == expected) { return true; } if (this.query == null && expected.query == null) { return true; } else if (this.query == null) { return false; } else if (expected.query == null) { return false; } return stringEquals(expected) || astEquals(expected); } private boolean astEquals(TestQuery expected) { try { return equalsAST(this.getAST(), expected.getAST()); } catch (LensException e) { log.error("AST not valid", e); return false; } } private boolean stringEquals(TestQuery expected) { processQueryAsString(); expected.processQueryAsString(); return new EqualsBuilder().append(this.joinTypeStrings, expected.joinTypeStrings) .append(this.preJoinQueryPart, expected.preJoinQueryPart) .append(this.postJoinQueryPart, expected.postJoinQueryPart).build(); } @Override public int hashCode() { return Objects.hashCode(query, joinQueryPart, trimmedQuery, joinTypeStrings); } public String toString() { return "Query: " + query + "\n" + "JoinQueryString: " + joinTypeStrings; } }