View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.server.core.avltree;
21  
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.ByteArrayOutputStream;
25  import java.io.DataInputStream;
26  import java.io.DataOutputStream;
27  import java.io.IOException;
28  import java.util.Comparator;
29  
30  import org.apache.directory.server.i18n.I18n;
31  
32  
33  /**
34   * Class to serialize the AvlTree node data.
35   *
36   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
37   */
38  @SuppressWarnings("unchecked")
39  public class AvlTreeMarshaller<E> implements Marshaller<AvlTree<E>>
40  {
41      /** used for serialized form of an empty AvlTree */
42      private static final byte[] EMPTY_TREE = new byte[1];
43  
44      /** marshaller to be used for marshalling the keys */
45      private Marshaller<E> keyMarshaller;
46  
47      /** key Comparator for the AvlTree */
48      private Comparator<E> comparator;
49  
50  
51      /**
52       * Creates a new instance of AvlTreeMarshaller with a custom key
53       * Marshaller.
54       *
55       * @param comparator Comparator to be used for key comparision
56       * @param keyMarshaller marshaller for keys
57       */
58      public AvlTreeMarshaller( Comparator<E> comparator, Marshaller<E> keyMarshaller )
59      {
60          this.comparator = comparator;
61          this.keyMarshaller = keyMarshaller;
62      }
63  
64  
65      /**
66       * Creates a new instance of AvlTreeMarshaller with the default key
67       * Marshaller which uses Java Serialization.
68       *
69       * @param comparator Comparator to be used for key comparision
70       */
71      public AvlTreeMarshaller( Comparator<E> comparator )
72      {
73          this.comparator = comparator;
74          this.keyMarshaller = ( Marshaller<E> ) DefaultMarshaller.INSTANCE;
75      }
76  
77  
78      /**
79       * Marshals the given tree to bytes
80       * @param tree the tree to be marshalled
81       */
82      public byte[] serialize( AvlTree<E> tree )
83      {
84          if ( tree.isEmpty() )
85          {
86              return EMPTY_TREE;
87          }
88  
89          LinkedAvlNode<E> x = tree.getFirst().next;
90  
91          while ( x != null )
92          {
93              x.setIndex( x.previous.getIndex() + 1 );
94              x = x.next;
95          }
96  
97          byte[] data = null;
98  
99          try ( ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
100             DataOutputStream out = new DataOutputStream( byteStream ) )
101         {
102             out.writeByte( 0 ); // represents the start of AvlTree byte stream
103             out.writeInt( tree.getSize() );
104             writeTree( tree.getRoot(), out );
105             out.flush();
106             data = byteStream.toByteArray();
107         }
108         catch ( IOException e )
109         {
110             e.printStackTrace();
111         }
112 
113         return data;
114     }
115 
116 
117     /**
118      * writes the content of the AVLTree to an output stream.
119      * The current format is 
120      *  
121      *  AvlTree = [0(zero-byte-value)][node] // the '0' (zero) is to distinguish AvlTree from BTreeRedirect which starts with 1 (one)
122      *   node = [size] [data-length] [data] [index] [child-marker] [node] [child-marker] [node]
123      *
124      * @param node the node to be marshalled to bytes
125      * @param out OutputStream
126      * @throws IOException on write failures of serialized tree to stream
127      */
128     private void writeTree( LinkedAvlNode<E> node, DataOutputStream out ) throws IOException
129     {
130         byte[] data = keyMarshaller.serialize( node.getKey() );
131 
132         out.writeInt( data.length ); // data-length
133         out.write( data ); // data
134         out.writeInt( node.getIndex() ); // index
135 
136         if ( node.getLeft() != null )
137         {
138             out.writeInt( 2 ); // left
139             writeTree( node.getLeft(), out );
140         }
141         else
142         {
143             out.writeInt( 0 );
144         }
145 
146         if ( node.getRight() != null )
147         {
148             out.writeInt( 4 ); // right
149             writeTree( node.getRight(), out );
150         }
151         else
152         {
153             out.writeInt( 0 );
154         }
155 
156     }
157 
158 
159     /**
160      * Creates an AVLTree from given bytes of data.
161      * 
162      * @param data byte array to be converted into AVLTree  
163      */
164     public AvlTree<E> deserialize( byte[] data ) throws IOException
165     {
166         if ( data == null || data.length == 0 )
167         {
168             throw new IOException( I18n.err( I18n.ERR_439 ) );
169         }
170 
171         if ( data.length == 1 && data[0] == 0 )
172         {
173             return new AvlTreeImpl<>( comparator );
174         }
175 
176         ByteArrayInputStream bin = new ByteArrayInputStream( data );
177         DataInputStream din = new DataInputStream( bin );
178 
179         byte startByte = din.readByte();
180 
181         if ( startByte != 0 )
182         {
183             throw new IOException( I18n.err( I18n.ERR_443 ) );
184         }
185 
186         int size = din.readInt();
187 
188         LinkedAvlNodere/avltree/LinkedAvlNode.html#LinkedAvlNode">LinkedAvlNode[] nodes = new LinkedAvlNode[size];
189         LinkedAvlNode<E> root = readTree( din, nodes );
190 
191         AvlTreeImpl<E> tree = new AvlTreeImpl<>( comparator );
192 
193         tree.setRoot( root );
194 
195         tree.setFirst( nodes[0] );
196 
197         // Update the size
198         tree.setSize( size );
199 
200         if ( nodes.length >= 1 )
201         {
202             tree.setLast( nodes[nodes.length - 1] );
203         }
204 
205         for ( int i = 0; i < nodes.length - 1; i++ )
206         {
207             nodes[i].setNext( nodes[i + 1] );
208             nodes[i + 1].setPrevious( nodes[i] );
209         }
210 
211         return tree;
212     }
213 
214 
215     /**
216      * Reads the data from given InputStream and creates the LinkedAvlNodes to
217      * form the tree node = [size] [data-length] [data] [index] [child-marker]
218      * [node] [child-marker] [node].
219      *
220      * @param in the input stream to deserialize from
221      * @param nodes the deserialized nodes
222      * @return the deserialized AvlTree node
223      * @throws IOException on failures to deserialize or read from the stream
224      */
225     public LinkedAvlNode<E> readTree( DataInputStream in, LinkedAvlNode[] nodes )
226         throws IOException
227     {
228         int dLen = in.readInt();
229 
230         byte[] data = new byte[dLen];
231 
232         //noinspection ResultOfMethodCallIgnored
233         in.readFully( data );
234 
235         E key = keyMarshaller.deserialize( data );
236         LinkedAvlNode<E> node = new LinkedAvlNode<>( key );
237 
238         int index = in.readInt();
239         nodes[index] = node;
240 
241         int childMarker = in.readInt();
242 
243         if ( childMarker == 2 )
244         {
245             node.setLeft( readTree( in, nodes ) );
246         }
247 
248         childMarker = in.readInt();
249 
250         if ( childMarker == 4 )
251         {
252             node.setRight( readTree( in, nodes ) );
253         }
254 
255         return node;
256     }
257 }