View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.normalization;
18  
19  
20  import org.apache.ldap.common.filter.*;
21  import org.apache.ldap.common.name.NameComponentNormalizer;
22  import org.slf4j.Logger;
23  import org.slf4j.LoggerFactory;
24  
25  import javax.naming.NamingException;
26  import java.util.ArrayList;
27  
28  
29  /***
30   * A filter visitor which normalizes leaf node values as it visits them.  It also removes
31   * leaf nodes from branches whose attributeType is undefined.  It obviously cannot remove
32   * a leaf node from a filter which is only a leaf node.  Checks to see if a filter is a
33   * leaf node with undefined attributeTypes should be done outside this visitor.
34   *
35   * Since this visitor may remove filter nodes it may produce negative results on filters,
36   * like NOT branch nodes without a child or AND and OR nodes with one or less children.  This
37   * might make some partition implementations choke.  To avoid this problem we clean up branch
38   * nodes that don't make sense.  For example all BranchNodes without children are just
39   * removed.  An AND and OR BranchNode with a single child is replaced with it's child for
40   * all but the topmost branchnode which we cannot replace.  So again the top most branch
41   * node must be inspected by code outside of this visitor.
42   *
43   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44   * @version $Rev$
45   */
46  public class ValueNormalizingVisitor implements FilterVisitor
47  {
48      /*** logger used by this class */
49      private final static Logger log = LoggerFactory.getLogger( ValueNormalizingVisitor.class );
50      /*** the name component normalizer used by this visitor */
51      private final NameComponentNormalizer ncn;
52  
53  
54      public ValueNormalizingVisitor( NameComponentNormalizer ncn )
55      {
56          this.ncn = ncn;
57      }
58  
59  
60      public void visit( ExprNode node )
61      {
62          if ( node instanceof SimpleNode )
63          {
64              SimpleNode snode = ( SimpleNode ) node;
65              String normalized;
66  
67              try
68              {
69                  // still need this check here in case the top level is a leaf node
70                  // with an undefined attributeType for its attribute
71                  if ( ! ncn.isDefined( snode.getAttribute() ) )
72                  {
73                      normalized = snode.getValue();
74                  }
75                  else if ( Character.isDigit( snode.getAttribute().charAt( 0 ) ) )
76                  {
77                      normalized = ncn.normalizeByOid( snode.getAttribute(), snode.getValue() );
78                  }
79                  else
80                  {
81                      normalized = ncn.normalizeByName( snode.getAttribute(), snode.getValue() );
82                  }
83              }
84              catch ( NamingException e )
85              {
86                  log.error( "Failed to normalize filter value: " + e.getMessage(), e );
87                  throw new RuntimeException( e.getMessage() );
88              }
89  
90              snode.setValue( normalized );
91              return;
92          }
93  
94          if ( node instanceof BranchNode )
95          {
96              BranchNode bnode = ( BranchNode ) node;
97              StringBuffer buf = null;
98              for ( int ii = 0; ii < bnode.getChildren().size() ; ii++ )
99              {
100                 // before visiting each node let's check to make sure non-branch children use
101                 // attributes that are defined in the system, if undefined nodes are removed
102                 ExprNode child = ( ExprNode ) bnode.getChildren().get( ii );
103                 if ( child.isLeaf() )
104                 {
105                     LeafNode ln = ( LeafNode ) child;
106                     if ( ! ncn.isDefined( ln.getAttribute() ) )
107                     {
108                         if ( buf == null )
109                         {
110                             buf = new StringBuffer();
111                         }
112                         else
113                         {
114                             buf.setLength( 0 );
115                         }
116                         buf.append( "Removing leaf node based on undefined attribute '" );
117                         buf.append( ln.getAttribute() );
118                         buf.append( "' from filter." );
119                         log.warn( buf.toString() );
120 
121                         // remove the child at ii
122                         bnode.getChildren().remove( child );
123                         ii--; // decrement so we can evaluate next child which has shifted to ii
124                         continue;
125                     }
126                 }
127 
128                 visit( child );
129             }
130 
131             // now see if any branch child nodes are damaged (NOT without children,
132             // AND/OR with one or less children) and repair them by removing branch
133             // nodes without children and replacing branch nodes like AND/OR with
134             // their single child if other branch nodes do not remain.
135             for ( int ii = 0; ii < bnode.getChildren().size() ; ii++ )
136             {
137                 ExprNode unknown = ( ExprNode ) bnode.getChildren().get( ii );
138                 if ( !unknown.isLeaf() )
139                 {
140                     BranchNode child = ( BranchNode ) unknown;
141 
142                     // remove child branch node that has no children left
143                     if ( child.getChildren().size() == 0 )
144                     {
145                         // remove the child at ii
146                         bnode.getChildren().remove( child );
147                         ii--; // decrement so we can evaluate next child which has shifted to ii
148                         continue;
149                     }
150 
151                     // now for AND & OR nodes with a single child left replace them
152                     // with their child at the same index they AND/OR node was in
153                     if ( child.getChildren().size() == 1 && child.getOperator() != BranchNode.NOT )
154                     {
155                         bnode.getChildren().remove( child );
156                         if ( ii >= bnode.getChildren().size() )
157                         {
158                             bnode.getChildren().add( child.getChild() );
159                         }
160                         else
161                         {
162                             bnode.getChildren().add( ii, child.getChild() );
163                         }
164                     }
165                 }
166             }
167         }
168     }
169 
170 
171     public boolean canVisit( ExprNode node )
172     {
173         return node instanceof BranchNode || node instanceof SimpleNode;
174     }
175 
176 
177     public boolean isPrefix()
178     {
179         return false;
180     }
181 
182 
183     public ArrayList getOrder( BranchNode node, ArrayList children )
184     {
185         return children;
186     }
187 }