KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > fo > flow > Table


1 /*
2  * $Id: Table.java,v 1.39.2.11 2003/04/11 00:24:39 pietsch Exp $
3  * ============================================================================
4  * The Apache Software License, Version 1.1
5  * ============================================================================
6  *
7  * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without modifica-
10  * tion, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * 3. The end-user documentation included with the redistribution, if any, must
20  * include the following acknowledgment: "This product includes software
21  * developed by the Apache Software Foundation (http://www.apache.org/)."
22  * Alternately, this acknowledgment may appear in the software itself, if
23  * and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. The names "FOP" and "Apache Software Foundation" must not be used to
26  * endorse or promote products derived from this software without prior
27  * written permission. For written permission, please contact
28  * apache@apache.org.
29  *
30  * 5. Products derived from this software may not be called "Apache", nor may
31  * "Apache" appear in their name, without prior written permission of the
32  * Apache Software Foundation.
33  *
34  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
37  * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
39  * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
40  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
41  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
42  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
43  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44  * ============================================================================
45  *
46  * This software consists of voluntary contributions made by many individuals
47  * on behalf of the Apache Software Foundation and was originally created by
48  * James Tauber <jtauber@jtauber.com>. For more information on the Apache
49  * Software Foundation, please see <http://www.apache.org/>.
50  */

