View Javadoc

1   /***
2    * Redistribution  and use  in source  and binary  forms, with  or without
3    * modification, are permitted provided  that the following conditions are
4    * met :
5    *
6    * . Redistributions  of  source  code  must  retain  the  above copyright
7    *   notice, this list of conditions and the following disclaimer.
8    *
9    * . Redistributions in  binary form  must reproduce  the above  copyright
10   *   notice, this list of conditions  and the following disclaimer in  the
11   *   documentation and/or other materials provided with the distribution.
12   *
13   * . The name of the author may not be used to endorse or promote products
14   *   derived from this software without specific prior written permission.
15   *
16   * THIS SOFTWARE IS  PROVIDED BY THE  AUTHOR ``AS IS''  AND ANY EXPRESS  OR
17   * IMPLIED  WARRANTIES,  INCLUDING,  BUT   NOT  LIMITED  TO,  THE   IMPLIED
18   * WARRANTIES OF MERCHANTABILITY AND  FITNESS FOR A PARTICULAR  PURPOSE ARE
19   * DISCLAIMED.  IN NO  EVENT SHALL  THE AUTHOR  BE LIABLE  FOR ANY  DIRECT,
20   * INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR  CONSEQUENTIAL  DAMAGES
21   * (INCLUDING,  BUT  NOT LIMITED  TO,  PROCUREMENT OF  SUBSTITUTE  GOODS OR
22   * SERVICES;  LOSS  OF USE,  DATA,  OR PROFITS;  OR  BUSINESS INTERRUPTION)
23   * HOWEVER CAUSED  AND ON  ANY THEORY  OF LIABILITY,  WHETHER IN  CONTRACT,
24   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25   * ANY  WAY  OUT OF  THE  USE OF  THIS  SOFTWARE, EVEN  IF  ADVISED OF  THE
26   * POSSIBILITY OF SUCH DAMAGE.
27   *
28   * $Id$
29   */
30  
31  package palmed.edit.text;
32  
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.OutputStream;
36  import java.util.Enumeration;
37  import java.util.Vector;
38  import palmed.edit.util.Coordinate;
39  
40  /***
41   * This class implements a composite of cachable text chunks.
42   *
43   * @author Mathieu Champlon
44   * @version $Revision$ $Date$
45   */
46  public final class LargeText implements IText
47  {
48      /***
49       * The maximum size of each text chunk in bytes.
50       */
51      private final int threshold_;
52      /***
53       * The views.
54       */
55      private final Vector views_;
56      /***
57       * The line inserter.
58       */
59      private final NewLineInserter newLineInserter_;
60      /***
61       * The text eraser.
62       */
63      private final TextEraser textEraser_;
64      /***
65       * The cache.
66       */
67      private final ICache cache_;
68  
69      /***
70       * Create a large text.
71       *
72       * @param cache the cache
73       * @param threshold the size of each text chunk in bytes.
74       */
75      public LargeText( final ICache cache, final int threshold )
76      {
77          if( cache == null )
78              throw new IllegalArgumentException( "parameter 'cache' is null" );
79          if( threshold <= 0 )
80              throw new IllegalArgumentException( "parameter 'threshold' must be > 0" );
81          cache_ = cache;
82          threshold_ = threshold;
83          views_ = new Vector();
84          newLineInserter_ = new NewLineInserter();
85          textEraser_ = new TextEraser();
86          cache_.add( new Chunk( cache_ ) );
87      }
88  
89      private boolean apply( final ILineExtractor functor, final int line )
90      {
91          if( line < 0 )
92              return false;
93          final Enumeration chunks = cache_.elements();
94          int height = 0;
95          while( chunks.hasMoreElements() && line >= height )
96              height += ((IChunk)chunks.nextElement()).handle( functor, line - height );
97          return line <= height;
98      }
99  
100     private void apply( final ICoordinateExtractor functor, final Coordinate coordinate )
101     {
102         checkPosition( coordinate );
103         apply( new CoordinateExtractor( coordinate, functor ), coordinate.y_ );
104     }
105 
106     private void apply( final ITextExtractor functor, final Coordinate from, final Coordinate to )
107     {
108         final Enumeration chunks = cache_.elements();
109         Coordinate start = new Coordinate();
110         while( chunks.hasMoreElements() && to.y_ >= start.y_ )
111             start = ((IChunk)chunks.nextElement()).handle( functor, start, from, to );
112     }
113 
114     /***
115      * {@inheritDoc}
116      */
117     public int getHeight()
118     {
119         int height = 1;
120         final Enumeration chunks = cache_.elements();
121         while( chunks.hasMoreElements() )
122             height += ((IChunk)chunks.nextElement()).getHeight();
123         return height;
124     }
125 
126     /***
127      * {@inheritDoc}
128      */
129     public String getLine( final int y )
130     {
131         final StringBuffer result = new StringBuffer();
132         if( apply( new LineBuilder( result ), y ) )
133             return result.toString();
134         return null;
135     }
136 
137     /***
138      * {@inheritDoc}
139      */
140     public void insert( final Coordinate position, final char c )
141     {
142         apply( new CharacterInserter( c ), position );
143         update();
144     }
145 
146     private void checkPosition( final Coordinate position )
147     {
148         if( position.y_ >= getHeight() || position.x_ > getLine( position.y_ ).length() )
149             throw new RuntimeException( "invalid position : " + position );
150     }
151 
152     /***
153      * {@inheritDoc}
154      */
155     public void insertNewLine( final Coordinate position )
156     {
157         apply( newLineInserter_, position );
158         update();
159     }
160 
161     /***
162      * {@inheritDoc}
163      */
164     public void remove( final Coordinate from, final Coordinate to )
165     {
166         apply( textEraser_, from, to );
167         update();
168     }
169 
170     /***
171      * {@inheritDoc}
172      */
173     public void register( final ITextView view )
174     {
175         if( view == null )
176             throw new IllegalArgumentException( "parameter 'view' is null" );
177         views_.addElement( view );
178     }
179 
180     /***
181      * {@inheritDoc}
182      */
183     public void clear()
184     {
185         cache_.clear();
186         cache_.add( new Chunk( cache_ ) );
187         update();
188     }
189 
190     /***
191      * {@inheritDoc}
192      */
193     public void write( final OutputStream stream, final Coordinate from, final Coordinate to ) throws IOException
194     {
195         final Vector exceptions = new Vector(); // FIXME dirty !
196         apply( new TextSerializer( stream, exceptions ), from, to );
197         if( !exceptions.isEmpty() )
198             throw (IOException)exceptions.firstElement();
199     }
200 
201     /***
202      * {@inheritDoc}
203      */
204     public void read( final InputStream stream, final Coordinate from ) throws IOException
205     {
206         final Vector exceptions = new Vector(); // FIXME dirty !
207         apply( new TextDeserializer( stream, exceptions ), from );
208         if( !exceptions.isEmpty() )
209             throw (IOException)exceptions.firstElement();
210         update();
211     }
212 
213     private void update()
214     {
215         final Coordinate size = new Coordinate( 0, 1 );
216         int length = 0;
217         boolean hasBeenModified = false;
218         final Enumeration chunks = cache_.elements();
219         while( chunks.hasMoreElements() )
220         {
221             final IChunk chunk = (IChunk)chunks.nextElement();
222             length = chunk.count( size, length );
223             hasBeenModified |= chunk.hasBeenModified();
224         }
225         final Enumeration views = views_.elements();
226         while( views.hasMoreElements() )
227         {
228             final ITextView view = (ITextView)views.nextElement();
229             view.update( size.x_, size.y_ );
230             view.modified( hasBeenModified );
231         }
232     }
233 
234     private void updateModificationStatus()
235     {
236         boolean hasBeenModified = false;
237         final Enumeration chunks = cache_.elements();
238         while( chunks.hasMoreElements() )
239         {
240             final IChunk chunk = (IChunk)chunks.nextElement();
241             hasBeenModified |= chunk.hasBeenModified();
242         }
243         final Enumeration views = views_.elements();
244         while( views.hasMoreElements() )
245             ((ITextView)views.nextElement()).modified( hasBeenModified );
246     }
247 
248     /***
249      * {@inheritDoc}
250      */
251     public void read( final InputStream stream ) throws IOException
252     {
253         final MultiInputStream multi = new MultiInputStream( stream, threshold_ );
254         cache_.delete();
255         do
256         {
257             final IChunk chunk = new Chunk( cache_ );
258             chunk.read( multi );
259             cache_.add( chunk );
260             multi.next();
261         }
262         while( stream.available() > 0 );
263         update();
264     }
265 
266     /***
267      * {@inheritDoc}
268      */
269     public void write( final OutputStream stream ) throws IOException
270     {
271         final Enumeration chunks = cache_.elements();
272         while( chunks.hasMoreElements() )
273             ((IChunk)chunks.nextElement()).write( stream );
274         updateModificationStatus();
275     }
276 
277     /***
278      * {@inheritDoc}
279      */
280     public void unmarshall( final InputStream stream ) throws IOException
281     {
282         cache_.unmarshall( stream );
283         update();
284     }
285 
286     /***
287      * {@inheritDoc}
288      */
289     public void marshall( final OutputStream stream ) throws IOException
290     {
291         cache_.marshall( stream );
292     }
293 
294     /***
295      * {@inheritDoc}
296      */
297     public void delete()
298     {
299         cache_.delete();
300     }
301 
302     /***
303      * {@inheritDoc}
304      */
305     public void setLineSeparator( final String separator )
306     {
307         // TODO support custom separator in large text
308         throw new RuntimeException( "TODO support custom separator in large text" );
309     }
310 }