1    
2    /* ====================================================================
3     * The Apache Software License, Version 1.1
4     *
5     * Copyright (c) 2002 The Apache Software Foundation.  All rights
6     * reserved.
7     *
8     * Redistribution and use in source and binary forms, with or without
9     * modification, are permitted provided that the following conditions
10    * are met:
11    *
12    * 1. Redistributions of source code must retain the above copyright
13    *    notice, this list of conditions and the following disclaimer.
14    *
15    * 2. Redistributions in binary form must reproduce the above copyright
16    *    notice, this list of conditions and the following disclaimer in
17    *    the documentation and/or other materials provided with the
18    *    distribution.
19    *
20    * 3. The end-user documentation included with the redistribution,
21    *    if any, must include the following acknowledgment:
22    *       "This product includes software developed by the
23    *        Apache Software Foundation (http://www.apache.org/)."
24    *    Alternately, this acknowledgment may appear in the software itself,
25    *    if and wherever such third-party acknowledgments normally appear.
26    *
27    * 4. The names "Apache" and "Apache Software Foundation" and
28    *    "Apache POI" must not be used to endorse or promote products
29    *    derived from this software without prior written permission. For
30    *    written permission, please contact apache@apache.org.
31    *
32    * 5. Products derived from this software may not be called "Apache",
33    *    "Apache POI", nor may "Apache" appear in their name, without
34    *    prior written permission of the Apache Software Foundation.
35    *
36    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47    * SUCH DAMAGE.
48    * ====================================================================
49    *
50    * This software consists of voluntary contributions made by many
51    * individuals on behalf of the Apache Software Foundation.  For more
52    * information on the Apache Software Foundation, please see
53    * <http://www.apache.org/>.
54    */
55   
56   package org.apache.poi.hssf.record;
57   
58   import java.util.ArrayList;
59   
60   import org.apache.poi.util.LittleEndian;
61   
62   /**
63    * Title: Merged Cells Record<P>
64    * Description:  Optional record defining a square area of cells to "merged" into
65    *               one cell. <P>
66    * REFERENCE:  NONE (UNDOCUMENTED PRESENTLY) <P>
67    * @author Andrew C. Oliver (acoliver at apache dot org)
68    * @version 2.0-pre
69    */
70   
71   public class MergeCellsRecord
72       extends Record
73   {
74       public final static short sid = 0xe5;
75       private short             field_1_num_areas;
76       private ArrayList         field_2_regions;
77   
78       public MergeCellsRecord()
79       {
80       }
81   
82       /**
83        * Constructs a MergedCellsRecord and sets its fields appropriately
84        *
85        * @param sid     id must be 0xe5 or an exception will be throw upon validation
86        * @param size  the size of the data area of the record
87        * @param data  data of the record (should not contain sid/len)
88        */
89   
90       public MergeCellsRecord(short sid, short size, byte [] data)
91       {
92           super(sid, size, data);
93       }
94   
95       /**
96        * Constructs a MergedCellsRecord and sets its fields appropriately
97        *
98        * @param sid     id must be 0xe5 or an exception will be throw upon validation
99        * @param size  the size of the data area of the record
100       * @param data  data of the record (should not contain sid/len)
101       * @param offset the offset of the record's data
102       */
103  
104      public MergeCellsRecord(short sid, short size, byte [] data, int offset)
105      {
106          super(sid, size, data, offset);
107      }
108  
109      protected void fillFields(byte [] data, short size, int offset)
110      {
111          field_1_num_areas = LittleEndian.getShort(data, 0 + offset);
112          field_2_regions   = new ArrayList(field_1_num_areas + 10);
113          int pos = 2;
114  
115          for (int k = 0; k < field_1_num_areas; k++)
116          {
117              MergedRegion region =
118                  new MergedRegiongetShortLittleEndian              .getShort(data, pos + offset), LittleEndian
119                      .getShort(data, pos + 2 + offset), LittleEndian
120                      .getShort(data, pos + 4 + offset), LittleEndian
121                      .getShort(data, pos + 6 + offset));
122  
123              pos += 8;
124              field_2_regions.add(region);
125          }
126      }
127  
128      /**
129       * get the number of merged areas.  If this drops down to 0 you should just go
130       * ahead and delete the record.
131       * @return number of areas
132       */
133  
134      public short getNumAreas()
135      {
136          return field_1_num_areas;
137      }
138  
139      /**
140       * set the number of merged areas.  You do not need to call this if you use addArea,
141       * it will be incremented automatically or decremented when an area is removed.  If
142       * you are setting this to 0 then you are a terrible person.  Just remove the record.
143       * (just kidding about you being a terrible person..hehe)
144       *
145       * @param numareas  number of areas
146       */
147  
148      public void setNumAreas(short numareas)
149      {
150          field_1_num_areas = numareas;
151      }
152  
153      /**
154       * Add an area to consider a merged cell.  The index returned is only gauranteed to
155       * be correct provided you do not add ahead of or remove ahead of it  (in which case
156       * you should increment or decrement appropriately....in other words its an arrayList)
157       *
158       * @param rowfrom - the upper left hand corner's row
159       * @param colfrom - the upper left hand corner's col
160       * @param rowto - the lower right hand corner's row
161       * @param colto - the lower right hand corner's col
162       * @return new index of said area (don't depend on it if you add/remove)
163       */
164  
165      public int addArea(short rowfrom, short colfrom, short rowto, short colto)
166      {
167          if (field_2_regions == null)
168          {
169              field_2_regions = new ArrayList(10);
170          }
171          MergedRegion region = new MergedRegion(rowfrom, rowto, colfrom,
172                                                 colto);
173  
174          field_2_regions.add(region);
175          field_1_num_areas++;
176          return field_2_regions.size() - 1;
177      }
178  
179      /**
180       * essentially unmerge the cells in the "area" stored at the passed in index
181       * @param area index
182       */
183  
184      public void removeAreaAt(int area)
185      {
186          field_2_regions.remove(area);
187          field_1_num_areas--;
188      }
189  
190      /**
191       * return the MergedRegion at the given index.
192       *
193       * @return MergedRegion representing the area that is Merged (r1,c1 - r2,c2)
194       */
195  
196      public MergedRegion getAreaAt(int index)
197      {
198          return ( MergedRegion ) field_2_regions.get(index);
199      }
200  
201      public int getRecordSize()
202      {
203          int retValue;
204  
205          retValue = 6 + (8 * field_2_regions.size());
206          return retValue;
207      }
208  
209      public short getSid()
210      {
211          return sid;
212      }
213  
214      public int serialize(int offset, byte [] data)
215      {
216          int recordsize = getRecordSize();
217          int pos        = 6;
218  
219          LittleEndian.putShort(data, offset + 0, sid);
220          LittleEndian.putShort(data, offset + 2, ( short ) (recordsize - 4));
221          LittleEndian.putShort(data, offset + 4, getNumAreas());
222          for (int k = 0; k < getNumAreas(); k++)
223          {
224              MergedRegion region = getAreaAt(k);
225  
226              LittleEndian.putShort(data, offset + pos, region.row_from);
227              pos += 2;
228              LittleEndian.putShort(data, offset + pos, region.row_to);
229              pos += 2;
230              LittleEndian.putShort(data, offset + pos, region.col_from);
231              pos += 2;
232              LittleEndian.putShort(data, offset + pos, region.col_to);
233              pos += 2;
234          }
235          return recordsize;
236      }
237  
238      public String toString()
239      {
240          StringBuffer retval = new StringBuffer();
241  
242          retval.append("[MERGEDCELLS]").append("\n");
243          retval.append("     .sid        =").append(sid).append("\n");
244          retval.append("     .numregions =").append(field_1_num_areas)
245              .append("\n");
246          for (int k = 0; k < field_1_num_areas; k++)
247          {
248              MergedRegion region = ( MergedRegion ) field_2_regions.get(k);
249  
250              retval.append("     .rowfrom    =").append(region.row_from)
251                  .append("\n");
252              retval.append("     .colfrom    =").append(region.col_from)
253                  .append("\n");
254              retval.append("     .rowto      =").append(region.row_to)
255                  .append("\n");
256              retval.append("     .colto      =").append(region.col_to)
257                  .append("\n");
258          }
259          retval.append("[MERGEDCELLS]").append("\n");
260          return retval.toString();
261      }
262  
263      protected void validateSid(short id)
264      {
265          if (id != sid)
266          {
267              throw new RecordFormatException("NOT A MERGEDCELLS RECORD!! "
268                                              + id);
269          }
270      }
271  
272      /**
273       * this is a low level representation of a MergedRegion of cells.  It is an
274       * inner class because we do not want it used without reference to this class.
275       *
276       */
277  
278      public class MergedRegion
279      {
280  
281          /**
282           * create a merged region all in one stroke.
283           */
284  
285          public MergedRegion(short row_from, short row_to, short col_from,
286                              short col_to)
287          {
288              this.row_from = row_from;
289              this.row_to   = row_to;
290              this.col_from = col_from;
291              this.col_to   = col_to;
292          }
293  
294          /**
295           * upper lefthand corner row
296           */
297  
298          public short row_from;
299  
300          /**
301           * lower right hand corner row
302           */
303  
304          public short row_to;
305  
306          /**
307           * upper right hand corner col
308           */
309  
310          public short col_from;
311  
312          /**
313           * lower right hand corner col
314           */
315  
316          public short col_to;
317      }
318  }
319