package com.onaro.util.jfc.tables.filter; import com.onaro.util.jfc.tables.filter.matcher.*; import java.util.*; public abstract class FilterMatcherFactory { private static final String QUOTE = "\""; //$NON-NLS-1$ private static final char QUOTE_CHAR = '"'; private static final String OR = "|"; //$NON-NLS-1$ private static final char OR_CHAR = '|'; private static final char GT_CHAR = '>'; private static final char LT_CHAR = '<'; private static final String RANGE = ".."; //$NON-NLS-1$ private static final char RANGE_CHAR = '.';// For ranges x..y protected static final String NOT = "-"; //$NON-NLS-1$ public FilterMatcher parse(String pattern) throws FilterException { Scanner scanner = new Scanner(adjustPattern(pattern)); return recursiveParse(scanner.next(), scanner); } /* If the pattern contains an unquoted OR, then ensure that it is surrounded by spaces so that * the Scanner object will break it out as a separate token. If we find <, >, or .. (range) operators * then make sure there is no whitespace around them. * * Return an updated string. */ private static String adjustPattern(String pattern) { int patternLen = pattern.length(); StringBuilder buf = new StringBuilder(); int ndx = 0; while(ndx < patternLen) { char ch = pattern.charAt(ndx); if(ch == QUOTE_CHAR) { buf.append(ch); // Copy up to and including ending quote while(++ndx < patternLen) { ch = pattern.charAt(ndx); buf.append(ch); // If we got to ending quote, we're done if(ch == QUOTE_CHAR) { break; } } } else if(ch == OR_CHAR) { // Pad with spaces on each side buf.append(' '); buf.append(ch); buf.append(' '); } else if(ch == LT_CHAR || ch == GT_CHAR) { buf.append(ch); ndx = skipWhitespace(pattern, ndx) - 1; } // If we have two consecutive range characters else if(ch == RANGE_CHAR && ndx < (patternLen - 1) && pattern.charAt(ndx + 1) == RANGE_CHAR) { /* Remove and preceding whitespace */ int bufNdx = buf.length() - 1; while(bufNdx >= 0 && Character.isWhitespace(buf.charAt(bufNdx))) { buf.deleteCharAt(bufNdx--); } buf.append(RANGE); ndx = skipWhitespace(pattern, ndx + 1) - 1; } else { // Just copy buf.append(ch); } ndx++; } return(buf.toString()); } /* Returns the index of the next non-whitespace character in the string starting with the character * after the given offset. * Returns the index of the end of the string if there is nothing but whitespace. */ private static int skipWhitespace(String str, int offset) { int strLen = str.length(); int ndx = offset + 1; while(ndx < strLen) { char ch = str.charAt(ndx); if(!Character.isWhitespace(ch)) { break; } ndx++; } return(ndx); } private FilterMatcher recursiveParse(String token, Scanner scanner) throws FilterException { //handle quotes expression if (startWithQuote(token)) { token = consumeQuoted(token, scanner); } //create the matcher for the first token FilterMatcher matcher = createConcreteMatcher(token); if(scanner.hasNext()) { String nextToken = scanner.next(); if(OR.equals(nextToken)){ if (scanner.hasNext()) { return new OrFilterMatcher(matcher, recursiveParse(scanner.next(), scanner)); }else{ throw new FilterException("the last token is an OR sign (|)"); //$NON-NLS-1$ } }else{ return new AndFilterMatcher(matcher, recursiveParse(nextToken, scanner)); } }else { return matcher; } } protected String stripQuotesIfNeeded(String token){ return token.length() > 1 && token.charAt(0) == '"' && token.charAt(token.length() - 1) == '"' ? token.substring(1, token.length() - 1) : token; } protected abstract FilterMatcher createConcreteMatcher(String token) throws FilterException; private boolean startWithQuote(String token) { int quoteIndex = token.indexOf(QUOTE); //the quote may be in the second index if there's - return quoteIndex == 0 || quoteIndex == 1; } /** * Finds the rest of the quoted pattern */ private String consumeQuoted(String firstToken, Scanner scanner) throws FilterException { if (firstToken.endsWith(QUOTE)) { //all the expression is in the same token return firstToken; }else{ //append the next tokens until the quotes expression is complete StringBuilder builder = new StringBuilder(firstToken); while(scanner.hasNext()) { String token = scanner.next(); builder.append(" ").append(token); //$NON-NLS-1$ if(token.endsWith(QUOTE)){ return builder.toString(); } } throw new FilterException("Could not close the quoted expression"); //$NON-NLS-1$ } } }