/*
* Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.wso2.carbon.registry.indexing.service;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.registry.core.Collection;
import org.wso2.carbon.registry.core.Registry;
import org.wso2.carbon.registry.core.RegistryConstants;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.registry.core.config.StaticConfiguration;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.session.UserRegistry;
/**
* Represents an advanced query on normal resources. Handles all details of defining, executing and
* parameter processing of such queries.
*/
@SuppressWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
public class AdvancedResourceQuery {
private static Log log = LogFactory.getLog(AdvancedResourceQuery.class);
/**
* List of parameters for the query. NOTE THAT THIS LIST SHOULD ALWAYS BE PROCESSED IN THE ORDER
* THEY ARE LISTED BELOW.
*/
private String resourceName;
private String authorName;
private String updaterName;
private Date createdAfter;
private Date createdBefore;
private Date updatedAfter;
private Date updatedBefore;
private String commentWords;
private String propertyName;
private String propertyValue;
private String content;
private Set<String> tags;
private String queryPath;
public Resource execute(Registry registry) throws RegistryException {
// find if query is already defined
// if defined, execute
// else, create new query and execute
String queryPath = computeQueryPathPrefix();
if (queryPath.indexOf("1") == -1 && queryPath.indexOf("T") == -1) {
String msg = "No parameters are specified for the query.";
log.error(msg);
throw new RegistryException(msg);
}
//these modifications are done so the queries are not stored in the registry
// String resourceQueryPath = queryPath + "r";
// if (!queryExists(resourceQueryPath)) {
String queryResourceContent = generateSQLForResources();
// if (queryResourceContent != null) {
// defineQueries(resourceQueryPath, queryResourceContent);
// }
// }
// String[] resourcePaths = executeResourceQuery(registry, resourceQueryPath);
String[] resourcePaths = executeResourceQuery(registry, queryResourceContent);
String[] collectionPaths;
// String collectionQueryPath = queryPath + "c";
String queryCollectionContent = generateSQLForCollections();
// if (queryCollectionContent != null) {
// defineQueries(collectionQueryPath, queryCollectionContent);
collectionPaths = executeCollectionQuery(registry, queryCollectionContent);
// }
ArrayList<String> totalPathsArr = new ArrayList<String>();
HashMap<String, Integer> resourceKeyHash = new HashMap<String, Integer>();
for (int i = 0;i < resourcePaths.length; i ++) {
totalPathsArr.add(resourcePaths[i]);
resourceKeyHash.put(resourcePaths[i], Integer.valueOf(1));
}
for (int i = 0;i < collectionPaths.length; i ++) {
if (resourceKeyHash.containsKey(collectionPaths[i])) {
// is a resource
continue;
}
totalPathsArr.add(collectionPaths[i]);
}
String[] totalPaths = totalPathsArr.toArray(new String[totalPathsArr.size()]);
Collection c = registry.newCollection();
c.setContent(totalPaths);
return c;
}
private String[] executeResourceQuery(Registry registry, String query) throws RegistryException {
List<Object> params = new ArrayList<Object>();
if (resourceName != null && resourceName.length() != 0) {
params.add(resourceName);
}
if (authorName != null && authorName.length() != 0) {
params.add(authorName);
}
if (updaterName != null && updaterName.length() != 0) {
params.add(updaterName);
}
if (createdAfter != null) {
params.add(new Timestamp(createdAfter.getTime()));
}
if (createdBefore != null) {
params.add(new Timestamp(createdBefore.getTime()));
}
if (updatedAfter != null) {
params.add(new Timestamp(updatedAfter.getTime()));
}
if (updatedBefore != null) {
params.add(new Timestamp(updatedBefore.getTime()));
}
if (commentWords != null && commentWords.length() != 0) {
params.add("%" + commentWords + "%");
}
if (tags != null && !tags.isEmpty()) {
params.addAll(tags);
}
if (propertyName != null) {
params.add(propertyName);
}
if (propertyValue != null) {
params.add(propertyValue);
}
Map paramMap = new HashMap();
for (int i = 0; i < params.size(); i++) {
Object value = params.get(i);
paramMap.put(Integer.toString(i + 1), value);
}
if (content != null){
paramMap.put("content",content);
}
paramMap.put("query", query);
Resource r = registry.executeQuery(null, paramMap);
return (String[])r.getContent();
}
private String[] executeCollectionQuery(Registry registry, String query) throws RegistryException {
List<Object> params = new ArrayList<Object>();
if (resourceName != null && resourceName.length() != 0) {
params.add("%/" + resourceName);
}
if (authorName != null && authorName.length() != 0) {
params.add(authorName);
}
if (updaterName != null && updaterName.length() != 0) {
params.add(updaterName);
}
if (createdAfter != null) {
params.add(new Timestamp(createdAfter.getTime()));
}
if (createdBefore != null) {
params.add(new Timestamp(createdBefore.getTime()));
}
if (updatedAfter != null) {
params.add(new Timestamp(updatedAfter.getTime()));
}
if (updatedBefore != null) {
params.add(new Timestamp(updatedBefore.getTime()));
}
if (commentWords != null && commentWords.length() != 0) {
params.add("%" + commentWords + "%");
}
if (tags != null && !tags.isEmpty()) {
params.addAll(tags);
}
if (propertyName != null) {
params.add(propertyName);
}
if (propertyValue != null) {
params.add(propertyValue);
}
Map paramMap = new HashMap();
for (int i = 0; i < params.size(); i++) {
Object value = params.get(i);
paramMap.put(Integer.toString(i + 1), value);
}
if (content != null){
paramMap.put("content",content);
}
paramMap.put("query", query);
Resource r = registry.executeQuery(null, paramMap);
return (String[])r.getContent();
}
public String getResourceName() {
return resourceName;
}
public void setResourceName(String resourceName) {
if ("".equals(resourceName)) resourceName = null;
this.resourceName = resourceName;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
if ("".equals(authorName)) authorName = null;
this.authorName = authorName;
}
public String getUpdaterName() {
return updaterName;
}
public void setUpdaterName(String updaterName) {
if ("".equals(updaterName)) updaterName = null;
this.updaterName = updaterName;
}
public Date getCreatedAfter() {
return createdAfter;
}
public void setCreatedAfter(Date createdAfter) {
this.createdAfter = createdAfter;
}
public Date getCreatedBefore() {
return createdBefore;
}
public void setCreatedBefore(Date createdBefore) {
this.createdBefore = createdBefore;
}
public Date getUpdatedAfter() {
return updatedAfter;
}
public void setUpdatedAfter(Date updatedAfter) {
this.updatedAfter = updatedAfter;
}
public Date getUpdatedBefore() {
return updatedBefore;
}
public void setUpdatedBefore(Date updatedBefore) {
this.updatedBefore = updatedBefore;
}
public void setTags(String tags) {
this.tags = parseTags(tags);
}
public void setPropertyName(String propertyName) {
if ("".equals(propertyName)) propertyName = null;
this.propertyName = propertyName;
}
public void setPropertyValue(String propertyValue) {
if ("".equals(propertyValue)) propertyValue = null;
this.propertyValue = propertyValue;
}
public String getCommentWords() {
return commentWords;
}
public void setCommentWords(String commentWords) {
if ("".equals(commentWords)) commentWords = null;
this.commentWords = commentWords;
}
public String getContent(){
return this.content;
}
public void setContent(String content){
this.content = content;
}
private boolean queryExists(String queryPath) throws RegistryException {
UserRegistry registry = (UserRegistry) Utils.getRegistry();
return registry.resourceExists(queryPath);
}
private void defineQueries(String queryPath, String queryContent) throws RegistryException {
UserRegistry registry =
(UserRegistry) Utils.getRegistry();
Resource q1 = registry.newResource();
q1.setContent(queryContent);
q1.setMediaType(RegistryConstants.SQL_QUERY_MEDIA_TYPE);
q1.addProperty(RegistryConstants.RESULT_TYPE_PROPERTY_NAME,
RegistryConstants.RESOURCES_RESULT_TYPE);
registry.put(queryPath, q1);
}
private String computeQueryPathPrefix() {
if (queryPath == null) {
StringBuffer buf = new StringBuffer("/system/queries/advanced");
buf.append(resourceName != null ? "1" : "0");
buf.append(authorName != null ? "1" : "0");
buf.append(updaterName != null ? "1" : "0");
buf.append(createdAfter != null ? "1" : "0");
buf.append(createdBefore != null ? "1" : "0");
buf.append(updatedAfter != null ? "1" : "0");
buf.append(updatedBefore != null ? "1" : "0");
buf.append(commentWords != null ? "1" : "0");
buf.append(propertyName != null ? "1" : "0");
buf.append(propertyValue != null ? "1" : "0");
if (tags != null) {
buf.append("T");
buf.append(tags.size());
}
queryPath = buf.toString();
}
return queryPath;
}
public Set<String> parseTags(String tags) {
Set<String> result = new HashSet<String>();
String[] parts = tags.split(",");
for (String part1 : parts) {
String part = part1.trim();
if (!"".equals(part)) {
result.add(part);
}
}
return result;
}
// get sql queries to search resources
private String generateSQLForResources() {
ArrayList<String> tables = new ArrayList<String>();
ArrayList<String> conditions = new ArrayList<String>();
if (resourceName != null && resourceName.length() != 0) {
conditions.add("R.REG_NAME LIKE ?");
}
if (authorName != null && authorName.length() != 0) {
conditions.add("R.REG_CREATOR LIKE ?");
}
if (updaterName != null && updaterName.length() != 0) {
conditions.add("R.REG_LAST_UPDATOR LIKE ?");
}
if (createdAfter != null) {
conditions.add("R.REG_CREATED_TIME > ?");
}
if (createdBefore != null) {
conditions.add("R.REG_CREATED_TIME < ?");
}
if (updatedAfter != null) {
conditions.add("R.REG_LAST_UPDATED_TIME > ?");
}
if (updatedBefore != null) {
conditions.add("R.REG_LAST_UPDATED_TIME < ?");
}
if (commentWords != null && commentWords.length() != 0) {
tables.add(", REG_COMMENT C");
tables.add(", REG_RESOURCE_COMMENT RC");
if(StaticConfiguration.isVersioningComments()) {
conditions.add("R.REG_VERSION=RC.REG_VERSION AND RC.REG_COMMENT_ID=C.REG_ID AND " +
"C.REG_COMMENT_TEXT LIKE ?");
}
else {
conditions.add("R.REG_PATH_ID=RC.REG_PATH_ID AND " +
"((R.REG_NAME = RC.REG_RESOURCE_NAME)) AND " +
"RC.REG_COMMENT_ID=C.REG_ID AND " +
"C.REG_COMMENT_TEXT LIKE ?");
}
}
if (tags != null && !tags.isEmpty()) {
tables.add(", REG_TAG T");
tables.add(", REG_RESOURCE_TAG RT");
StringBuffer tagClause = new StringBuffer();
if(StaticConfiguration.isVersioningTags()) {
tagClause.append("R.REG_VERSION=RT.REG_VERSION AND " +
"RT.REG_TAG_ID=T.REG_ID ");
}
else {
tagClause.append("R.REG_PATH_ID=RT.REG_PATH_ID AND " +
"((R.REG_NAME = RT.REG_RESOURCE_NAME)) AND " +
"RT.REG_TAG_ID=T.REG_ID ");
}
Iterator<String> i = tags.iterator();
int count = 0;
while (i.hasNext()) {
count++;
if(count == 1){
tagClause.append(" AND lower(T.REG_TAG_NAME)=lower(?)");
}else{
tagClause.append(" OR lower(T.REG_TAG_NAME)=lower(?)");
}
i.next();
}
//tagClause.append(")");
conditions.add(tagClause.toString());
}
if (propertyValue != null || propertyName != null) {
tables.add(", REG_PROPERTY PP");
tables.add(", REG_RESOURCE_PROPERTY RP");
StringBuffer propertyClause = new StringBuffer();
if(StaticConfiguration.isVersioningProperties()) {
propertyClause.append("R.REG_VERSION=RP.REG_VERSION AND " +
"RP.REG_PROPERTY_ID=PP.REG_ID");
}
else {
propertyClause.append("R.REG_PATH_ID=RP.REG_PATH_ID AND " +
"((R.REG_NAME = RP.REG_RESOURCE_NAME)) AND " +
"RP.REG_PROPERTY_ID=PP.REG_ID");
}
if (propertyName != null) {
propertyClause.append(" AND lower(PP.REG_NAME)=lower(?)");
}
if (propertyValue != null) {
propertyClause.append(" AND PP.REG_VALUE LIKE ?");
}
conditions.add(propertyClause.toString());
}
StringBuffer query = new StringBuffer();
query.append("SELECT R.REG_PATH_ID, R.REG_NAME FROM REG_RESOURCE R");
for (String table : tables) {
query.append(table);
}
boolean first = true;
for (String condition : conditions) {
if (first) {
query.append(" WHERE ");
first = false;
} else {
query.append(" AND ");
}
query.append(condition);
}
return query.toString();
}
// sql specific to collections
private String generateSQLForCollections() {
ArrayList<String> tables = new ArrayList<String>();
ArrayList<String> conditions = new ArrayList<String>();
conditions.add("R.REG_NAME IS NULL");
if (resourceName != null && resourceName.length() != 0) {
tables.add(", REG_PATH P");
conditions.add(" P.REG_PATH_VALUE LIKE ? AND P.REG_PATH_ID=R.REG_PATH_ID");
}
if (authorName != null && authorName.length() != 0) {
conditions.add("R.REG_CREATOR LIKE ?");
}
if (updaterName != null && updaterName.length() != 0) {
conditions.add("R.REG_LAST_UPDATOR LIKE ?");
}
if (createdAfter != null) {
conditions.add("R.REG_CREATED_TIME > ?");
}
if (createdBefore != null) {
conditions.add("R.REG_CREATED_TIME < ?");
}
if (updatedAfter != null) {
conditions.add("R.REG_LAST_UPDATED_TIME > ?");
}
if (updatedBefore != null) {
conditions.add("R.REG_LAST_UPDATED_TIME < ?");
}
if (commentWords != null && commentWords.length() != 0) {
tables.add(", REG_COMMENT C");
tables.add(", REG_RESOURCE_COMMENT RC");
if(StaticConfiguration.isVersioningComments()) {
conditions.add("R.REG_VERSION=RC.REG_VERSION AND RC.REG_COMMENT_ID=C.REG_ID AND " +
"C.REG_COMMENT_TEXT LIKE ?");
}
else {
conditions.add("R.REG_PATH_ID=RC.REG_PATH_ID AND " +
"R.REG_NAME IS NULL AND RC.REG_RESOURCE_NAME IS NULL AND " +
"RC.REG_COMMENT_ID=C.REG_ID AND " +
"C.REG_COMMENT_TEXT LIKE ?");
}
}
if (tags != null && !tags.isEmpty()) {
tables.add(", REG_TAG T");
tables.add(", REG_RESOURCE_TAG RT");
StringBuffer tagClause = new StringBuffer();
if(StaticConfiguration.isVersioningTags()) {
tagClause.append("R.REG_VERSION=RT.REG_VERSION AND " +
"RT.REG_TAG_ID=T.REG_ID ");
}
else {
tagClause.append("R.REG_PATH_ID=RT.REG_PATH_ID AND " +
"R.REG_NAME IS NULL AND RT.REG_RESOURCE_NAME IS NULL AND " +
"RT.REG_TAG_ID=T.REG_ID ");
}
Iterator<String> i = tags.iterator();
int count = 0;
while (i.hasNext()) {
count++;
if(count == 1){
tagClause.append(" AND lower(T.REG_TAG_NAME)=lower(?)");
}else{
tagClause.append(" OR lower(T.REG_TAG_NAME)=lower(?)");
}
i.next();
}
//tagClause.append(")");
conditions.add(tagClause.toString());
}
if (propertyValue != null || propertyName != null) {
tables.add(", REG_PROPERTY PP");
tables.add(", REG_RESOURCE_PROPERTY RP");
StringBuffer propertyClause = new StringBuffer();
if(StaticConfiguration.isVersioningProperties()) {
propertyClause.append("R.REG_VERSION=RP.REG_VERSION AND " +
"RP.REG_PROPERTY_ID=PP.REG_ID");
}
else {
propertyClause.append("R.REG_PATH_ID=RP.REG_PATH_ID AND " +
"R.REG_NAME IS NULL AND RP.REG_RESOURCE_NAME IS NULL AND " +
"RP.REG_PROPERTY_ID=PP.REG_ID");
}
if (propertyName != null) {
propertyClause.append(" AND lower(PP.REG_NAME)=lower(?)");
}
if (propertyValue != null) {
propertyClause.append(" AND PP.REG_VALUE LIKE ?");
}
conditions.add(propertyClause.toString());
}
if (conditions.size() == 0) {
return null;
}
StringBuffer query = new StringBuffer();
query.append("SELECT R.REG_PATH_ID, R.REG_NAME FROM REG_RESOURCE R");
for (String table : tables) {
query.append(table);
}
boolean first = true;
for (String condition : conditions) {
if (first) {
query.append(" WHERE ");
first = false;
} else {
query.append(" AND ");
}
query.append(condition);
}
return query.toString();
}
}