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      public int addArea(int rowfrom, short colfrom, int rowto, short colto)
167      {
168          if (field_2_regions == null)
169          {
170              field_2_regions = new ArrayList(10);
171          }
172          MergedRegion region = new MergedRegion(rowfrom, rowto, colfrom,
173                                                 colto);
174  
175          field_2_regions.add(region);
176          field_1_num_areas++;
177          return field_2_regions.size() - 1;
178      }
179  
180      /**
181       * essentially unmerge the cells in the "area" stored at the passed in index
182       * @param area index
183       */
184  
185      public void removeAreaAt(int area)
186      {
187          field_2_regions.remove(area);
188          field_1_num_areas--;
189      }
190  
191      /**
192       * return the MergedRegion at the given index.
193       *
194       * @return MergedRegion representing the area that is Merged (r1,c1 - r2,c2)
195       */
196  
197      public MergedRegion getAreaAt(int index)
198      {
199          return ( MergedRegion ) field_2_regions.get(index);
200      }
201  
202      public int getRecordSize()
203      {
204          int retValue;
205  
206          retValue = 6 + (8 * field_2_regions.size());
207          return retValue;
208      }
209  
210      public short getSid()
211      {
212          return sid;
213      }
214  
215      public int serialize(int offset, byte [] data)
216      {
217          int recordsize = getRecordSize();
218          int pos        = 6;
219  
220          LittleEndian.putShort(data, offset + 0, sid);
221          LittleEndian.putShort(data, offset + 2, ( short ) (recordsize - 4));
222          LittleEndian.putShort(data, offset + 4, getNumAreas());
223          for (int k = 0; k < getNumAreas(); k++)
224          {
225              MergedRegion region = getAreaAt(k);
226  
227              //LittleEndian.putShort(data, offset + pos, region.row_from);
228              LittleEndian.putShort(data, offset + pos, ( short ) region.row_from);
229              pos += 2;
230              //LittleEndian.putShort(data, offset + pos, region.row_to);
231              LittleEndian.putShort(data, offset + pos, ( short ) region.row_to);
232              pos += 2;
233              LittleEndian.putShort(data, offset + pos, region.col_from);
234              pos += 2;
235              LittleEndian.putShort(data, offset + pos, region.col_to);
236              pos += 2;
237          }
238          return recordsize;
239      }
240  
241      public String toString()
242      {
243          StringBuffer retval = new StringBuffer();
244  
245          retval.append("[MERGEDCELLS]").append("\n");
246          retval.append("     .sid        =").append(sid).append("\n");
247          retval.append("     .numregions =").append(field_1_num_areas)
248              .append("\n");
249          for (int k = 0; k < field_1_num_areas; k++)
250          {
251              MergedRegion region = ( MergedRegion ) field_2_regions.get(k);
252  
253              retval.append("     .rowfrom    =").append(region.row_from)
254                  .append("\n");
255              retval.append("     .colfrom    =").append(region.col_from)
256                  .append("\n");
257              retval.append("     .rowto      =").append(region.row_to)
258                  .append("\n");
259              retval.append("     .colto      =").append(region.col_to)
260                  .append("\n");
261          }
262          retval.append("[MERGEDCELLS]").append("\n");
263          return retval.toString();
264      }
265  
266      protected void validateSid(short id)
267      {
268          if (id != sid)
269          {
270              throw new RecordFormatException("NOT A MERGEDCELLS RECORD!! "
271                                              + id);
272          }
273      }
274  
275      /**
276       * this is a low level representation of a MergedRegion of cells.  It is an
277       * inner class because we do not want it used without reference to this class.
278       *
279       */
280  
281      public class MergedRegion
282      {
283  
284          /**
285           * create a merged region all in one stroke.
286           */
287  
288          //public MergedRegion(short row_from, short row_to, short col_from,
289          public MergedRegion(int row_from, int row_to, short col_from,
290                              short col_to)
291          {
292              this.row_from = row_from;
293              this.row_to   = row_to;
294              this.col_from = col_from;
295              this.col_to   = col_to;
296          }
297  
298          /**
299           * upper lefthand corner row
300           */
301  
302          //public short row_from;
303          public int row_from;
304  
305          /**
306           * lower right hand corner row
307           */
308  
309          //public short row_to;
310          public int row_to;
311  
312          /**
313           * upper right hand corner col
314           */
315  
316          public short col_from;
317  
318          /**
319           * lower right hand corner col
320           */
321  
322          public short col_to;
323      }
324  }
325