51 package org.apache.fop.fo.flow;
52
53 // FOP
54
import org.apache.fop.fo.*;
55 import org.apache.fop.fo.properties.*;
56 import org.apache.fop.layout.*;
57 import org.apache.fop.datatypes.*;
58 import org.apache.fop.apps.FOPException;
59
60 // Java
61
import java.util.ArrayList JavaDoc;
62
63 public class Table extends FObj {
64
65     public static class Maker extends FObj.Maker {
66         public FObj make(FObj parent, PropertyList propertyList,
67                          String JavaDoc systemId, int line, int column)
68             throws FOPException {
69             return new Table(parent, propertyList, systemId, line, column);
70         }
71
72     }
73
74     public static FObj.Maker maker() {
75         return new Table.Maker();
76     }
77
78     private static final int MINCOLWIDTH = 10000; // 10pt
79
int breakBefore;
80     int breakAfter;
81     int spaceBefore;
82     int spaceAfter;
83     LengthRange ipd;
84     int height;
85     String JavaDoc id;
86     TableHeader tableHeader = null;
87     TableFooter tableFooter = null;
88     boolean omitHeaderAtBreak = false;
89     boolean omitFooterAtBreak = false;
90
91     ArrayList JavaDoc columns = new ArrayList JavaDoc();
92     int bodyCount = 0;
93     private boolean bAutoLayout=false;
94     private int contentWidth = 0; // Sum of column widths
95
/** Optimum inline-progression-dimension */
96     private int optIPD;
97     /** Minimum inline-progression-dimension */
98     private int minIPD;
99     /** Maximum inline-progression-dimension */
100     private int maxIPD;
101
102     AreaContainer areaContainer;
103
104     public Table(FObj parent, PropertyList propertyList,
105                  String JavaDoc systemId, int line, int column) {
106         super(parent, propertyList, systemId, line, column);
107     }
108
109     public String JavaDoc getName() {
110         return "fo:table";
111     }
112
113     public int layout(Area area) throws FOPException {
114         if (this.marker == BREAK_AFTER) {
115             return Status.OK;
116         }
117
118         if (this.marker == START) {
119             // Common Accessibility Properties
120
AccessibilityProps mAccProps = propMgr.getAccessibilityProps();
121
122             // Common Aural Properties
123
AuralProps mAurProps = propMgr.getAuralProps();
124
125             // Common Border, Padding, and Background Properties
126
BorderAndPadding bap = propMgr.getBorderAndPadding();
127             BackgroundProps bProps = propMgr.getBackgroundProps();
128
129             // Common Margin Properties-Block
130
MarginProps mProps = propMgr.getMarginProps();
131
132             // Common Relative Position Properties
133
RelativePositionProps mRelProps = propMgr.getRelativePositionProps();
134         
135             // this.properties.get("block-progression-dimension");
136
// this.properties.get("border-after-precendence");
137
// this.properties.get("border-before-precedence");
138
// this.properties.get("border-collapse");
139
// this.properties.get("border-end-precendence");
140
// this.properties.get("border-separation");
141
// this.properties.get("border-start-precendence");
142
// this.properties.get("break-after");
143
// this.properties.get("break-before");
144
// this.properties.get("id");
145
// this.properties.get("inline-progression-dimension");
146
// this.properties.get("height");
147
// this.properties.get("keep-together");
148
// this.properties.get("keep-with-next");
149
// this.properties.get("keep-with-previous");
150
// this.properties.get("table-layout");
151
// this.properties.get("table-omit-footer-at-break");
152
// this.properties.get("table-omit-header-at-break");
153
// this.properties.get("width");
154
// this.properties.get("writing-mode");
155

156             this.breakBefore = this.properties.get("break-before").getEnum();
157             this.breakAfter = this.properties.get("break-after").getEnum();
158             this.spaceBefore =
159                 this.properties.get("space-before.optimum").getLength().mvalue();
160             this.spaceAfter =
161                 this.properties.get("space-after.optimum").getLength().mvalue();
162             this.ipd =
163                 this.properties.get("inline-progression-dimension").
164                 getLengthRange();
165             this.height = this.properties.get("height").getLength().mvalue();
166             this.bAutoLayout = (this.properties.get("table-layout").getEnum() ==
167                 TableLayout.AUTO);
168
169             this.id = this.properties.get("id").getString();
170
171             this.omitHeaderAtBreak =
172                 this.properties.get("table-omit-header-at-break").getEnum()
173                 == TableOmitHeaderAtBreak.TRUE;
174             this.omitFooterAtBreak =
175                 this.properties.get("table-omit-footer-at-break").getEnum()
176                 == TableOmitFooterAtBreak.TRUE;
177
178             if (area instanceof BlockArea) {
179                 area.end();
180             }
181             // check if anything was previously laid out
182
if (this.areaContainer == null) {
183                 try {
184                     area.getIDReferences().createID(id);
185                 }
186                 catch(FOPException e) {
187                     if (!e.isLocationSet()) {
188                         e.setLocation(systemId, line, column);
189                     }
190                     throw e;
191                 }
192             }
193
194
195             this.marker = 0;
196
197             if (breakBefore == BreakBefore.PAGE) {
198                 return Status.FORCE_PAGE_BREAK;
199             }
200
201             if (breakBefore == BreakBefore.ODD_PAGE) {
202                 return Status.FORCE_PAGE_BREAK_ODD;
203             }
204
205             if (breakBefore == BreakBefore.EVEN_PAGE) {
206                 return Status.FORCE_PAGE_BREAK_EVEN;
207             }
208
209         }
210
211         if ((spaceBefore != 0) && (this.marker == 0)) {
212             area.addDisplaySpace(spaceBefore);
213         }
214
215         if (marker == 0 && areaContainer == null) {
216             // configure id
217
area.getIDReferences().configureID(id, area);
218         }
219
220         int spaceLeft = area.spaceLeft();
221         this.areaContainer =
222             new AreaContainer(propMgr.getFontState(area.getFontInfo()), 0, 0,
223                               area.getAllocationWidth(), area.spaceLeft(),
224                               Position.STATIC);
225
226         areaContainer.foCreator = this; // G Seshadri
227
areaContainer.setPage(area.getPage());
228         areaContainer.setParent(area);
229         areaContainer.setBackground(propMgr.getBackgroundProps());
230         areaContainer.setBorderAndPadding(propMgr.getBorderAndPadding());
231         areaContainer.start();
232
233         areaContainer.setAbsoluteHeight(area.getAbsoluteHeight());
234         areaContainer.setIDReferences(area.getIDReferences());
235
236         boolean addedHeader = false;
237         boolean addedFooter = false;
238         int numChildren = this.children.size();
239
240         // Set up the column ArrayList;
241
// calculate width of all columns and get total width
242
if (columns.size()==0) {
243             findColumns(areaContainer);
244             if (this.bAutoLayout) {
245                 log.warn("table-layout=auto is not supported, using fixed!");
246             }
247             // Pretend it's fixed...
248
this.contentWidth =
249                 calcFixedColumnWidths(areaContainer.getAllocationWidth());
250         }
251         areaContainer.setAllocationWidth(this.contentWidth);
252         layoutColumns(areaContainer);
253         
254         for (int i = this.marker; i < numChildren; i++) {
255             FONode fo = (FONode)children.get(i);
256             if (fo instanceof Marker) {
257                  ((Marker)fo).layout(area);
258                  continue;
259              }
260             if (fo instanceof TableHeader) {
261                 if (columns.size() == 0) {
262                     log.warn("current implementation of tables requires a table-column for each column, indicating column-width");
263                     return Status.OK;
264                 }
265                 tableHeader = (TableHeader)fo;
266                 tableHeader.setColumns(columns);
267             } else if (fo instanceof TableFooter) {
268                 if (columns.size() == 0) {
269                     log.warn("current implementation of tables requires a table-column for each column, indicating column-width");
270                     return Status.OK;
271                 }
272                 tableFooter = (TableFooter)fo;
273                 tableFooter.setColumns(columns);
274             } else if (fo instanceof TableBody) {
275                 if (columns.size() == 0) {
276                     log.warn("current implementation of tables requires a table-column for each column, indicating column-width");
277                     return Status.OK;
278                 }
279                 int status;
280                 if (tableHeader != null &&!addedHeader) {
281                     if (Status.isIncomplete((status =
282                                                  tableHeader.layout(areaContainer)))) {
283                         tableHeader.resetMarker();
284                         return Status.AREA_FULL_NONE;
285                     }
286                     addedHeader = true;
287                     tableHeader.resetMarker();
288                     area.setMaxHeight(area.getMaxHeight() - spaceLeft
289                                       + this.areaContainer.getMaxHeight());
290                 }
291                 if (tableFooter != null &&!this.omitFooterAtBreak
292                         &&!addedFooter) {
293                     if (Status.isIncomplete((status =
294                                                  tableFooter.layout(areaContainer)))) {
295                         return Status.AREA_FULL_NONE;
296                     }
297                     addedFooter = true;
298                     tableFooter.resetMarker();
299                 }
300                 ((TableBody)fo).setColumns(columns);
301
302                 if (Status.isIncomplete((status = fo.layout(areaContainer)))) {
303                     this.marker = i;
304                     if (bodyCount == 0
305                             && status == Status.AREA_FULL_NONE) {
306                         if (tableHeader != null)
307                             tableHeader.removeLayout(areaContainer);
308                         if (tableFooter != null)
309                             tableFooter.removeLayout(areaContainer);
310                         resetMarker();
311                         // status = new Status(Status.AREA_FULL_SOME);
312
}
313                     // areaContainer.end();
314
if (areaContainer.getContentHeight() > 0) {
315                         area.addChild(areaContainer);
316                         area.increaseHeight(areaContainer.getHeight());
317                         if (this.omitHeaderAtBreak) {
318                             // remove header, no longer needed
319
tableHeader = null;
320                         }
321                         if (tableFooter != null &&!this.omitFooterAtBreak) {
322                             // move footer to bottom of area and move up body
323
((TableBody)fo).setYPosition(tableFooter.getYPosition());
324                             tableFooter.setYPosition(tableFooter.getYPosition()
325                                                      + ((TableBody)fo).getHeight());
326                         }
327                         setupColumnHeights();
328                         status = Status.AREA_FULL_SOME;
329                         this.areasGenerated++;
330                     }
331                     return status;
332                 } else {
333                     bodyCount++;
334                 }
335                 area.setMaxHeight(area.getMaxHeight() - spaceLeft
336                                   + this.areaContainer.getMaxHeight());
337                 if (tableFooter != null &&!this.omitFooterAtBreak) {
338                     // move footer to bottom of area and move up body
339
// space before and after footer will make this wrong
340
((TableBody)fo).setYPosition(tableFooter.getYPosition());
341                     tableFooter.setYPosition(tableFooter.getYPosition()
342                                              + ((TableBody)fo).getHeight());
343                 }
344             }
345         }
346         this.areasGenerated++;
347
348         if (tableFooter != null && this.omitFooterAtBreak) {
349             if (Status.isIncomplete(tableFooter.layout(areaContainer))) {
350                 // this is a problem since we need to remove a row
351
// from the last table body and place it on the
352
// next page so that it can have a footer at
353
// the end of the table.
354
log.warn("footer could not fit on page, moving last body row to next page");
355                 area.addChild(areaContainer);
356                 area.increaseHeight(areaContainer.getHeight());
357                 if (this.omitHeaderAtBreak) {
358                     // remove header, no longer needed
359
tableHeader = null;
360                 }
361                 tableFooter.removeLayout(areaContainer);
362                 tableFooter.resetMarker();
363                 return Status.AREA_FULL_SOME;
364             }
365         }
366
367         if (height != 0)
368             areaContainer.setHeight(height);
369
370         setupColumnHeights();
371
372         areaContainer.end();
373         area.addChild(areaContainer);
374
375         /* should this be combined into above? */
376         area.increaseHeight(areaContainer.getHeight());
377
378         if (spaceAfter != 0) {
379             area.addDisplaySpace(spaceAfter);
380         }
381
382         if (area instanceof BlockArea) {
383             area.start();
384         }
385
386         if (breakAfter == BreakAfter.PAGE) {
387             this.marker = BREAK_AFTER;
388             return Status.FORCE_PAGE_BREAK;
389         }
390
391         if (breakAfter == BreakAfter.ODD_PAGE) {
392             this.marker = BREAK_AFTER;
393             return Status.FORCE_PAGE_BREAK_ODD;
394         }
395
396         if (breakAfter == BreakAfter.EVEN_PAGE) {
397             this.marker = BREAK_AFTER;
398             return Status.FORCE_PAGE_BREAK_EVEN;
399         }
400
401         return Status.OK;
402     }
403
404     protected void setupColumnHeights() {
405         for (int i = 0; i < columns.size(); i++) {
406             TableColumn c = (TableColumn)columns.get(i);
407             if ( c != null) {
408                 c.setHeight(areaContainer.getContentHeight());
409             }
410         }
411     }
412
413     private void findColumns(Area areaContainer) throws FOPException {
414         int nextColumnNumber = 1;
415         for (int i = 0; i < children.size(); i++) {
416             FONode fo = (FONode)children.get(i);
417             if (fo instanceof TableColumn) {
418                 TableColumn c = (TableColumn)fo;
419                 c.doSetup(areaContainer);
420                 int numColumnsRepeated = c.getNumColumnsRepeated();
421                 int currentColumnNumber = c.getColumnNumber();
422                 if (currentColumnNumber == 0) {
423                     currentColumnNumber = nextColumnNumber;
424                 }
425                 if (currentColumnNumber + numColumnsRepeated > columns.size()) {
426                     columns.ensureCapacity(currentColumnNumber + numColumnsRepeated);
427                 }
428                 for (int j = 0; j < numColumnsRepeated; j++) {
429                     if (currentColumnNumber <= columns.size()) {
430                         if (columns.get(currentColumnNumber - 1) != null) {
431                             log.warn("More than one column object assigned " +
432                                      "to column " +
433                                      currentColumnNumber);
434                         }
435                         columns.set(currentColumnNumber - 1, c);
436                     } else {
437                       columns.add(currentColumnNumber - 1, c);
438                     }
439                     currentColumnNumber++;
440                 }
441                 nextColumnNumber = currentColumnNumber;
442             }
443         }
444     }
445
446
447
448     private int calcFixedColumnWidths(int maxAllocationWidth) {
449         int nextColumnNumber=1;
450         int iEmptyCols=0;
451         double dTblUnits=0.0;
452         int iFixedWidth=0;
453         double dWidthFactor = 0.0;
454         double dUnitLength = 0.0;
455         double tuMin = 100000.0 ; // Minimum number of proportional units
456
for (int i = 0; i < columns.size(); i++) {
457             TableColumn c = (TableColumn)columns.get(i);
458             if (c == null) {
459                 log.warn("No table-column specification for column " +
460                          nextColumnNumber);
461                 // What about sizing issues?
462
iEmptyCols++;
463             }
464             else {
465                 Length colLength = c.getColumnWidthAsLength();
466                 double tu = colLength.getTableUnits();
467                 if (tu > 0 && tu < tuMin && colLength.mvalue()==0) {
468                     /* Keep track of minimum number of proportional units
469                      * in any column which has only proportional units.
470                      */

471                     tuMin = tu;
472                 }
473                 dTblUnits += tu;
474                 iFixedWidth += colLength.mvalue();
475             }
476             nextColumnNumber++;
477         }
478
479         setIPD((dTblUnits > 0.0), maxAllocationWidth);
480         if (dTblUnits > 0.0) {
481             int iProportionalWidth = 0;
482             if (this.optIPD > iFixedWidth) {
483                 iProportionalWidth = this.optIPD - iFixedWidth;
484             }
485             else if (this.maxIPD > iFixedWidth) {
486                 iProportionalWidth = this.maxIPD - iFixedWidth;
487             }
488             else {
489                 iProportionalWidth = maxAllocationWidth - iFixedWidth;
490             }
491             if (iProportionalWidth > 0) {
492                 dUnitLength = ((double)iProportionalWidth)/dTblUnits;
493             }
494             else {
495                 log.error("Sum of fixed column widths " + iFixedWidth +
496                           " greater than maximum available IPD " +
497                           maxAllocationWidth + "; no space for " +
498                           dTblUnits + " proportional units.");
499                 /* Set remaining proportional units to a number which
500                  * will assure the minimum column size for tuMin.
501                  */

502                 dUnitLength = MINCOLWIDTH/tuMin;
503                 // Reduce fixed column widths by this much???
504
}
505             //log.debug("1 table-unit = " + dUnitLength + " mpt");
506
}
507         else {
508             /* No proportional units. If minimum IPD is specified, check
509              * that sum of column widths > minIPD.
510              */

511             int iTableWidth = iFixedWidth;
512             if (this.minIPD > iFixedWidth) {
513                 iTableWidth = this.minIPD;
514                 // Add extra space to each column
515
dWidthFactor = (double)this.minIPD/(double)iFixedWidth;
516             }
517             else if (this.maxIPD < iFixedWidth) {
518                 // Note: if maxIPD=auto, use maxAllocWidth
519
log.warn("Sum of fixed column widths " + iFixedWidth +
520                          " greater than maximum specified IPD " + this.maxIPD);
521             }
522             else if (this.optIPD != -1 && iFixedWidth != this.optIPD) {
523                 log.warn("Sum of fixed column widths " + iFixedWidth +
524                          " differs from specified optimum IPD " + this.optIPD);
525             }
526         }
527         // Now distribute the extra units onto each column and set offsets
528
int offset = 0;
529         for (int i = 0; i < columns.size(); i++) {
530             TableColumn c = (TableColumn)columns.get(i);
531             if (c != null) {
532                 c.setColumnOffset(offset);
533                 Length l = c.getColumnWidthAsLength();
534                 if (dUnitLength > 0) {
535                     l.resolveTableUnit(dUnitLength);
536                 }
537                 // Check minimum values and adjust if necessary
538
int colWidth = l.mvalue();
539                 if (colWidth <= 0) {
540                     log.warn("Zero-width table column!");
541                 }
542                 if (dWidthFactor > 0.0) {
543                     // Increase column sizes to use up extra space
544
colWidth *= dWidthFactor;
545                 }
546                 c.setColumnWidth(colWidth);
547                 offset += colWidth;
548             }
549         }
550         return offset;
551     }
552
553     private void layoutColumns(Area tableArea) throws FOPException {
554         for (int i = 0; i < columns.size(); i++) {
555             TableColumn c = (TableColumn)columns.get(i);
556             if (c != null) {
557                 c.layout(tableArea);
558             }
559         }
560     }
561
562
563     public int getAreaHeight() {
564         return areaContainer.getHeight();
565     }
566
567     /**
568      * Return the content width of the boxes generated by this table FO.
569      */

570     public int getContentWidth() {
571         if (areaContainer != null)
572             return areaContainer.getContentWidth(); // getAllocationWidth()??
573
else
574             return 0; // not laid out yet
575
}
576
577     /**
578      * Initialize table inline-progression-properties values
579      */

580     private void setIPD(boolean bHasProportionalUnits, int maxAllocIPD) {
581         boolean bMaxIsSpecified = !this.ipd.getMaximum().getLength().isAuto();
582         if (bMaxIsSpecified) {
583             this.maxIPD = ipd.getMaximum().getLength().mvalue();
584         }
585         else {
586             this.maxIPD = maxAllocIPD;
587         }
588
589         if (ipd.getOptimum().getLength().isAuto()) {
590             this.optIPD = -1;
591         }
592         else {
593             this.optIPD = ipd.getMaximum().getLength().mvalue();
594         }
595         if (ipd.getMinimum().getLength().isAuto()) {
596             this.minIPD = -1;
597         }
598         else {
599             this.minIPD = ipd.getMinimum().getLength().mvalue();
600         }
601         if (bHasProportionalUnits && this.optIPD < 0) {
602             if (this.minIPD > 0) {
603                 if (bMaxIsSpecified) {
604                     this.optIPD = (minIPD + maxIPD)/2;
605                 }
606                 else {
607                     this.optIPD = this.minIPD;
608                 }
609             }
610             else if (bMaxIsSpecified) {
611                 this.optIPD = this.maxIPD;
612             }
613             else {
614                 log.error("At least one of minimum, optimum, or maximum " +
615                           "IPD must be specified on table.");
616                 this.optIPD = this.maxIPD;
617             }
618         }
619     }
620
621
622     // /**
623
// * Return the last TableRow in the header or null if no header or
624
// * no header in non-first areas.
625
// * @param bForInitialArea If true, return the header row for the
626
// * initial table area, else for a continuation area, taking into
627
// * account the omit-header-at-break property.
628
// */
629
// TableRow getLastHeaderRow(boolean bForInitialArea) {
630
// // Check omit...
631
// if ((tableHeader != null) &&
632
// (bForInitialArea || omitHeaderAtBreak == false)) {
633
// return tableHeader.children.lastElement();
634
// }
635
// return null;
636
// }
637

638     // /**
639
// * Return the first TableRow in the footer or null if no footer or
640
// * no footer in non-last areas.
641
// * @param bForFinalArea If true, return the footer row for the
642
// * final table area, else for a non-final area, taking into
643
// * account the omit-footer-at-break property.
644
// */
645
// TableRow getLastFooterRow(boolean bForFinalArea) {
646
// if ((tableFooter != null) &&
647
// (bForFinalArea || omitFooterAtBreak == false)) {
648
// return tableFooter.children.firstElement();
649
// }
650
// return null;
651
// }
652

653
654     // /**
655
// * Return border information for the side (start/end) of the column
656
// * whose number is iColNumber (first column = 1).
657
// * ATTENTION: for now we assume columns are in order in the array!
658
// */
659
// BorderInfo getColumnBorder(BorderInfo.Side side, int iColNumber) {
660
// TableColumn col = (TableColumn)columns.get(iColNumber);
661
// return col.getBorderInfo(side);
662
// }
663
}
664
Popular Tags