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.server.core.avltree;
021    
022    
023    import java.io.ByteArrayInputStream;
024    import java.io.ByteArrayOutputStream;
025    import java.io.DataInputStream;
026    import java.io.DataOutputStream;
027    import java.io.IOException;
028    import java.util.Comparator;
029    
030    import org.apache.directory.server.i18n.I18n;
031    import org.apache.directory.shared.ldap.util.StringTools;
032    import org.slf4j.Logger;
033    import org.slf4j.LoggerFactory;
034    
035    
036    /**
037     * Class to serialize the Array data.
038     *
039     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040     * @version $Rev$, $Date$
041     */
042    @SuppressWarnings("unchecked")
043    public class ArrayMarshaller<E> implements Marshaller<ArrayTree<E>>
044    {
045        /** static logger */
046        private static final Logger LOG = LoggerFactory.getLogger( ArrayMarshaller.class );
047    
048        /** used for serialized form of an empty AvlTree */
049        private static final byte[] EMPTY_TREE = new byte[1];
050    
051        /** marshaller to be used for marshalling the keys */
052        private Marshaller<E> keyMarshaller;
053        
054        /** key Comparator for the AvlTree */
055        private Comparator<E> comparator;
056        
057    
058        /**
059         * Creates a new instance of AvlTreeMarshaller with a custom key
060         * Marshaller.
061         *
062         * @param comparator Comparator to be used for key comparision
063         * @param keyMarshaller marshaller for keys
064         */
065        public ArrayMarshaller( Comparator<E> comparator, Marshaller<E> keyMarshaller )
066        {
067            this.comparator = comparator;
068            this.keyMarshaller = keyMarshaller;
069        }
070    
071    
072        /**
073         * Creates a new instance of AvlTreeMarshaller with the default key
074         * Marshaller which uses Java Serialization.
075         *
076         * @param comparator Comparator to be used for key comparision
077         */
078        public ArrayMarshaller( Comparator<E> comparator )
079        {
080            this.comparator = comparator;
081            this.keyMarshaller = ( Marshaller<E> ) DefaultMarshaller.INSTANCE;
082        }
083    
084    
085        /**
086         * Marshals the given tree to bytes
087         * @param tree the tree to be marshalled
088         */
089        public byte[] serialize( ArrayTree<E> tree )
090        {
091            if ( ( tree == null ) || ( tree.size() == 0 ) )
092            {
093                return EMPTY_TREE;
094            }
095    
096            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
097            DataOutputStream out = new DataOutputStream( byteStream );
098            byte[] data = null;
099            
100            try
101            {
102                out.writeByte( 0 ); // represents the start of an Array byte stream
103                out.writeInt( tree.size() );
104    
105                for ( int position = 0; position < tree.size(); position++ )
106                {
107                    E value = tree.get( position );
108                    byte[] bytes = keyMarshaller.serialize( value );
109                    
110                    // Write the key length
111                    out.writeInt( bytes.length );
112                    
113                    // Write the key if its length is not null
114                    if ( bytes.length != 0 )
115                    {
116                        out.write( bytes );
117                    }
118                }
119                
120                out.flush();
121                data = byteStream.toByteArray();
122                
123                // Try to deserialize, just to see
124                try
125                {
126                    deserialize( data );
127                }
128                catch (NullPointerException npe )
129                {
130                    System.out.println( I18n.err( I18n.ERR_438, StringTools.dumpBytes( data ) ) );
131                    throw npe;
132                }
133    
134                out.close();
135            }
136            catch( IOException e )
137            {
138                e.printStackTrace();
139            }
140            
141            return data;
142        }
143    
144        
145        /**
146         * Creates an Array from given bytes of data.
147         * 
148         * @param data byte array to be converted into an array  
149         */
150        public ArrayTree<E> deserialize( byte[] data ) throws IOException
151        {
152            //LOG.debug( "Deserializing the tree, called by {}", Reflection.getCallerClass( 2 ).getSimpleName() );
153    
154            try
155            {
156                if ( ( data == null ) || ( data.length == 0 ) )
157                {
158                    throw new IOException( I18n.err( I18n.ERR_439 ) );
159                }
160        
161                if ( ( data.length == 1 ) && ( data[0] == 0 ) )
162                {
163                    E[] array = (E[])new Object[]{};
164                    ArrayTree<E> tree = new ArrayTree<E>( comparator, array );
165                    return tree;
166                }
167        
168                ByteArrayInputStream bin = new ByteArrayInputStream( data );
169                DataInputStream din = new DataInputStream( bin );
170                
171                byte startByte = din.readByte();
172                
173                if( startByte != 0 )
174                {
175                    throw new IOException( I18n.err( I18n.ERR_440 ) );
176                }
177                
178                int size = din.readInt();
179                E[] nodes = (E[])new Object[size];
180                
181                for ( int i = 0; i < size; i++ )
182                {
183                    // Read the object's size
184                    int dataSize = din.readInt();
185                    
186                    if ( dataSize != 0 )
187                    {
188                        byte[] bytes = new byte[ dataSize ];
189                        
190                        din.read( bytes );
191                        E key = keyMarshaller.deserialize( bytes );
192                        nodes[i] = key;
193                    }
194                }
195    
196                ArrayTree<E> arrayTree = new ArrayTree<E>( comparator, nodes );
197                
198                return arrayTree;
199            }
200            catch (NullPointerException npe )
201            {
202                System.out.println( I18n.err( I18n.ERR_441, StringTools.dumpBytes( data ) ) );
203                throw npe;
204            }
205        }
206    }