| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.directory.shared.ldap.util; |
| |
| |
| import java.text.ParseException; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import javax.naming.NamingEnumeration; |
| import javax.naming.NamingException; |
| import javax.naming.directory.Attribute; |
| import javax.naming.directory.Attributes; |
| import javax.naming.directory.BasicAttribute; |
| import javax.naming.directory.BasicAttributes; |
| |
| import org.apache.commons.lang.ArrayUtils; |
| import org.apache.directory.shared.i18n.I18n; |
| import org.apache.directory.shared.ldap.entry.DefaultEntry; |
| import org.apache.directory.shared.ldap.entry.DefaultEntryAttribute; |
| import org.apache.directory.shared.ldap.entry.Entry; |
| import org.apache.directory.shared.ldap.entry.EntryAttribute; |
| import org.apache.directory.shared.ldap.entry.Modification; |
| import org.apache.directory.shared.ldap.entry.Value; |
| import org.apache.directory.shared.ldap.exception.LdapException; |
| import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeTypeException; |
| import org.apache.directory.shared.ldap.name.DN; |
| import org.apache.directory.shared.ldap.schema.AttributeType; |
| import org.apache.directory.shared.ldap.schema.MatchingRule; |
| import org.apache.directory.shared.ldap.schema.Normalizer; |
| import org.apache.directory.shared.ldap.schema.normalizers.NoOpNormalizer; |
| |
| |
| /** |
| * A set of utility fuctions for working with Attributes. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| * @version $Rev$ |
| */ |
| public class AttributeUtils |
| { |
| /** |
| * Correctly removes an attribute from an entry using it's attributeType information. |
| * |
| * @param type the attributeType of the attribute to remove |
| * @param entry the entry to remove the attribute from |
| * @return the Attribute that is removed |
| */ |
| public static Attribute removeAttribute( AttributeType type, Attributes entry ) |
| { |
| Attribute attr = entry.get( type.getOid() ); |
| |
| if ( attr == null ) |
| { |
| List<String> aliases = type.getNames(); |
| |
| for ( String alias : aliases ) |
| { |
| attr = entry.get( alias ); |
| |
| if ( attr != null ) |
| { |
| return entry.remove( attr.getID() ); |
| } |
| } |
| } |
| |
| if ( attr == null ) |
| { |
| return null; |
| } |
| |
| return entry.remove( attr.getID() ); |
| } |
| |
| |
| /** |
| * Compare two values and return true if they are equal. |
| * |
| * @param value1 The first value |
| * @param value2 The second value |
| * @return true if both value are null or if they are equal. |
| */ |
| public static final boolean equals( Object value1, Object value2 ) |
| { |
| if ( value1 == value2 ) |
| { |
| return true; |
| } |
| |
| if ( value1 == null ) |
| { |
| return ( value2 == null ); |
| } |
| |
| if ( value1 instanceof byte[] ) |
| { |
| if ( value2 instanceof byte[] ) |
| { |
| return Arrays.equals( ( byte[] ) value1, ( byte[] ) value2 ); |
| } |
| else |
| { |
| return false; |
| } |
| } |
| else |
| { |
| return value1.equals( value2 ); |
| } |
| } |
| |
| |
| /** |
| * Clone the value. An attribute value is supposed to be either a String |
| * or a byte array. If it's a String, then we just return it ( as String |
| * is immutable, we don't need to copy it). If it's a bu=yte array, we |
| * create a new byte array and copy the bytes into it. |
| * |
| * @param value The value to clone |
| * @return The cloned value |
| */ |
| public static Object cloneValue( Object value ) |
| { |
| // First copy the value |
| Object newValue = null; |
| |
| if ( value instanceof byte[] ) |
| { |
| newValue = ( ( byte[] ) value ).clone(); |
| } |
| else |
| { |
| newValue = value; |
| } |
| |
| return newValue; |
| } |
| |
| |
| /** |
| * Switch from a BasicAttribute to a AttributeImpl. This is |
| * necessary to allow cloning to be correctly handled. |
| * |
| * @param attribute The attribute to transform |
| * @return A instance of AttributeImpl |
| */ |
| public static final Attribute toBasicAttribute( Attribute attribute ) |
| { |
| if ( attribute instanceof BasicAttribute ) |
| { |
| // Just return the attribute |
| return attribute; |
| } |
| else |
| { |
| // Create a new AttributeImpl from the original attribute |
| Attribute newAttribute = new BasicAttribute( attribute.getID() ); |
| |
| try |
| { |
| NamingEnumeration<?> values = attribute.getAll(); |
| |
| while ( values.hasMoreElements() ) |
| { |
| newAttribute.add( cloneValue( values.next() ) ); |
| } |
| |
| return newAttribute; |
| } |
| catch ( NamingException ne ) |
| { |
| return newAttribute; |
| } |
| } |
| } |
| |
| |
| /** |
| * Utility method to extract an attribute from Attributes object using |
| * all combinationos of the name including aliases. |
| * |
| * @param attrs the Attributes to get the Attribute object from |
| * @param type the attribute type specification |
| * @return an Attribute with matching the attributeType spec or null |
| */ |
| public static final Attribute getAttribute( Attributes attrs, AttributeType type ) |
| { |
| // check if the attribute's OID is used |
| Attribute attr = attrs.get( type.getOid() ); |
| |
| if ( attr != null ) |
| { |
| return attr; |
| } |
| |
| // optimization bypass to avoid cost of the loop below |
| if ( type.getNames().size() == 1 ) |
| { |
| attr = attrs.get( type.getNames().get( 0 ) ); |
| |
| if ( attr != null ) |
| { |
| return attr; |
| } |
| } |
| |
| // iterate through aliases |
| for ( String alias : type.getNames() ) |
| { |
| attr = attrs.get( alias ); |
| |
| if ( attr != null ) |
| { |
| return attr; |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| /** |
| * Check if an attribute contains a specific value, using the associated matchingRule for that |
| * |
| * @param attr The attribute we are searching in |
| * @param compared The object we are looking for |
| * @param type The attribute type |
| * @return <code>true</code> if the value exists in the attribute</code> |
| * @throws LdapException If something went wrong while accessing the data |
| */ |
| public static boolean containsValue( Attribute attr, Value<?> compared, AttributeType type ) throws LdapException |
| { |
| // quick bypass test |
| if ( attr.contains( compared ) ) |
| { |
| return true; |
| } |
| |
| MatchingRule matchingRule = type.getEquality(); |
| |
| Normalizer normalizer = null; |
| |
| if ( matchingRule != null ) |
| { |
| normalizer = matchingRule.getNormalizer(); |
| } |
| else |
| { |
| normalizer = new NoOpNormalizer( type.getOid() ); |
| } |
| |
| if ( type.getSyntax().isHumanReadable() ) |
| { |
| try |
| { |
| String comparedStr = normalizer.normalize( compared.getString() ); |
| |
| for ( NamingEnumeration<?> values = attr.getAll(); values.hasMoreElements(); /**/) |
| { |
| String value = ( String ) values.nextElement(); |
| if ( comparedStr.equals( normalizer.normalize( value ) ) ) |
| { |
| return true; |
| } |
| } |
| } |
| catch( NamingException e ) |
| { |
| throw new LdapException( e.getMessage() ); |
| } |
| } |
| else |
| { |
| byte[] comparedBytes = null; |
| |
| if ( !compared.isBinary() ) |
| { |
| if ( compared.getString().length() < 3 ) |
| { |
| return false; |
| } |
| |
| // Transform the String to a byte array |
| int state = 1; |
| comparedBytes = new byte[compared.getString().length() / 3]; |
| int pos = 0; |
| |
| for ( char c : compared.getString().toCharArray() ) |
| { |
| switch ( state ) |
| { |
| case 1: |
| if ( c != '\\' ) |
| { |
| return false; |
| } |
| |
| state++; |
| break; |
| |
| case 2: |
| int high = StringTools.getHexValue( c ); |
| |
| if ( high == -1 ) |
| { |
| return false; |
| } |
| |
| comparedBytes[pos] = ( byte ) ( high << 4 ); |
| |
| state++; |
| break; |
| |
| case 3: |
| int low = StringTools.getHexValue( c ); |
| |
| if ( low == -1 ) |
| { |
| return false; |
| } |
| |
| comparedBytes[pos] += ( byte ) low; |
| pos++; |
| |
| state = 1; |
| break; |
| } |
| } |
| } |
| else |
| { |
| comparedBytes = compared.getBytes(); |
| } |
| |
| try |
| { |
| for ( NamingEnumeration<?> values = attr.getAll(); values.hasMoreElements(); /**/) |
| { |
| Object value = values.nextElement(); |
| |
| if ( value instanceof byte[] && ArrayUtils.isEquals( comparedBytes, value ) ) |
| { |
| return true; |
| } |
| } |
| } |
| catch ( NamingException ne ) |
| { |
| throw new LdapException( ne.getMessage() ); |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| /** |
| * Check if an attribute contains a value. The test is case insensitive, |
| * and the value is supposed to be a String. If the value is a byte[], |
| * then the case sensitivity is useless. |
| * |
| * @param attr The attribute to check |
| * @param value The value to look for |
| * @return true if the value is present in the attribute |
| */ |
| public static boolean containsValueCaseIgnore( Attribute attr, Object value ) |
| { |
| // quick bypass test |
| if ( attr.contains( value ) ) |
| { |
| return true; |
| } |
| |
| try |
| { |
| if ( value instanceof String ) |
| { |
| String strVal = ( String ) value; |
| |
| NamingEnumeration<?> attrVals = attr.getAll(); |
| |
| while ( attrVals.hasMoreElements() ) |
| { |
| Object attrVal = attrVals.nextElement(); |
| |
| if ( attrVal instanceof String && strVal.equalsIgnoreCase( ( String ) attrVal ) ) |
| { |
| return true; |
| } |
| } |
| } |
| else |
| { |
| byte[] valueBytes = ( byte[] ) value; |
| |
| NamingEnumeration<?> attrVals = attr.getAll(); |
| |
| while ( attrVals.hasMoreElements() ) |
| { |
| Object attrVal = attrVals.nextElement(); |
| |
| if ( attrVal instanceof byte[] && Arrays.equals( ( byte[] ) attrVal, valueBytes ) ) |
| { |
| return true; |
| } |
| } |
| } |
| } |
| catch ( NamingException ne ) |
| { |
| return false; |
| } |
| |
| return false; |
| } |
| |
| |
| /* |
| public static boolean containsAnyValues( Attribute attr, Object[] compared, AttributeType type ) |
| throws NamingException |
| { |
| // quick bypass test |
| for ( Object object : compared ) |
| { |
| if ( attr.contains( object ) ) |
| { |
| return true; |
| } |
| } |
| |
| Normalizer normalizer = type.getEquality().getNormalizer(); |
| |
| if ( type.getSyntax().isHumanReadable() ) |
| { |
| for ( Object object : compared ) |
| { |
| String comparedStr = ( String ) normalizer.normalize( object ); |
| |
| for ( int ii = attr.size(); ii >= 0; ii-- ) |
| { |
| String value = ( String ) attr.get( ii ); |
| |
| if ( comparedStr.equals( normalizer.normalize( value ) ) ) |
| { |
| return true; |
| } |
| } |
| } |
| } |
| else |
| { |
| for ( Object object : compared ) |
| { |
| byte[] comparedBytes = ( byte[] ) object; |
| |
| for ( int ii = attr.size(); ii >= 0; ii-- ) |
| { |
| if ( ArrayUtils.isEquals( comparedBytes, attr.get( ii ) ) ) |
| { |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| */ |
| |
| |
| /** |
| * Creates a new attribute which contains the values representing the |
| * difference of two attributes. If both attributes are null then we cannot |
| * determine the attribute ID and an {@link IllegalArgumentException} is |
| * raised. Note that the order of arguments makes a difference. |
| * |
| * @param attr0 |
| * the first attribute |
| * @param attr1 |
| * the second attribute |
| * @return a new attribute with the difference of values from both attribute |
| * arguments |
| * @throws NamingException |
| * if there are problems accessing attribute values |
| */ |
| public static Attribute getDifference( Attribute attr0, Attribute attr1 ) throws NamingException |
| { |
| String id; |
| |
| if ( ( attr0 == null ) && ( attr1 == null ) ) |
| { |
| throw new IllegalArgumentException( I18n.err( I18n.ERR_04339 ) ); |
| } |
| else if ( attr0 == null ) |
| { |
| return new BasicAttribute( attr1.getID() ); |
| } |
| else if ( attr1 == null ) |
| { |
| return ( Attribute ) attr0.clone(); |
| } |
| else if ( !attr0.getID().equalsIgnoreCase( attr1.getID() ) ) |
| { |
| throw new IllegalArgumentException( I18n.err( I18n.ERR_04340 ) ); |
| } |
| else |
| { |
| id = attr0.getID(); |
| } |
| |
| Attribute attr = new BasicAttribute( id ); |
| |
| for ( int ii = 0; ii < attr0.size(); ii++ ) |
| { |
| attr.add( attr0.get( ii ) ); |
| } |
| |
| for ( int ii = 0; ii < attr1.size(); ii++ ) |
| { |
| attr.remove( attr1.get( ii ) ); |
| } |
| |
| return attr; |
| } |
| |
| |
| /** |
| * Creates a new attribute which contains the values representing the union |
| * of two attributes. If one attribute is null then the resultant attribute |
| * returned is a copy of the non-null attribute. If both are null then we |
| * cannot determine the attribute ID and an {@link IllegalArgumentException} |
| * is raised. |
| * |
| * @param attr0 |
| * the first attribute |
| * @param attr1 |
| * the second attribute |
| * @return a new attribute with the union of values from both attribute |
| * arguments |
| * @throws NamingException |
| * if there are problems accessing attribute values |
| */ |
| public static Attribute getUnion( Attribute attr0, Attribute attr1 ) throws NamingException |
| { |
| String id; |
| |
| if ( attr0 == null && attr1 == null ) |
| { |
| throw new IllegalArgumentException( I18n.err( I18n.ERR_04341 ) ); |
| } |
| else if ( attr0 == null ) |
| { |
| id = attr1.getID(); |
| } |
| else if ( attr1 == null ) |
| { |
| id = attr0.getID(); |
| } |
| else if ( !attr0.getID().equalsIgnoreCase( attr1.getID() ) ) |
| { |
| throw new IllegalArgumentException( I18n.err( I18n.ERR_04342 ) ); |
| } |
| else |
| { |
| id = attr0.getID(); |
| } |
| |
| Attribute attr = new BasicAttribute( id ); |
| |
| if ( attr0 != null ) |
| { |
| for ( int ii = 0; ii < attr0.size(); ii++ ) |
| { |
| attr.add( attr0.get( ii ) ); |
| } |
| } |
| |
| if ( attr1 != null ) |
| { |
| for ( int ii = 0; ii < attr1.size(); ii++ ) |
| { |
| attr.add( attr1.get( ii ) ); |
| } |
| } |
| |
| return attr; |
| } |
| |
| |
| /** |
| * Check if the attributes is a BasicAttributes, and if so, switch |
| * the case sensitivity to false to avoid tricky problems in the server. |
| * (Ldap attributeTypes are *always* case insensitive) |
| * |
| * @param attributes The Attributes to check |
| */ |
| public static Attributes toCaseInsensitive( Attributes attributes ) |
| { |
| if ( attributes == null ) |
| { |
| return attributes; |
| } |
| |
| if ( attributes instanceof BasicAttributes ) |
| { |
| if ( attributes.isCaseIgnored() ) |
| { |
| // Just do nothing if the Attributes is already case insensitive |
| return attributes; |
| } |
| else |
| { |
| // Ok, bad news : we have to create a new BasicAttributes |
| // which will be case insensitive |
| Attributes newAttrs = new BasicAttributes( true ); |
| |
| NamingEnumeration<?> attrs = attributes.getAll(); |
| |
| if ( attrs != null ) |
| { |
| // Iterate through the attributes now |
| while ( attrs.hasMoreElements() ) |
| { |
| newAttrs.put( ( Attribute ) attrs.nextElement() ); |
| } |
| } |
| |
| return newAttrs; |
| } |
| } |
| else |
| { |
| // we can safely return the attributes if it's not a BasicAttributes |
| return attributes; |
| } |
| } |
| |
| |
| /** |
| * Return a string representing the attributes with tabs in front of the |
| * string |
| * |
| * @param tabs |
| * Spaces to be added before the string |
| * @param attribute |
| * The attribute to print |
| * @return A string |
| */ |
| public static String toString( String tabs, Attribute attribute ) |
| { |
| StringBuffer sb = new StringBuffer(); |
| |
| sb.append( tabs ).append( "Attribute\n" ); |
| |
| if ( attribute != null ) |
| { |
| sb.append( tabs ).append( " Type : '" ).append( attribute.getID() ).append( "'\n" ); |
| |
| for ( int j = 0; j < attribute.size(); j++ ) |
| { |
| |
| try |
| { |
| Object attr = attribute.get( j ); |
| |
| if ( attr != null ) |
| { |
| if ( attr instanceof String ) |
| { |
| sb.append( tabs ).append( " Val[" ).append( j ).append( "] : " ).append( attr ) |
| .append( " \n" ); |
| } |
| else if ( attr instanceof byte[] ) |
| { |
| String string = StringTools.utf8ToString( ( byte[] ) attr ); |
| |
| sb.append( tabs ).append( " Val[" ).append( j ).append( "] : " ); |
| sb.append( string ).append( '/' ); |
| sb.append( StringTools.dumpBytes( ( byte[] ) attr ) ); |
| sb.append( " \n" ); |
| } |
| else |
| { |
| sb.append( tabs ).append( " Val[" ).append( j ).append( "] : " ).append( attr ) |
| .append( " \n" ); |
| } |
| } |
| } |
| catch ( NamingException ne ) |
| { |
| sb.append( "Bad attribute : " ).append( ne.getMessage() ); |
| } |
| } |
| } |
| |
| return sb.toString(); |
| } |
| |
| |
| /** |
| * Return a string representing the attribute |
| * |
| * @param attribute |
| * The attribute to print |
| * @return A string |
| */ |
| public static String toString( Attribute attribute ) |
| { |
| return toString( "", attribute ); |
| } |
| |
| |
| /** |
| * Return a string representing the attributes with tabs in front of the |
| * string |
| * |
| * @param tabs |
| * Spaces to be added before the string |
| * @param attributes |
| * The attributes to print |
| * @return A string |
| */ |
| public static String toString( String tabs, Attributes attributes ) |
| { |
| StringBuffer sb = new StringBuffer(); |
| sb.append( tabs ).append( "Attributes\n" ); |
| |
| if ( attributes != null ) |
| { |
| NamingEnumeration<?> attributesIterator = attributes.getAll(); |
| |
| while ( attributesIterator.hasMoreElements() ) |
| { |
| Attribute attribute = ( Attribute ) attributesIterator.nextElement(); |
| sb.append( tabs ).append( attribute.toString() ); |
| } |
| } |
| |
| return sb.toString(); |
| } |
| |
| |
| /** |
| * Parse attribute's options : |
| * |
| * options = *( ';' option ) |
| * option = 1*keychar |
| * keychar = 'a'-z' | 'A'-'Z' / '0'-'9' / '-' |
| */ |
| private static void parseOptions( String str, Position pos ) throws ParseException |
| { |
| while ( StringTools.isCharASCII( str, pos.start, ';' ) ) |
| { |
| pos.start++; |
| |
| // We have an option |
| if ( !StringTools.isAlphaDigitMinus( str, pos.start ) ) |
| { |
| // We must have at least one keychar |
| throw new ParseException( I18n.err( I18n.ERR_04343 ), pos.start ); |
| } |
| |
| pos.start++; |
| |
| while ( StringTools.isAlphaDigitMinus( str, pos.start ) ) |
| { |
| pos.start++; |
| } |
| } |
| } |
| |
| |
| /** |
| * Parse a number : |
| * |
| * number = '0' | '1'..'9' digits |
| * digits = '0'..'9'* |
| * |
| * @return true if a number has been found |
| */ |
| private static boolean parseNumber( String filter, Position pos ) |
| { |
| char c = StringTools.charAt( filter, pos.start ); |
| |
| switch ( c ) |
| { |
| case '0': |
| // If we get a starting '0', we should get out |
| pos.start++; |
| return true; |
| |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| pos.start++; |
| break; |
| |
| default: |
| // Not a number. |
| return false; |
| } |
| |
| while ( StringTools.isDigit( filter, pos.start ) ) |
| { |
| pos.start++; |
| } |
| |
| return true; |
| } |
| |
| |
| /** |
| * |
| * Parse an OID. |
| * |
| * numericoid = number 1*( '.' number ) |
| * number = '0'-'9' / ( '1'-'9' 1*'0'-'9' ) |
| * |
| * @param str The OID to parse |
| * @param pos The current position in the string |
| * @throws ParseException If we don't have a valid OID |
| */ |
| public static void parseOID( String str, Position pos ) throws ParseException |
| { |
| // We have an OID |
| parseNumber( str, pos ); |
| |
| // We must have at least one '.' number |
| if ( !StringTools.isCharASCII( str, pos.start, '.' ) ) |
| { |
| throw new ParseException( I18n.err( I18n.ERR_04344 ), pos.start ); |
| } |
| |
| pos.start++; |
| |
| if ( !parseNumber( str, pos ) ) |
| { |
| throw new ParseException( I18n.err( I18n.ERR_04345 ), pos.start ); |
| } |
| |
| while ( true ) |
| { |
| // Break if we get something which is not a '.' |
| if ( !StringTools.isCharASCII( str, pos.start, '.' ) ) |
| { |
| break; |
| } |
| |
| pos.start++; |
| |
| if ( !parseNumber( str, pos ) ) |
| { |
| throw new ParseException(I18n.err( I18n.ERR_04345 ), pos.start ); |
| } |
| } |
| } |
| |
| |
| /** |
| * Parse an attribute. The grammar is : |
| * attributedescription = attributetype options |
| * attributetype = oid |
| * oid = descr / numericoid |
| * descr = keystring |
| * numericoid = number 1*( '.' number ) |
| * options = *( ';' option ) |
| * option = 1*keychar |
| * keystring = leadkeychar *keychar |
| * leadkeychar = 'a'-z' | 'A'-'Z' |
| * keychar = 'a'-z' | 'A'-'Z' / '0'-'9' / '-' |
| * number = '0'-'9' / ( '1'-'9' 1*'0'-'9' ) |
| * |
| * @param str The parsed attribute, |
| * @param pos The position of the attribute in the current string |
| * @return The parsed attribute if valid |
| */ |
| public static String parseAttribute( String str, Position pos, boolean withOption ) throws ParseException |
| { |
| // We must have an OID or an DESCR first |
| char c = StringTools.charAt( str, pos.start ); |
| |
| if ( c == '\0' ) |
| { |
| throw new ParseException( I18n.err( I18n.ERR_04346 ), pos.start ); |
| } |
| |
| int start = pos.start; |
| |
| if ( StringTools.isAlpha( c ) ) |
| { |
| // A DESCR |
| pos.start++; |
| |
| while ( StringTools.isAlphaDigitMinus( str, pos.start ) ) |
| { |
| pos.start++; |
| } |
| |
| // Parse the options if needed |
| if ( withOption ) |
| { |
| parseOptions( str, pos ); |
| } |
| |
| return str.substring( start, pos.start ); |
| } |
| else if ( StringTools.isDigit( c ) ) |
| { |
| // An OID |
| pos.start++; |
| |
| // Parse the OID |
| parseOID( str, pos ); |
| |
| // Parse the options |
| if ( withOption ) |
| { |
| parseOptions( str, pos ); |
| } |
| |
| return str.substring( start, pos.start ); |
| } |
| else |
| { |
| throw new ParseException( I18n.err( I18n.ERR_04347 ), pos.start ); |
| } |
| } |
| |
| |
| /** |
| * Return a string representing the attributes |
| * |
| * @param attributes |
| * The attributes to print |
| * @return A string |
| */ |
| public static String toString( Attributes attributes ) |
| { |
| return toString( "", attributes ); |
| } |
| |
| |
| /** |
| * A method to apply a modification to an existing entry. |
| * |
| * @param entry The entry on which we want to apply a modification |
| * @param modification the Modification to be applied |
| * @throws LdapException if some operation fails. |
| */ |
| public static void applyModification( Entry entry, Modification modification ) throws LdapException |
| { |
| EntryAttribute modAttr = modification.getAttribute(); |
| String modificationId = modAttr.getId(); |
| |
| switch ( modification.getOperation() ) |
| { |
| case ADD_ATTRIBUTE: |
| EntryAttribute modifiedAttr = entry.get( modificationId ); |
| |
| if ( modifiedAttr == null ) |
| { |
| // The attribute should be added. |
| entry.put( modAttr ); |
| } |
| else |
| { |
| // The attribute exists : the values can be different, |
| // so we will just add the new values to the existing ones. |
| for ( Value<?> value : modAttr ) |
| { |
| // If the value already exist, nothing is done. |
| // Note that the attribute *must* have been |
| // normalized before. |
| modifiedAttr.add( value ); |
| } |
| } |
| |
| break; |
| |
| case REMOVE_ATTRIBUTE: |
| if ( modAttr.get() == null ) |
| { |
| // We have no value in the ModificationItem attribute : |
| // we have to remove the whole attribute from the initial |
| // entry |
| entry.removeAttributes( modificationId ); |
| } |
| else |
| { |
| // We just have to remove the values from the original |
| // entry, if they exist. |
| modifiedAttr = entry.get( modificationId ); |
| |
| if ( modifiedAttr == null ) |
| { |
| break; |
| } |
| |
| for ( Value<?> value : modAttr ) |
| { |
| // If the value does not exist, nothing is done. |
| // Note that the attribute *must* have been |
| // normalized before. |
| modifiedAttr.remove( value ); |
| } |
| |
| if ( modifiedAttr.size() == 0 ) |
| { |
| // If this was the last value, remove the attribute |
| entry.removeAttributes( modifiedAttr.getId() ); |
| } |
| } |
| |
| break; |
| |
| case REPLACE_ATTRIBUTE: |
| if ( modAttr.get() == null ) |
| { |
| // If the modification does not have any value, we have |
| // to delete the attribute from the entry. |
| entry.removeAttributes( modificationId ); |
| } |
| else |
| { |
| // otherwise, just substitute the existing attribute. |
| entry.put( modAttr ); |
| } |
| |
| break; |
| } |
| } |
| |
| |
| /* |
| * Check if an attribute contains a specific value and remove it using the associated |
| * matchingRule for the attribute type supplied. |
| * |
| * @param attr the attribute we are searching in |
| * @param compared the object we are looking for |
| * @param type the attribute type |
| * @return the value removed from the attribute, otherwise null |
| * @throws NamingException if something went wrong while removing the value |
| * |
| public static Object removeValue( Attribute attr, Object compared, AttributeType type ) throws NamingException |
| { |
| // quick bypass test |
| if ( attr.contains( compared ) ) |
| { |
| return attr.remove( compared ); |
| } |
| |
| MatchingRule matchingRule = type.getEquality(); |
| Normalizer normalizer; |
| |
| if ( matchingRule != null ) |
| { |
| normalizer = type.getEquality().getNormalizer(); |
| } |
| else |
| { |
| normalizer = new NoOpNormalizer(); |
| } |
| |
| if ( type.getSyntax().isHumanReadable() ) |
| { |
| String comparedStr = ( String ) normalizer.normalize( compared ); |
| |
| for ( NamingEnumeration<?> values = attr.getAll(); values.hasMoreElements(); ) |
| { |
| String value = ( String ) values.nextElement(); |
| if ( comparedStr.equals( normalizer.normalize( value ) ) ) |
| { |
| return attr.remove( value ); |
| } |
| } |
| } |
| else |
| { |
| byte[] comparedBytes = null; |
| |
| if ( compared instanceof String ) |
| { |
| if ( ( ( String ) compared ).length() < 3 ) |
| { |
| return null; |
| } |
| |
| // Tansform the String to a byte array |
| int state = 1; |
| comparedBytes = new byte[( ( String ) compared ).length() / 3]; |
| int pos = 0; |
| |
| for ( char c : ( ( String ) compared ).toCharArray() ) |
| { |
| switch ( state ) |
| { |
| case 1: |
| if ( c != '\\' ) |
| { |
| return null; |
| } |
| |
| state++; |
| break; |
| |
| case 2: |
| int high = StringTools.getHexValue( c ); |
| |
| if ( high == -1 ) |
| { |
| return null; |
| } |
| |
| comparedBytes[pos] = ( byte ) ( high << 4 ); |
| |
| state++; |
| break; |
| |
| case 3: |
| int low = StringTools.getHexValue( c ); |
| |
| if ( low == -1 ) |
| { |
| return null; |
| } |
| |
| comparedBytes[pos] += ( byte ) low; |
| pos++; |
| |
| state = 1; |
| break; |
| } |
| } |
| } |
| else |
| { |
| comparedBytes = ( byte[] ) compared; |
| } |
| |
| for ( NamingEnumeration<?> values = attr.getAll(); values.hasMoreElements(); ) |
| { |
| Object value = values.nextElement(); |
| |
| if ( value instanceof byte[] ) |
| { |
| if ( ArrayUtils.isEquals( comparedBytes, value ) ) |
| { |
| return attr.remove( value ); |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| */ |
| |
| |
| /** |
| * Convert a BasicAttributes or a AttributesImpl to a ServerEntry |
| * |
| * @param attributes the BasicAttributes or AttributesImpl instance to convert |
| * @param dn The DN which is needed by the ServerEntry |
| * @return An instance of a ServerEntry object |
| * |
| * @throws InvalidAttributeIdentifierException If we get an invalid attribute |
| */ |
| public static Entry toClientEntry( Attributes attributes, DN dn ) throws LdapException |
| { |
| if ( attributes instanceof BasicAttributes ) |
| { |
| try |
| { |
| Entry entry = new DefaultEntry( dn ); |
| |
| for ( NamingEnumeration<? extends Attribute> attrs = attributes.getAll(); attrs.hasMoreElements(); ) |
| { |
| Attribute attr = attrs.nextElement(); |
| |
| EntryAttribute entryAttribute = toClientAttribute( attr ); |
| |
| if ( entryAttribute != null ) |
| { |
| entry.put( entryAttribute ); |
| } |
| } |
| |
| return entry; |
| } |
| catch ( LdapException ne ) |
| { |
| throw new LdapInvalidAttributeTypeException( ne.getMessage() ); |
| } |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| |
| /** |
| * Converts an {@link Entry} to an {@link Attributes}. |
| * |
| * @param entry |
| * the {@link Entry} to convert |
| * @return |
| * the equivalent {@link Attributes} |
| */ |
| public static Attributes toAttributes( Entry entry ) |
| { |
| if ( entry != null ) |
| { |
| Attributes attributes = new BasicAttributes( true ); |
| |
| // Looping on attributes |
| for ( Iterator<EntryAttribute> attributeIterator = entry.iterator(); attributeIterator.hasNext(); ) |
| { |
| EntryAttribute entryAttribute = ( EntryAttribute ) attributeIterator.next(); |
| |
| attributes.put( toAttribute( entryAttribute ) ); |
| } |
| |
| return attributes; |
| } |
| |
| return null; |
| } |
| |
| |
| /** |
| * Converts an {@link EntryAttribute} to an {@link Attribute}. |
| * |
| * @param entryAttribute |
| * the {@link EntryAttribute} to convert |
| * @return |
| * the equivalent {@link Attribute} |
| */ |
| public static Attribute toAttribute( EntryAttribute entryAttribute ) |
| { |
| if ( entryAttribute != null ) |
| { |
| Attribute attribute = new BasicAttribute( entryAttribute.getId() ); |
| |
| // Looping on values |
| for ( Iterator<Value<?>> valueIterator = entryAttribute.iterator(); valueIterator.hasNext(); ) |
| { |
| Value<?> value = valueIterator.next(); |
| attribute.add( value.get() ); |
| } |
| |
| return attribute; |
| } |
| |
| return null; |
| } |
| |
| |
| /** |
| * Convert a BasicAttribute or a AttributeImpl to a EntryAttribute |
| * |
| * @param attribute the BasicAttributes or AttributesImpl instance to convert |
| * @return An instance of a ClientEntry object |
| * |
| * @throws InvalidAttributeIdentifierException If we had an incorrect attribute |
| */ |
| public static EntryAttribute toClientAttribute( Attribute attribute ) |
| { |
| if ( attribute == null ) |
| { |
| return null; |
| } |
| |
| try |
| { |
| EntryAttribute clientAttribute = new DefaultEntryAttribute( attribute.getID() ); |
| |
| for ( NamingEnumeration<?> values = attribute.getAll(); values.hasMoreElements(); ) |
| { |
| Object value = values.nextElement(); |
| |
| if ( value instanceof String ) |
| { |
| clientAttribute.add( ( String ) value ); |
| } |
| else if ( value instanceof byte[] ) |
| { |
| clientAttribute.add( ( byte[] ) value ); |
| } |
| else |
| { |
| clientAttribute.add( ( String ) null ); |
| } |
| } |
| |
| return clientAttribute; |
| } |
| catch ( NamingException ne ) |
| { |
| return null; |
| } |
| } |
| } |