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