001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    https://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.directory.api.ldap.model.filter;
021
022
023import java.util.HashMap;
024import java.util.Map;
025
026import org.apache.directory.api.i18n.I18n;
027
028
029/**
030 * Abstract implementation of a expression node.
031 * 
032 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
033 */
034public abstract class AbstractExprNode implements ExprNode
035{
036    /** The map of annotations */
037    protected Map<String, Object> annotations;
038
039    /** The node type */
040    protected final AssertionType assertionType;
041
042
043    /**
044     * Creates a node by setting abstract node type.
045     * 
046     * @param assertionType The node's type
047     */
048    protected AbstractExprNode( AssertionType assertionType )
049    {
050        this.assertionType = assertionType;
051    }
052
053
054    /**
055     * @see ExprNode#getAssertionType()
056     * 
057     * @return the node's type
058     */
059    @Override
060    public AssertionType getAssertionType()
061    {
062        return assertionType;
063    }
064
065
066    /**
067     * @see Object#equals(Object)
068     *@return <code>true</code> if both objects are equal 
069     */
070    @Override
071    public boolean equals( Object o )
072    {
073        // Shortcut for equals object
074        if ( this == o )
075        {
076            return true;
077        }
078
079        if ( !( o instanceof AbstractExprNode ) )
080        {
081            return false;
082        }
083
084        AbstractExprNode that = ( AbstractExprNode ) o;
085
086        // Check the node type
087        if ( this.assertionType != that.assertionType )
088        {
089            return false;
090        }
091
092        if ( annotations == null )
093        {
094            return that.annotations == null;
095        }
096        else if ( that.annotations == null )
097        {
098            return false;
099        }
100
101        // Check all the annotation
102        for ( Map.Entry<String, Object> entry : annotations.entrySet() )
103        {
104            String key = entry.getKey();
105        
106            if ( !that.annotations.containsKey( key ) )
107            {
108                return false;
109            }
110
111            Object thisAnnotation = entry.getValue();
112            Object thatAnnotation = that.annotations.get( key );
113
114            if ( thisAnnotation == null )
115            {
116                if ( thatAnnotation != null )
117                {
118                    return false;
119                }
120            }
121            else
122            {
123                if ( !thisAnnotation.equals( thatAnnotation ) )
124                {
125                    return false;
126                }
127            }
128        }
129
130        return true;
131    }
132
133
134    /**
135     * @see Object#hashCode()
136     * @return the instance's hash code 
137     */
138    @Override
139    public int hashCode()
140    {
141        int h = 37;
142
143        if ( annotations != null )
144        {
145            for ( Map.Entry<String, Object> entry : annotations.entrySet() )
146            {
147                String key = entry.getKey();
148                Object value = entry.getValue();
149
150                h = h * 17 + key.hashCode();
151                h = h * 17 + ( value == null ? 0 : value.hashCode() );
152            }
153        }
154
155        return h;
156    }
157
158
159    /**
160     * @see ExprNode#get(java.lang.Object)
161     * 
162     * @return the annotation value.
163     */
164    @Override
165    public Object get( Object key )
166    {
167        if ( null == annotations )
168        {
169            return null;
170        }
171
172        return annotations.get( key );
173    }
174
175
176    /**
177     * @see ExprNode#set(String, Object)
178     */
179    @Override
180    public void set( String key, Object value )
181    {
182        if ( null == annotations )
183        {
184            annotations = new HashMap<>( 2 );
185        }
186
187        annotations.put( key, value );
188    }
189
190
191    /**
192     * Gets the annotations as a Map.
193     * 
194     * @return the annotation map.
195     */
196    protected Map<String, Object> getAnnotations()
197    {
198        return annotations;
199    }
200
201
202    /**
203     * Default implementation for this method : just throw an exception.
204     * 
205     * @param buf the buffer to append to.
206     * @return The buffer in which the refinement has been appended
207     * @throws UnsupportedOperationException if this node isn't a part of a refinement.
208     */
209    @Override
210    public StringBuilder printRefinementToBuffer( StringBuilder buf )
211    {
212        throw new UnsupportedOperationException( I18n.err( I18n.ERR_13303_SCOPENODE_IN_REFINEMENT ) );
213    }
214
215
216    /**
217     * Clone the object
218     */
219    @Override
220    public ExprNode clone()
221    {
222        try
223        {
224            ExprNode clone = ( ExprNode ) super.clone();
225
226            if ( annotations != null )
227            {
228                for ( Map.Entry<String, Object> entry : annotations.entrySet() )
229                {
230                    // Note : the value aren't cloned ! 
231                    ( ( AbstractExprNode ) clone ).annotations.put( entry.getKey(), entry.getValue() );
232                }
233            }
234
235            return clone;
236        }
237        catch ( CloneNotSupportedException cnse )
238        {
239            return null;
240        }
241    }
242
243
244    /**
245     * @see Object#toString()
246     */
247    @Override
248    public String toString()
249    {
250        if ( ( null != annotations ) && annotations.containsKey( "count" ) )
251        {
252            Long count = ( Long ) annotations.get( "count" );
253
254            if ( count == Long.MAX_VALUE )
255            {
256                return ":[\u221E]";
257            }
258
259            return ":[" + count + "]";
260        }
261        else
262        {
263            return "";
264        }
265    }
266}