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