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