Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 14022 invoked from network); 26 Jan 2010 22:43:52 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 26 Jan 2010 22:43:52 -0000 Received: (qmail 19109 invoked by uid 500); 26 Jan 2010 22:43:52 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 19044 invoked by uid 500); 26 Jan 2010 22:43:51 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 19035 invoked by uid 99); 26 Jan 2010 22:43:51 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 26 Jan 2010 22:43:51 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 26 Jan 2010 22:43:49 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id CC51E23888D1; Tue, 26 Jan 2010 22:43:29 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r903465 [1/5] - in /directory/shared/trunk/ldap-ldif: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/directory/ src/main/java/org/apache/directory/shared/ src/main/java/org/apache/dire... Date: Tue, 26 Jan 2010 22:43:29 -0000 To: commits@directory.apache.org From: elecharny@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100126224329.CC51E23888D1@eris.apache.org> Author: elecharny Date: Tue Jan 26 22:43:27 2010 New Revision: 903465 URL: http://svn.apache.org/viewvc?rev=903465&view=rev Log: Moved the LDIF part from shared-ldap Added: directory/shared/trunk/ldap-ldif/ (with props) directory/shared/trunk/ldap-ldif/pom.xml directory/shared/trunk/ldap-ldif/src/ directory/shared/trunk/ldap-ldif/src/main/ directory/shared/trunk/ldap-ldif/src/main/java/ directory/shared/trunk/ldap-ldif/src/main/java/org/ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/ChangeType.java (with props) directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifAttributesReader.java directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifControl.java directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifEntry.java directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifReader.java directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifRevertor.java directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java directory/shared/trunk/ldap-ldif/src/test/ directory/shared/trunk/ldap-ldif/src/test/java/ directory/shared/trunk/ldap-ldif/src/test/java/org/ directory/shared/trunk/ldap-ldif/src/test/java/org/apache/ directory/shared/trunk/ldap-ldif/src/test/java/org/apache/directory/ directory/shared/trunk/ldap-ldif/src/test/java/org/apache/directory/shared/ directory/shared/trunk/ldap-ldif/src/test/java/org/apache/directory/shared/ldap/ directory/shared/trunk/ldap-ldif/src/test/java/org/apache/directory/shared/ldap/ldif/ directory/shared/trunk/ldap-ldif/src/test/java/org/apache/directory/shared/ldap/ldif/LdifAttributesReaderTest.java directory/shared/trunk/ldap-ldif/src/test/java/org/apache/directory/shared/ldap/ldif/LdifReaderTest.java directory/shared/trunk/ldap-ldif/src/test/java/org/apache/directory/shared/ldap/ldif/LdifRevertorTest.java directory/shared/trunk/ldap-ldif/src/test/java/org/apache/directory/shared/ldap/ldif/LdifUtilsTest.java directory/shared/trunk/ldap-ldif/src/test/resources/ directory/shared/trunk/ldap-ldif/src/test/resources/log4j.properties Propchange: directory/shared/trunk/ldap-ldif/ ------------------------------------------------------------------------------ --- svn:ignore (added) +++ svn:ignore Tue Jan 26 22:43:27 2010 @@ -0,0 +1,5 @@ +target +.classpath +.project +.settings +.buildpath Added: directory/shared/trunk/ldap-ldif/pom.xml URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap-ldif/pom.xml?rev=903465&view=auto ============================================================================== --- directory/shared/trunk/ldap-ldif/pom.xml (added) +++ directory/shared/trunk/ldap-ldif/pom.xml Tue Jan 26 22:43:27 2010 @@ -0,0 +1,99 @@ + + + + + + 4.0.0 + + org.apache.directory.shared + shared-parent + 0.9.18-SNAPSHOT + + shared-ldif + Apache Directory Shared LDIF + 2010 + + + jira + http://issues.apache.org/jira/browse/DIRSHARED + + + + Common LDIF packages used by clients and servers. + + + + + commons-lang + commons-lang + + + + commons-collections + commons-collections + + + + shared-asn1 + ${groupId} + ${version} + + + + shared-ldap + ${groupId} + ${version} + + + + shared-ldap-constants + ${groupId} + ${version} + + + + + + + maven-antrun-plugin + + + generate-sources + + + run + + + + + + + maven-surefire-plugin + + + **/Abstract* + **/*RegressionTest* + + + + + + + Added: directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/ChangeType.java URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/ChangeType.java?rev=903465&view=auto ============================================================================== --- directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/ChangeType.java (added) +++ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/ChangeType.java Tue Jan 26 22:43:27 2010 @@ -0,0 +1,95 @@ +/* + * 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.ldif; + + +/** + * A type safe enumeration for an LDIF record's change type. + * + * @author Apache Directory Project + * @version $Rev$, $Date$ + */ +public enum ChangeType +{ + Add( 0 ), + Modify( 1 ), + ModDn( 2 ), + ModRdn( 3 ), + Delete( 4 ); + + /** Add ordinal value */ + public static final int ADD_ORDINAL = 0; + + /** Modify ordinal value */ + public static final int MODIFY_ORDINAL = 1; + + /** ModDN ordinal value */ + public static final int MODDN_ORDINAL = 2; + + /** ModRDN ordinal value */ + public static final int MODRDN_ORDINAL = 3; + + /** Delete ordinal value */ + public static final int DELETE_ORDINAL = 4; + + /* the ordinal value for a change type */ + private final int changeType; + + + /** + * Creates a new instance of ChangeType. + * + * @param changeType + */ + private ChangeType( int changeType ) + { + this.changeType = changeType; + } + + + /** + * Get's the ordinal value for a ChangeType. + * + * @return the changeType + */ + public int getChangeType() + { + return changeType; + } + + public static ChangeType getChangeType( int val ) + { + switch( val ) + { + case 0: return Add; + + case 1: return Modify; + + case 2: return ModDn; + + case 3: return ModRdn; + + case 4: return Delete; + + default: + throw new IllegalArgumentException( "Unknown change type value " + val ); + } + } +} Propchange: directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/ChangeType.java ------------------------------------------------------------------------------ svn:executable = * Added: directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifAttributesReader.java URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifAttributesReader.java?rev=903465&view=auto ============================================================================== --- directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifAttributesReader.java (added) +++ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifAttributesReader.java Tue Jan 26 22:43:27 2010 @@ -0,0 +1,310 @@ +/* + * 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.ldif; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; + +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; + +import org.apache.directory.shared.ldap.util.StringTools; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *
+ *  <ldif-file> ::= "version:" <fill> <number> <seps> <dn-spec> <sep> 
+ *  <ldif-content-change>
+ *  
+ *  <ldif-content-change> ::= 
+ *    <number> <oid> <options-e> <value-spec> <sep> <attrval-specs-e> 
+ *    <ldif-attrval-record-e> | 
+ *    <alpha> <chars-e> <options-e> <value-spec> <sep> <attrval-specs-e> 
+ *    <ldif-attrval-record-e> | 
+ *    "control:" <fill> <number> <oid> <spaces-e> <criticality> 
+ *    <value-spec-e> <sep> <controls-e> 
+ *        "changetype:" <fill> <changerecord-type> <ldif-change-record-e> |
+ *    "changetype:" <fill> <changerecord-type> <ldif-change-record-e>
+ *                              
+ *  <ldif-attrval-record-e> ::= <seps> <dn-spec> <sep> <attributeType> 
+ *    <options-e> <value-spec> <sep> <attrval-specs-e> 
+ *    <ldif-attrval-record-e> | e
+ *                              
+ *  <ldif-change-record-e> ::= <seps> <dn-spec> <sep> <controls-e> 
+ *    "changetype:" <fill> <changerecord-type> <ldif-change-record-e> | e
+ *                              
+ *  <dn-spec> ::= "dn:" <fill> <safe-string> | "dn::" <fill> <base64-string>
+ *                              
+ *  <controls-e> ::= "control:" <fill> <number> <oid> <spaces-e> <criticality> 
+ *    <value-spec-e> <sep> <controls-e> | e
+ *                              
+ *  <criticality> ::= "true" | "false" | e
+ *                              
+ *  <oid> ::= '.' <number> <oid> | e
+ *                              
+ *  <attrval-specs-e> ::= <number> <oid> <options-e> <value-spec> <sep> 
+ *  <attrval-specs-e> | 
+ *    <alpha> <chars-e> <options-e> <value-spec> <sep> <attrval-specs-e> | e
+ *                              
+ *  <value-spec-e> ::= <value-spec> | e
+ *  
+ *  <value-spec> ::= ':' <fill> <safe-string-e> | 
+ *    "::" <fill> <base64-chars> | 
+ *    ":<" <fill> <url>
+ *  
+ *  <attributeType> ::= <number> <oid> | <alpha> <chars-e>
+ *  
+ *  <options-e> ::= ';' <char> <chars-e> <options-e> |e
+ *                              
+ *  <chars-e> ::= <char> <chars-e> |  e
+ *  
+ *  <changerecord-type> ::= "add" <sep> <attributeType> <options-e> <value-spec> 
+ *  <sep> <attrval-specs-e> | 
+ *    "delete" <sep> | 
+ *    "modify" <sep> <mod-type> <fill> <attributeType> <options-e> <sep> 
+ *    <attrval-specs-e> <sep> '-' <sep> <mod-specs-e> | 
+ *    "moddn" <sep> <newrdn> <sep> "deleteoldrdn:" <fill> <0-1> <sep> 
+ *    <newsuperior-e> <sep> |
+ *    "modrdn" <sep> <newrdn> <sep> "deleteoldrdn:" <fill> <0-1> <sep> 
+ *    <newsuperior-e> <sep>
+ *  
+ *  <newrdn> ::= ':' <fill> <safe-string> | "::" <fill> <base64-chars>
+ *  
+ *  <newsuperior-e> ::= "newsuperior" <newrdn> | e
+ *  
+ *  <mod-specs-e> ::= <mod-type> <fill> <attributeType> <options-e> 
+ *    <sep> <attrval-specs-e> <sep> '-' <sep> <mod-specs-e> | e
+ *  
+ *  <mod-type> ::= "add:" | "delete:" | "replace:"
+ *  
+ *  <url> ::= <a Uniform Resource Locator, as defined in [6]>
+ *  
+ *  
+ *  
+ *  LEXICAL
+ *  -------
+ *  
+ *  <fill>           ::= ' ' <fill> | e
+ *  <char>           ::= <alpha> | <digit> | '-'
+ *  <number>         ::= <digit> <digits>
+ *  <0-1>            ::= '0' | '1'
+ *  <digits>         ::= <digit> <digits> | e
+ *  <digit>          ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
+ *  <seps>           ::= <sep> <seps-e> 
+ *  <seps-e>         ::= <sep> <seps-e> | e
+ *  <sep>            ::= 0x0D 0x0A | 0x0A
+ *  <spaces>         ::= ' ' <spaces-e>
+ *  <spaces-e>       ::= ' ' <spaces-e> | e
+ *  <safe-string-e>  ::= <safe-string> | e
+ *  <safe-string>    ::= <safe-init-char> <safe-chars>
+ *  <safe-init-char> ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x1F] | [0x21-0x39] | 0x3B | [0x3D-0x7F]
+ *  <safe-chars>     ::= <safe-char> <safe-chars> | e
+ *  <safe-char>      ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x7F]
+ *  <base64-string>  ::= <base64-char> <base64-chars>
+ *  <base64-chars>   ::= <base64-char> <base64-chars> | e
+ *  <base64-char>    ::= 0x2B | 0x2F | [0x30-0x39] | 0x3D | [0x41-9x5A] | [0x61-0x7A]
+ *  <alpha>          ::= [0x41-0x5A] | [0x61-0x7A]
+ *  
+ *  COMMENTS
+ *  --------
+ *  - The ldap-oid VN is not correct in the RFC-2849. It has been changed from 1*DIGIT 0*1("." 1*DIGIT) to
+ *  DIGIT+ ("." DIGIT+)*
+ *  - The mod-spec lacks a sep between *attrval-spec and "-".
+ *  - The BASE64-UTF8-STRING should be BASE64-CHAR BASE64-STRING
+ *  - The ValueSpec rule must accept multilines values. In this case, we have a LF followed by a 
+ *  single space before the continued value.
+ * 
+ * + * @author Apache Directory Project + * @version $Rev$, $Date$ + */ +public class LdifAttributesReader extends LdifReader +{ + /** A logger */ + private static final Logger LOG = LoggerFactory.getLogger( LdifAttributesReader.class ); + + /** + * Constructors + */ + public LdifAttributesReader() + { + lines = new ArrayList(); + position = new Position(); + version = DEFAULT_VERSION; + } + + + /** + * Parse an AttributeType/AttributeValue + * + * @param attributes The entry where to store the value + * @param line The line to parse + * @param lowerLine The same line, lowercased + * @throws NamingException If anything goes wrong + */ + private void parseAttribute( Attributes attributes, String line, String lowerLine ) throws NamingException + { + int colonIndex = line.indexOf( ':' ); + + String attributeType = lowerLine.substring( 0, colonIndex ); + + // We should *not* have a DN twice + if ( attributeType.equals( "dn" ) ) + { + LOG.error( "An entry must not have two DNs" ); + throw new NamingException( "A ldif entry should not have two DNs" ); + } + + Object attributeValue = parseValue( line, colonIndex ); + + // Update the entry + Attribute attribute = attributes.get( attributeType ); + + if ( attribute == null ) + { + attributes.put( attributeType, attributeValue ); + } + else + { + attribute.add( attributeValue ); + } + } + + /** + * Parse a ldif file. The following rules are processed : + * + * <ldif-file> ::= <ldif-attrval-record> <ldif-attrval-records> | + * <ldif-change-record> <ldif-change-records> <ldif-attrval-record> ::= + * <dn-spec> <sep> <attrval-spec> <attrval-specs> <ldif-change-record> ::= + * <dn-spec> <sep> <controls-e> <changerecord> <dn-spec> ::= "dn:" <fill> + * <distinguishedName> | "dn::" <fill> <base64-distinguishedName> + * <changerecord> ::= "changetype:" <fill> <change-op> + * + * @return The read entry + * @throws NamingException If the entry can't be read or is invalid + */ + private Attributes parseAttributes() throws NamingException + { + if ( ( lines == null ) || ( lines.size() == 0 ) ) + { + LOG.debug( "The entry is empty : end of ldif file" ); + return null; + } + + Attributes attributes = new BasicAttributes( true ); + + // Now, let's iterate through the other lines + for ( String line:lines ) + { + // Each line could start either with an OID, an attribute type, with + // "control:" or with "changetype:" + String lowerLine = line.toLowerCase(); + + // We have three cases : + // 1) The first line after the DN is a "control:" -> this is an error + // 2) The first line after the DN is a "changeType:" -> this is an error + // 3) The first line after the DN is anything else + if ( lowerLine.startsWith( "control:" ) ) + { + LOG.error( "We cannot have changes when reading a file which already contains entries" ); + throw new NamingException( "No changes withing entries" ); + } + else if ( lowerLine.startsWith( "changetype:" ) ) + { + LOG.error( "We cannot have changes when reading a file which already contains entries" ); + throw new NamingException( "No changes withing entries" ); + } + else if ( line.indexOf( ':' ) > 0 ) + { + parseAttribute( attributes, line, lowerLine ); + } + else + { + // Invalid attribute Value + LOG.error( "Expecting an attribute type" ); + throw new NamingException( "Bad attribute" ); + } + } + + LOG.debug( "Read an attributes : {}", attributes ); + + return attributes; + } + + + /** + * A method which parses a ldif string and returns a list of entries. + * + * @param ldif The ldif string + * @return A list of entries, or an empty List + * @throws NamingException + * If something went wrong + */ + public Attributes parseAttributes( String ldif ) throws NamingException + { + lines = new ArrayList(); + position = new Position(); + + LOG.debug( "Starts parsing ldif buffer" ); + + if ( StringTools.isEmpty( ldif ) ) + { + return new BasicAttributes( true ); + } + + StringReader strIn = new StringReader( ldif ); + reader = new BufferedReader( strIn ); + + try + { + readLines(); + + Attributes attributes = parseAttributes(); + + if ( LOG.isDebugEnabled() ) + { + LOG.debug( "Parsed {} entries.", ( attributes == null ? 0 : 1 ) ); + } + + return attributes; + } + catch (NamingException ne) + { + LOG.error( "Cannot parse the ldif buffer : {}", ne.getMessage() ); + throw new NamingException( "Error while parsing the ldif buffer" ); + } + finally + { + try + { + reader.close(); + } + catch ( IOException ioe ) + { + // Do nothing + } + } + } +} \ No newline at end of file Added: directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifControl.java URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifControl.java?rev=903465&view=auto ============================================================================== --- directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifControl.java (added) +++ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifControl.java Tue Jan 26 22:43:27 2010 @@ -0,0 +1,52 @@ +/* + * 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.ldif; + +import org.apache.directory.shared.ldap.message.control.AbstractControl; +import org.apache.directory.shared.ldap.util.StringTools; + +/** + * The LdifControl class stores a control defined for an entry found in a ldif + * file. + * + * @author Apache Directory Project + * @version $Rev$, $Date$ + */ +public class LdifControl extends AbstractControl +{ + private static final long serialVersionUID = 1L; + + /** + * Create a new Control + * + * @param oid + * OID of the created control + */ + public LdifControl( String oid ) + { + super( oid ); + } + + public String toString() + { + return "LdifControl : {" + getOid() + ", " + isCritical() + ", " + StringTools.dumpBytes( getValue() ) + "}"; + } +} Added: directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifEntry.java URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifEntry.java?rev=903465&view=auto ============================================================================== --- directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifEntry.java (added) +++ directory/shared/trunk/ldap-ldif/src/main/java/org/apache/directory/shared/ldap/ldif/LdifEntry.java Tue Jan 26 22:43:27 2010 @@ -0,0 +1,1061 @@ +/* + * 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.ldif; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.naming.InvalidNameException; +import javax.naming.NamingException; + +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.ModificationOperation; +import org.apache.directory.shared.ldap.entry.Value; +import org.apache.directory.shared.ldap.entry.client.ClientEntry; +import org.apache.directory.shared.ldap.entry.client.ClientModification; +import org.apache.directory.shared.ldap.entry.client.ClientStringValue; +import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute; +import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry; +import org.apache.directory.shared.ldap.message.control.Control; +import org.apache.directory.shared.ldap.name.LdapDN; +import org.apache.directory.shared.ldap.name.RDN; +import org.apache.directory.shared.ldap.util.StringTools; + + +/** + * A entry to be populated by an ldif parser. + * + * We will have different kind of entries : + * - added entries + * - deleted entries + * - modified entries + * - RDN modified entries + * - DN modified entries + * + * @author Apache Directory Project + * @version $Rev$, $Date$ + */ +public class LdifEntry implements Cloneable, Externalizable +{ + private static final long serialVersionUID = 2L; + + /** Used in toArray() */ + public static final Modification[] EMPTY_MODS = new Modification[0]; + + /** the change type */ + private ChangeType changeType; + + /** the modification item list */ + private List modificationList; + + private Map modificationItems; + + /** The new superior */ + private String newSuperior; + + /** The new rdn */ + private String newRdn; + + /** The delete old rdn flag */ + private boolean deleteOldRdn; + + /** the entry */ + private ClientEntry entry; + + + /** The control */ + private Control control; + + /** + * Creates a new Entry object. + */ + public LdifEntry() + { + changeType = ChangeType.Add; // Default LDIF content + modificationList = new LinkedList(); + modificationItems = new HashMap(); + entry = new DefaultClientEntry( null ); + control = null; + } + + + /** + * Set the Distinguished Name + * + * @param dn + * The Distinguished Name + */ + public void setDn( LdapDN dn ) + { + entry.setDn( (LdapDN)dn.clone() ); + } + + + /** + * Set the Distinguished Name + * + * @param dn + * The Distinguished Name + */ + public void setDn( String dn ) throws InvalidNameException + { + LdapDN ldapDn = new LdapDN( dn ); + entry.setDn( ldapDn ); + } + + + /** + * Set the modification type + * + * @param changeType + * The change type + * + */ + public void setChangeType( ChangeType changeType ) + { + this.changeType = changeType; + } + + /** + * Set the change type + * + * @param changeType + * The change type + */ + public void setChangeType( String changeType ) + { + if ( "add".equals( changeType ) ) + { + this.changeType = ChangeType.Add; + } + else if ( "modify".equals( changeType ) ) + { + this.changeType = ChangeType.Modify; + } + else if ( "moddn".equals( changeType ) ) + { + this.changeType = ChangeType.ModDn; + } + else if ( "modrdn".equals( changeType ) ) + { + this.changeType = ChangeType.ModRdn; + } + else if ( "delete".equals( changeType ) ) + { + this.changeType = ChangeType.Delete; + } + } + + /** + * Add a modification item (used by modify operations) + * + * @param modification The modification to be added + */ + public void addModificationItem( Modification modification ) + { + if ( changeType == ChangeType.Modify ) + { + modificationList.add( modification ); + modificationItems.put( modification.getAttribute().getId(), modification ); + } + } + + /** + * Add a modification item (used by modify operations) + * + * @param modOp The operation. One of : + * - ModificationOperation.ADD_ATTRIBUTE + * - ModificationOperation.REMOVE_ATTRIBUTE + * - ModificationOperation.REPLACE_ATTRIBUTE + * + * @param attr The attribute to be added + */ + public void addModificationItem( ModificationOperation modOp, EntryAttribute attr ) + { + if ( changeType == ChangeType.Modify ) + { + Modification item = new ClientModification( modOp, attr ); + modificationList.add( item ); + modificationItems.put( attr.getId(), item ); + } + } + + + /** + * Add a modification item + * + * @param modOp The operation. One of : + * - ModificationOperation.ADD_ATTRIBUTE + * - ModificationOperation.REMOVE_ATTRIBUTE + * - ModificationOperation.REPLACE_ATTRIBUTE + * + * @param modOp The modification operation value + * @param id The attribute's ID + * @param value The attribute's value + */ + public void addModificationItem( ModificationOperation modOp, String id, Object value ) + { + if ( changeType == ChangeType.Modify ) + { + EntryAttribute attr = null; + + if ( value == null ) + { + value = new ClientStringValue( null ); + attr = new DefaultClientAttribute( id, (Value)value ); + } + else + { + attr = (EntryAttribute)value; + } + + Modification item = new ClientModification( modOp, attr ); + modificationList.add( item ); + modificationItems.put( id, item ); + } + } + + + /** + * Add an attribute to the entry + * + * @param attr + * The attribute to be added + */ + public void addAttribute( EntryAttribute attr ) throws NamingException + { + entry.put( attr ); + } + + /** + * Add an attribute to the entry + * + * @param id + * The attribute ID + * + * @param value + * The attribute value + * + */ + public void addAttribute( String id, Object value ) throws NamingException + { + if ( value instanceof String ) + { + entry.add( id, (String)value ); + } + else + { + entry.add( id, (byte[])value ); + } + } + + + /** + * Remove a list of Attributes from the LdifEntry + * + * @param ids The Attributes to remove + * @return The list of removed EntryAttributes + */ + public List removeAttribute( String... ids ) + { + if ( entry.containsAttribute( ids ) ) + { + return entry.removeAttributes( ids ); + } + else + { + return null; + } + } + + /** + * Add an attribute value to an existing attribute + * + * @param id + * The attribute ID + * + * @param value + * The attribute value + * + */ + public void putAttribute( String id, Object value ) throws NamingException + { + if ( value instanceof String ) + { + entry.add( id, (String)value ); + } + else + { + entry.add( id, (byte[])value ); + } + } + + /** + * Get the change type + * + * @return The change type. One of : ADD = 0; MODIFY = 1; MODDN = 2; MODRDN = + * 3; DELETE = 4; + */ + public ChangeType getChangeType() + { + return changeType; + } + + /** + * @return The list of modification items + */ + public List getModificationItems() + { + return modificationList; + } + + + /** + * Gets the modification items as an array. + * + * @return modification items as an array. + */ + public Modification[] getModificationItemsArray() + { + return modificationList.toArray( EMPTY_MODS ); + } + + + /** + * @return The entry Distinguished name + */ + public LdapDN getDn() + { + return entry.getDn(); + } + + /** + * @return The number of entry modifications + */ + public int size() + { + return modificationList.size(); + } + + /** + * Returns a attribute given it's id + * + * @param attributeId + * The attribute Id + * @return The attribute if it exists + */ + public EntryAttribute get( String attributeId ) + { + if ( "dn".equalsIgnoreCase( attributeId ) ) + { + return new DefaultClientAttribute( "dn", entry.getDn().getName() ); + } + + return entry.get( attributeId ); + } + + /** + * Get the entry's entry + * + * @return the stored Entry + */ + public Entry getEntry() + { + if ( isEntry() ) + { + return entry; + } + else + { + return null; + } + } + + /** + * @return True, if the old RDN should be deleted. + */ + public boolean isDeleteOldRdn() + { + return deleteOldRdn; + } + + /** + * Set the flage deleteOldRdn + * + * @param deleteOldRdn + * True if the old RDN should be deleted + */ + public void setDeleteOldRdn( boolean deleteOldRdn ) + { + this.deleteOldRdn = deleteOldRdn; + } + + /** + * @return The new RDN + */ + public String getNewRdn() + { + return newRdn; + } + + /** + * Set the new RDN + * + * @param newRdn + * The new RDN + */ + public void setNewRdn( String newRdn ) + { + this.newRdn = newRdn; + } + + /** + * @return The new superior + */ + public String getNewSuperior() + { + return newSuperior; + } + + /** + * Set the new superior + * + * @param newSuperior + * The new Superior + */ + public void setNewSuperior( String newSuperior ) + { + this.newSuperior = newSuperior; + } + + /** + * @return True if the entry is an ADD entry + */ + public boolean isChangeAdd() + { + return changeType == ChangeType.Add; + } + + /** + * @return True if the entry is a DELETE entry + */ + public boolean isChangeDelete() + { + return changeType == ChangeType.Delete; + } + + /** + * @return True if the entry is a MODDN entry + */ + public boolean isChangeModDn() + { + return changeType == ChangeType.ModDn; + } + + /** + * @return True if the entry is a MODRDN entry + */ + public boolean isChangeModRdn() + { + return changeType == ChangeType.ModRdn; + } + + /** + * @return True if the entry is a MODIFY entry + */ + public boolean isChangeModify() + { + return changeType == ChangeType.Modify; + } + + /** + * Tells if the current entry is a added one + * + * @return true if the entry is added + */ + public boolean isEntry() + { + return changeType == ChangeType.Add; + } + + /** + * @return The associated control, if any + */ + public Control getControl() + { + return control; + } + + /** + * Add a control to the entry + * + * @param control + * The control + */ + public void setControl( Control control ) + { + this.control = control; + } + + /** + * Clone method + * @return a clone of the current instance + * @exception CloneNotSupportedException If there is some problem while cloning the instance + */ + public LdifEntry clone() throws CloneNotSupportedException + { + LdifEntry clone = (LdifEntry) super.clone(); + + if ( modificationList != null ) + { + for ( Modification modif:modificationList ) + { + Modification modifClone = new ClientModification( modif.getOperation(), + (EntryAttribute) modif.getAttribute().clone() ); + clone.modificationList.add( modifClone ); + } + } + + if ( modificationItems != null ) + { + for ( String key:modificationItems.keySet() ) + { + Modification modif = modificationItems.get( key ); + Modification modifClone = new ClientModification( modif.getOperation(), + (EntryAttribute) modif.getAttribute().clone() ); + clone.modificationItems.put( key, modifClone ); + } + + } + + if ( entry != null ) + { + clone.entry = (ClientEntry)entry.clone(); + } + + return clone; + } + + /** + * Dumps the attributes + * @return A String representing the attributes + */ + private String dumpAttributes() + { + StringBuffer sb = new StringBuffer(); + + for ( EntryAttribute attribute:entry ) + { + if ( attribute == null ) + { + sb.append( " Null attribute\n" ); + continue; + } + + sb.append( " ").append( attribute.getId() ).append( ":\n" ); + + for ( Value value:attribute ) + { + if ( !value.isBinary() ) + { + sb.append( " " ).append( value.getString() ).append('\n' ); + } + else + { + sb.append( " " ).append( StringTools.dumpBytes( value.getBytes() ) ).append('\n' ); + } + } + } + + return sb.toString(); + } + + /** + * Dumps the modifications + * @return A String representing the modifications + */ + private String dumpModificationItems() + { + StringBuffer sb = new StringBuffer(); + + for ( Modification modif:modificationList ) + { + sb.append( " Operation: " ); + + switch ( modif.getOperation() ) + { + case ADD_ATTRIBUTE : + sb.append( "ADD\n" ); + break; + + case REMOVE_ATTRIBUTE : + sb.append( "REMOVE\n" ); + break; + + case REPLACE_ATTRIBUTE : + sb.append( "REPLACE \n" ); + break; + + default : + break; // Do nothing + } + + EntryAttribute attribute = modif.getAttribute(); + + sb.append( " Attribute: " ).append( attribute.getId() ).append( '\n' ); + + if ( attribute.size() != 0 ) + { + for ( Value value:attribute ) + { + if ( !value.isBinary() ) + { + sb.append( " " ).append( value.getString() ).append('\n' ); + } + else + { + sb.append( " " ).append( StringTools.dumpBytes( value.getBytes() ) ).append('\n' ); + } + } + } + } + + return sb.toString(); + } + + + /** + * @return a String representing the Entry, as a LDIF + */ + public String toString() + { + try + { + return LdifUtils.convertToLdif( this ); + } + catch ( NamingException ne ) + { + return null; + } + } + + + /** + * @see Object#hashCode() + * + * @return the instance's hash code + */ + public int hashCode() + { + int result = 37; + + if ( entry.getDn() != null ) + { + result = result*17 + entry.getDn().hashCode(); + } + + if ( changeType != null ) + { + result = result*17 + changeType.hashCode(); + + // Check each different cases + switch ( changeType ) + { + case Add : + // Checks the attributes + if ( entry != null ) + { + result = result * 17 + entry.hashCode(); + } + + break; + + case Delete : + // Nothing to compute + break; + + case Modify : + if ( modificationList != null ) + { + result = result * 17 + modificationList.hashCode(); + + for ( Modification modification:modificationList ) + { + result = result * 17 + modification.hashCode(); + } + } + + break; + + case ModDn : + case ModRdn : + result = result * 17 + ( deleteOldRdn ? 1 : -1 ); + + if ( newRdn != null ) + { + result = result*17 + newRdn.hashCode(); + } + + if ( newSuperior != null ) + { + result = result*17 + newSuperior.hashCode(); + } + + break; + + default : + break; // do nothing + } + } + + if ( control != null ) + { + result = result * 17 + control.hashCode(); + } + + return result; + } + + /** + * @see Object#equals(Object) + * @return true if both values are equal + */ + public boolean equals( Object o ) + { + // Basic equals checks + if ( this == o ) + { + return true; + } + + if ( o == null ) + { + return false; + } + + if ( ! (o instanceof LdifEntry ) ) + { + return false; + } + + LdifEntry otherEntry = (LdifEntry)o; + + // Check the DN + LdapDN thisDn = entry.getDn(); + LdapDN dnEntry = otherEntry.getDn(); + + if ( !thisDn.equals( dnEntry ) ) + { + return false; + } + + + // Check the changeType + if ( changeType != otherEntry.changeType ) + { + return false; + } + + // Check each different cases + switch ( changeType ) + { + case Add : + // Checks the attributes + if ( entry == null ) + { + if ( otherEntry.entry != null ) + { + return false; + } + else + { + break; + } + } + + if ( otherEntry.entry == null ) + { + return false; + } + + if ( entry.size() != otherEntry.entry.size() ) + { + return false; + } + + if ( !entry.equals( otherEntry.entry ) ) + { + return false; + } + + break; + + case Delete : + // Nothing to do, if the DNs are equals + break; + + case Modify : + // Check the modificationItems list + + // First, deal with special cases + if ( modificationList == null ) + { + if ( otherEntry.modificationList != null ) + { + return false; + } + else + { + break; + } + } + + if ( otherEntry.modificationList == null ) + { + return false; + } + + if ( modificationList.size() != otherEntry.modificationList.size() ) + { + return false; + } + + // Now, compares the contents + int i = 0; + + for ( Modification modification:modificationList ) + { + if ( ! modification.equals( otherEntry.modificationList.get( i ) ) ) + { + return false; + } + + i++; + } + + break; + + case ModDn : + case ModRdn : + // Check the deleteOldRdn flag + if ( deleteOldRdn != otherEntry.deleteOldRdn ) + { + return false; + } + + // Check the newRdn value + try + { + RDN thisNewRdn = new RDN( newRdn ); + RDN entryNewRdn = new RDN( otherEntry.newRdn ); + + if ( !thisNewRdn.equals( entryNewRdn ) ) + { + return false; + } + } + catch ( InvalidNameException ine ) + { + return false; + } + + // Check the newSuperior value + try + { + LdapDN thisNewSuperior = new LdapDN( newSuperior ); + LdapDN entryNewSuperior = new LdapDN( otherEntry.newSuperior ); + + if ( ! thisNewSuperior.equals( entryNewSuperior ) ) + { + return false; + } + } + catch ( InvalidNameException ine ) + { + return false; + } + + break; + + default : + break; // do nothing + } + + if ( control != null ) + { + return control.equals( otherEntry.control ); + } + else + { + return otherEntry.control == null; + } + } + + + /** + * @see Externalizable#readExternal(ObjectInput) + * + * @param in The stream from which the LdifEntry is read + * @throws IOException If the stream can't be read + * @throws ClassNotFoundException If the LdifEntry can't be created + */ + public void readExternal( ObjectInput in ) throws IOException , ClassNotFoundException + { + // Read the changeType + int type = in.readInt(); + changeType = ChangeType.getChangeType( type ); + entry = (ClientEntry)in.readObject(); + + switch ( changeType ) + { + case Add : + // Fallback + case Delete : + // we don't have anything to read, but the control + break; + + case ModDn : + // Fallback + case ModRdn : + deleteOldRdn = in.readBoolean(); + + if ( in.readBoolean() ) + { + newRdn = in.readUTF(); + } + + if ( in.readBoolean() ) + { + newSuperior = in.readUTF(); + } + + break; + + case Modify : + // Read the modification + int nbModifs = in.readInt(); + + + for ( int i = 0; i < nbModifs; i++ ) + { + int operation = in.readInt(); + String modStr = in.readUTF(); + DefaultClientAttribute value = (DefaultClientAttribute)in.readObject(); + + addModificationItem( ModificationOperation.getOperation( operation ), modStr, value ); + } + + break; + } + + if ( in.available() > 0 ) + { + // We have a control + control = (Control)in.readObject(); + } + } + + + /** + * @see Externalizable#readExternal(ObjectInput)

+ * + *@param out The stream in which the ChangeLogEvent will be serialized. + * + *@throws IOException If the serialization fail + */ + public void writeExternal( ObjectOutput out ) throws IOException + { + // Write the changeType + out.writeInt( changeType.getChangeType() ); + + // Write the entry + out.writeObject( entry ); + + // Write the data + switch ( changeType ) + { + case Add : + // Fallback + case Delete : + // we don't have anything to write, but the control + break; + + case ModDn : + // Fallback + case ModRdn : + out.writeBoolean( deleteOldRdn ); + + if ( newRdn != null ) + { + out.writeBoolean( true ); + out.writeUTF( newRdn ); + } + else + { + out.writeBoolean( false ); + } + + if ( newSuperior != null ) + { + out.writeBoolean( true ); + out.writeUTF( newSuperior ); + } + else + { + out.writeBoolean( false ); + } + break; + + case Modify : + // Read the modification + out.writeInt( modificationList.size() ); + + for ( Modification modification:modificationList ) + { + out.writeInt( modification.getOperation().getValue() ); + out.writeUTF( modification.getAttribute().getId() ); + + EntryAttribute attribute = modification.getAttribute(); + out.writeObject( attribute ); + } + + break; + } + + if ( control != null ) + { + // Write the control + out.writeObject( control ); + + } + + // and flush the result + out.flush(); + } +}