001/*
002 * ====================================================================
003 *
004 * The Apache Software License, Version 1.1
005 *
006 * Copyright (c) 1999-2003 The Apache Software Foundation. All rights reserved.
007 *
008 * Redistribution and use in source and binary forms, with or without
009 * modification, are permitted provided that the following conditions are met:
010 *
011 * 1. Redistributions of source code must retain the above copyright notice,
012 * this list of conditions and the following disclaimer.
013 *
014 * 2. Redistributions in binary form must reproduce the above copyright notice,
015 * this list of conditions and the following disclaimer in the documentation
016 * and/or other materials provided with the distribution.
017 *
018 * 3. The end-user documentation included with the redistribution, if any, must
019 * include the following acknowledgement: "This product includes software
020 * developed by the Apache Software Foundation (http://www.apache.org/)."
021 * Alternately, this acknowledgement may appear in the software itself, if and
022 * wherever such third-party acknowledgements normally appear.
023 *
024 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
025 * Foundation" must not be used to endorse or promote products derived from this
026 * software without prior written permission. For written permission, please
027 * contact [email protected].
028 *
029 * 5. Products derived from this software may not be called "Apache" nor may
030 * "Apache" appear in their names without prior written permission of the Apache
031 * Software Foundation.
032 *
033 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
034 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE
036 * SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
037 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
038 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
039 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
040 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
041 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
042 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
043 * ====================================================================
044 *
045 * This software consists of voluntary contributions made by many individuals on
046 * behalf of the Apache Software Foundation. For more information on the Apache
047 * Software Foundation, please see <http://www.apache.org/>.
048 *
049 */
050
051package org.apache.wicket.util.diff;
052
053import java.util.ArrayList;
054import java.util.Arrays;
055import java.util.List;
056
057/**
058 * Holds a information about a part of the text involved in a differencing or patching operation.
059 * 
060 * @version $Id: Chunk.java,v 1.1 2006/03/12 00:24:21 juanca Exp $
061 * @author <a href="mailto:[email protected]">Juanco Anez</a>
062 * @see Diff
063 * @see Delta
064 */
065public class Chunk extends ToString
066{
067
068        protected int anchor;
069
070        protected int count;
071
072        protected List<Object> chunk;
073
074        /**
075         * Creates a chunk that doesn't copy the original text.
076         * 
077         * @param pos
078         *            the start position in the text.
079         * @param count
080         *            the size of the chunk.
081         */
082        public Chunk(final int pos, final int count)
083        {
084                anchor = pos;
085                this.count = (count >= 0 ? count : 0);
086        }
087
088        /**
089         * Creates a chunk and saves a copy the original chunk's text.
090         * 
091         * @param iseq
092         *            the original text.
093         * @param pos
094         *            the start position in the text.
095         * @param count
096         *            the size of the chunk.
097         */
098        public Chunk(final Object[] iseq, final int pos, final int count)
099        {
100                this(pos, count);
101                chunk = slice(iseq, pos, count);
102        }
103
104        /**
105         * Creates a chunk that will be displaced in the resulting text, and saves a copy the original
106         * chunk's text.
107         * 
108         * @param iseq
109         *            the original text.
110         * @param pos
111         *            the start position in the text.
112         * @param count
113         *            the size of the chunk.
114         * @param offset
115         *            the position the chunk should have in the resulting text.
116         */
117        public Chunk(final Object[] iseq, final int pos, final int count, final int offset)
118        {
119                this(offset, count);
120                chunk = slice(iseq, pos, count);
121        }
122
123        /**
124         * Creates a chunk and saves a copy the original chunk's text.
125         * 
126         * @param iseq
127         *            the original text.
128         * @param pos
129         *            the start position in the text.
130         * @param count
131         *            the size of the chunk.
132         */
133        public Chunk(final List<Object> iseq, final int pos, final int count)
134        {
135                this(pos, count);
136                chunk = slice(iseq, pos, count);
137        }
138
139        /**
140         * Creates a chunk that will be displaced in the resulting text, and saves a copy the original
141         * chunk's text.
142         * 
143         * @param iseq
144         *            the original text.
145         * @param pos
146         *            the start position in the text.
147         * @param count
148         *            the size of the chunk.
149         * @param offset
150         *            the position the chunk should have in the resulting text.
151         */
152        public Chunk(final List<Object> iseq, final int pos, final int count, final int offset)
153        {
154                this(offset, count);
155                chunk = slice(iseq, pos, count);
156        }
157
158        /**
159         * Returns the anchor position of the chunk.
160         * 
161         * @return the anchor position.
162         */
163        public int anchor()
164        {
165                return anchor;
166        }
167
168        /**
169         * Returns the size of the chunk.
170         * 
171         * @return the size.
172         */
173        public int size()
174        {
175                return count;
176        }
177
178        /**
179         * Returns the index of the first line of the chunk.
180         * 
181         * @return int
182         */
183        public int first()
184        {
185                return anchor();
186        }
187
188        /**
189         * Returns the index of the last line of the chunk.
190         * 
191         * @return int
192         */
193        public int last()
194        {
195                return anchor() + size() - 1;
196        }
197
198        /**
199         * Returns the <i>from</i> index of the chunk in RCS terms.
200         * 
201         * @return int
202         */
203        public int rcsfrom()
204        {
205                return anchor + 1;
206        }
207
208        /**
209         * Returns the <i>to</i> index of the chunk in RCS terms.
210         * 
211         * @return int
212         */
213        public int rcsto()
214        {
215                return anchor + count;
216        }
217
218        /**
219         * Returns the text saved for this chunk.
220         * 
221         * @return the text.
222         */
223        public List<Object> chunk()
224        {
225                return chunk;
226        }
227
228        /**
229         * Verifies that this chunk's saved text matches the corresponding text in the given sequence.
230         * 
231         * @param target
232         *            the sequence to verify against.
233         * @return true if the texts match.
234         */
235        public boolean verify(final List<Object> target)
236        {
237                if (chunk == null)
238                {
239                        return true;
240                }
241                if (last() > target.size())
242                {
243                        return false;
244                }
245                for (int i = 0; i < count; i++)
246                {
247                        if (!target.get(anchor + i).equals(chunk.get(i)))
248                        {
249                                return false;
250                        }
251                }
252                return true;
253        }
254
255        /**
256         * Delete this chunk from he given text.
257         * 
258         * @param target
259         *            the text to delete from.
260         */
261        public void applyDelete(final List<Object> target)
262        {
263                for (int i = last(); i >= first(); i--)
264                {
265                        target.remove(i);
266                }
267        }
268
269        /**
270         * Add the text of this chunk to the target at the given position.
271         * 
272         * @param start
273         *            where to add the text.
274         * @param target
275         *            the text to add to.
276         */
277        public void applyAdd(int start, final List<Object> target)
278        {
279                for (Object aChunk : chunk)
280                {
281                        target.add(start++, aChunk);
282                }
283        }
284
285        /**
286         * Provide a string image of the chunk using the an empty prefix and postfix.
287         * 
288         * @param s
289         */
290        @Override
291        public void toString(final StringBuilder s)
292        {
293                toString(s, "", "");
294        }
295
296        /**
297         * Provide a string image of the chunk using the given prefix and postfix.
298         * 
299         * @param s
300         *            where the string image should be appended.
301         * @param prefix
302         *            the text that should prefix each line.
303         * @param postfix
304         *            the text that should end each line.
305         * @return StringBuilder
306         */
307        public StringBuilder toString(final StringBuilder s, final String prefix, final String postfix)
308        {
309                if (chunk != null)
310                {
311                        for (Object aChunk : chunk)
312                        {
313                                s.append(prefix);
314                                s.append(aChunk);
315                                s.append(postfix);
316                        }
317                }
318                return s;
319        }
320
321        /**
322         * Retrieves the specified part from a {@link List List}.
323         * 
324         * @param <T>
325         *            the type of objects contained in <code>seq</code>
326         * 
327         * @param seq
328         *            the list to retrieve a slice from.
329         * @param pos
330         *            the start position.
331         * @param count
332         *            the number of items in the slice.
333         * @return a {@link List List} containing the specified items.
334         */
335        public static <T> List<T> slice(final List<T> seq, final int pos, final int count)
336        {
337                if (count <= 0)
338                {
339                        return new ArrayList<>();
340                }
341                else
342                {
343                        return new ArrayList<>(seq.subList(pos, pos + count));
344                }
345        }
346
347        /**
348         * Retrieves a slice from an {@link Object Object} array.
349         * 
350         * @param seq
351         *            the list to retrieve a slice from.
352         * @param pos
353         *            the start position.
354         * @param count
355         *            the number of items in the slice.
356         * @return a {@link List List} containing the specified items.
357         */
358        public static List<Object> slice(final Object[] seq, final int pos, final int count)
359        {
360                return slice(Arrays.asList(seq), pos, count);
361        }
362
363        /**
364         * Provide a string representation of the numeric range of this chunk.
365         * 
366         * @return String
367         */
368        public String rangeString()
369        {
370                StringBuilder result = new StringBuilder();
371                rangeString(result);
372                return result.toString();
373        }
374
375        /**
376         * Provide a string representation of the numeric range of this chunk.
377         * 
378         * @param s
379         *            where the string representation should be appended.
380         */
381        public void rangeString(final StringBuilder s)
382        {
383                rangeString(s, ",");
384        }
385
386        /**
387         * Provide a string representation of the numeric range of this chunk.
388         * 
389         * @param s
390         *            where the string representation should be appended.
391         * @param separ
392         *            what to use as line separator.
393         */
394        public void rangeString(final StringBuilder s, final String separ)
395        {
396                if (size() <= 1)
397                {
398                        s.append(Integer.toString(rcsfrom()));
399                }
400                else
401                {
402                        s.append(Integer.toString(rcsfrom()));
403                        s.append(separ);
404                        s.append(Integer.toString(rcsto()));
405                }
406        }
407}