View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.api.ldap.model.filter;
21  
22  
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import org.apache.directory.api.i18n.I18n;
27  import org.apache.directory.api.ldap.model.entry.BinaryValue;
28  import org.apache.directory.api.ldap.model.entry.StringValue;
29  import org.apache.directory.api.ldap.model.entry.Value;
30  
31  
32  /**
33   * Abstract implementation of a expression node.
34   * 
35   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
36   */
37  public abstract class AbstractExprNode implements ExprNode
38  {
39      /** The map of annotations */
40      protected Map<String, Object> annotations;
41  
42      /** The node type */
43      protected final AssertionType assertionType;
44  
45      /** A flag set to true if the Node is Schema aware */
46      protected boolean isSchemaAware;
47  
48  
49      /**
50       * Creates a node by setting abstract node type.
51       * 
52       * @param assertionType The node's type
53       */
54      protected AbstractExprNode( AssertionType assertionType )
55      {
56          this.assertionType = assertionType;
57      }
58  
59  
60      /**
61       * @see ExprNode#getAssertionType()
62       * 
63       * @return the node's type
64       */
65      public AssertionType getAssertionType()
66      {
67          return assertionType;
68      }
69  
70  
71      /**
72       * @see Object#equals(Object)
73       *@return <code>true</code> if both objects are equal 
74       */
75      public boolean equals( Object o )
76      {
77          // Shortcut for equals object
78          if ( this == o )
79          {
80              return true;
81          }
82  
83          if ( !( o instanceof AbstractExprNode ) )
84          {
85              return false;
86          }
87  
88          AbstractExprNode that = ( AbstractExprNode ) o;
89  
90          // Check the node type
91          if ( this.assertionType != that.assertionType )
92          {
93              return false;
94          }
95  
96          if ( annotations == null )
97          {
98              return that.annotations == null;
99          }
100         else if ( that.annotations == null )
101         {
102             return false;
103         }
104 
105         // Check all the annotation
106         for ( String key : annotations.keySet() )
107         {
108             if ( !that.annotations.containsKey( key ) )
109             {
110                 return false;
111             }
112 
113             Object thisAnnotation = annotations.get( key );
114             Object thatAnnotation = that.annotations.get( key );
115 
116             if ( thisAnnotation == null )
117             {
118                 if ( thatAnnotation != null )
119                 {
120                     return false;
121                 }
122             }
123             else
124             {
125                 if ( !thisAnnotation.equals( thatAnnotation ) )
126                 {
127                     return false;
128                 }
129             }
130         }
131 
132         return true;
133     }
134 
135 
136     /**
137      * Handles the escaping of special characters in LDAP search filter assertion values using the
138      * &lt;valueencoding&gt; rule as described in
139      * <a href="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</a>. Needed so that
140      * {@link ExprNode#printToBuffer(StringBuffer)} results in a valid filter string that can be parsed
141      * again (as a way of cloning filters).
142      *
143      * @param value Right hand side of "attrId=value" assertion occurring in an LDAP search filter.
144      * @return Escaped version of <code>value</code>
145      */
146     protected static Value<?> escapeFilterValue( Value<?> value )
147     {
148         if ( value.isNull() )
149         {
150             return value;
151         }
152 
153         StringBuilder sb = null;
154         String val;
155 
156         if ( !value.isHumanReadable() )
157         {
158             sb = new StringBuilder( ( ( BinaryValue ) value ).getReference().length * 3 );
159 
160             for ( byte b : ( ( BinaryValue ) value ).getReference() )
161             {
162                 if ( ( b < 0x7F ) && ( b >= 0 ) )
163                 {
164                     switch ( b )
165                     {
166                         case '*':
167                             sb.append( "\\2A" );
168                             break;
169 
170                         case '(':
171                             sb.append( "\\28" );
172                             break;
173 
174                         case ')':
175                             sb.append( "\\29" );
176                             break;
177 
178                         case '\\':
179                             sb.append( "\\5C" );
180                             break;
181 
182                         case '\0':
183                             sb.append( "\\00" );
184                             break;
185 
186                         default:
187                             sb.append( ( char ) b );
188                     }
189                 }
190                 else
191                 {
192                     sb.append( '\\' );
193                     String digit = Integer.toHexString( b & 0x00FF );
194 
195                     if ( digit.length() == 1 )
196                     {
197                         sb.append( '0' );
198                     }
199 
200                     sb.append( digit.toUpperCase() );
201                 }
202             }
203 
204             return new StringValue( sb.toString() );
205         }
206 
207         val = ( ( StringValue ) value ).getString();
208         String encodedVal = FilterEncoder.encodeFilterValue( val );
209         if ( val.equals( encodedVal ) )
210         {
211             return value;
212         }
213         else
214         {
215             return new StringValue( encodedVal );
216         }
217     }
218 
219 
220     /**
221      * @see Object#hashCode()
222      * @return the instance's hash code 
223      */
224     public int hashCode()
225     {
226         int h = 37;
227 
228         if ( annotations != null )
229         {
230             for ( String key : annotations.keySet() )
231             {
232                 Object value = annotations.get( key );
233 
234                 h = h * 17 + key.hashCode();
235                 h = h * 17 + ( value == null ? 0 : value.hashCode() );
236             }
237         }
238 
239         return h;
240     }
241 
242 
243     /**
244      * @see ExprNode#get(java.lang.Object)
245      * 
246      * @return the annotation value.
247      */
248     public Object get( Object key )
249     {
250         if ( null == annotations )
251         {
252             return null;
253         }
254 
255         return annotations.get( key );
256     }
257 
258 
259     /**
260      * @see ExprNode#set(java.lang.Object,
261      *      java.lang.Object)
262      */
263     public void set( String key, Object value )
264     {
265         if ( null == annotations )
266         {
267             annotations = new HashMap<String, Object>( 2 );
268         }
269 
270         annotations.put( key, value );
271     }
272 
273 
274     /**
275      * Gets the annotations as a Map.
276      * 
277      * @return the annotation map.
278      */
279     protected Map<String, Object> getAnnotations()
280     {
281         return annotations;
282     }
283 
284 
285     /**
286      * Tells if this Node is Schema aware.
287      * 
288      * @return true if the Node is SchemaAware
289      */
290     public boolean isSchemaAware()
291     {
292         return isSchemaAware;
293     }
294 
295 
296     /**
297      * Default implementation for this method : just throw an exception.
298      * 
299      * @param buf the buffer to append to.
300      * @return The buffer in which the refinement has been appended
301      * @throws UnsupportedOperationException if this node isn't a part of a refinement.
302      */
303     public StringBuilder printRefinementToBuffer( StringBuilder buf )
304     {
305         throw new UnsupportedOperationException( I18n.err( I18n.ERR_04144 ) );
306     }
307 
308 
309     /**
310      * Clone the object
311      */
312     @Override
313     public ExprNode clone()
314     {
315         try
316         {
317             ExprNode clone = ( ExprNode ) super.clone();
318 
319             if ( annotations != null )
320             {
321                 for ( String key : annotations.keySet() )
322                 {
323                     Object value = annotations.get( key );
324 
325                     // Note : the value aren't cloned ! 
326                     ( ( AbstractExprNode ) clone ).annotations.put( key, value );
327                 }
328             }
329 
330             return clone;
331         }
332         catch ( CloneNotSupportedException cnse )
333         {
334             return null;
335         }
336     }
337 
338 
339     /**
340      * @see Object#toString()
341      */
342     public String toString()
343     {
344         if ( ( null != annotations ) && annotations.containsKey( "count" ) )
345         {
346             Long count = ( Long ) annotations.get( "count" );
347 
348             if ( count == Long.MAX_VALUE )
349             {
350                 return ":[\u221E]";
351             }
352 
353             return ":[" + count + "]";
354         }
355         else
356         {
357             return "";
358         }
359     }
360 }