001/* 002 * (C) Copyright 2010 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Anahide Tchertchian 018 */ 019package org.nuxeo.ecm.platform.smart.query.jsf; 020 021import java.text.SimpleDateFormat; 022 023import org.apache.commons.lang.StringUtils; 024import org.nuxeo.ecm.core.query.QueryParseException; 025import org.nuxeo.ecm.core.query.sql.SQLQueryParser; 026import org.nuxeo.ecm.core.search.api.client.querymodel.Escaper; 027import org.nuxeo.ecm.core.search.api.client.querymodel.LuceneMinimalEscaper; 028import org.nuxeo.ecm.platform.smart.query.IncrementalSmartQuery; 029 030/** 031 * NXQL implementation of an incremental smart query 032 * 033 * @since 5.4 034 * @author Anahide Tchertchian 035 */ 036public class IncrementalSmartNXQLQuery extends IncrementalSmartQuery { 037 038 private static final long serialVersionUID = 1L; 039 040 public static final String GENERIC_QUERY_SELECT = "SELECT * FROM DOCUMENT WHERE "; 041 042 public static enum SPECIAL_OPERATORS { 043 044 CONTAINS("CONTAINS"), BETWEEN("BETWEEN"), NOT_CONTAINS("NOT CONTAINS"), NOT_STARTSWITH("NOT STARTSWITH"); 045 046 String stringValue; 047 048 SPECIAL_OPERATORS(String stringValue) { 049 this.stringValue = stringValue; 050 } 051 052 public String getStringValue() { 053 return stringValue; 054 } 055 056 } 057 058 // XXX: figure out when this is needed so that it is used 059 public static final Escaper escaper = new LuceneMinimalEscaper(); 060 061 final SimpleDateFormat isoDate = new SimpleDateFormat("yyyy-MM-dd"); 062 063 final SimpleDateFormat isoTimeStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 064 065 public IncrementalSmartNXQLQuery(String existingQueryPart) { 066 super(existingQueryPart); 067 } 068 069 @Override 070 public String buildQuery() { 071 StringBuilder builder = new StringBuilder(); 072 if (existingQueryPart != null) { 073 builder.append(existingQueryPart); 074 builder.append(" "); 075 } 076 // perform simple check before changing query 077 if (leftExpression != null && conditionalOperator != null) { 078 if (logicalOperator != null) { 079 builder.append(logicalOperator); 080 builder.append(" "); 081 } 082 if (Boolean.TRUE.equals(openParenthesis)) { 083 builder.append("("); 084 } 085 if (Boolean.TRUE.equals(addNotOperator) 086 || SPECIAL_OPERATORS.NOT_STARTSWITH.getStringValue().equals(conditionalOperator)) { 087 builder.append("NOT "); 088 } 089 if (leftExpression != null) { 090 builder.append(leftExpression); 091 builder.append(" "); 092 } 093 if (conditionalOperator != null) { 094 if (SPECIAL_OPERATORS.CONTAINS.getStringValue().equals(conditionalOperator)) { 095 builder.append("LIKE"); 096 } else if (SPECIAL_OPERATORS.NOT_CONTAINS.getStringValue().equals(conditionalOperator)) { 097 builder.append("NOT LIKE"); 098 } else if (SPECIAL_OPERATORS.NOT_STARTSWITH.getStringValue().equals(conditionalOperator)) { 099 // negation already added above 100 builder.append("STARTSWITH"); 101 } else { 102 builder.append(conditionalOperator); 103 } 104 builder.append(" "); 105 } 106 if (value != null) { 107 if (booleanValue != null) { 108 if (Boolean.TRUE.equals(booleanValue)) { 109 builder.append(1); 110 } else { 111 builder.append(0); 112 } 113 } else if (stringValue != null) { 114 if (SPECIAL_OPERATORS.CONTAINS.getStringValue().equals(conditionalOperator) 115 || SPECIAL_OPERATORS.NOT_CONTAINS.getStringValue().equals(conditionalOperator)) { 116 builder.append("'%"); 117 if (Boolean.TRUE.equals(escapeValue)) { 118 builder.append(String.format("%s", escaper.escape(stringValue))); 119 } else { 120 builder.append(stringValue); 121 } 122 builder.append("%'"); 123 } else { 124 if (Boolean.TRUE.equals(escapeValue)) { 125 builder.append(String.format("'%s'", escaper.escape(stringValue))); 126 } else { 127 builder.append(String.format("'%s'", stringValue)); 128 } 129 } 130 } else if (stringListValue != null) { 131 String[] values = new String[stringListValue.size()]; 132 values = stringListValue.toArray(values); 133 if (Boolean.TRUE.equals(escapeValue)) { 134 for (int i = 0; i < values.length; i++) { 135 values[i] = String.format("'%s'", escaper.escape(values[i])); 136 } 137 } else { 138 for (int i = 0; i < values.length; i++) { 139 values[i] = String.format("'%s'", values[i]); 140 } 141 } 142 builder.append(String.format("(%s)", StringUtils.join(values, ","))); 143 } else if (stringArrayValue != null) { 144 String[] values = new String[stringArrayValue.length]; 145 if (Boolean.TRUE.equals(escapeValue)) { 146 for (int i = 0; i < stringArrayValue.length; i++) { 147 values[i] = String.format("'%s'", escaper.escape(stringArrayValue[i])); 148 } 149 } else { 150 for (int i = 0; i < stringArrayValue.length; i++) { 151 values[i] = String.format("'%s'", stringArrayValue[i]); 152 } 153 } 154 builder.append(String.format("(%s)", StringUtils.join(values, ","))); 155 } else if (datetimeValue != null) { 156 builder.append(String.format("TIMESTAMP '%s'", 157 isoTimeStamp.format(Long.valueOf(datetimeValue.getTime())))); 158 if (otherDatetimeValue != null) { 159 builder.append(" AND "); 160 builder.append(String.format("TIMESTAMP '%s'", 161 isoTimeStamp.format(Long.valueOf(otherDatetimeValue.getTime())))); 162 } 163 } else if (dateValue != null) { 164 // TODO: handle other date 165 builder.append(String.format("DATE '%s'", isoDate.format(Long.valueOf(dateValue.getTime())))); 166 if (otherDateValue != null) { 167 builder.append(" AND "); 168 builder.append(String.format("DATE '%s'", 169 isoDate.format(Long.valueOf(otherDateValue.getTime())))); 170 } 171 } else if (integerValue != null) { 172 builder.append(integerValue); 173 } else if (floatValue != null) { 174 builder.append(floatValue); 175 } else { 176 // value type not supported 177 builder.append(value.toString()); 178 } 179 } 180 if (Boolean.TRUE.equals(closeParenthesis)) { 181 builder.append(")"); 182 } 183 } 184 String newValue = builder.toString().trim(); 185 clear(); 186 existingQueryPart = newValue; 187 return existingQueryPart; 188 } 189 190 @Override 191 public boolean isValid() { 192 return isValid(existingQueryPart); 193 } 194 195 public static boolean isValid(String queryPart) { 196 String query = GENERIC_QUERY_SELECT + queryPart; 197 try { 198 SQLQueryParser.parse(query); 199 } catch (QueryParseException e) { 200 return false; 201 } 202 return true; 203 } 204 205}