001    package org.apache.ojb.broker.query;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    /**
019     * Model a Like Criteria</br> 
020     * Escape Processing by Paul R. Nase 
021     * <p>
022     * The pattern string is a simple pattern string using % or * as a wildcard.
023     * So Ander* would match Anderson and Anderton. The _ or ? character is used to match a single occurence
024     * of a character. The '\' is used to escape the wildcard characters so that we can search for
025     * strings containing * and ?. 
026     * <p>
027     * To change the escape character use setEscapeCharacter. 
028     * @see LikeCriteria#setEscapeCharacter(char)
029     * 
030     * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi </a>
031     * @author <a href="mailto:Nase.Paul@mayo.edu">Paul Nase </a>
032     * @version $Id: LikeCriteria.java,v 1.1 2007-08-24 22:17:36 ewestfal Exp $
033     */
034    public class LikeCriteria extends ValueCriteria
035    {
036        /**
037         * The Dfault-Character used for Escaping Wildcards
038         */
039        public static final char DEFAULT_ESCPAPE_CHARACTER = '\\';
040    
041        /**
042         * The Character used for Escaping Wildcards
043         */
044        private static char escapeCharacter = DEFAULT_ESCPAPE_CHARACTER;
045    
046            /**
047             * @param anAttribute
048             * @param aValue
049             * @param aClause
050             * @param anAlias
051             */
052            public LikeCriteria(Object anAttribute, Object aValue, String aClause, String anAlias)
053            {
054                    super(anAttribute, generateSQLSearchPattern(aValue), aClause, anAlias);
055            }
056    
057            /**
058             * @param anAttribute
059             * @param aValue
060             * @param aClause
061             * @param anAlias
062             */
063            public LikeCriteria(Object anAttribute, Object aValue, String aClause, UserAlias anAlias)
064            {
065                    super(anAttribute, generateSQLSearchPattern(aValue), aClause, anAlias);
066            }
067    
068        /**
069         * @see org.apache.ojb.broker.query.SelectionCriteria#bind(java.lang.Object)
070         */
071        public void bind(Object newValue)
072        {
073            super.bind(generateSQLSearchPattern(newValue));
074        }
075    
076        /**
077         * Generate a SQL search string from the pattern string passed. 
078         * The pattern string is a simple pattern string using % or * as a wildcard. 
079         * So Ander* would match Anderson and Anderton. The _ or ? character is used to match a single occurence
080         * of a character. The escapeCharacter is used to escape the wildcard characters so that we can search for
081         * strings containing * and ?. This method converts the criteria wildcard strings to SQL wildcards.
082         * 
083         * @param pattern a criteria search pattern containing optional wildcards
084         * @return a SQL search pattern string with all escape codes processed.
085         */
086            private static String generateSQLSearchPattern(Object pattern)
087            {
088                    if (pattern == null)
089                    {
090                            return null;
091                    }
092                    else
093                    {
094                            StringBuffer sqlpattern = new StringBuffer();
095                            char[] chars = pattern.toString().toCharArray();
096    
097                            for (int i = 0; i < chars.length; i++)
098                            {
099                                    if (chars[i] == escapeCharacter)
100                                    {
101                                            // for the escape character add the next char as is.
102                                            // find the next non-escape character.
103                                            int x = i + 1;
104                                            for (;(x < chars.length); x++)
105                                            {
106                                                    if (chars[x] != escapeCharacter)
107                                                    {
108                                                            break;
109                                                    }
110                                            }
111                                            boolean oddEscapes = (((x - i) % 2) > 0) ? true : false;
112                                            if (oddEscapes)
113                                            {
114                                                    // only escape characters allowed are '%', '_', and '\'
115                                                    // if the escaped character is a '\', then oddEscapes
116                                                    // will be false.
117                                                    // if the character following this last escape is not a
118                                                    // '%' or an '_', eat this escape character.
119                                                    if ((x < chars.length)
120                                                            && ((chars[x] == '%') || (chars[x] == '_')))
121                                                    {
122                                                            // leave the escape character in, along with the following char
123                                                            x++;
124                                                    }
125                                                    else
126                                                    {
127                                                            // remove the escape character, will cause problems in sql statement.
128                                                            i++; // removing the first escape character.
129                                                            if ((x < chars.length)
130                                                                    && ((chars[x] == '*') || (chars[x] == '?')))
131                                                            {
132                                                                    // but if it is a '*' or a '?', we want to keep these
133                                                                    // characters as is, they were 'escaped' out.
134                                                                    x++; // include the first non-escape character.
135                                                            }
136                                                    }
137                                            }
138                                            if (i < chars.length)
139                                            {
140                                                    sqlpattern.append(chars, i, x - i);
141                                            }
142                                            i = x - 1; // set index to last character copied.
143                                    }
144                                    else if (chars[i] == '*')
145                                    {
146                                            sqlpattern.append("%");
147                                    }
148                                    else if (chars[i] == '?')
149                                    {
150                                            sqlpattern.append("_");
151                                    }
152                                    else
153                                    {
154                                            sqlpattern.append(chars[i]);
155                                    }
156                            }
157                            return sqlpattern.toString();
158                    }
159            }
160    
161        /**
162         * @return Returns the escapeCharacter.
163         */
164        public static char getEscapeCharacter()
165        {
166            return escapeCharacter;
167        }
168        
169        /**
170         * Global change of the escapeCharacter
171         * @param escChar The escapeCharacter to set.
172         */
173        public static void setEscapeCharacter(char escChar)
174        {
175            escapeCharacter = escChar;
176        }
177        
178    }