alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (SearchFilter.java)

This example Java source code file (SearchFilter.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

approx_match, atomicfilter, attribute, begin_filter_token, compoundfilter, end_filter_token, greater_match, invalidsearchfilterexception, less_match, naming, namingexception, string, stringbuffer, stringfilter, util, wildcard_token

The SearchFilter.java Java example source code

/*
 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.jndi.toolkit.dir;

import javax.naming.*;
import javax.naming.directory.*;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Locale;

/**
  * A class for parsing LDAP search filters (defined in RFC 1960, 2254)
  *
  * @author Jon Ruiz
  * @author Rosanna Lee
  */
public class SearchFilter implements AttrFilter {

    interface StringFilter extends AttrFilter {
        public void parse() throws InvalidSearchFilterException;
    }

    // %%% "filter" and "pos" are not declared "private" due to bug 4064984.
    String                      filter;
    int                         pos;
    private StringFilter        rootFilter;

    protected static final boolean debug = false;

    protected static final char         BEGIN_FILTER_TOKEN = '(';
    protected static final char         END_FILTER_TOKEN = ')';
    protected static final char         AND_TOKEN = '&';
    protected static final char         OR_TOKEN = '|';
    protected static final char         NOT_TOKEN = '!';
    protected static final char         EQUAL_TOKEN = '=';
    protected static final char         APPROX_TOKEN = '~';
    protected static final char         LESS_TOKEN = '<';
    protected static final char         GREATER_TOKEN = '>';
    protected static final char         EXTEND_TOKEN = ':';
    protected static final char         WILDCARD_TOKEN = '*';

    public SearchFilter(String filter) throws InvalidSearchFilterException {
        this.filter = filter;
        pos = 0;
        normalizeFilter();
        rootFilter = this.createNextFilter();
    }

    // Returns true if targetAttrs passes the filter
    public boolean check(Attributes targetAttrs) throws NamingException {
        if (targetAttrs == null)
            return false;

        return rootFilter.check(targetAttrs);
    }

    /*
     * Utility routines used by member classes
     */

    // does some pre-processing on the string to make it look exactly lik
    // what the parser expects. This only needs to be called once.
    protected void normalizeFilter() {
        skipWhiteSpace(); // get rid of any leading whitespaces

        // Sometimes, search filters don't have "(" and ")" - add them
        if(getCurrentChar() != BEGIN_FILTER_TOKEN) {
            filter = BEGIN_FILTER_TOKEN + filter + END_FILTER_TOKEN;
        }
        // this would be a good place to strip whitespace if desired

        if(debug) {System.out.println("SearchFilter: normalized filter:" +
                                      filter);}
    }

    private void skipWhiteSpace() {
        while (Character.isWhitespace(getCurrentChar())) {
            consumeChar();
        }
    }

    protected StringFilter createNextFilter()
        throws InvalidSearchFilterException {
        StringFilter filter;

        skipWhiteSpace();

        try {
            // make sure every filter starts with "("
            if(getCurrentChar() != BEGIN_FILTER_TOKEN) {
                throw new InvalidSearchFilterException("expected \"" +
                                                       BEGIN_FILTER_TOKEN +
                                                       "\" at position " +
                                                       pos);
            }

            // skip past the "("
            this.consumeChar();

            skipWhiteSpace();

            // use the next character to determine the type of filter
            switch(getCurrentChar()) {
            case AND_TOKEN:
                if (debug) {System.out.println("SearchFilter: creating AND");}
                filter = new CompoundFilter(true);
                filter.parse();
                break;
            case OR_TOKEN:
                if (debug) {System.out.println("SearchFilter: creating OR");}
                filter = new CompoundFilter(false);
                filter.parse();
                break;
            case NOT_TOKEN:
                if (debug) {System.out.println("SearchFilter: creating OR");}
                filter = new NotFilter();
                filter.parse();
                break;
            default:
                if (debug) {System.out.println("SearchFilter: creating SIMPLE");}
                filter = new AtomicFilter();
                filter.parse();
                break;
            }

            skipWhiteSpace();

            // make sure every filter ends with ")"
            if(getCurrentChar() != END_FILTER_TOKEN) {
                throw new InvalidSearchFilterException("expected \"" +
                                                       END_FILTER_TOKEN +
                                                       "\" at position " +
                                                       pos);
            }

            // skip past the ")"
            this.consumeChar();
        } catch (InvalidSearchFilterException e) {
            if (debug) {System.out.println("rethrowing e");}
            throw e; // just rethrow these

        // catch all - any uncaught exception while parsing will end up here
        } catch  (Exception e) {
            if(debug) {System.out.println(e.getMessage());e.printStackTrace();}
            throw new InvalidSearchFilterException("Unable to parse " +
                    "character " + pos + " in \""+
                    this.filter + "\"");
        }

        return filter;
    }

    protected char getCurrentChar() {
        return filter.charAt(pos);
    }

    protected char relCharAt(int i) {
        return filter.charAt(pos + i);
    }

    protected void consumeChar() {
        pos++;
    }

    protected void consumeChars(int i) {
        pos += i;
    }

    protected int relIndexOf(int ch) {
        return filter.indexOf(ch, pos) - pos;
    }

    protected String relSubstring(int beginIndex, int endIndex){
        if(debug){System.out.println("relSubString: " + beginIndex +
                                     " " + endIndex);}
        return filter.substring(beginIndex+pos, endIndex+pos);
    }


   /**
     * A class for dealing with compound filters ("and" & "or" filters).
     */
    final class CompoundFilter implements StringFilter {
        private Vector<StringFilter>  subFilters;
        private boolean polarity;

        CompoundFilter(boolean polarity) {
            subFilters = new Vector<>();
            this.polarity = polarity;
        }

        public void parse() throws InvalidSearchFilterException {
            SearchFilter.this.consumeChar(); // consume the "&"
            while(SearchFilter.this.getCurrentChar() != END_FILTER_TOKEN) {
                if (debug) {System.out.println("CompoundFilter: adding");}
                StringFilter filter = SearchFilter.this.createNextFilter();
                subFilters.addElement(filter);
                skipWhiteSpace();
            }
        }

        public boolean check(Attributes targetAttrs) throws NamingException {
            for(int i = 0; i<subFilters.size(); i++) {
                StringFilter filter = subFilters.elementAt(i);
                if(filter.check(targetAttrs) != this.polarity) {
                    return !polarity;
                }
            }
            return polarity;
        }
    } /* CompoundFilter */

   /**
     * A class for dealing with NOT filters
     */
    final class NotFilter implements StringFilter {
        private StringFilter    filter;

        public void parse() throws InvalidSearchFilterException {
            SearchFilter.this.consumeChar(); // consume the "!"
            filter = SearchFilter.this.createNextFilter();
        }

        public boolean check(Attributes targetAttrs) throws NamingException {
            return !filter.check(targetAttrs);
        }
    } /* notFilter */

    // note: declared here since member classes can't have static variables
    static final int EQUAL_MATCH = 1;
    static final int APPROX_MATCH = 2;
    static final int GREATER_MATCH = 3;
    static final int LESS_MATCH = 4;

    /**
     * A class for dealing wtih atomic filters
     */
    final class AtomicFilter implements StringFilter {
        private String attrID;
        private String value;
        private int    matchType;

        public void parse() throws InvalidSearchFilterException {

            skipWhiteSpace();

            try {
                // find the end
                int endPos = SearchFilter.this.relIndexOf(END_FILTER_TOKEN);

                //determine the match type
                int i = SearchFilter.this.relIndexOf(EQUAL_TOKEN);
                if(debug) {System.out.println("AtomicFilter: = at " + i);}
                int qualifier = SearchFilter.this.relCharAt(i-1);
                switch(qualifier) {
                case APPROX_TOKEN:
                    if (debug) {System.out.println("Atomic: APPROX found");}
                    matchType = APPROX_MATCH;
                    attrID = SearchFilter.this.relSubstring(0, i-1);
                    value = SearchFilter.this.relSubstring(i+1, endPos);
                    break;

                case GREATER_TOKEN:
                    if (debug) {System.out.println("Atomic: GREATER found");}
                    matchType = GREATER_MATCH;
                    attrID = SearchFilter.this.relSubstring(0, i-1);
                    value = SearchFilter.this.relSubstring(i+1, endPos);
                    break;

                case LESS_TOKEN:
                    if (debug) {System.out.println("Atomic: LESS found");}
                    matchType = LESS_MATCH;
                    attrID = SearchFilter.this.relSubstring(0, i-1);
                    value = SearchFilter.this.relSubstring(i+1, endPos);
                    break;

                case EXTEND_TOKEN:
                    if(debug) {System.out.println("Atomic: EXTEND found");}
                    throw new OperationNotSupportedException("Extensible match not supported");

                default:
                    if (debug) {System.out.println("Atomic: EQUAL found");}
                    matchType = EQUAL_MATCH;
                    attrID = SearchFilter.this.relSubstring(0,i);
                    value = SearchFilter.this.relSubstring(i+1, endPos);
                    break;
                }

                attrID = attrID.trim();
                value = value.trim();

                //update our position
                SearchFilter.this.consumeChars(endPos);

            } catch (Exception e) {
                if (debug) {System.out.println(e.getMessage());
                            e.printStackTrace();}
                InvalidSearchFilterException sfe =
                    new InvalidSearchFilterException("Unable to parse " +
                    "character " + SearchFilter.this.pos + " in \""+
                    SearchFilter.this.filter + "\"");
                sfe.setRootCause(e);
                throw(sfe);
            }

            if(debug) {System.out.println("AtomicFilter: " + attrID + "=" +
                                          value);}
        }

        public boolean check(Attributes targetAttrs) {
            Enumeration<?> candidates;

            try {
                Attribute attr = targetAttrs.get(attrID);
                if(attr == null) {
                    return false;
                }
                candidates = attr.getAll();
            } catch (NamingException ne) {
                if (debug) {System.out.println("AtomicFilter: should never " +
                                               "here");}
                return false;
            }

            while(candidates.hasMoreElements()) {
                String val = candidates.nextElement().toString();
                if (debug) {System.out.println("Atomic: comparing: " + val);}
                switch(matchType) {
                case APPROX_MATCH:
                case EQUAL_MATCH:
                    if(substringMatch(this.value, val)) {
                    if (debug) {System.out.println("Atomic: EQUAL match");}
                        return true;
                    }
                    break;
                case GREATER_MATCH:
                    if (debug) {System.out.println("Atomic: GREATER match");}
                    if(val.compareTo(this.value) >= 0) {
                        return true;
                    }
                    break;
                case LESS_MATCH:
                    if (debug) {System.out.println("Atomic: LESS match");}
                    if(val.compareTo(this.value) <= 0) {
                        return true;
                    }
                    break;
                default:
                    if (debug) {System.out.println("AtomicFilter: unkown " +
                                                   "matchType");}
                }
            }
            return false;
        }

        // used for substring comparisons (where proto has "*" wildcards
        private boolean substringMatch(String proto, String value) {
            // simple case 1: "*" means attribute presence is being tested
            if(proto.equals(new Character(WILDCARD_TOKEN).toString())) {
                if(debug) {System.out.println("simple presence assertion");}
                return true;
            }

            // simple case 2: if there are no wildcards, call String.equals()
            if(proto.indexOf(WILDCARD_TOKEN) == -1) {
                return proto.equalsIgnoreCase(value);
            }

            if(debug) {System.out.println("doing substring comparison");}
            // do the work: make sure all the substrings are present
            int currentPos = 0;
            StringTokenizer subStrs = new StringTokenizer(proto, "*", false);

            // do we need to begin with the first token?
            if(proto.charAt(0) != WILDCARD_TOKEN &&
                    !value.toLowerCase(Locale.ENGLISH).startsWith(
                        subStrs.nextToken().toLowerCase(Locale.ENGLISH))) {
                if(debug) {
                    System.out.println("faild initial test");
                }
                return false;
            }

            while(subStrs.hasMoreTokens()) {
                String currentStr = subStrs.nextToken();
                if (debug) {System.out.println("looking for \"" +
                                               currentStr +"\"");}
                currentPos = value.toLowerCase(Locale.ENGLISH).indexOf(
                       currentStr.toLowerCase(Locale.ENGLISH), currentPos);

                if(currentPos == -1) {
                    return false;
                }
                currentPos += currentStr.length();
            }

            // do we need to end with the last token?
            if(proto.charAt(proto.length() - 1) != WILDCARD_TOKEN &&
               currentPos != value.length() ) {
                if(debug) {System.out.println("faild final test");}
                return false;
            }

            return true;
        }

    } /* AtomicFilter */

    // ----- static methods for producing string filters given attribute set
    // ----- or object array


    /**
      * Creates an LDAP filter as a conjuction of the attributes supplied.
      */
    public static String format(Attributes attrs) throws NamingException {
        if (attrs == null || attrs.size() == 0) {
            return "objectClass=*";
        }

        String answer;
        answer = "(& ";
        Attribute attr;
        for (NamingEnumeration<? extends Attribute> e = attrs.getAll();
             e.hasMore(); ) {
            attr = e.next();
            if (attr.size() == 0 || (attr.size() == 1 && attr.get() == null)) {
                // only checking presence of attribute
                answer += "(" + attr.getID() + "=" + "*)";
            } else {
                for (NamingEnumeration<?> ve = attr.getAll();
                     ve.hasMore(); ) {
                    String val = getEncodedStringRep(ve.next());
                    if (val != null) {
                        answer += "(" + attr.getID() + "=" + val + ")";
                    }
                }
            }
        }

        answer += ")";
        //System.out.println("filter: " + answer);
        return answer;
    }

    // Writes the hex representation of a byte to a StringBuffer.
    private static void hexDigit(StringBuffer buf, byte x) {
        char c;

        c = (char) ((x >> 4) & 0xf);
        if (c > 9)
            c = (char) ((c-10) + 'A');
        else
            c = (char)(c + '0');

        buf.append(c);
        c = (char) (x & 0xf);
        if (c > 9)
            c = (char)((c-10) + 'A');
        else
            c = (char)(c + '0');
        buf.append(c);
    }


    /**
      * Returns the string representation of an object (such as an attr value).
      * If obj is a byte array, encode each item as \xx, where xx is hex encoding
      * of the byte value.
      * Else, if obj is not a String, use its string representation (toString()).
      * Special characters in obj (or its string representation) are then
      * encoded appropriately according to RFC 2254.
      *         *       \2a
      *         (       \28
      *         )       \29
      *         \       \5c
      *         NUL     \00
      */
    private static String getEncodedStringRep(Object obj) throws NamingException {
        String str;
        if (obj == null)
            return null;

        if (obj instanceof byte[]) {
            // binary data must be encoded as \hh where hh is a hex char
            byte[] bytes = (byte[])obj;
            StringBuffer b1 = new StringBuffer(bytes.length*3);
            for (int i = 0; i < bytes.length; i++) {
                b1.append('\\');
                hexDigit(b1, bytes[i]);
            }
            return b1.toString();
        }
        if (!(obj instanceof String)) {
            str = obj.toString();
        } else {
            str = (String)obj;
        }
        int len = str.length();
        StringBuffer buf = new StringBuffer(len);
        char ch;
        for (int i = 0; i < len; i++) {
            switch (ch=str.charAt(i)) {
            case '*':
                buf.append("\\2a");
                break;
            case '(':
                buf.append("\\28");
                break;
            case ')':
                buf.append("\\29");
                break;
            case '\\':
                buf.append("\\5c");
                break;
            case 0:
                buf.append("\\00");
                break;
            default:
                buf.append(ch);
            }
        }
        return buf.toString();
    }


    /**
      * Finds the first occurrence of <tt>ch in val starting
      * from position <tt>start. It doesn't count if ch
      * has been escaped by a backslash (\)
      */
    public static int findUnescaped(char ch, String val, int start) {
        int len = val.length();

        while (start < len) {
            int where = val.indexOf(ch, start);
            // if at start of string, or not there at all, or if not escaped
            if (where == start || where == -1 || val.charAt(where-1) != '\\')
                return where;

            // start search after escaped star
            start = where + 1;
        }
        return -1;
    }

    /**
     * Formats the expression <tt>expr using arguments from the array
     * <tt>args.
     *
     * <code>{i} specifies the i'th element from
     * the array <code>args is to be substituted for the
     * string "<code>{i}".
     *
     * To escape '{' or '}' (or any other character), use '\'.
     *
     * Uses getEncodedStringRep() to do encoding.
     */

    public static String format(String expr, Object[] args)
        throws NamingException {

         int param;
         int where = 0, start = 0;
         StringBuffer answer = new StringBuffer(expr.length());

         while ((where = findUnescaped('{', expr, start)) >= 0) {
             int pstart = where + 1; // skip '{'
             int pend = expr.indexOf('}', pstart);

             if (pend < 0) {
                 throw new InvalidSearchFilterException("unbalanced {: " + expr);
             }

             // at this point, pend should be pointing at '}'
             try {
                 param = Integer.parseInt(expr.substring(pstart, pend));
             } catch (NumberFormatException e) {
                 throw new InvalidSearchFilterException(
                     "integer expected inside {}: " + expr);
             }

             if (param >= args.length) {
                 throw new InvalidSearchFilterException(
                     "number exceeds argument list: " + param);
             }

             answer.append(expr.substring(start, where)).append(getEncodedStringRep(args[param]));
             start = pend + 1; // skip '}'
         }

         if (start < expr.length())
             answer.append(expr.substring(start));

        return answer.toString();
    }

    /*
     * returns an Attributes instance containing only attributeIDs given in
     * "attributeIDs" whose values come from the given DSContext.
     */
    public static Attributes selectAttributes(Attributes originals,
        String[] attrIDs) throws NamingException {

        if (attrIDs == null)
            return originals;

        Attributes result = new BasicAttributes();

        for(int i=0; i<attrIDs.length; i++) {
            Attribute attr = originals.get(attrIDs[i]);
            if(attr != null) {
                result.put(attr);
            }
        }

        return result;
    }

/*  For testing filter
    public static void main(String[] args) {

        Attributes attrs = new BasicAttributes(LdapClient.caseIgnore);
        attrs.put("cn", "Rosanna Lee");
        attrs.put("sn", "Lee");
        attrs.put("fn", "Rosanna");
        attrs.put("id", "10414");
        attrs.put("machine", "jurassic");


        try {
            System.out.println(format(attrs));

            String  expr = "(&(Age = {0})(Account Balance <= {1}))";
            Object[] fargs = new Object[2];
            // fill in the parameters
            fargs[0] = new Integer(65);
            fargs[1] = new Float(5000);

            System.out.println(format(expr, fargs));


            System.out.println(format("bin={0}",
                new Object[] {new byte[] {0, 1, 2, 3, 4, 5}}));

            System.out.println(format("bin=\\{anything}", null));

        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
*/

}

Other Java examples (source code examples)

Here is a short list of links related to this Java SearchFilter.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.