package org.infoglue.cms.controllers.kernel.impl.simple; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import javax.transaction.NotSupportedException; import org.exolab.castor.jdo.Database; import org.infoglue.cms.entities.management.CategoryVO; import org.infoglue.cms.exception.SystemException; import org.infoglue.cms.util.CmsPropertyHandler; // TODO: cleanup /** * */ interface ICategoryCondition { /** * */ String getWhereClauseOQL(final List bindings); /** * */ Collection getFromClauseTables(); /** * */ boolean hasCondition(); } /** * */ interface ICategoryContainerCondition extends ICategoryCondition { /** * */ void add(ICategoryCondition condition); /** * */ void addCategory(final String attributeName, final CategoryVO categoryVO); /** * */ void addCategory(final String attributeName, final CategoryVO categoryVO, final Boolean notSetArgument, final Boolean isSetArgument); /** * */ ICategoryContainerCondition and(); /** * */ ICategoryContainerCondition or(); } /** * */ abstract class AbstractCategoryCondition implements ICategoryCondition { protected static final String LEFT = "("; protected static final String RIGHT = ")"; protected static final String SPACE = " "; protected static final String COMMA = ","; protected static final String AND = "AND"; protected static final String OR = "OR"; private static final String CATEGORY_ALIAS_PREFIX = "cat"; private static final String CONTENT_CATEGORY_ALIAS_PREFIX = "ccat"; private static final String CONTENT_VERSION_ALIAS = "cv"; private static final String CATEGORY_TABLE = "cmCategory"; private static final String CONTENT_CATEGORY_TABLE = "cmContentCategory"; //OLD WAY protected static final String ONE_CATEGORY_CLAUSE = SPACE + LEFT + CATEGORY_ALIAS_PREFIX + "{0}.categoryId={1} " + AND + SPACE + CONTENT_CATEGORY_ALIAS_PREFIX + "{0}.attributeName={2}" + RIGHT + SPACE; protected static final String CATEGORY_CLAUSE = "(" + CATEGORY_ALIAS_PREFIX + "{0}.active=1 " + AND + " {1} " + AND + SPACE + CONTENT_CATEGORY_ALIAS_PREFIX + "{0}.categoryId = " + CATEGORY_ALIAS_PREFIX + "{0}.categoryId " + AND + SPACE + CONTENT_CATEGORY_ALIAS_PREFIX + "{0}.ContentVersionId=" + CONTENT_VERSION_ALIAS + ".ContentVersionId)"; protected static final String CATEGORY_CLAUSE_SHORT = "(" + CATEGORY_ALIAS_PREFIX + "{0}.active=1 " + AND + " {1} " + AND + SPACE + CONTENT_CATEGORY_ALIAS_PREFIX + "{0}.categoryId = " + CATEGORY_ALIAS_PREFIX + "{0}.categoryId " + AND + SPACE + CONTENT_CATEGORY_ALIAS_PREFIX + "{0}.ContVerId=" + CONTENT_VERSION_ALIAS + ".ContVerId)"; //NEW WAY protected static final String CATEGORY_CLAUSE_GENERAL = "(" + CONTENT_VERSION_ALIAS + ".contentVersionId IN (SELECT contentVersionId from " + CONTENT_CATEGORY_TABLE + " WHERE categoryId={0} AND attributeName={1}))"; protected static final String CATEGORY_CLAUSE_GENERAL_SHORT = "(" + CONTENT_VERSION_ALIAS + ".contVerId IN (SELECT contVerId from " + CONTENT_CATEGORY_TABLE + " WHERE categoryId={0} AND attributeName={1}))"; protected static final String CATEGORY_NOT_SET_CLAUSE_GENERAL = "(" + CONTENT_VERSION_ALIAS + ".contentVersionId NOT IN (SELECT contentVersionId from " + CONTENT_CATEGORY_TABLE + " WHERE attributeName={0}))"; protected static final String CATEGORY_NOT_SET_CLAUSE_GENERAL_SHORT = "(" + CONTENT_VERSION_ALIAS + ".contVerId NOT IN (SELECT contVerId from " + CONTENT_CATEGORY_TABLE + " WHERE attributeName={0}))"; protected static final String CATEGORY_IS_SET_CLAUSE_GENERAL = "(" + CONTENT_VERSION_ALIAS + ".contentVersionId IN (SELECT contentVersionId from " + CONTENT_CATEGORY_TABLE + " WHERE attributeName={0}))"; protected static final String CATEGORY_IS_SET_CLAUSE_GENERAL_SHORT = "(" + CONTENT_VERSION_ALIAS + ".contVerId IN (SELECT contVerId from " + CONTENT_CATEGORY_TABLE + " WHERE attributeName={0}))"; /** * */ private static int counter; /** * */ private Integer uniqueID; /** * */ private synchronized Integer createUniqueId() { return new Integer(counter++); } /** * */ AbstractCategoryCondition() { this.uniqueID = createUniqueId(); } /** * */ protected String getUniqueID() { return uniqueID == null ? "" : uniqueID.toString(); } /** * */ protected String getBindingVariable(final Collection bindings) { return "$" + (bindings.size() + 1); } /** * */ public Collection getFromClauseTables() { final Collection result = new ArrayList(); String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) { result.add(CATEGORY_TABLE + SPACE + CATEGORY_ALIAS_PREFIX + uniqueID); result.add(CONTENT_CATEGORY_TABLE + SPACE + CONTENT_CATEGORY_ALIAS_PREFIX + uniqueID); } return result; } /** * */ protected String getOneCategoryClause(final String attributeName, final CategoryVO categoryVO, final List bindings) { final String categoryVariable = getBindingVariable(bindings); bindings.add(categoryVO.getId()); final String nameVariable = getBindingVariable(bindings); bindings.add(attributeName); String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) { return MessageFormat.format(ONE_CATEGORY_CLAUSE, new Object[] { getUniqueID(), categoryVariable, nameVariable }); } else { String categoryClause = ExtendedSearchController.useFull() ? CATEGORY_CLAUSE_GENERAL : CATEGORY_CLAUSE_GENERAL_SHORT; return MessageFormat.format(categoryClause, new Object[] { categoryVariable, nameVariable }); } } /** * */ protected String getOneExcludingCategoryClause(final String attributeName, final List bindings) throws NotSupportedException { final String nameVariable = getBindingVariable(bindings); bindings.add(attributeName); String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) { throw new NotSupportedException("Not in searches are not supported in the old category search - use the new one (application settings)."); } else { String categoryClause = ExtendedSearchController.useFull() ? CATEGORY_NOT_SET_CLAUSE_GENERAL : CATEGORY_NOT_SET_CLAUSE_GENERAL_SHORT; return MessageFormat.format(categoryClause, new Object[] { nameVariable }); } } /** * */ public boolean hasCondition() { return true; } } /** * */ class CategoryAndCondition extends AbstractCategoryCondition { /** * */ private String attributeName; /** * */ private CategoryVO categoryVO; private Boolean notSetArgument = false; private Boolean isSetArgument = false; /** * */ CategoryAndCondition(final String attributeName, final CategoryVO categoryVO, Boolean notSetArgument, Boolean isSetArgument) { this.attributeName = attributeName; this.categoryVO = categoryVO; this.notSetArgument = notSetArgument; this.isSetArgument = isSetArgument; } /** * */ public String getWhereClauseOQL(final List bindings) { String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) { final String categoryClause = getOneCategoryClause(attributeName, categoryVO, bindings); return MessageFormat.format(getCATEGORY_CLAUSE(), new Object[] { getUniqueID(), categoryClause }); } else { if(notSetArgument) { final String nameVariable = getBindingVariable(bindings); bindings.add(attributeName); return MessageFormat.format(getCATEGORY_NOT_SET_CLAUSE(), new Object[] { nameVariable }); } else if(isSetArgument) { final String nameVariable = getBindingVariable(bindings); bindings.add(attributeName); return MessageFormat.format(getCATEGORY_IS_SET_CLAUSE(), new Object[] { nameVariable }); } else { final String categoryVariable = getBindingVariable(bindings); bindings.add(categoryVO.getId()); final String nameVariable = getBindingVariable(bindings); bindings.add(attributeName); return MessageFormat.format(getCATEGORY_CLAUSE(), new Object[] { categoryVariable, nameVariable }); } } } public static String getCATEGORY_CLAUSE() { String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) return (ExtendedSearchController.useFull()) ? CATEGORY_CLAUSE : CATEGORY_CLAUSE_SHORT; else return ExtendedSearchController.useFull() ? CATEGORY_CLAUSE_GENERAL : CATEGORY_CLAUSE_GENERAL_SHORT; } public static String getCATEGORY_NOT_SET_CLAUSE() throws RuntimeException { String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) throw new RuntimeException("Not in searches are not supported in the old category search - use the new one (application settings)."); else return ExtendedSearchController.useFull() ? CATEGORY_NOT_SET_CLAUSE_GENERAL : CATEGORY_NOT_SET_CLAUSE_GENERAL_SHORT; } public static String getCATEGORY_IS_SET_CLAUSE() throws RuntimeException { String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) throw new RuntimeException("In searches are not supported in the old category search - use the new one (application settings)."); else return ExtendedSearchController.useFull() ? CATEGORY_IS_SET_CLAUSE_GENERAL : CATEGORY_IS_SET_CLAUSE_GENERAL_SHORT; } } /** * */ class CategoryOrCondition extends AbstractCategoryCondition { /** * */ private List names = new ArrayList(); /** * */ private List categories = new ArrayList(); /** * */ CategoryOrCondition(final String attributeName, final CategoryVO categoryVO) { addCategory(attributeName, categoryVO); } /** * */ void addCategory(final String attributeName, final CategoryVO categoryVO) { names.add(attributeName); categories.add(categoryVO); } /** * */ public String getWhereClauseOQL(final List bindings) { final StringBuffer categoryClauses = new StringBuffer(); for(int i=0; i<names.size(); ++i) { final String attributeName = (String) names.get(i); final CategoryVO categoryVO = (CategoryVO) categories.get(i); if(i > 0) categoryClauses.append(SPACE + OR + SPACE); categoryClauses.append(getOneCategoryClause(attributeName, categoryVO, bindings)); } String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) { return MessageFormat.format(CategoryAndCondition.getCATEGORY_CLAUSE(), new Object[] { getUniqueID(), LEFT + categoryClauses.toString() + RIGHT }); } else { String apa2 = LEFT + categoryClauses.toString() + RIGHT; return apa2; } } } /** * */ public class CategoryConditions implements ICategoryContainerCondition { private static final String LEFT = "("; private static final String RIGHT = ")"; private static final String SPACE = " "; private static final String AND = "AND"; private static final String OR = "OR"; /** * */ private List children = new ArrayList(); /** * */ private String delimiter; /** * */ protected CategoryConditions(final String delimiter) { this.delimiter = delimiter; } /** * */ public void add(final ICategoryCondition condition) { if(condition != null) children.add(condition); } /** * */ public void addCategory(final String attributeName, final CategoryVO categoryVO) { children.add(new CategoryAndCondition(attributeName, categoryVO, false, false)); } /** * */ public void addCategory(final String attributeName, final CategoryVO categoryVO, final Boolean notSetArgument, final Boolean isSetArgument) { children.add(new CategoryAndCondition(attributeName, categoryVO, notSetArgument, isSetArgument)); } /** * */ public ICategoryContainerCondition and() { final ICategoryContainerCondition container = createAndConditions(); add(container); return container; } /** * */ public ICategoryContainerCondition or() { final ICategoryContainerCondition container = createOrConditions(); add(container); return container; } /** * */ public static CategoryConditions createAndConditions() { return new CategoryAndConditions(); } /** * */ public static CategoryConditions createOrConditions() { return new CategoryOrConditions(); } /** * */ public static CategoryConditions parse(final String s, Database db) { return new ConditionsParser().parse(s, db); } /** * */ public String getWhereClauseOQL(final List bindings) { final StringBuffer sb = new StringBuffer(); int counter = 0; for(Iterator i=children.iterator(); i.hasNext(); ) { ICategoryCondition condition = (ICategoryCondition) i.next(); if(condition.hasCondition()) { if(counter++ > 0) sb.append(SPACE + delimiter + SPACE); sb.append(condition.getWhereClauseOQL(bindings)); } } return (counter > 1) ? (LEFT + sb.toString() + RIGHT) : sb.toString(); } /** * */ public Collection getFromClauseTables() { final List result = new ArrayList(); for(Iterator i=children.iterator(); i.hasNext(); ) { ICategoryCondition condition = (ICategoryCondition) i.next(); String useImprovedContentCategorySearch = CmsPropertyHandler.getUseImprovedContentCategorySearch(); if(useImprovedContentCategorySearch != null && useImprovedContentCategorySearch.equalsIgnoreCase("false")) result.addAll(condition.getFromClauseTables()); } return result; } /** * */ public boolean hasCondition() { for(Iterator i=children.iterator(); i.hasNext(); ) { ICategoryCondition condition = (ICategoryCondition) i.next(); if(condition.hasCondition()) return true; } return false; } } /** * */ class CategoryAndConditions extends CategoryConditions { /** * */ CategoryAndConditions() { super("AND"); } } /** * */ class CategoryOrConditions extends CategoryConditions { /** * */ private CategoryOrCondition compound; /** * */ CategoryOrConditions() { super("OR"); } /** * */ public void addCategory(final String attributeName, final CategoryVO categoryVO) { if(compound == null) { compound = new CategoryOrCondition(attributeName, categoryVO); super.add(compound); } else compound.addCategory(attributeName, categoryVO); } } /** * */ class ConditionsParser { private static final String AND_START = "{"; private static final String AND_END = "}"; private static final String OR_START = "["; private static final String OR_END = "]"; private static final String CONDITION_DELIMITER = ","; private static final String CATEGORY_DELIMITER = "="; /** * */ ConditionsParser() {} /** * */ public CategoryConditions parse(final String s, Database db) { final String parseString = (s == null ? "" : s); final StringTokenizer st = new StringTokenizer(AND_START + parseString + AND_END, AND_START + AND_END + OR_START + OR_END + CONDITION_DELIMITER, true); final List tokens = tokensToList(st); final CategoryConditions conditions = createContainer(tokens); parse(conditions, tokens, db); return conditions; } /** * */ private void parse(CategoryConditions conditions, final List tokens, Database db) { if(tokens.isEmpty() || isContainerEndToken(tokens)) return; if(isContainerStartToken(tokens)) parseContainer(conditions, tokens, db); else if(isConditionDelimiterToken(tokens)) parseConditionDelimiter(conditions, tokens); else parseCategory(conditions, tokens, db); parse(conditions, tokens, db); } /** * */ private void parseContainer(CategoryConditions conditions, final List tokens, Database db) { final CategoryConditions newConditions = createContainer(tokens); final String startToken = (String) tokens.remove(0); parse(newConditions, tokens, db); matchContainerTokens(startToken, tokens); conditions.add(newConditions); } /** * */ private void parseConditionDelimiter(CategoryConditions conditions, final List tokens) { if(!conditions.hasCondition()) throw new IllegalArgumentException("ConditionsParser.parseConditionDelimiter() - empty condition."); tokens.remove(0); } /** * */ private void parseCategory(CategoryConditions conditions, final List tokens, Database db) { final String token = (String) tokens.remove(0); final List terms = tokensToList(new StringTokenizer(token, CATEGORY_DELIMITER, true)); if(terms.size() != 3) throw new IllegalArgumentException("ConditionsParser.parseCategory() - illegal category syntax."); final String attributeName = (String) terms.get(0); final String path = (String) terms.get(2); final Boolean isNotSetArgument = (path.equalsIgnoreCase("UNDEFINED") ? true : false); final Boolean isSetArgument = (path.equalsIgnoreCase("*") ? true : false); try { CategoryVO categoryVO = null; if(!isNotSetArgument && !isSetArgument) { categoryVO = CategoryController.getController().findByPath(path, db); if(categoryVO == null) throw new IllegalArgumentException("ConditionsParser.parseCategory() - no such category [" + path + "]."); } conditions.addCategory(attributeName, categoryVO, isNotSetArgument, isSetArgument); } catch(SystemException e) { //e.printStackTrace(); throw new IllegalArgumentException("ConditionsParser.parseCategory() - unknown category path [" + path + "]."); } } /** * */ private CategoryConditions createContainer(final List tokens) { if(tokens.size() < 2) throw new IllegalArgumentException("ConditionsParser.createContainer() - no trailing container delimiter."); final String startToken = (String) tokens.get(0); final String endToken = (String) tokens.get(tokens.size() - 1); if(AND_START.equals(startToken)) return CategoryConditions.createAndConditions(); if(OR_START.equals(startToken)) return CategoryConditions.createOrConditions(); throw new IllegalArgumentException("ConditionsParser.createContainer() - illegal state."); } /** * */ private boolean isContainerStartToken(final List tokens) { if(tokens.isEmpty()) return false; final String token = (String) tokens.get(0); return AND_START.equals(token) || OR_START.equals(token); } /** * */ private boolean isContainerEndToken(final List tokens) { if(tokens.isEmpty()) return false; final String token = (String) tokens.get(0); return AND_END.equals(token) || OR_END.equals(token); } /** * */ private boolean isConditionDelimiterToken(final List tokens) { if(tokens.isEmpty()) return false; final String token = (String) tokens.get(0); return CONDITION_DELIMITER.equals(token); } /** * */ private void matchContainerTokens(final String startToken, final List tokens) { if(tokens.isEmpty()) throw new IllegalArgumentException("ConditionsParser.matchContainerTokens() - no closing container token."); final String endToken = (String) tokens.remove(0); if(startToken.equals(AND_START) && !endToken.equals(AND_END)) throw new IllegalArgumentException("ConditionsParser.matchContainerTokens() - no matching closing container token."); if(startToken.equals(OR_START) && !endToken.equals(OR_END)) throw new IllegalArgumentException("ConditionsParser.matchContainerTokens() - no matching closing container token."); } /** * */ private List tokensToList(final StringTokenizer st) { final List result = new ArrayList(); while(st.hasMoreElements()) result.add(st.nextElement()); return result; } }