View Javadoc

1   /*
2    * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: Mtable.java,v bc1d5fde7b73 2009/06/01 14:40:54 maxberger $ */
18  
19  package net.sourceforge.jeuclid.elements.presentation.table;
20  
21  import java.awt.Color;
22  import java.awt.Graphics2D;
23  import java.awt.geom.Dimension2D;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.Locale;
27  
28  import net.sourceforge.jeuclid.Constants;
29  import net.sourceforge.jeuclid.LayoutContext;
30  import net.sourceforge.jeuclid.context.InlineLayoutContext;
31  import net.sourceforge.jeuclid.context.Parameter;
32  import net.sourceforge.jeuclid.elements.JEuclidElement;
33  import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
34  import net.sourceforge.jeuclid.elements.support.ElementListSupport;
35  import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
36  import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
37  import net.sourceforge.jeuclid.elements.support.attributes.HAlign;
38  import net.sourceforge.jeuclid.layout.GraphicsObject;
39  import net.sourceforge.jeuclid.layout.LayoutInfo;
40  import net.sourceforge.jeuclid.layout.LayoutStage;
41  import net.sourceforge.jeuclid.layout.LayoutView;
42  import net.sourceforge.jeuclid.layout.LayoutableNode;
43  import net.sourceforge.jeuclid.layout.LineObject;
44  
45  import org.apache.batik.dom.AbstractDocument;
46  import org.apache.commons.logging.Log;
47  import org.apache.commons.logging.LogFactory;
48  import org.w3c.dom.Node;
49  import org.w3c.dom.mathml.MathMLLabeledRowElement;
50  import org.w3c.dom.mathml.MathMLNodeList;
51  import org.w3c.dom.mathml.MathMLTableCellElement;
52  import org.w3c.dom.mathml.MathMLTableElement;
53  import org.w3c.dom.mathml.MathMLTableRowElement;
54  
55  /**
56   * This class presents a table.
57   * 
58   * @version $Revision: bc1d5fde7b73 $
59   */
60  // CHECKSTYLE:OFF
61  // Data abstraction coupling is "to high". but this is necessary for proper
62  // layout.
63  public final class Mtable extends AbstractTableElement implements
64          MathMLTableElement {
65      // CHECKSTYLE:ON
66  
67      /**
68       * The XML element from this class.
69       */
70      public static final String ELEMENT = "mtable";
71  
72      /** Attribute for columnalign. */
73      static final String ATTR_COLUMNALIGN = "columnalign";
74  
75      /** Attribute for rowalign. */
76      static final String ATTR_ROWALIGN = "rowalign";
77  
78      /** Attribute for groupalign. */
79      static final String ATTR_GROUPALIGN = "groupalign";
80  
81      /** attribute for rowlines. */
82      private static final String ATTR_ROWLINES = "rowlines";
83  
84      /** attribute for columnlines. */
85      private static final String ATTR_COLUMNLINES = "columnlines";
86  
87      /** attribute for align. */
88      private static final String ATTR_ALIGN = "align";
89  
90      /** attribute for alignmentscope. */
91      private static final String ATTR_ALIGNMENTSCOPE = "alignmentscope";
92  
93      /** attribute for columnwidth. */
94      private static final String ATTR_COLUMNWIDTH = "columnwidth";
95  
96      /** attribute for width. */
97      private static final String ATTR_WIDTH = "width";
98  
99      /** attribute for rowspacing. */
100     private static final String ATTR_ROWSPACING = "rowspacing";
101 
102     /** attribute for columnspacing. */
103     private static final String ATTR_COLUMNSPACING = "columnspacing";
104 
105     /** attribute for frame. */
106     private static final String ATTR_FRAME = "frame";
107 
108     /** attribute for framespacing. */
109     private static final String ATTR_FRAMESPACING = "framespacing";
110 
111     /** attribute for equalrows. */
112     private static final String ATTR_EQUALROWS = "equalrows";
113 
114     /** attribute for equalcolumns. */
115     private static final String ATTR_EQUALCOLUMNS = "equalcolumns";
116 
117     /** attribute for displaystyle. */
118     private static final String ATTR_DISPLAYSTYLE = "displaystyle";
119 
120     /** attribute for side. */
121     private static final String ATTR_SIDE = "side";
122 
123     /** attribute for minlabelspacing. */
124     private static final String ATTR_MINLABELSPACING = "minlabelspacing";
125 
126     /** value for no lines. */
127     private static final String VALUE_NONE = "none";
128 
129     /** value for dashed lines. */
130     private static final String VALUE_DASHED = "dashed";
131 
132     /** value for solid lines. */
133     private static final String VALUE_SOLID = "solid";
134 
135     private static final long serialVersionUID = 1L;
136 
137     /**
138      * Default column spacing.
139      */
140     private static final String DEFAULT_COLUMNSPACING = "0.8em";
141 
142     /**
143      * Default row spacing.
144      */
145     private static final String DEFAULT_ROWSPACING = "1.0ex";
146 
147     /**
148      * Default frame spacing.
149      */
150     private static final String DEFAULT_FRAMESPACING = "0.4em 0.5ex";
151 
152     private static final String VALUE_AUTO = "auto";
153 
154     /**
155      * Logger for this class
156      */
157     private static final Log LOGGER = LogFactory.getLog(Mtable.class);
158 
159     /**
160      * Class for line types.
161      */
162     public enum LineType {
163         /** No lines. */
164         NONE,
165         /** Solid line. */
166         SOLID,
167         /** Dashed line. */
168         DASHED;
169 
170         /**
171          * Parse a string and return a linetype.
172          * 
173          * @param s
174          *            the string to parse
175          * @return a line type for this string type
176          */
177         public static LineType parseLineType(final String s) {
178             final LineType retVal;
179             if (s.equalsIgnoreCase(Mtable.VALUE_SOLID)) {
180                 retVal = SOLID;
181             } else if (s.equalsIgnoreCase(Mtable.VALUE_DASHED)) {
182                 retVal = DASHED;
183             } else {
184                 retVal = NONE;
185             }
186             return retVal;
187         }
188     };
189 
190     /**
191      * Class for alignment types.
192      */
193     private static final class VAlign {
194 
195         /** Align to top. */
196         static final int TOP = 0;
197 
198         /** Align to bottom. */
199         static final int BOTTOM = 1;
200 
201         /** Align to center. */
202         static final int CENTER = 2;
203 
204         /** Align to baseline. */
205         static final int BASELINE = 3;
206 
207         /** Align to axis. */
208         static final int AXIS = 4;
209 
210         static final String VALUE_TOP = "top";
211 
212         static final String VALUE_BOTTOM = "bottom";
213 
214         static final String VALUE_CENTER = "center";
215 
216         static final String VALUE_BASELINE = "baseline";
217 
218         static final String VALUE_AXIS = "axis";
219 
220         static final VAlign BASELINE_ALIGN = new VAlign(Mtable.VAlign.BASELINE,
221                 0);
222 
223         private static final String INVALID_VERTICAL_ALIGNMENT_VALUE = "Invalid vertical alignment value: ";
224 
225         private final int valign;
226 
227         private final int alignTo;
228 
229         private VAlign(final int align, final int relativeTo) {
230             this.valign = align;
231             this.alignTo = relativeTo;
232         }
233 
234         /**
235          * Parse a string and return a alignment.
236          * 
237          * @param s
238          *            the string to parse
239          * @return an alignment for this string type
240          */
241         public static VAlign parseString(final String s) {
242             if ((s == null) || (s.length() == 0)) {
243                 return null;
244             }
245             final int align;
246             int relativeTo = 0;
247             final String s2 = s.trim().toLowerCase(Locale.ENGLISH);
248             final String s3;
249             if (s2.startsWith(Mtable.VAlign.VALUE_TOP)) {
250                 align = Mtable.VAlign.TOP;
251                 s3 = s2.substring(Mtable.VAlign.VALUE_TOP.length()).trim();
252             } else if (s2.startsWith(Mtable.VAlign.VALUE_BOTTOM)) {
253                 align = Mtable.VAlign.BOTTOM;
254                 s3 = s2.substring(Mtable.VAlign.VALUE_BOTTOM.length()).trim();
255             } else if (s2.startsWith(Mtable.VAlign.VALUE_CENTER)) {
256                 align = Mtable.VAlign.CENTER;
257                 s3 = s2.substring(Mtable.VAlign.VALUE_CENTER.length()).trim();
258             } else if (s2.startsWith(Mtable.VAlign.VALUE_BASELINE)) {
259                 align = Mtable.VAlign.BASELINE;
260                 s3 = s2.substring(Mtable.VAlign.VALUE_BASELINE.length()).trim();
261             } else if (s2.startsWith(Mtable.VAlign.VALUE_AXIS)) {
262                 align = Mtable.VAlign.AXIS;
263                 s3 = s2.substring(Mtable.VAlign.VALUE_AXIS.length()).trim();
264             } else {
265                 Mtable.LOGGER
266                         .warn(Mtable.VAlign.INVALID_VERTICAL_ALIGNMENT_VALUE
267                                 + s);
268                 align = Mtable.VAlign.BASELINE;
269                 s3 = "0";
270             }
271             if (s3.length() > 0) {
272                 try {
273                     relativeTo = Integer.parseInt(s3);
274                 } catch (final NumberFormatException nfe) {
275                     Mtable.LOGGER
276                             .warn(Mtable.VAlign.INVALID_VERTICAL_ALIGNMENT_VALUE
277                                     + s);
278                 }
279             }
280             return new VAlign(align, relativeTo);
281         }
282 
283         public int getAlign() {
284             return this.valign;
285         }
286 
287         public int getRelative() {
288             return this.alignTo;
289         }
290     }
291 
292     /**
293      * Default constructor. Sets MathML Namespace.
294      * 
295      * @param qname
296      *            Qualified name.
297      * @param odoc
298      *            Owner Document.
299      */
300     public Mtable(final String qname, final AbstractDocument odoc) {
301         super(qname, odoc);
302 
303         this.setDefaultMathAttribute(Mtable.ATTR_ALIGN,
304                 Mtable.VAlign.VALUE_AXIS);
305         this.setDefaultMathAttribute(Mtable.ATTR_ROWALIGN,
306                 Mtable.VAlign.VALUE_BASELINE);
307         this.setDefaultMathAttribute(Mtable.ATTR_COLUMNALIGN,
308                 HAlign.ALIGN_CENTER);
309         this.setDefaultMathAttribute(Mtable.ATTR_GROUPALIGN, "{left}");
310         this
311                 .setDefaultMathAttribute(Mtable.ATTR_ALIGNMENTSCOPE,
312                         Constants.TRUE);
313         this
314                 .setDefaultMathAttribute(Mtable.ATTR_COLUMNWIDTH,
315                         Mtable.VALUE_AUTO);
316         this.setDefaultMathAttribute(Mtable.ATTR_WIDTH, Mtable.VALUE_AUTO);
317         this.setDefaultMathAttribute(Mtable.ATTR_ROWSPACING,
318                 Mtable.DEFAULT_ROWSPACING);
319         this.setDefaultMathAttribute(Mtable.ATTR_COLUMNSPACING,
320                 Mtable.DEFAULT_COLUMNSPACING);
321         this.setDefaultMathAttribute(Mtable.ATTR_ROWLINES, Mtable.VALUE_NONE);
322         this
323                 .setDefaultMathAttribute(Mtable.ATTR_COLUMNLINES,
324                         Mtable.VALUE_NONE);
325         this.setDefaultMathAttribute(Mtable.ATTR_FRAME, Mtable.VALUE_NONE);
326         this.setDefaultMathAttribute(Mtable.ATTR_FRAMESPACING,
327                 Mtable.DEFAULT_FRAMESPACING);
328         this.setDefaultMathAttribute(Mtable.ATTR_EQUALROWS, Constants.FALSE);
329         this.setDefaultMathAttribute(Mtable.ATTR_EQUALCOLUMNS, Constants.FALSE);
330         this.setDefaultMathAttribute(Mtable.ATTR_DISPLAYSTYLE, Constants.FALSE);
331         this.setDefaultMathAttribute(Mtable.ATTR_SIDE, HAlign.ALIGN_RIGHT);
332         this.setDefaultMathAttribute(Mtable.ATTR_MINLABELSPACING,
333                 Mtable.DEFAULT_COLUMNSPACING);
334     }
335 
336     /** {@inheritDoc} */
337     @Override
338     protected Node newNode() {
339         return new Mtable(this.nodeName, this.ownerDocument);
340     }
341 
342     /** {@inheritDoc} */
343     @Override
344     public LayoutContext getChildLayoutContext(final int childNum,
345             final LayoutContext context) {
346         return new InlineLayoutContext(this
347                 .applyLocalAttributesToContext(context));
348     }
349 
350     private float getFramespacingh(final LayoutContext now) {
351         if (Mtable.LineType.NONE.equals(this.getFrameAsLineType())) {
352             return 0;
353         }
354         final String spacing = this.getSpaceArrayEntry(this.getFramespacing(),
355                 0);
356         return AttributesHelper.convertSizeToPt(spacing, now,
357                 AttributesHelper.PT);
358     }
359 
360     private float getFramespacingv(final LayoutContext now) {
361         if (Mtable.LineType.NONE.equals(this.getFrameAsLineType())) {
362             return 0;
363         }
364         final String spacing = this.getSpaceArrayEntry(this.getFramespacing(),
365                 1);
366         return AttributesHelper.convertSizeToPt(spacing, now,
367                 AttributesHelper.PT);
368     }
369 
370     /** {@inheritDoc} */
371     public String getRowlines() {
372         return this.getMathAttribute(Mtable.ATTR_ROWLINES);
373     }
374 
375     /** {@inheritDoc} */
376     public void setRowlines(final String rowlines) {
377         this.setAttribute(Mtable.ATTR_ROWLINES, rowlines);
378     }
379 
380     /** {@inheritDoc} */
381     public String getColumnlines() {
382         return this.getMathAttribute(Mtable.ATTR_COLUMNLINES);
383     }
384 
385     /** {@inheritDoc} */
386     public void setColumnlines(final String columnlines) {
387         this.setAttribute(Mtable.ATTR_COLUMNLINES, columnlines);
388     }
389 
390     private LineType getRowLine(final int row) {
391         return Mtable.LineType.parseLineType(this.getSpaceArrayEntry(this
392                 .getRowlines(), row));
393     }
394 
395     private LineType getColumnLine(final int col) {
396         return Mtable.LineType.parseLineType(this.getSpaceArrayEntry(this
397                 .getColumnlines(), col));
398     }
399 
400     private LineType getFrameAsLineType() {
401         return Mtable.LineType.parseLineType(this.getFrame());
402     }
403 
404     /**
405      * Gets an entry in a white-space separated string.
406      * <p>
407      * If the entry requested is beyond the index, the last entry is returned.
408      * 
409      * @todo This method is probably useful for other attribute values. Examine,
410      *       and move to a more common place. (like AttrHelper)
411      * @param string
412      *            the string in which to look.
413      * @param index
414      *            index of the element (0-based)
415      * @return the element at that index
416      */
417     private String getSpaceArrayEntry(final String string, final int index) {
418         final String[] array;
419         if (string == null) {
420             array = new String[0];
421         } else {
422             array = string.split("\\s");
423         }
424         int cur = -1;
425         String last = "";
426         for (final String s : array) {
427             if (s.length() > 0) {
428                 cur++;
429                 if (cur == index) {
430                     return s;
431                 }
432                 last = s;
433             }
434         }
435         return last;
436     }
437 
438     /** {@inheritDoc} */
439     public String getColumnwidth() {
440         return this.getMathAttribute(Mtable.ATTR_COLUMNWIDTH);
441     }
442 
443     /** {@inheritDoc} */
444     public void setColumnwidth(final String columnwidth) {
445         this.setAttribute(Mtable.ATTR_COLUMNWIDTH, columnwidth);
446     }
447 
448     /** {@inheritDoc} */
449     public String getWidth() {
450         return this.getMathAttribute(Mtable.ATTR_WIDTH);
451     }
452 
453     /** {@inheritDoc} */
454     public void setWidth(final String width) {
455         this.setAttribute(Mtable.ATTR_WIDTH, width);
456     }
457 
458     /** {@inheritDoc} */
459     public String getAlign() {
460         return this.getMathAttribute(Mtable.ATTR_ALIGN);
461     }
462 
463     /** {@inheritDoc} */
464     public void setAlign(final String align) {
465         this.setAttribute(Mtable.ATTR_ALIGN, align);
466     }
467 
468     /** {@inheritDoc} */
469     public String getAlignmentscope() {
470         return this.getMathAttribute(Mtable.ATTR_ALIGNMENTSCOPE);
471     }
472 
473     /** {@inheritDoc} */
474     public void setAlignmentscope(final String alignmentscope) {
475         this.setAttribute(Mtable.ATTR_ALIGNMENTSCOPE, alignmentscope);
476     }
477 
478     /** {@inheritDoc} */
479     public String getRowspacing() {
480         return this.getMathAttribute(Mtable.ATTR_ROWSPACING);
481     }
482 
483     /** {@inheritDoc} */
484     public void setRowspacing(final String rowspacing) {
485         this.setAttribute(Mtable.ATTR_ROWSPACING, rowspacing);
486     }
487 
488     /** {@inheritDoc} */
489     public String getColumnspacing() {
490         return this.getMathAttribute(Mtable.ATTR_COLUMNSPACING);
491     }
492 
493     /** {@inheritDoc} */
494     public void setColumnspacing(final String columnspacing) {
495         this.setAttribute(Mtable.ATTR_COLUMNSPACING, columnspacing);
496     }
497 
498     /** {@inheritDoc} */
499     public String getFrame() {
500         return this.getMathAttribute(Mtable.ATTR_FRAME);
501     }
502 
503     /** {@inheritDoc} */
504     public void setFrame(final String frame) {
505         this.setAttribute(Mtable.ATTR_FRAME, frame);
506     }
507 
508     /** {@inheritDoc} */
509     public String getFramespacing() {
510         return this.getMathAttribute(Mtable.ATTR_FRAMESPACING);
511     }
512 
513     /** {@inheritDoc} */
514     public void setFramespacing(final String framespacing) {
515         this.setAttribute(Mtable.ATTR_FRAMESPACING, framespacing);
516     }
517 
518     /** {@inheritDoc} */
519     public String getEqualrows() {
520         return this.getMathAttribute(Mtable.ATTR_EQUALROWS);
521     }
522 
523     /** {@inheritDoc} */
524     public void setEqualrows(final String equalrows) {
525         this.setAttribute(Mtable.ATTR_EQUALROWS, equalrows);
526     }
527 
528     /** {@inheritDoc} */
529     public String getEqualcolumns() {
530         return this.getMathAttribute(Mtable.ATTR_EQUALCOLUMNS);
531     }
532 
533     /** {@inheritDoc} */
534     public void setEqualcolumns(final String equalcolumns) {
535         this.setAttribute(Mtable.ATTR_EQUALCOLUMNS, equalcolumns);
536     }
537 
538     /** {@inheritDoc} */
539     public String getDisplaystyle() {
540         return this.getMathAttribute(Mtable.ATTR_DISPLAYSTYLE);
541     }
542 
543     /** {@inheritDoc} */
544     public void setDisplaystyle(final String displaystyle) {
545         this.setAttribute(Mtable.ATTR_DISPLAYSTYLE, displaystyle);
546     }
547 
548     /** {@inheritDoc} */
549     public String getSide() {
550         return this.getMathAttribute(Mtable.ATTR_SIDE);
551     }
552 
553     /** {@inheritDoc} */
554     public void setSide(final String side) {
555         this.setAttribute(Mtable.ATTR_SIDE, side);
556     }
557 
558     /** {@inheritDoc} */
559     public String getMinlabelspacing() {
560         return this.getMathAttribute(Mtable.ATTR_MINLABELSPACING);
561     }
562 
563     /** {@inheritDoc} */
564     public void setMinlabelspacing(final String minlabelspacing) {
565         this.setAttribute(Mtable.ATTR_MINLABELSPACING, minlabelspacing);
566     }
567 
568     /** {@inheritDoc} */
569     public MathMLNodeList getRows() {
570         // TODO: Implement
571         return null;
572     }
573 
574     /** {@inheritDoc} */
575     public MathMLTableRowElement insertEmptyRow(final int index) {
576         // TODO: Implement
577         return null;
578     }
579 
580     /** {@inheritDoc} */
581     public MathMLLabeledRowElement insertEmptyLabeledRow(final int index) {
582         // TODO: Implement
583         return null;
584     }
585 
586     /** {@inheritDoc} */
587     public MathMLTableRowElement getRow(final int index) {
588         // TODO: Implement
589         return null;
590     }
591 
592     /** {@inheritDoc} */
593     public MathMLTableRowElement insertRow(final int index,
594             final MathMLTableRowElement newRow) {
595         // TODO: Implement
596         return null;
597     }
598 
599     /** {@inheritDoc} */
600     public MathMLTableRowElement setRow(final int index,
601             final MathMLTableRowElement newRow) {
602         // TODO: Implement
603         return null;
604     }
605 
606     /** {@inheritDoc} */
607     public void deleteRow(final int index) {
608         // TODO: Implement
609     }
610 
611     /** {@inheritDoc} */
612     public MathMLTableRowElement removeRow(final int index) {
613         // TODO: Implement
614         return null;
615     }
616 
617     /** {@inheritDoc} */
618     public void deleteRow(final long index) {
619         // TODO Auto-generated method stub
620 
621     }
622 
623     /** {@inheritDoc} */
624     public MathMLTableRowElement getRow(final long index) {
625         // TODO Auto-generated method stub
626         return null;
627     }
628 
629     /** {@inheritDoc} */
630     public MathMLLabeledRowElement insertEmptyLabeledRow(final long index) {
631         // TODO Auto-generated method stub
632         return null;
633     }
634 
635     /** {@inheritDoc} */
636     public MathMLTableRowElement insertEmptyRow(final long index) {
637         // TODO Auto-generated method stub
638         return null;
639     }
640 
641     /** {@inheritDoc} */
642     public MathMLTableRowElement insertRow(final long index,
643             final MathMLTableRowElement newRow) {
644         // TODO Auto-generated method stub
645         return null;
646     }
647 
648     /** {@inheritDoc} */
649     public MathMLTableRowElement removeRow(final long index) {
650         // TODO Auto-generated method stub
651         return null;
652     }
653 
654     /** {@inheritDoc} */
655     public MathMLTableRowElement setRow(final long index,
656             final MathMLTableRowElement newRow) {
657         // TODO Auto-generated method stub
658         return null;
659     }
660 
661     /** {@inheritDoc} */
662     // CHECKSTYLE:OFF
663     // Function is too long. Unfortunately it has to be for proper layout
664     @Override
665     public void layoutStageInvariant(final LayoutView view,
666             final LayoutInfo info, final LayoutStage stage,
667             final LayoutContext context) {
668         // CHECKSTYLE:ON
669         final Graphics2D g = view.getGraphics();
670         final LayoutContext now = this.applyLocalAttributesToContext(context);
671         final List<LayoutableNode> children = this.getChildrenToLayout();
672         final LayoutInfo[] rowInfos = new LayoutInfo[children.size()];
673         final LayoutableNode[] rowChild = new LayoutableNode[children.size()];
674         float y = 0;
675         int rows = 0;
676 
677         // Layout Rows vertically, calculate height of the table.
678         final float vFrameSpacing = this.getFramespacingv(now);
679         float height = vFrameSpacing;
680         for (final LayoutableNode child : children) {
681             rowChild[rows] = child;
682             final LayoutInfo mtrInfo = view.getInfo(child);
683             y += mtrInfo.getAscentHeight(stage);
684             rowInfos[rows] = mtrInfo;
685             rows++;
686             mtrInfo.moveTo(0, y, stage);
687             y += mtrInfo.getDescentHeight(stage);
688             height = y;
689             y += AttributesHelper.convertSizeToPt(this.getSpaceArrayEntry(this
690                     .getRowspacing(), rows), now, AttributesHelper.PT);
691         }
692         height += vFrameSpacing;
693 
694         final float verticalShift = this.shiftTableVertically(stage, context,
695                 g, rowInfos, rows, height);
696 
697         final List<LayoutableNode>[] mtdChildren = this
698                 .createListOfMtdChildren(rowChild, rows);
699         this.stretchAndAlignMtds(view, mtdChildren, rowInfos, rows, stage);
700 
701         final List<Float> columnwidth = this.calculateBasicColumnWidth(view,
702                 stage, rows, mtdChildren);
703 
704         // TODO This is where Alignment-Groups should be calculated
705 
706         final float totalWidth = this.layoutColumnsHorizontally(view, stage,
707                 now, rows, mtdChildren, columnwidth);
708 
709         this.setRowWidth(stage, rowInfos, rows, totalWidth);
710 
711         this.addRowLines(info, rowInfos, rows, totalWidth, stage, now);
712         this.addColumnLines(info, columnwidth, verticalShift, height, now);
713         this.addFrame(info, totalWidth, verticalShift, height, now);
714 
715         final float hFrameSpacing = this.getFramespacingh(now);
716         final Dimension2D borderLeftTop = new Dimension2DImpl(hFrameSpacing,
717                 vFrameSpacing);
718         final Dimension2D borderRightBottom = new Dimension2DImpl(
719                 hFrameSpacing, vFrameSpacing);
720         ElementListSupport.fillInfoFromChildren(view, info, this, stage,
721                 borderLeftTop, borderRightBottom);
722     }
723 
724     private void addFrame(final LayoutInfo info, final float width,
725             final float verticalShift, final float height,
726             final LayoutContext now) {
727         final LineType lineType = this.getFrameAsLineType();
728         final boolean solid = Mtable.LineType.SOLID.equals(lineType);
729         final boolean dashed = Mtable.LineType.DASHED.equals(lineType);
730         if (dashed || solid) {
731             final float lineWidth = GraphicsSupport.lineWidth(now);
732             final float lineInset = lineWidth / 2.0f;
733             final Color color = (Color) now.getParameter(Parameter.MATHCOLOR);
734             final List<GraphicsObject> go = info.getGraphicObjects();
735             final float vFrameSpacing = this.getFramespacingv(now);
736 
737             final float left = lineInset;
738             final float right = width - lineInset;
739             final float top = verticalShift + lineInset - vFrameSpacing;
740             final float bottom = height + verticalShift - lineInset;
741             go.add(new LineObject(left, top, right, top, lineWidth, color,
742                     dashed));
743             go.add(new LineObject(left, bottom, right, bottom, lineWidth,
744                     color, dashed));
745             go.add(new LineObject(left, top, left, bottom, lineWidth, color,
746                     dashed));
747             go.add(new LineObject(right, top, right, bottom, lineWidth, color,
748                     dashed));
749         }
750     }
751 
752     private void addRowLines(final LayoutInfo info,
753             final LayoutInfo[] rowInfos, final int rows, final float width,
754             final LayoutStage stage, final LayoutContext now) {
755         final float lineWidth = GraphicsSupport.lineWidth(now);
756         final Color color = (Color) now.getParameter(Parameter.MATHCOLOR);
757 
758         final float inFrameStart = this.getFramespacingh(now);
759         for (int row = 0; row < rows - 1; row++) {
760             final LineType lineType = this.getRowLine(row);
761             final boolean solid = Mtable.LineType.SOLID.equals(lineType);
762             final boolean dashed = Mtable.LineType.DASHED.equals(lineType);
763             if (dashed || solid) {
764                 final float y = (rowInfos[row].getPosY(stage)
765                         + rowInfos[row].getDescentHeight(stage)
766                         + rowInfos[row + 1].getPosY(stage) - rowInfos[row + 1]
767                         .getAscentHeight(stage)) / 2.0f;
768                 info.getGraphicObjects().add(
769                         new LineObject(inFrameStart, y, width - inFrameStart,
770                                 y, lineWidth, color, dashed));
771             }
772         }
773     }
774 
775     private void addColumnLines(final LayoutInfo info,
776             final List<Float> columnwidth, final float verticalShift,
777             final float height, final LayoutContext now) {
778         final float lineWidth = GraphicsSupport.lineWidth(now);
779         final Color color = (Color) now.getParameter(Parameter.MATHCOLOR);
780         float x = this.getFramespacingh(now);
781 
782         final float inFrameStart = this.getFramespacingv(now);
783         final float colsm1 = columnwidth.size() - 1;
784         for (int col = 0; col < colsm1; col++) {
785             final LineType lineType = this.getColumnLine(col);
786             final boolean solid = Mtable.LineType.SOLID.equals(lineType);
787             final boolean dashed = Mtable.LineType.DASHED.equals(lineType);
788             if (dashed || solid) {
789                 final float halfSpace = this.getSpaceAfterColumn(now, col) / 2.0f;
790                 x += columnwidth.get(col) + halfSpace;
791                 info.getGraphicObjects().add(
792                         new LineObject(x, verticalShift, x, height
793                                 + verticalShift - inFrameStart, lineWidth,
794                                 color, dashed));
795                 x += halfSpace;
796             }
797         }
798     }
799 
800     private void stretchAndAlignMtds(final LayoutView view,
801             final List<LayoutableNode>[] mtdChildren,
802             final LayoutInfo[] rowInfos, final int rows, final LayoutStage stage) {
803         for (int i = 0; i < rows; i++) {
804             final float rowAscent = rowInfos[i].getAscentHeight(stage);
805             final float rowDescent = rowInfos[i].getDescentHeight(stage);
806             for (final LayoutableNode n : mtdChildren[i]) {
807                 final LayoutInfo mtdInfo = view.getInfo(n);
808 
809                 final VAlign valign = this.getVAlign((JEuclidElement) n, i);
810                 final float verticalShift;
811                 if (valign.getAlign() == Mtable.VAlign.TOP) {
812                     verticalShift = -rowAscent + mtdInfo.getAscentHeight(stage);
813                 } else if (valign.getAlign() == Mtable.VAlign.BOTTOM) {
814                     verticalShift = rowDescent
815                             - mtdInfo.getDescentHeight(stage);
816                 } else if (valign.getAlign() == Mtable.VAlign.CENTER) {
817                     verticalShift = (-rowAscent + rowDescent
818                             + mtdInfo.getAscentHeight(stage) - mtdInfo
819                             .getDescentHeight(stage)) / 2.0f;
820                 } else if (valign.getAlign() == Mtable.VAlign.AXIS) {
821                     // TODO: This uses center instead of axis.
822                     verticalShift = (-rowAscent + rowDescent
823                             + mtdInfo.getAscentHeight(stage) - mtdInfo
824                             .getDescentHeight(stage)) / 2.0f;
825                 } else {
826                     // BASELINE
827                     verticalShift = 0.0f;
828                 }
829                 mtdInfo.shiftVertically(verticalShift, stage);
830                 mtdInfo.setStretchAscent(rowAscent + verticalShift);
831                 mtdInfo.setStretchDescent(rowDescent - verticalShift);
832             }
833         }
834     }
835 
836     private float shiftTableVertically(final LayoutStage stage,
837             final LayoutContext context, final Graphics2D g,
838             final LayoutInfo[] rowInfos, final int rows, final float height) {
839         // Shift table by given vertical alignment
840         // final String alignStr = this.getAlign();
841         // AlignmentType align =
842         // Mtable.AlignmentType.parseAlignmentType(alignStr);
843         // TODO: Proper vertical alignment;
844         // This is "axis" alignment.
845         final float verticalShift = -this.getMiddleShift(g, context) - height
846                 / 2.0f;
847 
848         for (int i = 0; i < rows; i++) {
849             rowInfos[i].shiftVertically(verticalShift, stage);
850         }
851         return verticalShift;
852     }
853 
854     @SuppressWarnings("unchecked")
855     private List<LayoutableNode>[] createListOfMtdChildren(
856             final LayoutableNode[] rowChild, final int rows) {
857         final List<LayoutableNode>[] mtdChildren = new List[rows];
858         for (int i = 0; i < rows; i++) {
859             if (rowChild[i] instanceof MathMLTableRowElement) {
860                 mtdChildren[i] = rowChild[i].getChildrenToLayout();
861             } else {
862                 mtdChildren[i] = new ArrayList<LayoutableNode>(1);
863                 mtdChildren[i].add(rowChild[i]);
864             }
865         }
866         return mtdChildren;
867     }
868 
869     private List<Float> calculateBasicColumnWidth(final LayoutView view,
870             final LayoutStage stage, final int rows,
871             final List<LayoutableNode>[] mtdChildren) {
872         final List<Float> columnwidth = new ArrayList<Float>();
873         for (int i = 0; i < rows; i++) {
874             int missing = mtdChildren[i].size() - columnwidth.size();
875             while (missing > 0) {
876                 columnwidth.add(0.0f);
877                 missing--;
878             }
879             int col = 0;
880             for (final LayoutableNode n : mtdChildren[i]) {
881                 final float width = Math.max(columnwidth.get(col), view
882                         .getInfo(n).getWidth(stage));
883                 columnwidth.set(col, width);
884                 col++;
885             }
886         }
887         if (Boolean.parseBoolean(this.getEqualcolumns())) {
888             this.makeEqual(columnwidth);
889         }
890         return columnwidth;
891     }
892 
893     private void makeEqual(final List<Float> columnwidth) {
894         float maxWidth = 0.0f;
895         for (final Float width : columnwidth) {
896             maxWidth = Math.max(width, maxWidth);
897         }
898         final int cols = columnwidth.size();
899         for (int i = 0; i < cols; i++) {
900             columnwidth.set(i, maxWidth);
901         }
902     }
903 
904     private void setRowWidth(final LayoutStage stage,
905             final LayoutInfo[] rowInfos, final int rows, final float totalWidth) {
906         for (int i = 0; i < rows; i++) {
907             rowInfos[i].setWidth(totalWidth, stage);
908         }
909     }
910 
911     private float layoutColumnsHorizontally(final LayoutView view,
912             final LayoutStage stage, final LayoutContext now, final int rows,
913             final List<LayoutableNode>[] mtdChildren,
914             final List<Float> columnwidth) {
915         final float hFrameSpacing = this.getFramespacingh(now);
916         float totalWidth = hFrameSpacing;
917         for (int i = 0; i < rows; i++) {
918             float x = hFrameSpacing;
919             int col = 0;
920             for (final LayoutableNode n : mtdChildren[i]) {
921                 final LayoutInfo mtdInfo = view.getInfo(n);
922                 final HAlign halign = this.getHAlign((JEuclidElement) n, col);
923                 final float colwi = columnwidth.get(col);
924                 final float xo = halign.getHAlignOffset(stage, mtdInfo, colwi);
925                 mtdInfo.moveTo(x + xo, mtdInfo.getPosY(stage), stage);
926                 // mtdInfo.setWidth(colwi, stage);
927                 mtdInfo.setStretchWidth(colwi);
928                 x += colwi;
929                 totalWidth = Math.max(totalWidth, x);
930                 x += this.getSpaceAfterColumn(now, col);
931                 col++;
932             }
933         }
934         return totalWidth + hFrameSpacing;
935     }
936 
937     private float getSpaceAfterColumn(final LayoutContext now, final int col) {
938         final float columnSpace = AttributesHelper.convertSizeToPt(this
939                 .getSpaceArrayEntry(this.getColumnspacing(), col), now,
940                 AttributesHelper.PT);
941         return columnSpace;
942     }
943 
944     private HAlign getHAlign(final JEuclidElement n, final int col) {
945         assert n != null;
946         final HAlign retVal;
947         if (n instanceof MathMLTableCellElement) {
948             final MathMLTableCellElement cell = (MathMLTableCellElement) n;
949             final String alignString = cell.getColumnalign();
950             final HAlign halign = HAlign.parseString(alignString, null);
951             if (halign == null) {
952                 retVal = this.getHAlign(n.getParent(), col);
953             } else {
954                 retVal = halign;
955             }
956         } else if (n instanceof MathMLTableRowElement) {
957             final MathMLTableRowElement rowE = (MathMLTableRowElement) n;
958             final String alignArray = rowE.getColumnalign();
959             if ((alignArray != null) && (alignArray.length() > 0)) {
960                 retVal = HAlign.parseString(this.getSpaceArrayEntry(alignArray,
961                         col), HAlign.CENTER);
962             } else {
963                 retVal = this.getHAlign(n.getParent(), col);
964             }
965         } else if (n instanceof MathMLTableElement) {
966             final MathMLTableElement table = (MathMLTableElement) n;
967             final String alignArray = table.getColumnalign();
968             if ((alignArray != null) && (alignArray.length() > 0)) {
969                 retVal = HAlign.parseString(this.getSpaceArrayEntry(alignArray,
970                         col), HAlign.CENTER);
971             } else {
972                 retVal = HAlign.CENTER;
973             }
974         } else {
975             retVal = this.getHAlign(n.getParent(), col);
976         }
977         return retVal;
978     }
979 
980     private VAlign getVAlign(final JEuclidElement n, final int row) {
981         assert n != null;
982         final VAlign retVal;
983         if (n instanceof MathMLTableCellElement) {
984             final MathMLTableCellElement cell = (MathMLTableCellElement) n;
985             final String alignString = cell.getRowalign();
986             final Mtable.VAlign valign = Mtable.VAlign.parseString(alignString);
987             if (valign == null) {
988                 retVal = this.getVAlign(n.getParent(), row);
989             } else {
990                 retVal = valign;
991             }
992         } else if (n instanceof MathMLTableRowElement) {
993             final MathMLTableRowElement rowE = (MathMLTableRowElement) n;
994             final String alignString = rowE.getRowalign();
995             final Mtable.VAlign valign = Mtable.VAlign.parseString(alignString);
996             if (valign == null) {
997                 retVal = this.getVAlign(n.getParent(), row);
998             } else {
999                 retVal = valign;
1000             }
1001         } else if (n instanceof MathMLTableElement) {
1002             final MathMLTableElement table = (MathMLTableElement) n;
1003             final String alignArray = table.getRowalign();
1004             if ((alignArray != null) && (alignArray.length() > 0)) {
1005                 retVal = Mtable.VAlign.parseString(this.getSpaceArrayEntry(
1006                         alignArray, row));
1007             } else {
1008                 retVal = Mtable.VAlign.BASELINE_ALIGN;
1009             }
1010         } else {
1011             retVal = this.getVAlign(n.getParent(), row);
1012         }
1013         return retVal;
1014     }
1015 
1016 }