KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jxl > biff > drawing > DrawingGroup


1 /*********************************************************************
2 *
3 * Copyright (C) 2004 Andrew Khan
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ***************************************************************************/

19
20 package jxl.biff.drawing;
21
22 import java.io.IOException JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Iterator JavaDoc;
26
27 import common.Assert;
28 import common.Logger;
29
30 import jxl.read.biff.Record;
31 import jxl.write.biff.File;
32
33 /**
34  * This class contains the Excel picture data in Escher format for the
35  * entire workbook
36  */

37 public class DrawingGroup implements EscherStream
38 {
39   /**
40    * The logger
41    */

42   private static Logger logger = Logger.getLogger(DrawingGroup.class);
43
44   /**
45    * The escher data read in from file
46    */

47   private byte[] drawingData;
48
49   /**
50    * The top level escher container
51    */

52   private EscherContainer escherData;
53
54   /**
55    * The Bstore container, which contains all the drawing data
56    */

57   private BStoreContainer bstoreContainer;
58
59   /**
60    * The initialized flag
61    */

62   private boolean initialized;
63
64   /**
65    * The list of user added drawings
66    */

67   private ArrayList JavaDoc drawings;
68
69   /**
70    * The number of blips
71    */

72   private int numBlips;
73
74   /**
75    * The number of charts
76    */

77   private int numCharts;
78
79   /**
80    * The number of shape ids used on the second Dgg cluster
81    */

82   private int drawingGroupId;
83
84   /**
85    * Flag which indicates that at least one of the drawings has been omitted
86    * from the worksheet
87    */

88   private boolean drawingsOmitted;
89
90   /**
91    * The origin of this drawing group
92    */

93   private Origin origin;
94
95   /**
96    * A hash map of images keyed on the file path, containing the
97    * reference count
98    */

99   private HashMap JavaDoc imageFiles;
100
101   /**
102    * A count of the next available object id
103    */

104   private int maxObjectId;
105
106   /**
107    * The maximum shape id so far encountered
108    */

109   private int maxShapeId;
110
111   /**
112    * Constructor
113    *
114    * @param o the origin of this drawing group
115    */

116   public DrawingGroup(Origin o)
117   {
118     origin = o;
119     initialized = o == Origin.WRITE ? true : false;
120     drawings = new ArrayList JavaDoc();
121     imageFiles = new HashMap JavaDoc();
122     drawingsOmitted = false;
123     maxObjectId = 1;
124     maxShapeId = 1024;
125   }
126
127   /**
128    * Copy constructor
129    * Uses a shallow copy for most things, since as soon as anything
130    * is changed, the drawing group is invalidated and all the data blocks
131    * regenerated
132    *
133    * @param dg the drawing group to copy
134    */

135   public DrawingGroup(DrawingGroup dg)
136   {
137     drawingData = dg.drawingData;
138     escherData = dg.escherData;
139     bstoreContainer = dg.bstoreContainer;
140     initialized = dg.initialized;
141     drawingData = dg.drawingData;
142     escherData = dg.escherData;
143     bstoreContainer = dg.bstoreContainer;
144     numBlips = dg.numBlips;
145     numCharts = dg.numCharts;
146     drawingGroupId = dg.drawingGroupId;
147     drawingsOmitted = dg.drawingsOmitted;
148     origin = dg.origin;
149     imageFiles = (HashMap JavaDoc) dg.imageFiles.clone();
150     maxObjectId = dg.maxObjectId;
151     maxShapeId = dg.maxShapeId;
152
153     // Create this as empty, because all drawings will get added later
154
// as part of the sheet copy process
155
drawings = new ArrayList JavaDoc();
156   }
157
158   /**
159
160   /**
161    * Adds in a drawing group record to this drawing group. The binary
162    * data is extracted from the drawing group and added to a single
163    * byte array
164    *
165    * @param mso the drawing group record to add
166    */

167   public void add(MsoDrawingGroupRecord mso)
168   {
169     addData(mso.getData());
170   }
171
172   /**
173    * Adds a continue record to this drawing group. the binary data is
174    * extracted and appended to the byte array
175    *
176    * @param cont the continue record
177    */

178   public void add(Record cont)
179   {
180     addData(cont.getData());
181   }
182
183   /**
184    * Adds the mso record data to the drawing data
185    */

186   private void addData(byte[] msodata)
187   {
188     if (drawingData == null)
189     {
190       drawingData = new byte[msodata.length];
191       System.arraycopy(msodata, 0, drawingData, 0, msodata.length);
192       return;
193     }
194
195     // Grow the array
196
byte[] newdata = new byte[drawingData.length + msodata.length];
197     System.arraycopy(drawingData, 0, newdata, 0, drawingData.length);
198     System.arraycopy(msodata, 0, newdata, drawingData.length, msodata.length);
199     drawingData = newdata;
200   }
201   
202   /**
203    * Adds a drawing to the drawing group
204    *
205    * @param d the drawing to add
206    */

207   final void addDrawing(DrawingGroupObject d)
208   {
209     drawings.add(d);
210     maxObjectId = Math.max(maxObjectId, d.getObjectId());
211     maxShapeId = Math.max(maxShapeId, d.getShapeId());
212   }
213
214   /**
215    * Adds a chart to the drawing group
216    *
217    * @param c
218    */

219   public void add(Chart c)
220   {
221     numCharts++;
222   }
223
224   /**
225    * Adds a drawing from the public, writable interface
226    *
227    * @param d the drawing to add
228    */

229   public void add(DrawingGroupObject d)
230   {
231     if (origin == Origin.READ)
232     {
233       origin = Origin.READ_WRITE;
234       BStoreContainer bsc = getBStoreContainer(); // force initialization
235
Dgg dgg = (Dgg) escherData.getChildren()[0];
236       drawingGroupId = dgg.getCluster(1).drawingGroupId - numBlips - 1;
237       numBlips = dgg.getDrawingsSaved();
238
239       if (bsc != null)
240       {
241         Assert.verify(numBlips == bsc.getNumBlips());
242       }
243     }
244
245     if (!(d instanceof Drawing))
246     {
247       // Assign a new object id and add it to the list
248
// drawings.add(d);
249
maxObjectId++;
250       maxShapeId++;
251       d.setDrawingGroup(this);
252       d.setObjectId(maxObjectId, numBlips+1, maxShapeId);
253       if (drawings.size() > maxObjectId)
254       {
255         logger.warn("drawings length " + drawings.size() +
256                     " exceeds the max object id " + maxObjectId);
257       }
258       // numBlips++;
259
return;
260     }
261     
262     Drawing drawing = (Drawing) d;
263
264     // See if this is referenced elsewhere
265
Drawing refImage =
266       (Drawing) imageFiles.get(d.getImageFilePath());
267
268     if (refImage == null)
269     {
270       // There are no other references to this drawing, so assign
271
// a new object id and put it on the hash map
272
maxObjectId++;
273       maxShapeId++;
274       drawings.add(drawing);
275       drawing.setDrawingGroup(this);
276       drawing.setObjectId(maxObjectId, numBlips+1, maxShapeId);
277       numBlips++;
278       imageFiles.put(drawing.getImageFilePath(), drawing);
279     }
280     else
281     {
282       // This drawing is used elsewhere in the workbook. Increment the
283
// reference count on the drawing, and set the object id of the drawing
284
// passed in
285
refImage.setReferenceCount(refImage.getReferenceCount() + 1);
286       drawing.setDrawingGroup(this);
287       drawing.setObjectId(refImage.getObjectId(),
288                           refImage.getBlipId(),
289                           refImage.getShapeId());
290     }
291   }
292
293   /**
294    * Interface method to remove a drawing from the group
295    *
296    * @param d the drawing to remove
297    */

298   public void remove(DrawingGroupObject d)
299   {
300     if (origin == Origin.READ)
301     {
302       origin = Origin.READ_WRITE;
303       numBlips = getBStoreContainer().getNumBlips();
304       Dgg dgg = (Dgg) escherData.getChildren()[0];
305       drawingGroupId = dgg.getCluster(1).drawingGroupId - numBlips - 1 ;
306     }
307
308     // Get the blip
309
EscherRecord[] children = getBStoreContainer().getChildren();
310     BlipStoreEntry bse = (BlipStoreEntry) children[d.getBlipId()-1];
311     
312     bse.dereference();
313     
314     if (bse.getReferenceCount() == 0)
315     {
316       // Remove the blip
317
getBStoreContainer().remove(bse);
318
319       // Adjust blipId on the other blips
320
for (Iterator JavaDoc i = drawings.iterator() ; i.hasNext() ; )
321       {
322         DrawingGroupObject drawing = (DrawingGroupObject) i.next();
323         
324         if (drawing.getBlipId() > d.getBlipId())
325         {
326           drawing.setObjectId(drawing.getObjectId(),
327                               drawing.getBlipId() - 1,
328                               drawing.getShapeId());
329         }
330       }
331       
332       numBlips--;
333     }
334   }
335
336
337   /**
338    * Initializes the drawing data from the escher record read in
339    */

340   private void initialize()
341   {
342     EscherRecordData er = new EscherRecordData(this, 0);
343
344     Assert.verify(er.isContainer());
345     
346     escherData = new EscherContainer(er);
347
348     Assert.verify(escherData.getLength() == drawingData.length);
349     Assert.verify(escherData.getType() == EscherRecordType.DGG_CONTAINER);
350
351     initialized = true;
352   }
353
354   /**
355    * Gets hold of the BStore container from the Escher data
356    *
357    * @return the BStore container
358    */

359   private BStoreContainer getBStoreContainer()
360   {
361     if (bstoreContainer == null)
362     {
363       if (!initialized)
364       {
365         initialize();
366       }
367       
368       EscherRecord[] children = escherData.getChildren();
369       if (children.length > 1 &&
370           children[1].getType() == EscherRecordType.BSTORE_CONTAINER)
371       {
372         bstoreContainer = (BStoreContainer) children[1];
373       }
374       else
375       {
376         // logger.warn("Bstore container is null");
377
}
378     }
379
380     return bstoreContainer;
381   }
382
383   /**
384    * Gets hold of the binary data
385    *
386    * @return the data
387    */

388   public byte[] getData()
389   {
390     return drawingData;
391   }
392
393   /**
394    * Writes the drawing group to the output file
395    *
396    * @param outputFile the file to write to
397    * @exception IOException
398    */

399   public void write(File outputFile) throws IOException JavaDoc
400   {
401     if (origin == Origin.WRITE)
402     {
403       DggContainer dggContainer = new DggContainer();
404
405       Dgg dgg = new Dgg(numBlips+numCharts+1, numBlips);
406
407       dgg.addCluster(1,0);
408       dgg.addCluster(numBlips+1,0);
409
410       dggContainer.add(dgg);
411
412       int drawingsAdded = 0;
413       BStoreContainer bstoreCont = new BStoreContainer();
414
415       // Create a blip entry for each drawing
416
for (Iterator JavaDoc i = drawings.iterator(); i.hasNext();)
417       {
418         Object JavaDoc o = i.next();
419         if (o instanceof Drawing)
420         {
421           Drawing d = (Drawing) o;
422           BlipStoreEntry bse = new BlipStoreEntry(d);
423           
424           bstoreCont.add(bse);
425           drawingsAdded++;
426         }
427       }
428       if (drawingsAdded > 0)
429       {
430         bstoreCont.setNumBlips(drawingsAdded);
431         dggContainer.add(bstoreCont);
432       }
433
434       Opt opt = new Opt();
435
436       dggContainer.add(opt);
437
438       SplitMenuColors splitMenuColors = new SplitMenuColors();
439       dggContainer.add(splitMenuColors);
440
441       drawingData = dggContainer.getData();
442     }
443     else if (origin == Origin.READ_WRITE)
444     {
445       DggContainer dggContainer = new DggContainer();
446
447       Dgg dgg = new Dgg(numBlips+numCharts+1, numBlips);
448
449       dgg.addCluster(1,0);
450       dgg.addCluster(drawingGroupId+numBlips+1,0);
451
452       dggContainer.add(dgg);
453
454       BStoreContainer bstoreCont = new BStoreContainer();
455       bstoreCont.setNumBlips(numBlips);
456
457       // Create a blip entry for each drawing that was read in
458
BStoreContainer readBStoreContainer = getBStoreContainer();
459
460       if (readBStoreContainer != null)
461       {
462         EscherRecord[] children = readBStoreContainer.getChildren();
463         for (int i = 0; i < children.length ; i++)
464         {
465           BlipStoreEntry bse = (BlipStoreEntry) children[i];
466           bstoreCont.add(bse);
467         }
468
469         // Create a blip entry for each drawing that has been added
470
for (Iterator JavaDoc i = drawings.iterator(); i.hasNext();)
471         {
472           DrawingGroupObject dgo = (DrawingGroupObject) i.next();
473           if (dgo instanceof Drawing)
474           {
475             Drawing d = (Drawing) dgo;
476             if (d.getOrigin() != Origin.READ)
477             {
478               BlipStoreEntry bse = new BlipStoreEntry(d);
479               bstoreCont.add(bse);
480             }
481           }
482         }
483         dggContainer.add(bstoreCont);
484       }
485
486       Opt opt = new Opt();
487
488       opt.addProperty(191, false, false, 524296);
489       opt.addProperty(385, false, false, 134217737);
490       opt.addProperty(448, false, false, 134217792);
491
492       dggContainer.add(opt);
493
494       SplitMenuColors splitMenuColors = new SplitMenuColors();
495       dggContainer.add(splitMenuColors);
496
497       drawingData = dggContainer.getData();
498     }
499
500     MsoDrawingGroupRecord msodg = new MsoDrawingGroupRecord(drawingData);
501     outputFile.write(msodg);
502   }
503
504   /**
505    * Accessor for the number of blips in the drawing group
506    *
507    * @return the number of blips
508    */

509   final int getNumberOfBlips()
510   {
511     return numBlips;
512   }
513
514   /**
515    * Gets the drawing data for the given blip id. Called by the Drawing
516    * object
517    *
518    * @param blipId the blipId
519    * @return the drawing data
520    */

521   byte[] getImageData(int blipId)
522   {
523     numBlips = getBStoreContainer().getNumBlips();
524
525     Assert.verify(blipId <= numBlips);
526     Assert.verify(origin == Origin.READ || origin == Origin.READ_WRITE);
527     
528     // Get the blip
529
EscherRecord[] children = getBStoreContainer().getChildren();
530     BlipStoreEntry bse = (BlipStoreEntry) children[blipId-1];
531
532     return bse.getImageData();
533   }
534
535   /**
536    * Indicates that at least one of the drawings has been omitted from
537    * the worksheet
538    */

539   public void setDrawingsOmitted(MsoDrawingRecord mso, ObjRecord obj)
540   {
541     drawingsOmitted = true;
542    
543     if (obj != null)
544     {
545       maxObjectId = Math.max(maxObjectId, obj.getObjectId());
546     }
547   }
548
549   /**
550    * Accessor for the drawingsOmitted flag
551    *
552    * @return TRUE if a drawing has been omitted, FALSE otherwise
553    */

554   public boolean hasDrawingsOmitted()
555   {
556     return drawingsOmitted;
557   }
558
559   /**
560    * Updates this with the appropriate data from the drawing group passed in
561    * This is called during the copy process: this is first initialised as
562    * an empty object, but during the copy, the source DrawingGroup may
563    * change. After the copy process, this method is then called to update
564    * the relevant fields. Unfortunately, the copy process required the
565    * presence of a drawing group
566    *
567    * @param dg the drawing group containing the updated data
568    */

569   public void updateData(DrawingGroup dg)
570   {
571     drawingsOmitted = dg.drawingsOmitted;
572     maxObjectId = dg.maxObjectId;
573     maxShapeId = dg.maxShapeId;
574   }
575 }
576
Popular Tags