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     *    http://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     */
020    package org.apache.directory.shared.ldap.name;
021    
022    import java.io.IOException;
023    import java.io.ObjectInput;
024    import java.io.ObjectOutput;
025    
026    import org.apache.directory.shared.ldap.entry.BinaryValue;
027    import org.apache.directory.shared.ldap.entry.StringValue;
028    import org.apache.directory.shared.ldap.entry.Value;
029    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
030    import org.apache.directory.shared.ldap.util.StringTools;
031    import org.slf4j.Logger;
032    import org.slf4j.LoggerFactory;
033    
034    /**
035     * A helper class which serialize and deserialize an AttributeTypeAndValue
036     *
037     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038     * @version $Rev$, $Date$
039     */
040    public class AVASerializer
041    {
042        /** The LoggerFactory used by this class */
043        protected static final Logger LOG = LoggerFactory.getLogger( AVASerializer.class );
044    
045        /**
046         * Serialize an AttributeTypeAndValue object.
047         * 
048         * An AttributeTypeAndValue is composed of  a type and a value.
049         * The data are stored following the structure :
050         * 
051         * <li>upName</li> The User provided ATAV
052         * <li>start</li> The position of this ATAV in the DN
053         * <li>length</li> The ATAV length
054         * <li>upType</li> The user Provided Type
055         * <li>normType</li> The normalized AttributeType
056         * <li>isHR<li> Tells if the value is a String or not
057         * <p>
058         * if the value is a String :
059         * <li>upValue</li> The User Provided value.
060         * <li>value</li> The normalized value.
061         * <p>
062         * if the value is binary :
063         * <li>upValueLength</li>
064         * <li>upValue</li> The User Provided value.
065         * <li>valueLength</li>
066         * <li>value</li> The normalized value.
067         *
068         * @param atav the AttributeTypeAndValue to serialize
069         * @param out the OutputStream in which the atav will be serialized
070         * @throws IOException If we can't serialize the atav
071         */
072        public static void serialize( AVA atav, ObjectOutput out ) throws IOException
073        {
074            if ( StringTools.isEmpty( atav.getUpName() ) || 
075                 StringTools.isEmpty( atav.getUpType() ) ||
076                 StringTools.isEmpty( atav.getNormType() ) ||
077                 ( atav.getStart() < 0 ) ||
078                 ( atav.getLength() < 2 ) ||             // At least a type and '='
079                 ( atav.getUpValue().isNull() ) ||
080                 ( atav.getNormValue().isNull() ) )
081            {
082                String message = "Cannot serialize an wrong ATAV, ";
083                
084                if ( StringTools.isEmpty( atav.getUpName() ) )
085                {
086                    message += "the upName should not be null or empty";
087                }
088                else if ( StringTools.isEmpty( atav.getUpType() ) )
089                {
090                    message += "the upType should not be null or empty";
091                }
092                else if ( StringTools.isEmpty( atav.getNormType() ) )
093                {
094                    message += "the normType should not be null or empty";
095                }
096                else if ( atav.getStart() < 0 )
097                {
098                    message += "the start should not be < 0";
099                }
100                else if ( atav.getLength() < 2 )
101                {
102                    message += "the length should not be < 2";
103                }
104                else if ( atav.getUpValue().isNull() )
105                {
106                    message += "the upValue should not be null";
107                }
108                else if ( atav.getNormValue().isNull() )
109                {
110                    message += "the value should not be null";
111                }
112                    
113                LOG.error( message );
114                throw new IOException( message );
115            }
116            
117            out.writeUTF( atav.getUpName() );
118            out.writeInt( atav.getStart() );
119            out.writeInt( atav.getLength() );
120            out.writeUTF( atav.getUpType() );
121            out.writeUTF( atav.getNormType() );
122            
123            boolean isHR = !atav.getNormValue().isBinary();
124            
125            out.writeBoolean( isHR );
126            
127            if ( isHR )
128            {
129                out.writeUTF( atav.getUpValue().getString() );
130                out.writeUTF( atav.getNormValue().getString() );
131            }
132            else
133            {
134                out.writeInt( atav.getUpValue().length() );
135                out.write( atav.getUpValue().getBytes() );
136                out.writeInt( atav.getNormValue().length() );
137                out.write( atav.getNormValue().getBytes() );
138            }
139        }
140        
141        
142        /**
143         * Deserialize an AttributeTypeAndValue object
144         * 
145         * We read back the data to create a new ATAV. The structure 
146         * read is exposed in the {@link AVA#writeExternal(ObjectOutput)} 
147         * method<p>
148         * 
149         * @param in the input stream
150         * @throws IOException If the input stream can't be read
151         * @return The constructed AttributeTypeAndValue
152         */
153        public static AVA deserialize( ObjectInput in ) throws IOException
154        {
155            String upName = in.readUTF();
156            int start = in.readInt();
157            int length = in.readInt();
158            String upType = in.readUTF();
159            String normType = in.readUTF();
160            
161            boolean isHR = in.readBoolean();
162    
163            try
164            {
165                if ( isHR )
166                {
167                    Value<String> upValue = new StringValue( in.readUTF() );
168                    Value<String> normValue = new StringValue( in.readUTF() );
169                    
170                    AVA atav = 
171                        new AVA( upType, normType, upValue, normValue, upName );
172                    
173                    return atav;
174                }
175                else
176                {
177                    int upValueLength = in.readInt();
178                    byte[] upValue = new byte[upValueLength];
179                    in.readFully( upValue );
180        
181                    int valueLength = in.readInt();
182                    byte[] normValue = new byte[valueLength];
183                    in.readFully( normValue );
184        
185                    AVA atav = 
186                        new AVA( upType, normType, 
187                            new BinaryValue( upValue) , 
188                            new BinaryValue( normValue ), upName );
189                    
190                    return atav;
191                }
192            }
193            catch ( LdapInvalidDnException ine )
194            {
195                throw new IOException( ine.getMessage() );
196            }
197        }
198    }