001    /*
002     * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: Mtable.java,v bc1d5fde7b73 2009/06/01 14:40:54 maxberger $ */
018    
019    package net.sourceforge.jeuclid.elements.presentation.table;
020    
021    import java.awt.Color;
022    import java.awt.Graphics2D;
023    import java.awt.geom.Dimension2D;
024    import java.util.ArrayList;
025    import java.util.List;
026    import java.util.Locale;
027    
028    import net.sourceforge.jeuclid.Constants;
029    import net.sourceforge.jeuclid.LayoutContext;
030    import net.sourceforge.jeuclid.context.InlineLayoutContext;
031    import net.sourceforge.jeuclid.context.Parameter;
032    import net.sourceforge.jeuclid.elements.JEuclidElement;
033    import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
034    import net.sourceforge.jeuclid.elements.support.ElementListSupport;
035    import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
036    import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
037    import net.sourceforge.jeuclid.elements.support.attributes.HAlign;
038    import net.sourceforge.jeuclid.layout.GraphicsObject;
039    import net.sourceforge.jeuclid.layout.LayoutInfo;
040    import net.sourceforge.jeuclid.layout.LayoutStage;
041    import net.sourceforge.jeuclid.layout.LayoutView;
042    import net.sourceforge.jeuclid.layout.LayoutableNode;
043    import net.sourceforge.jeuclid.layout.LineObject;
044    
045    import org.apache.batik.dom.AbstractDocument;
046    import org.apache.commons.logging.Log;
047    import org.apache.commons.logging.LogFactory;
048    import org.w3c.dom.Node;
049    import org.w3c.dom.mathml.MathMLLabeledRowElement;
050    import org.w3c.dom.mathml.MathMLNodeList;
051    import org.w3c.dom.mathml.MathMLTableCellElement;
052    import org.w3c.dom.mathml.MathMLTableElement;
053    import org.w3c.dom.mathml.MathMLTableRowElement;
054    
055    /**
056     * This class presents a table.
057     * 
058     * @version $Revision: bc1d5fde7b73 $
059     */
060    // CHECKSTYLE:OFF
061    // Data abstraction coupling is "to high". but this is necessary for proper
062    // layout.
063    public final class Mtable extends AbstractTableElement implements
064            MathMLTableElement {
065        // CHECKSTYLE:ON
066    
067        /**
068         * The XML element from this class.
069         */
070        public static final String ELEMENT = "mtable";
071    
072        /** Attribute for columnalign. */
073        static final String ATTR_COLUMNALIGN = "columnalign";
074    
075        /** Attribute for rowalign. */
076        static final String ATTR_ROWALIGN = "rowalign";
077    
078        /** Attribute for groupalign. */
079        static final String ATTR_GROUPALIGN = "groupalign";
080    
081        /** attribute for rowlines. */
082        private static final String ATTR_ROWLINES = "rowlines";
083    
084        /** attribute for columnlines. */
085        private static final String ATTR_COLUMNLINES = "columnlines";
086    
087        /** attribute for align. */
088        private static final String ATTR_ALIGN = "align";
089    
090        /** attribute for alignmentscope. */
091        private static final String ATTR_ALIGNMENTSCOPE = "alignmentscope";
092    
093        /** attribute for columnwidth. */
094        private static final String ATTR_COLUMNWIDTH = "columnwidth";
095    
096        /** attribute for width. */
097        private static final String ATTR_WIDTH = "width";
098    
099        /** 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    }