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.infoglue.cms.entities.management.CategoryVO; import org.infoglue.cms.exception.SystemException; import org.infoglue.cms.util.CmsPropertyHandler; // TODO: cleanup /** * */ interface ILuceneCategoryCondition { /** * */ String getWhereClause(); /** * */ boolean hasCondition(); } /** * */ interface ILuceneCategoryContainerCondition extends ILuceneCategoryCondition { /** * */ void add(ILuceneCategoryCondition condition); /** * */ void addCategory(final String attributeName, final CategoryVO categoryVO); /** * */ void addCategory(final String attributeName, final CategoryVO categoryVO, final Boolean notSetArgument, final Boolean isSetArgument); /** * */ ILuceneCategoryContainerCondition and(); /** * */ ILuceneCategoryContainerCondition or(); } /** * */ abstract class AbstractLuceneCategoryCondition implements ILuceneCategoryCondition { 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"; //NEW WAY //protected static final String CATEGORY_CANBE_SET_CLAUSE_GENERAL = "+{1}EQ{0}"; protected static final String CATEGORY_CANBE_SET_CLAUSE_GENERAL = "{1}EQ{0}"; protected static final String CATEGORY_IS_SET_CLAUSE_GENERAL = "+{0}EQ*"; protected static final String CATEGORY_IS_NOT_SET_CLAUSE_GENERAL = "-{0}EQ*"; /** * */ private static int counter; /** * */ private Integer uniqueID; /** * */ private synchronized Integer createUniqueId() { return new Integer(counter++); } /** * */ AbstractLuceneCategoryCondition() { this.uniqueID = createUniqueId(); } /** * */ protected String getUniqueID() { return uniqueID == null ? "" : uniqueID.toString(); } /** * */ protected String getOneCategoryClause(final String attributeName, final CategoryVO categoryVO) { String categoryClause = CATEGORY_CANBE_SET_CLAUSE_GENERAL; return MessageFormat.format(categoryClause, new Object[] { "" + categoryVO.getId(), attributeName.toLowerCase().replaceAll(" ", "_") }); } /** * */ protected String getOneExcludingCategoryClause(final String attributeName, final CategoryVO categoryVO) throws NotSupportedException { String categoryClause = CATEGORY_IS_NOT_SET_CLAUSE_GENERAL; return MessageFormat.format(categoryClause, new Object[] { attributeName.toLowerCase().replaceAll(" ", "_") }); } /** * */ public boolean hasCondition() { return true; } } /** * */ class LuceneCategoryAndCondition extends AbstractLuceneCategoryCondition { /** * */ private String attributeName; /** * */ private CategoryVO categoryVO; private Boolean notSetArgument = false; private Boolean isSetArgument = false; /** * */ LuceneCategoryAndCondition(final String attributeName, final CategoryVO categoryVO, Boolean notSetArgument, Boolean isSetArgument) { this.attributeName = attributeName; this.categoryVO = categoryVO; this.notSetArgument = notSetArgument; this.isSetArgument = isSetArgument; } /** * */ public String getWhereClause() { if(notSetArgument) { return MessageFormat.format(CATEGORY_IS_NOT_SET_CLAUSE_GENERAL, new Object[] { attributeName.toLowerCase().replaceAll(" ", "_") }); } else if(isSetArgument) { return MessageFormat.format(CATEGORY_IS_SET_CLAUSE_GENERAL, new Object[] { attributeName.toLowerCase().replaceAll(" ", "_") }); } else { return MessageFormat.format(CATEGORY_CANBE_SET_CLAUSE_GENERAL, new Object[] { "" + categoryVO.getId(), attributeName.toLowerCase().replaceAll(" ", "_") }); } } } /** * */ class LuceneCategoryOrCondition extends AbstractLuceneCategoryCondition { /** * */ private List names = new ArrayList(); /** * */ private List categories = new ArrayList(); /** * */ LuceneCategoryOrCondition(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 getWhereClause() { 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)); } return MessageFormat.format(LuceneCategoryAndCondition.CATEGORY_CANBE_SET_CLAUSE_GENERAL, new Object[] { getUniqueID(), LEFT + categoryClauses.toString() + RIGHT }); } } /** * */ public class LuceneCategoryConditions implements ILuceneCategoryContainerCondition { private static final String LEFT = "("; private static final String RIGHT = ")"; private static final String SPACE = " "; /** * */ private List children = new ArrayList(); /** * */ private String delimiter; /** * */ protected LuceneCategoryConditions(final String delimiter) { this.delimiter = delimiter; } /** * */ public void add(final ILuceneCategoryCondition condition) { if(condition != null) children.add(condition); } /** * */ public void addCategory(final String attributeName, final CategoryVO categoryVO) { children.add(new LuceneCategoryAndCondition(attributeName, categoryVO, false, false)); } /** * */ public void addCategory(final String attributeName, final CategoryVO categoryVO, final Boolean notSetArgument, final Boolean isSetArgument) { children.add(new LuceneCategoryAndCondition(attributeName, categoryVO, notSetArgument, isSetArgument)); } /** * */ public ILuceneCategoryContainerCondition and() { final ILuceneCategoryContainerCondition container = createAndConditions(); add(container); return container; } /** * */ public ILuceneCategoryContainerCondition or() { final ILuceneCategoryContainerCondition container = createOrConditions(); add(container); return container; } /** * */ public static LuceneCategoryConditions createAndConditions() { return new LuceneCategoryAndConditions(); } /** * */ public static LuceneCategoryConditions createOrConditions() { return new LuceneCategoryOrConditions(); } /** * */ public static String parse(final String s) { return new LuceneConditionsParser().parse(s); } /** * */ public String getWhereClause() { final StringBuffer sb = new StringBuffer(); int counter = 0; for(Iterator i=children.iterator(); i.hasNext(); ) { ILuceneCategoryCondition condition = (ILuceneCategoryCondition) i.next(); if(condition.hasCondition()) { if(counter++ > 0) sb.append(SPACE + delimiter + SPACE); sb.append(condition.getWhereClause()); } } return (counter > 1) ? (LEFT + sb.toString() + RIGHT) : sb.toString(); } /** * */ public boolean hasCondition() { for(Iterator i=children.iterator(); i.hasNext(); ) { ILuceneCategoryCondition condition = (ILuceneCategoryCondition) i.next(); if(condition.hasCondition()) return true; } return false; } } /** * */ class LuceneCategoryAndConditions extends LuceneCategoryConditions { /** * */ LuceneCategoryAndConditions() { super("AND"); } } /** * */ class LuceneCategoryOrConditions extends LuceneCategoryConditions { /** * */ private LuceneCategoryOrCondition compound; /** * */ LuceneCategoryOrConditions() { super("OR"); } /** * */ public void addCategory(final String attributeName, final CategoryVO categoryVO) { if(compound == null) { compound = new LuceneCategoryOrCondition(attributeName, categoryVO); super.add(compound); } else compound.addCategory(attributeName, categoryVO); } } /** * */ class LuceneConditionsParser { 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 = "="; /** * */ LuceneConditionsParser() {} /** * */ public String parse(final String s) { 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<String> tokens = tokensToList(st); final LuceneCategoryConditions conditions = createContainer(tokens); parse(conditions, tokens); return conditions.getWhereClause(); } /** * */ private void parse(LuceneCategoryConditions conditions, final List tokens) { if(tokens.isEmpty() || isContainerEndToken(tokens)) return; if(isContainerStartToken(tokens)) parseContainer(conditions, tokens); else if(isConditionDelimiterToken(tokens)) parseConditionDelimiter(conditions, tokens); else parseCategory(conditions, tokens); parse(conditions, tokens); } /** * */ private void parseContainer(LuceneCategoryConditions conditions, final List tokens) { final LuceneCategoryConditions newConditions = createContainer(tokens); final String startToken = (String) tokens.remove(0); parse(newConditions, tokens); matchContainerTokens(startToken, tokens); conditions.add(newConditions); } /** * */ private void parseConditionDelimiter(LuceneCategoryConditions conditions, final List tokens) { if(!conditions.hasCondition()) throw new IllegalArgumentException("ConditionsParser.parseConditionDelimiter() - empty condition."); tokens.remove(0); } /** * */ private void parseCategory(LuceneCategoryConditions conditions, final List tokens) { 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); 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 LuceneCategoryConditions createContainer(final List tokens) { if(tokens.size() < 2) throw new IllegalArgumentException("ConditionsParser.createContainer() - no trailing container delimiter."); final String startToken = (String) tokens.get(0); if(AND_START.equals(startToken)) return LuceneCategoryConditions.createAndConditions(); if(OR_START.equals(startToken)) return LuceneCategoryConditions.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<String> tokensToList(final StringTokenizer st) { final List<String> result = new ArrayList<String>(); while(st.hasMoreElements()) result.add((String)st.nextElement()); return result; } }