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