1 package org.infoglue.cms.controllers.kernel.impl.simple; 2 3 import java.text.MessageFormat ; 4 import java.util.ArrayList ; 5 import java.util.Collection ; 6 import java.util.Iterator ; 7 import java.util.List ; 8 import java.util.StringTokenizer ; 9 10 import org.infoglue.cms.entities.management.CategoryVO; 11 import org.infoglue.cms.exception.SystemException; 12 13 15 18 interface ICategoryCondition { 19 22 String getWhereClauseOQL(final List bindings); 23 24 27 Collection getFromClauseTables(); 28 29 32 boolean hasCondition(); 33 } 34 35 38 interface ICategoryContainerCondition extends ICategoryCondition { 39 42 void add(ICategoryCondition condition); 43 44 47 void addCategory(final String attributeName, final CategoryVO categoryVO); 48 49 52 ICategoryContainerCondition and(); 53 54 57 ICategoryContainerCondition or(); 58 } 59 60 63 abstract class AbstractCategoryCondition implements ICategoryCondition { 64 protected static final String LEFT = "("; 65 protected static final String RIGHT = ")"; 66 protected static final String SPACE = " "; 67 protected static final String COMMA = ","; 68 protected static final String AND = "AND"; 69 protected static final String OR = "OR"; 70 71 private static final String CATEGORY_ALIAS_PREFIX = "cat"; 72 private static final String CONTENT_CATEGORY_ALIAS_PREFIX = "ccat"; 73 private static final String CONTENT_VERSION_ALIAS = "cv"; 74 75 private static final String CATEGORY_TABLE = "cmCategory"; 76 private static final String CONTENT_CATEGORY_TABLE = "cmContentCategory"; 77 78 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; 79 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)"; 80 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)"; 81 83 86 private static int counter; 87 88 91 private Integer uniqueID; 92 93 94 95 96 99 private synchronized Integer createUniqueId() { 100 return new Integer (counter++); 101 } 102 103 106 AbstractCategoryCondition() { 107 this.uniqueID = createUniqueId(); 108 } 109 110 113 protected String getUniqueID() { 114 return uniqueID == null ? "" : uniqueID.toString(); 115 } 116 117 120 protected String getBindingVariable(final Collection bindings) { 121 return "$" + (bindings.size() + 1); 122 } 123 124 127 public Collection getFromClauseTables() { 128 final Collection result = new ArrayList (); 129 result.add(CATEGORY_TABLE + SPACE + CATEGORY_ALIAS_PREFIX + uniqueID); 130 result.add(CONTENT_CATEGORY_TABLE + SPACE + CONTENT_CATEGORY_ALIAS_PREFIX + uniqueID); 131 return result; 132 } 133 134 137 protected String getOneCategoryClause(final String attributeName, final CategoryVO categoryVO, final List bindings) { 138 final String categoryVariable = getBindingVariable(bindings); 139 bindings.add(categoryVO.getId()); 140 final String nameVariable = getBindingVariable(bindings); 141 bindings.add(attributeName); 142 return MessageFormat.format(ONE_CATEGORY_CLAUSE, new Object [] { getUniqueID(), categoryVariable, nameVariable }); 143 } 144 145 148 public boolean hasCondition() { return true; } 149 } 150 151 154 class CategoryAndCondition extends AbstractCategoryCondition { 155 158 private String attributeName; 159 160 163 private CategoryVO categoryVO; 164 165 166 167 170 CategoryAndCondition(final String attributeName, final CategoryVO categoryVO) { 171 this.attributeName = attributeName; 172 this.categoryVO = categoryVO; 173 } 174 175 178 public String getWhereClauseOQL(final List bindings) { 179 final String categoryClause = getOneCategoryClause(attributeName, categoryVO, bindings); 180 return MessageFormat.format(getCATEGORY_CLAUSE(), new Object [] { getUniqueID(), categoryClause }); 181 } 182 183 public static String getCATEGORY_CLAUSE() 184 { 185 return (ExtendedSearchController.useFull()) ? CATEGORY_CLAUSE : CATEGORY_CLAUSE_SHORT; 186 } 187 188 } 189 190 193 class CategoryOrCondition extends AbstractCategoryCondition { 194 197 private List names = new ArrayList (); 198 199 202 private List categories = new ArrayList (); 203 204 205 208 CategoryOrCondition(final String attributeName, final CategoryVO categoryVO) { 209 addCategory(attributeName, categoryVO); 210 } 211 212 215 void addCategory(final String attributeName, final CategoryVO categoryVO) { 216 names.add(attributeName); 217 categories.add(categoryVO); 218 } 219 220 223 public String getWhereClauseOQL(final List bindings) { 224 final StringBuffer categoryClauses = new StringBuffer (); 225 for(int i=0; i<names.size(); ++i) { 226 final String attributeName = (String ) names.get(i); 227 final CategoryVO categoryVO = (CategoryVO) categories.get(i); 228 229 if(i > 0) 230 categoryClauses.append(SPACE + OR + SPACE); 231 categoryClauses.append(getOneCategoryClause(attributeName, categoryVO, bindings)); 232 } 233 234 return MessageFormat.format(CategoryAndCondition.getCATEGORY_CLAUSE(), new Object [] { getUniqueID(), LEFT + categoryClauses.toString() + RIGHT }); 235 } 236 } 237 238 241 public class CategoryConditions implements ICategoryContainerCondition { 242 private static final String LEFT = "("; 243 private static final String RIGHT = ")"; 244 private static final String SPACE = " "; 245 private static final String AND = "AND"; 246 private static final String OR = "OR"; 247 248 251 private List children = new ArrayList (); 252 253 256 private String delimiter; 257 258 259 260 263 protected CategoryConditions(final String delimiter) { 264 this.delimiter = delimiter; 265 } 266 267 270 public void add(final ICategoryCondition condition) { 271 if(condition != null) 272 children.add(condition); 273 } 274 275 278 public void addCategory(final String attributeName, final CategoryVO categoryVO) { 279 children.add(new CategoryAndCondition(attributeName, categoryVO)); 280 } 281 282 285 public ICategoryContainerCondition and() { 286 final ICategoryContainerCondition container = createAndConditions(); 287 add(container); 288 return container; 289 } 290 291 294 public ICategoryContainerCondition or() { 295 final ICategoryContainerCondition container = createOrConditions(); 296 add(container); 297 return container; 298 } 299 300 303 public static CategoryConditions createAndConditions() { return new CategoryAndConditions(); } 304 305 308 public static CategoryConditions createOrConditions() { return new CategoryOrConditions(); } 309 310 313 public static CategoryConditions parse(final String s) { return new ConditionsParser().parse(s); } 314 315 318 public String getWhereClauseOQL(final List bindings) { 319 final StringBuffer sb = new StringBuffer (); 320 int counter = 0; 321 for(Iterator i=children.iterator(); i.hasNext(); ) { 322 ICategoryCondition condition = (ICategoryCondition) i.next(); 323 if(condition.hasCondition()) { 324 if(counter++ > 0) 325 sb.append(SPACE + delimiter + SPACE); 326 sb.append(condition.getWhereClauseOQL(bindings)); 327 } 328 } 329 return (counter > 1) ? (LEFT + sb.toString() + RIGHT) : sb.toString(); 330 } 331 332 335 public Collection getFromClauseTables() { 336 final List result = new ArrayList (); 337 for(Iterator i=children.iterator(); i.hasNext(); ) { 338 ICategoryCondition condition = (ICategoryCondition) i.next(); 339 result.addAll(condition.getFromClauseTables()); 340 } 341 return result; 342 } 343 344 347 public boolean hasCondition() { 348 for(Iterator i=children.iterator(); i.hasNext(); ) { 349 ICategoryCondition condition = (ICategoryCondition) i.next(); 350 if(condition.hasCondition()) 351 return true; 352 } 353 return false; 354 } 355 } 356 357 360 class CategoryAndConditions extends CategoryConditions { 361 364 CategoryAndConditions() { 365 super("AND"); 366 } 367 } 368 369 372 class CategoryOrConditions extends CategoryConditions { 373 376 private CategoryOrCondition compound; 377 378 381 CategoryOrConditions() { 382 super("OR"); 383 } 384 385 388 public void addCategory(final String attributeName, final CategoryVO categoryVO) { 389 if(compound == null) { 390 compound = new CategoryOrCondition(attributeName, categoryVO); 391 super.add(compound); 392 } 393 else 394 compound.addCategory(attributeName, categoryVO); 395 } 396 } 397 398 401 class ConditionsParser { 402 private static final String AND_START = "{"; 403 private static final String AND_END = "}"; 404 private static final String OR_START = "["; 405 private static final String OR_END = "]"; 406 private static final String CONDITION_DELIMITER = ","; 407 private static final String CATEGORY_DELIMITER = "="; 408 409 410 413 ConditionsParser() {} 414 415 418 public CategoryConditions parse(final String s) { 419 final String parseString = (s == null ? "" : s); 420 final StringTokenizer st = new StringTokenizer (AND_START + parseString + AND_END, AND_START + AND_END + OR_START + OR_END + CONDITION_DELIMITER, true); 421 final List tokens = tokensToList(st); 422 423 final CategoryConditions conditions = createContainer(tokens); 424 parse(conditions, tokens); 425 return conditions; 426 } 427 428 431 private void parse(CategoryConditions conditions, final List tokens) { 432 if(tokens.isEmpty() || isContainerEndToken(tokens)) 433 return; 434 if(isContainerStartToken(tokens)) 435 parseContainer(conditions, tokens); 436 else if(isConditionDelimiterToken(tokens)) 437 parseConditionDelimiter(conditions, tokens); 438 else 439 parseCategory(conditions, tokens); 440 441 parse(conditions, tokens); 442 } 443 444 447 private void parseContainer(CategoryConditions conditions, final List tokens) { 448 final CategoryConditions newConditions = createContainer(tokens); 449 450 final String startToken = (String ) tokens.remove(0); 451 parse(newConditions, tokens); 452 matchContainerTokens(startToken, tokens); 453 conditions.add(newConditions); 454 } 455 456 459 private void parseConditionDelimiter(CategoryConditions conditions, final List tokens) { 460 if(!conditions.hasCondition()) 461 throw new IllegalArgumentException ("ConditionsParser.parseConditionDelimiter() - empty condition."); 462 tokens.remove(0); 463 } 464 465 468 private void parseCategory(CategoryConditions conditions, final List tokens) { 469 final String token = (String ) tokens.remove(0); 470 final List terms = tokensToList(new StringTokenizer (token, CATEGORY_DELIMITER, true)); 471 if(terms.size() != 3) 472 throw new IllegalArgumentException ("ConditionsParser.parseCategory() - illegal category syntax."); 473 474 final String attributeName = (String ) terms.get(0); 475 final String path = (String ) terms.get(2); 476 477 try { 478 final CategoryVO categoryVO = CategoryController.getController().findByPath(path); 479 if(categoryVO == null) 480 throw new IllegalArgumentException ("ConditionsParser.parseCategory() - no such category [" + path + "]."); 481 conditions.addCategory(attributeName, categoryVO); 482 } catch(SystemException e) { 483 e.printStackTrace(); 484 throw new IllegalArgumentException ("ConditionsParser.parseCategory() - unknown category path [" + path + "]."); 485 } 486 } 487 488 491 private CategoryConditions createContainer(final List tokens) { 492 if(tokens.size() < 2) 493 throw new IllegalArgumentException ("ConditionsParser.createContainer() - no trailing container delimiter."); 494 495 final String startToken = (String ) tokens.get(0); 496 final String endToken = (String ) tokens.get(tokens.size() - 1); 497 498 if(AND_START.equals(startToken)) 499 return CategoryConditions.createAndConditions(); 500 if(OR_START.equals(startToken)) 501 return CategoryConditions.createOrConditions(); 502 503 throw new IllegalArgumentException ("ConditionsParser.createContainer() - illegal state."); 504 } 505 506 509 private boolean isContainerStartToken(final List tokens) { 510 if(tokens.isEmpty()) 511 return false; 512 final String token = (String ) tokens.get(0); 513 return AND_START.equals(token) || OR_START.equals(token); 514 } 515 516 519 private boolean isContainerEndToken(final List tokens) { 520 if(tokens.isEmpty()) 521 return false; 522 final String token = (String ) tokens.get(0); 523 return AND_END.equals(token) || OR_END.equals(token); 524 } 525 526 529 private boolean isConditionDelimiterToken(final List tokens) { 530 if(tokens.isEmpty()) 531 return false; 532 final String token = (String ) tokens.get(0); 533 return CONDITION_DELIMITER.equals(token); 534 } 535 536 539 private void matchContainerTokens(final String startToken, final List tokens) { 540 if(tokens.isEmpty()) 541 throw new IllegalArgumentException ("ConditionsParser.matchContainerTokens() - no closing container token."); 542 final String endToken = (String ) tokens.remove(0); 543 if(startToken.equals(AND_START) && !endToken.equals(AND_END)) 544 throw new IllegalArgumentException ("ConditionsParser.matchContainerTokens() - no matching closing container token."); 545 if(startToken.equals(OR_START) && !endToken.equals(OR_END)) 546 throw new IllegalArgumentException ("ConditionsParser.matchContainerTokens() - no matching closing container token."); 547 } 548 549 552 private List tokensToList(final StringTokenizer st) { 553 final List result = new ArrayList (); 554 while(st.hasMoreElements()) 555 result.add(st.nextElement()); 556 return result; 557 } 558 } | Popular Tags |