Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2012 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.adobe.cq.social.ugc.api;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Responsible for writing a UgcFilter as a JCR SQL2 query.
*/
public class UgcFilterQueryWriter {
private static final String SELECT = "SELECT";
private static final char SELECT_ALL = '*';
private static final String FROM = "FROM";
private static final String WHERE = "WHERE";
private static final String ORDER_BY = "ORDER BY";
private static final String CONTAINS = "CONTAINS";
private static final String LIKE = "LIKE";
private static final String IS = "IS";
private static final String NULL = "NULL";
private static final String IS_DESCENDANT_NODE = "ISDESCENDANTNODE";
private static final String IS_SAME_NODE = "ISSAMENODE";
private static final String IS_CHILD_NODE = "ISCHILDNODE";
private static final String ASCENDING = "ASC";
private static final String DESCENDING = "DESC";
private static final String AND = "AND";
private static final String OR = "OR";
private static final String NOT = "NOT";
private static final String AS = "AS";
private static final char OPEN_FROM = '[';
private static final char CLOSE_FROM = ']';
private static final char SPACING = ' ';
private static final char OPEN_GROUP = '(';
private static final char CLOSE_GROUP = ')';
private static final char OPEN_ORDER_NAME = '[';
private static final char CLOSE_ORDER_NAME = ']';
private static final char ORDERING_SEPARATOR = ',';
private static final char VARIABLE_IDENTIFIER = '$';
private static final char OPEN_SINGLE_QUOTE = '\'';
private static final char CLOSE_SINGLE_QUOTE = '\'';
private static final char OPEN_FUNCTION = '(';
private static final char CLOSE_FUNCTION = ')';
private static final char FUNCTION_PARAM_SEPARATOR = ',';
private static final char SELECTOR_NAME = 'c';
private static final char SELECTOR_SEPARATOR = '.';
private static final char OPEN_FIELD_NAME = '[';
private static final char CLOSE_FIELD_NAME = ']';
private static final char EQUAL = '=';
private static final char LESS_THAN = '<';
private static final char GREATER_THAN = '>';
private static final String LESS_THAN_OR_EQUAL = "<=";
private static final String GREATER_THAN_OR_EQUAL = ">=";
private static final String NOT_EQUAL = "<>";
/**
* Writes a JCR SQL2 query from the given UgcFilter.
* @param ugcFilter UgcFilter to write as a JCR SQL2 query.
* @return String containing the JCR SQL2 query.
*/
public String write(final UgcFilter ugcFilter) {
final StringBuffer query = new StringBuffer();
query.append(SELECT);
query.append(SPACING);
query.append(SELECT_ALL);
query.append(SPACING);
query.append(FROM);
query.append(SPACING);
query.append(OPEN_FROM);
query.append(ugcFilter.getContentType());
query.append(CLOSE_FROM);
query.append(SPACING);
query.append(AS);
query.append(SPACING);
query.append(SELECTOR_NAME);
query.append(SPACING);
query.append(WHERE);
if (ugcFilter.hasConstraints()) {
query.append(SPACING);
ConstraintVisitor constraintVisitor;
if (ugcFilter.hasVariables()) {
constraintVisitor = new QueryWriterConstraintVisitor(query, ugcFilter.getVariables());
} else {
constraintVisitor = new QueryWriterConstraintVisitor(query);
}
final Collection constraints = ugcFilter.getConstraints();
for (final Constraint constraint : constraints) {
constraint.accept(constraintVisitor);
}
}
handleSort(query, ugcFilter);
return query.toString();
}
/**
* Adds sort information to the query if present.
* @param query StringBuffer containing the query.
* @param ugcFilter UgcFilter containing sort information.
*/
private void handleSort(final StringBuffer query, final UgcFilter ugcFilter) {
if (ugcFilter.isSorted()) {
query.append(SPACING);
query.append(ORDER_BY);
final List sortOrder = ugcFilter.getSortOrder();
boolean first = true;
for (final UgcSort sort : sortOrder) {
if (first) {
first = false;
} else {
query.append(ORDERING_SEPARATOR);
}
query.append(SPACING);
final String propertyName = sort.getPropertyName();
query.append(OPEN_ORDER_NAME);
query.append(propertyName);
query.append(CLOSE_ORDER_NAME);
query.append(SPACING);
if (sort.isAscending()) {
query.append(ASCENDING);
} else {
query.append(DESCENDING);
}
}
}
}
/**
* QueryWriterConstraintVisitor is a ConstraintVisitor implementation which traverses the constraint hierarchy in
* order to construct a JCR SQL2 where clause from the constraints that have been added to a UgcFilter.
*/
private static class QueryWriterConstraintVisitor extends DefaultConstraintVisitor implements ConstraintVisitor {
private boolean first = true;
private final StringBuffer query;
private Map variableMap;
/**
* Creates a new QueryWriterConstraintVisitor with a StringBufer containing a partially constructed query.
* @param query StringBuffer to write the where clause to.
*/
public QueryWriterConstraintVisitor(final StringBuffer query) {
super();
this.query = query;
}
/**
* Creates a new QueryWriterConstraintVisitor with a StringBufer containing a partially constructed query and
* a variable map so that variables can be replaced with bound values as needed.
* @param query StringBuffer to write the where clause to.
*/
public QueryWriterConstraintVisitor(final StringBuffer query, final Map variables) {
super();
this.query = query;
this.variableMap = variables;
}
/**
* Write a SetConstraint in JCR SQL2 format.
* @param setConstraint SetConstraint to write
*/
@Override
public void visitSetConstraint(final SetConstraint setConstraint) {
writeOperator(setConstraint);
writeNegation(setConstraint);
if (setConstraint.size() > 1) {
query.append(OPEN_GROUP);
}
for (int i = 0; i < setConstraint.size(); i++) {
if (i > 0) {
query.append(SPACING);
query.append(OR);
query.append(SPACING);
}
appendSelector(query, setConstraint.getPropertyName());
query.append(SPACING);
query.append(EQUAL);
query.append(SPACING);
writeValue(query, setConstraint.get(i), "");
}
if (setConstraint.size() > 1) {
query.append(CLOSE_GROUP);
}
}
/**
* Write a PathConstraint in JCR SQL2 format.
* @param pathConstraint PathConstraint to write.
*/
@Override
public void visitPathConstraint(final PathConstraint pathConstraint) {
writeOperator(pathConstraint);
// Enclose the NOT constraint within () - otherwise the query gets messed up when converting to lucene syntax
// A AND NOT B AND C effectively becomes A AND NOT (B AND C) if we don't enclose NOT in ()
// But If I do A AND (NOT B) AND C - things work fine .
// Primarily the impact was that scheduled post search was browsing through whole massive UGC instead of just fetching the scheduled posts (Refer CQ-4250463)
if(pathConstraint.isNegated()) {
query.append(OPEN_FUNCTION);
}
writeNegation(pathConstraint);
final String path = pathConstraint.getPath();
switch (pathConstraint.getPathConstraintType()) {
case IsSameNode:
query.append(IS_SAME_NODE);
break;
case IsDescendantNode:
query.append(IS_DESCENDANT_NODE);
break;
case IsChildNode:
query.append(IS_CHILD_NODE);
break;
default:
throw new RuntimeException(pathConstraint.getPathConstraintType().name() + " is not supported");
}
query.append(OPEN_FUNCTION);
query.append(SELECTOR_NAME);
query.append(FUNCTION_PARAM_SEPARATOR);
query.append(SPACING);
query.append(OPEN_SINGLE_QUOTE);
/*
* As of 3/2015 rryan's research indicates SQL2 only needs the single quote quoted.
* http://stackoverflow.com/
* questions/27239837/how-to-escape-dynamically-generated-string-values-in-a-jcr-sql2-query
*/
query.append(path.replace("'", "''"));
query.append(CLOSE_SINGLE_QUOTE);
query.append(CLOSE_FUNCTION);
if(pathConstraint.isNegated()) {
query.append(CLOSE_FUNCTION);
}
}
/**
* Write a ValueConstraint in JCR SQL2 format.
* @param valueConstraint ValueConstraint to write.
*/
@Override
public void visitValueConstraint(final ValueConstraint valueConstraint) {
writeOperator(valueConstraint);
if (ComparisonType.NotEquals == valueConstraint.getComparison()) {
query.append(OPEN_GROUP);
query.append(NOT);
query.append(SPACING);
query.append(OPEN_GROUP);
} else {
writeNegation(valueConstraint);
}
final String name = valueConstraint.getPropertyName();
final Object value = valueConstraint.getValue();
appendSelector(query, name);
query.append(SPACING);
final ComparisonType comparisonType = valueConstraint.getComparison();
Object v = value;
if (value instanceof String) {
v = fetchVariable((String) value);
}
switch (comparisonType) {
case Equals:
if (v == null) {
query.append(IS);
} else {
query.append(EQUAL);
}
break;
case NotEquals:
if (v == null) {
query.append(IS);
} else {
query.append(EQUAL);
}
break;
case GreaterThan:
query.append(GREATER_THAN);
break;
case GreaterThanOrEqualTo:
query.append(GREATER_THAN_OR_EQUAL);
break;
case LessThan:
query.append(LESS_THAN);
break;
case LessThanOrEqualTo:
query.append(LESS_THAN_OR_EQUAL);
break;
case BeginsWith:
query.append(LIKE);
break;
default:
throw new RuntimeException("ComparisonType " + comparisonType.name() + " is not supported");
}
query.append(SPACING);
if (v == null) {
query.append((String) null);
} else {
writeValue(query, value, ComparisonType.BeginsWith == valueConstraint.getComparison() ? "%" : "");
}
if (ComparisonType.NotEquals == valueConstraint.getComparison()) {
query.append(CLOSE_GROUP);
query.append(CLOSE_GROUP);
}
}
/**
* Add the selector, separator, and square brackets to escape necessary characters such as :, with the
* selector name inside. So the format will be like "c.[my:name]".
* @param sb a StringBuffer to append to
* @param name the selector to add.
*/
private void appendSelector(final StringBuffer sb, final String name) {
sb.append(SELECTOR_NAME);
sb.append(SELECTOR_SEPARATOR);
sb.append(OPEN_FIELD_NAME);
sb.append(name);
sb.append(CLOSE_FIELD_NAME);
}
/**
* Write a ConstraintGroup in JCR SQL2 format.
* @param constraintGroup ConstraintGroup to write.
*/
@Override
public void visitConstraintGroup(final ConstraintGroup constraintGroup) {
if (constraintGroup.hasConstraints()) {
writeOperator(constraintGroup);
writeNegation(constraintGroup);
final Collection constraints = constraintGroup.getConstraints();
if (constraints.size() > 1) {
query.append(OPEN_GROUP);
}
this.first = true; // reset first so that operator is not added after the (
for (final Constraint constraint : constraints) {
constraint.accept(this);
}
if (constraints.size() > 1) {
query.append(CLOSE_GROUP);
}
}
}
/**
* Write a RangeConstraint in JCR SLQ2 format.
* @param rangeConstraint RangeConstraint to write.
*/
@Override
public void visitRangeConstraint(final RangeConstraint rangeConstraint) {
writeOperator(rangeConstraint);
writeNegation(rangeConstraint);
final Object min = rangeConstraint.getMinValue();
final Object max = rangeConstraint.getMaxValue();
final String propertyName = rangeConstraint.getPropertyName();
query.append(OPEN_GROUP);
appendSelector(query, propertyName);
query.append(SPACING);
if (rangeConstraint.isInclusive()) {
query.append(GREATER_THAN_OR_EQUAL);
} else {
query.append(GREATER_THAN);
}
query.append(SPACING);
writeValue(query, min, "");
query.append(SPACING);
query.append(AND);
query.append(SPACING);
appendSelector(query, propertyName);
query.append(SPACING);
if (rangeConstraint.isInclusive()) {
query.append(LESS_THAN_OR_EQUAL);
} else {
query.append(LESS_THAN);
}
query.append(SPACING);
writeValue(query, max, "");
query.append(CLOSE_GROUP);
}
/**
* Write a FullTextConstraint in JCR SLQ2 format.
* @param fullTextConstraint FullTextConstraint to write.
*/
@Override
public void visitFullTextConstraint(final FullTextConstraint fullTextConstraint) {
writeOperator(fullTextConstraint);
writeNegation(fullTextConstraint);
query.append(CONTAINS);
query.append(OPEN_FUNCTION);
if (fullTextConstraint.definesPropertiesToInclude()) {
boolean first = true;
for (final String fieldName : fullTextConstraint.getIncludedProperties()) {
if (first) {
first = false;
} else {
query.append(FUNCTION_PARAM_SEPARATOR);
query.append(SPACING);
}
appendSelector(query, fieldName);
}
} else {
query.append(SELECTOR_NAME);
query.append(SELECTOR_SEPARATOR);
query.append(SELECT_ALL);
}
query.append(FUNCTION_PARAM_SEPARATOR);
query.append(SPACING);
writeValue(query, fullTextConstraint.getExpression(), "");
query.append(CLOSE_FUNCTION);
}
private Object fetchVariable(final String valueString) {
if (VARIABLE_IDENTIFIER == valueString.charAt(0)) {
if (this.variableMap != null) {
final String varName = valueString.substring(1);
if (this.variableMap.containsKey(varName)) {
return this.variableMap.get(varName);
}
}
}
return valueString;
}
/**
* Writes a value to the query. If the value is an unknown variable, long or integer it is written without
* quotes, otherwise it is wrapped in single quotes. If the value is a known variable, it gets replaced with
* its actual mapped value, and wrapped in single quotes unless it's a long or integer.
* @param query StringBuffer containing the query being constructed.
* @param value Value to write.
* @param suffix The suffix to add to a string parameter
*/
protected void writeValue(final StringBuffer query, final Object value, final String suffix) {
final String valueString = value.toString();
final Object varValue = fetchVariable(valueString);
if (varValue != null) {
if (varValue instanceof Integer || varValue instanceof Long) {
query.append(varValue);
} else {
query.append(OPEN_SINGLE_QUOTE);
query.append((varValue + suffix).replaceAll("'", "''"));
query.append(CLOSE_SINGLE_QUOTE);
}
} else {
if (value instanceof Integer || value instanceof Long) {
query.append(valueString);
} else {
query.append(OPEN_SINGLE_QUOTE);
query.append(valueString);
query.append(CLOSE_SINGLE_QUOTE);
}
}
}
/**
* Writes negation in JCR SQL2 format if present in the constraint.
* @param constraint Constraint to check for negation.
*/
protected void writeNegation(final Constraint constraint) {
if (constraint.isNegated()) {
this.query.append(NOT);
this.query.append(SPACING);
}
}
/**
* Writes the operator in JCR SQL2 format if it is not the first constraint being handled.
* @param constraint Constraint containing the operator.
*/
protected void writeOperator(final Constraint constraint) {
if (this.first) {
this.first = false;
} else {
this.query.append(SPACING);
final Operator operator = constraint.getOperator();
switch (operator) {
case And:
this.query.append(AND);
break;
case Or:
this.query.append(OR);
break;
default:
throw new RuntimeException("Operator " + operator.name() + " is not supported");
}
this.query.append(SPACING);
}
}
}
}