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.entry;
021
022
023import java.io.IOException;
024import java.io.ObjectInput;
025import java.io.ObjectOutput;
026
027import org.apache.directory.api.i18n.I18n;
028import org.apache.directory.api.ldap.model.exception.LdapException;
029import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
030import org.apache.directory.api.ldap.model.schema.AttributeType;
031import org.apache.directory.api.ldap.model.schema.SchemaManager;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035
036/**
037 * An internal implementation for a ModificationItem. The name has been
038 * chosen so that it does not conflict with @see ModificationItem
039 *
040 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
041 */
042public class DefaultModification implements Modification
043{
044    /** The modification operation */
045    private ModificationOperation operation;
046
047    /** The attribute which contains the modification */
048    private Attribute attribute;
049
050    /** The AtributeType */
051    private AttributeType attributeType;
052
053    /** logger for reporting errors that might not be handled properly upstream */
054    protected static final Logger LOG = LoggerFactory.getLogger( Modification.class );
055
056
057    /**
058     * Creates a new instance of DefaultModification.
059     */
060    public DefaultModification()
061    {
062        // Default empty constructor
063    }
064
065
066    /**
067     * Creates a new instance of DefaultModification.
068     *
069     * @param operation The modification operation
070     * @param attribute The associated attribute 
071     */
072    public DefaultModification( ModificationOperation operation, Attribute attribute )
073    {
074        this.operation = operation;
075        this.attribute = attribute;
076    }
077
078
079    /**
080     * Creates a new instance of DefaultModification.
081     *
082     * @param operation The modification operation
083     * @param attributeId The associated attribute ID
084     * @param values the associated values
085     */
086    public DefaultModification( ModificationOperation operation, String attributeId, String... values )
087    {
088        this.operation = operation;
089        this.attribute = new DefaultAttribute( attributeId, values );
090    }
091
092
093    /**
094     * Creates a new instance of DefaultModification.
095     *
096     * @param operation The modification operation
097     * @param attributeId The associated attribute ID
098     * @param values the associated values
099     */
100    public DefaultModification( ModificationOperation operation, String attributeId, byte[]... values )
101    {
102        this.operation = operation;
103        this.attribute = new DefaultAttribute( attributeId, values );
104    }
105
106
107    /**
108     * Creates a new instance of DefaultModification.
109     *
110     * @param operation The modification operation
111     * @param attributeId The associated attribute ID
112     * @param values the associated values
113     */
114    public DefaultModification( ModificationOperation operation, String attributeId, Value... values )
115    {
116        this.operation = operation;
117        this.attribute = new DefaultAttribute( attributeId, values );
118    }
119
120
121    /**
122     * Creates a new instance of DefaultModification with no value
123     *
124     * @param operation The modification operation
125     * @param attributeId The associated attribute ID
126     */
127    public DefaultModification( ModificationOperation operation, String attributeId )
128    {
129        this.operation = operation;
130        this.attribute = new DefaultAttribute( attributeId );
131    }
132
133
134    /**
135     * Creates a new instance of DefaultModification.
136     *
137     * @param operation The modification operation
138     * @param attributeType The associated attributeType
139     * @param values the associated values
140     * @throws LdapInvalidAttributeValueException If the value is invalid
141     */
142    public DefaultModification( ModificationOperation operation, AttributeType attributeType, String... values )
143        throws LdapInvalidAttributeValueException
144    {
145        this.operation = operation;
146        this.attribute = new DefaultAttribute( attributeType, values );
147    }
148
149
150    /**
151     * Creates a new instance of DefaultModification.
152     *
153     * @param operation The modification operation
154     * @param attributeType The associated attributeType
155     * @param values the associated values
156     * @throws LdapInvalidAttributeValueException If the value is invalid
157     */
158    public DefaultModification( ModificationOperation operation, AttributeType attributeType, byte[]... values )
159        throws LdapInvalidAttributeValueException
160    {
161        this.operation = operation;
162        this.attribute = new DefaultAttribute( attributeType, values );
163    }
164
165
166    /**
167     * Creates a new instance of DefaultModification.
168     *
169     * @param operation The modification operation
170     * @param attributeType The associated attributeType
171     * @param values the associated values
172     * @throws LdapInvalidAttributeValueException If the value is invalid
173     */
174    public DefaultModification( ModificationOperation operation, AttributeType attributeType, Value... values )
175        throws LdapInvalidAttributeValueException
176    {
177        this.operation = operation;
178        this.attribute = new DefaultAttribute( attributeType, values );
179    }
180
181
182    /**
183     * Creates a new instance of DefaultModification with no value.
184     *
185     * @param operation The modification operation
186     * @param attributeType The associated attributeType
187     * @throws LdapInvalidAttributeValueException If the value is invalid
188     */
189    public DefaultModification( ModificationOperation operation, AttributeType attributeType )
190        throws LdapInvalidAttributeValueException
191    {
192        this.operation = operation;
193        this.attribute = new DefaultAttribute( attributeType );
194    }
195
196
197    /**
198     * Creates a new instance of DefaultModification.
199     *
200     * @param schemaManager The schema manager 
201     * @param modification The modification
202     */
203    public DefaultModification( SchemaManager schemaManager, Modification modification )
204    {
205        operation = modification.getOperation();
206
207        Attribute modAttribute = modification.getAttribute();
208
209        try
210        {
211            AttributeType at = modAttribute.getAttributeType();
212
213            if ( at == null )
214            {
215                at = schemaManager.lookupAttributeTypeRegistry( modAttribute.getId() );
216            }
217
218            attribute = new DefaultAttribute( at, modAttribute );
219        }
220        catch ( LdapException ne )
221        {
222            // The attributeType is incorrect. Log, but do nothing otherwise.
223            LOG.error( I18n.err( I18n.ERR_13230_INCORRECT_ATTRIBUTE, modAttribute.getId() ) );
224        }
225    }
226
227
228    /**
229     * {@inheritDoc}
230     */
231    @Override
232    public ModificationOperation getOperation()
233    {
234        return operation;
235    }
236
237
238    /**
239     * {@inheritDoc}
240     */
241    @Override
242    public void setOperation( int operation )
243    {
244        this.operation = ModificationOperation.getOperation( operation );
245    }
246
247
248    /**
249     * {@inheritDoc}
250     */
251    @Override
252    public void setOperation( ModificationOperation operation )
253    {
254        this.operation = operation;
255    }
256
257
258    /**
259     * {@inheritDoc}
260     */
261    @Override
262    public Attribute getAttribute()
263    {
264        return attribute;
265    }
266
267
268    /**
269     * {@inheritDoc}
270     */
271    @Override
272    public void setAttribute( Attribute attribute )
273    {
274        this.attribute = attribute;
275    }
276
277
278    /**
279     * {@inheritDoc}
280     */
281    @Override
282    public void apply( AttributeType attributeType ) throws LdapInvalidAttributeValueException
283    {
284        this.attributeType = attributeType;
285
286        if ( attribute != null )
287        {
288            attribute.apply( attributeType );
289        }
290    }
291
292
293    /**
294     * @return The associated AttributeType
295     */
296    public AttributeType getAttributeType()
297    {
298        return attributeType;
299    }
300
301
302    /**
303     * @see Object#equals(Object)
304     * @return <code>true</code> if both values are equal
305     */
306    @Override
307    public boolean equals( Object that )
308    {
309        // Basic equals checks
310        if ( this == that )
311        {
312            return true;
313        }
314
315        if ( !( that instanceof Modification ) )
316        {
317            return false;
318        }
319
320        Modification otherModification = ( Modification ) that;
321
322        // Check the operation
323        if ( operation != otherModification.getOperation() )
324        {
325            return false;
326        }
327
328        // Check the attribute
329        if ( attribute == null )
330        {
331            return otherModification.getAttribute() == null;
332        }
333
334        return attribute.equals( otherModification.getAttribute() );
335    }
336
337
338    /**
339     * Compute the modification @see Object#hashCode
340     * @return the instance's hash code 
341     */
342    @Override
343    public int hashCode()
344    {
345        int h = 37;
346
347        h += h * 17 + operation.getValue();
348        h += h * 17 + attribute.hashCode();
349
350        return h;
351    }
352
353
354    /**
355     * @see java.io.Externalizable#readExternal(ObjectInput)
356     */
357    @Override
358    public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
359    {
360        // The operation
361        operation = ModificationOperation.getOperation( in.readInt() );
362
363        // The EntryAttribute if we have some
364        boolean hasAttribute = in.readBoolean();
365
366        if ( hasAttribute )
367        {
368            attribute = new DefaultAttribute();
369            attribute.readExternal( in );
370        }
371    }
372
373
374    /**
375     * @see java.io.Externalizable#writeExternal(ObjectOutput)
376     */
377    @Override
378    public void writeExternal( ObjectOutput out ) throws IOException
379    {
380        // The operation
381        out.writeInt( operation.getValue() );
382
383        // The EntryAttribute if not null
384        if ( attribute != null )
385        {
386            out.writeBoolean( true );
387            attribute.writeExternal( out );
388        }
389        else
390        {
391            out.writeBoolean( false );
392        }
393
394        out.flush();
395    }
396
397
398    /**
399     * {@inheritDoc}
400     */
401    @Override
402    public DefaultModification clone()
403    {
404        try
405        {
406            DefaultModification clone = ( DefaultModification ) super.clone();
407
408            clone.attribute = this.attribute.clone();
409            return clone;
410        }
411        catch ( CloneNotSupportedException cnse )
412        {
413            return null;
414        }
415    }
416
417
418    /**
419     * @see Object#toString()
420     */
421    @Override
422    public String toString()
423    {
424        StringBuilder sb = new StringBuilder();
425
426        sb.append( "Modification: " ).
427            append( operation ).
428            append( ", [" ).
429            append( attribute.getId() );
430        
431        if ( attribute.size() == 0 )
432        {
433            if ( operation == ModificationOperation.INCREMENT_ATTRIBUTE )
434            { 
435                sb.append( " : 1" );
436            }
437        }
438        else
439        {
440            sb.append( " : " ).append( attribute );
441        }
442        
443        sb.append( "]" );
444
445        return sb.toString();
446    }
447}