001    /*
002     * Copyright 2002 - 2009 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: AbstractJEuclidElement.java,v 62d8defc665e 2009/09/25 14:22:59 max $ */
018    
019    package net.sourceforge.jeuclid.elements;
020    
021    import java.awt.Color;
022    import java.awt.Font;
023    import java.awt.FontMetrics;
024    import java.awt.Graphics2D;
025    import java.util.HashMap;
026    import java.util.HashSet;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Set;
030    
031    import net.sourceforge.jeuclid.LayoutContext;
032    import net.sourceforge.jeuclid.context.StyleAttributeLayoutContext;
033    import net.sourceforge.jeuclid.elements.presentation.token.Mo;
034    import net.sourceforge.jeuclid.elements.presentation.token.Mtext;
035    import net.sourceforge.jeuclid.elements.support.ElementListSupport;
036    import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
037    import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
038    import net.sourceforge.jeuclid.elements.support.attributes.MathVariant;
039    import net.sourceforge.jeuclid.elements.support.text.TextContent;
040    import net.sourceforge.jeuclid.layout.LayoutInfo;
041    import net.sourceforge.jeuclid.layout.LayoutStage;
042    import net.sourceforge.jeuclid.layout.LayoutView;
043    import net.sourceforge.jeuclid.layout.LayoutableNode;
044    
045    import org.apache.batik.dom.AbstractDocument;
046    import org.apache.batik.dom.GenericElementNS;
047    import org.apache.batik.dom.events.DOMMutationEvent;
048    import org.w3c.dom.Attr;
049    import org.w3c.dom.Node;
050    import org.w3c.dom.events.Event;
051    import org.w3c.dom.mathml.MathMLElement;
052    import org.w3c.dom.mathml.MathMLMathElement;
053    import org.w3c.dom.mathml.MathMLNodeList;
054    
055    /**
056     * The basic class for all math elements. Every element class inherits from this
057     * class. It provides basic functionality for drawing.
058     * 
059     * @version $Revision: 62d8defc665e $
060     */
061    // CHECKSTYLE:OFF
062    public abstract class AbstractJEuclidElement extends
063    // CHECKSTYLE:ON
064            GenericElementNS implements JEuclidElement {
065    
066        /** Constant for mathvariant attribute. */
067        public static final String ATTR_MATHVARIANT = "mathvariant";
068    
069        /** Constant for mathcolor attribute. */
070        public static final String ATTR_MATHCOLOR = "mathcolor";
071    
072        /** Constant for mathsize attribute. */
073        public static final String ATTR_MATHSIZE = "mathsize";
074    
075        /** Constant for fontfamily attribute. */
076        public static final String ATTR_DEPRECATED_FONTFAMILY = "fontfamily";
077    
078        /** Constant for fontstyle attribute. */
079        public static final String ATTR_DEPRECATED_FONTSTYLE = "fontstyle";
080    
081        /** Constant for fontweight attribute. */
082        public static final String ATTR_DEPRECATED_FONTWEIGHT = "fontweight";
083    
084        /** Constant for fontsize attribute. */
085        public static final String ATTR_DEPRECATED_FONTSIZE = "fontsize";
086    
087        /** Constant for color attribute. */
088        public static final String ATTR_DEPRECATED_COLOR = "color";
089    
090        /** Constant for background attribute. */
091        public static final String ATTR_DEPRECATED_BACKGROUND = "background";
092    
093        /** Constant for class attribute. */
094        public static final String ATTR_CLASS = "class";
095    
096        /** Constant for style attribute. */
097        public static final String ATTR_STYLE = "style";
098    
099        /** Constant for id attribute. */
100        public static final String ATTR_ID = "id";
101    
102        /** Constant for href attribute. */
103        public static final String ATTR_HREF = "xlink:href";
104    
105        /** Constant for xref attribute. */
106        public static final String ATTR_XREF = "xref";
107    
108        /** The mathbackground attribute. */
109        public static final String ATTR_MATHBACKGROUND = "mathbackground";
110    
111        /**
112         * largest value for all trivial spaces (= spaces that can be ignored /
113         * shortened).
114         */
115        public static final int TRIVIAL_SPACE_MAX = 0x20;
116    
117        /**
118         * The URI from MathML.
119         */
120        public static final String URI = "http://www.w3.org/1998/Math/MathML";
121    
122        private static final float MIDDLE_SHIFT = 0.38f;
123    
124        // /**
125        // * Logger for this class
126        // */
127        // private static final Log LOGGER = LogFactory
128        // .getLog(AbstractJEuclidElement.class);
129    
130        private static final Set<String> DEPRECATED_ATTRIBUTES = new HashSet<String>();
131    
132        /**
133         * Reference to the element acting as parent if there is no parent.
134         */
135        private JEuclidElement fakeParent;
136    
137        private final Map<String, String> defaultMathAttributes = new HashMap<String, String>();
138    
139        /**
140         * Default constructor. Sets MathML Namespace.
141         * 
142         * @param qname
143         *            Qualified name.
144         * @param odoc
145         *            Owner Document.
146         */
147        public AbstractJEuclidElement(final String qname,
148                final AbstractDocument odoc) {
149            super(AbstractJEuclidElement.URI, qname, odoc);
150        }
151    
152        /**
153         * Constructor to explicitly set the namespace.
154         * 
155         * @param nsUri
156         *            Namespace URI.
157         * @param qname
158         *            Qualified name.
159         * @param odoc
160         *            Owner Document.
161         */
162        public AbstractJEuclidElement(final String nsUri, final String qname,
163                final AbstractDocument odoc) {
164            super(nsUri, qname, odoc);
165        }
166    
167        /**
168         * Gets the used font. Everything regardes font, processed by MathBase
169         * object.
170         * 
171         * @param context
172         *            LayoutContext to use.
173         * @return Font Font object.
174         */
175        public Font getFont(final LayoutContext context) {
176            final String content = this.getText();
177            final char aChar;
178            if (content.length() > 0) {
179                aChar = content.charAt(0);
180            } else {
181                aChar = 'A';
182            }
183            return this.getMathvariantAsVariant().createFont(
184                    GraphicsSupport.getFontsizeInPoint(context), aChar,
185                    this.applyLocalAttributesToContext(context), true);
186    
187        }
188    
189        /** {@inheritDoc} */
190        public MathVariant getMathvariantAsVariant() {
191            // TODO: Support deprecated variant names
192            String setMv = this.getMathAttribute(
193                    AbstractJEuclidElement.ATTR_MATHVARIANT, false);
194    
195            JEuclidElement parent = this.getParent();
196            while ((setMv == null) && (parent != null)) {
197                // element is not set, try to inherit
198                if (parent instanceof AbstractJEuclidElement) {
199                    setMv = ((AbstractJEuclidElement) parent).getMathAttribute(
200                            AbstractJEuclidElement.ATTR_MATHVARIANT, false);
201                }
202                parent = parent.getParent();
203            }
204            if (setMv == null) {
205                setMv = this.defaultMathAttributes
206                        .get(AbstractJEuclidElement.ATTR_MATHVARIANT);
207            }
208            MathVariant variant;
209            if (setMv == null) {
210                variant = MathVariant.NORMAL;
211            } else {
212                variant = MathVariant.stringToMathVariant(setMv);
213                if (variant == null) {
214                    variant = MathVariant.NORMAL;
215                }
216            }
217            return variant;
218        }
219    
220        // /**
221        // * Setting size of child of the element.
222        // *
223        // * @param child
224        // * Child element
225        // * @param childpos
226        // * Position of the child element
227        // */
228        //
229        // private void setChildSize(AbstractMathElement child, int childpos) {
230        //
231        // float childSize = (float) Math.pow(getScriptSizeMultiplier(), child
232        // .getAbsoluteScriptLevel());
233        //
234        // child.multipleFont(m_font, (float) Math.pow(getScriptSizeMultiplier(),
235        // child.getAbsoluteScriptLevel()));
236        // System.out.println(child.toString() + " "
237        // + child.getAbsoluteScriptLevel() + " "
238        // + getScriptSizeMultiplier() + " " + childSize);
239        //
240        // child.setScriptSizeMultiplier(getScriptSizeMultiplier());
241        //
242        // if (this instanceof MathMultiScripts) {
243        // if (childpos > 0) {
244        // child.multipleFont(m_font, getScriptSizeMultiplier());
245        // child.setDisplayStyle(false);
246        // child.setInheritSisplayStyle(false);
247        // }
248        // } else if (this instanceof MathOver) {
249        // if (childpos == 1) {
250        // if (((getMathElement(0) instanceof MathOperator) && ((MathOperator)
251        // getMathElement(0))
252        // .getMoveableLimits())
253        // || (!((MathOver) this).getAccent())) {
254        // child.multipleFont(m_font, getScriptSizeMultiplier());
255        // child.setDisplayStyle(false);
256        // child.setInheritSisplayStyle(false);
257        // }
258        // }
259        // } else if (this instanceof MathUnder) {
260        // if (childpos == 1) {
261        // if (((getMathElement(0) instanceof MathOperator) && ((MathOperator)
262        // getMathElement(0))
263        // .getMoveableLimits())
264        // || (!((MathUnder) this).getAccentUnder())) {
265        // child.multipleFont(m_font, getScriptSizeMultiplier());
266        // child.setDisplayStyle(false);
267        // child.setInheritSisplayStyle(false);
268        // }
269        // }
270        // } else if (this instanceof MathUnderOver) {
271        // if (childpos > 0) {
272        // if (((getMathElement(0) instanceof MathOperator) && ((MathOperator)
273        // getMathElement(0))
274        // .getMoveableLimits())
275        // || ((childpos == 1) && (!((MathUnderOver) this)
276        // .getAccentUnder()))
277        // || ((childpos == 2) && (!((MathUnderOver) this)
278        // .getAccent()))) {
279        // child.multipleFont(m_font, getScriptSizeMultiplier());
280        // child.setDisplayStyle(false);
281        // child.setInheritSisplayStyle(false);
282        // }
283        // }
284        // } else if (this instanceof MathRoot) {
285        // if (childpos == 1) {
286        // child.multipleFont(m_font, (float) Math.pow(
287        // getScriptSizeMultiplier(), 2));
288        // child.setDisplayStyle(false);
289        // child.setInheritSisplayStyle(false);
290        // }
291        // } else if (this instanceof MathSub) {
292        // if (childpos == 1) {
293        // child.multipleFont(m_font, getScriptSizeMultiplier());
294        // child.setDisplayStyle(false);
295        // child.setInheritSisplayStyle(false);
296        // }
297        // } else if (this instanceof MathSup) {
298        // if (childpos == 1) {
299        // child.multipleFont(m_font, getScriptSizeMultiplier());
300        // child.setDisplayStyle(false);
301        // child.setInheritSisplayStyle(false);
302        // }
303        // } else if (this instanceof MathSubSup) {
304        // if (childpos > 0) {
305        // child.multipleFont(m_font, getScriptSizeMultiplier());
306        // child.setDisplayStyle(false);
307        // child.setInheritSisplayStyle(false);
308        // }
309        // } else if (this instanceof MathStyle) {
310        // // child.multipleFont(m_font, (float) Math.pow(
311        // // getScriptSizeMultiplier(), ((MathStyle) this)
312        // // .getScriptlevel()));
313        // } else {
314        // child.setFont(m_font);
315        // }
316        // }
317    
318        /**
319         * Add a math element as a child.
320         * 
321         * @param child
322         *            Math element object.
323         */
324        public final void addMathElement(final MathMLElement child) {
325            if (child != null) {
326                this.appendChild(child);
327            }
328        }
329    
330        /**
331         * Gets a child from this element.
332         * <p>
333         * Please note, that unlike the MathML DOM model functions this function
334         * uses a 0-based index.
335         * 
336         * @param index
337         *            Index of the child (0-based).
338         * @return The child MathElement object.
339         */
340        protected JEuclidElement getMathElement(final int index) {
341            final List<Node> childList = ElementListSupport
342                    .createListOfChildren(this);
343            int count = 0;
344            for (final Node n : childList) {
345                if (n instanceof JEuclidElement) {
346                    if (count == index) {
347                        return (JEuclidElement) n;
348                    }
349                    count++;
350                }
351            }
352            for (; count < index; count++) {
353                this.appendChild(this.ownerDocument.createElement(Mtext.ELEMENT));
354            }
355            final JEuclidElement last = (JEuclidElement) this.ownerDocument
356                    .createElement(Mtext.ELEMENT);
357            this.appendChild(last);
358            return last;
359        }
360    
361        /**
362         * Sets a specific child to the newElement, creating other subelements as
363         * necessary.
364         * 
365         * @param index
366         *            the index to set (0=the first child)
367         * @param newElement
368         *            new element to be set as child.
369         */
370        protected void setMathElement(final int index,
371                final MathMLElement newElement) {
372            final org.w3c.dom.NodeList childList = this.getChildNodes();
373            while (childList.getLength() < index) {
374                this.appendChild(this.getOwnerDocument().createTextNode(""));
375            }
376            if (childList.getLength() == index) {
377                this.addMathElement(newElement);
378            } else {
379                this.replaceChild(newElement, childList.item(index));
380            }
381        }
382    
383        /** {@inheritDoc} */
384        public int getIndexOfMathElement(final JEuclidElement element) {
385            final org.w3c.dom.NodeList childList = this.getChildNodes();
386            for (int i = 0; i < childList.getLength(); i++) {
387                if (childList.item(i).equals(element)) {
388                    return i;
389                }
390            }
391            return -1;
392        }
393    
394        /** {@inheritDoc} */
395        public int getMathElementCount() {
396            final List<Node> childList = ElementListSupport
397                    .createListOfChildren(this);
398            int count = 0;
399            for (final Node n : childList) {
400                if (n instanceof JEuclidElement) {
401                    count++;
402                }
403            }
404            return count;
405        }
406    
407        /**
408         * Returns the text content of this element.
409         * 
410         * @return Text content.
411         */
412        public String getText() {
413            return TextContent.getText(this);
414        }
415    
416        /** {@inheritDoc} */
417        public void setFakeParent(final JEuclidElement parent) {
418            this.fakeParent = parent;
419        }
420    
421        private JEuclidNode getParentAsJEuclidNode() {
422            final Node parentNode = this.getParentNode();
423            final JEuclidNode theParent;
424            if (parentNode instanceof JEuclidNode) {
425                theParent = (JEuclidNode) parentNode;
426            } else {
427                theParent = null;
428            }
429            if (theParent == null) {
430                return this.fakeParent;
431            } else {
432                return theParent;
433            }
434    
435        }
436    
437        /** {@inheritDoc} */
438        public JEuclidElement getParent() {
439            final JEuclidNode parentNode = this.getParentAsJEuclidNode();
440            if (parentNode instanceof JEuclidElement) {
441                return (JEuclidElement) parentNode;
442            } else {
443                return null;
444            }
445        }
446    
447        /**
448         * Sets value of mathvariant attribute (style of the element).
449         * 
450         * @param mathvariant
451         *            Value of mathvariant.
452         */
453        public void setMathvariant(final String mathvariant) {
454            this.setAttribute(AbstractJEuclidElement.ATTR_MATHVARIANT, mathvariant);
455        }
456    
457        /**
458         * Returns value of mathvariant attribute (style of the element).
459         * 
460         * @return Value of mathvariant.
461         */
462        public String getMathvariant() {
463            return this.getMathAttribute(AbstractJEuclidElement.ATTR_MATHVARIANT);
464        }
465    
466        /**
467         * Gets the font metrics of the used font.
468         * 
469         * @return Font metrics.
470         * @param context
471         *            LayoutContext to use.
472         * @param g
473         *            Graphics2D context to use.
474         */
475        public FontMetrics getFontMetrics(final Graphics2D g,
476                final LayoutContext context) {
477            return g.getFontMetrics(this.getFont(context));
478        }
479    
480        /**
481         * Sets value of math color attribute.
482         * 
483         * @param mathcolor
484         *            Color object.
485         */
486        public void setMathcolor(final String mathcolor) {
487            this.setAttribute(AbstractJEuclidElement.ATTR_MATHCOLOR, mathcolor);
488        }
489    
490        /**
491         * Returns value of mathcolor attribute.
492         * 
493         * @return Color as string.
494         */
495        public String getMathcolor() {
496            String color;
497            color = this.getMathAttribute(AbstractJEuclidElement.ATTR_MATHCOLOR);
498            if (color == null) {
499                color = this
500                        .getMathAttribute(AbstractJEuclidElement.ATTR_DEPRECATED_COLOR);
501            }
502            return color;
503        }
504    
505        /**
506         * Retrieve the mathsize attribute.
507         * 
508         * @return the mathsize attribute.
509         */
510        public String getMathsize() {
511            String size;
512            size = this.getMathAttribute(AbstractJEuclidElement.ATTR_MATHSIZE);
513            if (size == null) {
514                size = this
515                        .getMathAttribute(AbstractJEuclidElement.ATTR_DEPRECATED_FONTSIZE);
516            }
517            return size;
518    
519        }
520    
521        /**
522         * Sets mathsize to a new value.
523         * 
524         * @param mathsize
525         *            value of mathsize.
526         */
527        public void setMathsize(final String mathsize) {
528            this.setAttribute(AbstractJEuclidElement.ATTR_MATHSIZE, mathsize);
529        }
530    
531        /**
532         * Sets default values for math attributes. Default values are returned
533         * through getMathAttribute, but not stored in the actual DOM tree. This is
534         * necessary to support proper serialization.
535         * 
536         * @param key
537         *            the attribute to set.
538         * @param value
539         *            value of the attribute.
540         */
541        protected void setDefaultMathAttribute(final String key, final String value) {
542            this.defaultMathAttributes.put(key, value);
543        }
544    
545        /**
546         * retrieve an attribute from the MathML or default name space, returning
547         * the default value if the attribute is not set.
548         * 
549         * @param attrName
550         *            the name of the attribute
551         * @return attribute value or null if not set.
552         * @see #getMathAttribute(String, boolean)
553         */
554        protected String getMathAttribute(final String attrName) {
555            return this.getMathAttribute(attrName, true);
556        }
557    
558        /**
559         * retrieve an attribute from the MathML or default name space.
560         * 
561         * @param attrName
562         *            the name of the attribute
563         * @param useDefault
564         *            is true, the default value is used if the attribute is not
565         *            set.
566         * @return attribute value or null if not set.
567         * @see #getMathAttribute(String)
568         */
569        protected String getMathAttribute(final String attrName,
570                final boolean useDefault) {
571            final String attrValue;
572            Attr attr = this.getAttributeNodeNS(AbstractJEuclidElement.URI,
573                    attrName);
574            if (attr == null) {
575                attr = this.getAttributeNode(attrName);
576            }
577            if (attr == null) {
578                if (useDefault) {
579                    attrValue = this.getDefaultMathAttribute(attrName);
580                } else {
581                    attrValue = null;
582                }
583            } else {
584                attrValue = attr.getValue().trim();
585            }
586            return attrValue;
587        }
588    
589        /**
590         * Retrieves the previously stored default value for this attribute.
591         * 
592         * @param attrName
593         *            name of the Attribute
594         * @return value set by {@link #setDefaultMathAttribute(String, String)} or
595         *         null if not set.
596         */
597        private String getDefaultMathAttribute(final String attrName) {
598            return this.defaultMathAttributes.get(attrName);
599        }
600    
601        /**
602         * Returns value of mathbackground attribute.
603         * 
604         * @return Color as string.
605         */
606        public String getMathbackground() {
607            String color;
608            color = this
609                    .getMathAttribute(AbstractJEuclidElement.ATTR_MATHBACKGROUND);
610            if (color == null) {
611                color = this
612                        .getMathAttribute(AbstractJEuclidElement.ATTR_DEPRECATED_BACKGROUND);
613            }
614            return color;
615        }
616    
617        /**
618         * Sets the value of the machbackground attribute.
619         * 
620         * @param mathbackground
621         *            a string to be used as background color.
622         */
623        public void setMathbackground(final String mathbackground) {
624            this.setAttribute(AbstractJEuclidElement.ATTR_MATHBACKGROUND,
625                    mathbackground);
626        }
627    
628        /**
629         * Returns the distance of the baseline and the middleline.
630         * 
631         * @return Distance baseline - middleline.
632         * @param context
633         *            Layout Context to use
634         * @param g
635         *            Graphics2D context to use.
636         */
637        public float getMiddleShift(final Graphics2D g, final LayoutContext context) {
638            return this.getFontMetrics(g, context).getAscent()
639                    * AbstractJEuclidElement.MIDDLE_SHIFT;
640        }
641    
642        /** {@inheritDoc} */
643        public String getClassName() {
644            return this.getAttribute(AbstractJEuclidElement.ATTR_CLASS);
645        }
646    
647        /** {@inheritDoc} */
648        public void setClassName(final String className) {
649            this.setAttribute(AbstractJEuclidElement.ATTR_CLASS, className);
650        }
651    
652        /** {@inheritDoc} */
653        public String getMathElementStyle() {
654            return this.getAttribute(AbstractJEuclidElement.ATTR_STYLE);
655        }
656    
657        /** {@inheritDoc} */
658        public void setMathElementStyle(final String mathElementStyle) {
659            this.setAttribute(AbstractJEuclidElement.ATTR_STYLE, mathElementStyle);
660        }
661    
662        /** {@inheritDoc} */
663        @Override
664        public String getId() {
665            return this.getAttribute(AbstractJEuclidElement.ATTR_ID);
666        }
667    
668        /** {@inheritDoc} */
669        public void setId(final String id) {
670            this.setAttribute(AbstractJEuclidElement.ATTR_ID, id);
671        }
672    
673        /** {@inheritDoc} */
674        public String getXref() {
675            return this.getAttribute(AbstractJEuclidElement.ATTR_XREF);
676        }
677    
678        /** {@inheritDoc} */
679        public void setXref(final String xref) {
680            this.setAttribute(AbstractJEuclidElement.ATTR_XREF, xref);
681        }
682    
683        /** {@inheritDoc} */
684        public String getHref() {
685            return this.getAttribute(AbstractJEuclidElement.ATTR_HREF);
686        }
687    
688        /** {@inheritDoc} */
689        public void setHref(final String href) {
690            this.setAttribute(AbstractJEuclidElement.ATTR_HREF, href);
691        }
692    
693        /** {@inheritDoc} */
694        public MathMLMathElement getOwnerMathElement() {
695            JEuclidElement node = this.getParent();
696            while (node != null) {
697                if (node instanceof MathMLMathElement) {
698                    return (MathMLMathElement) node;
699                }
700                node = node.getParent();
701            }
702            return null;
703        }
704    
705        /** {@inheritDoc} */
706        public boolean hasChildPrescripts(final JEuclidElement child) {
707            return false;
708        }
709    
710        /** {@inheritDoc} */
711        public boolean hasChildPostscripts(final JEuclidElement child,
712                final LayoutContext context) {
713            return false;
714        }
715    
716        /**
717         * Returns the children as a MathML NodeList.
718         * 
719         * @return a list of children
720         */
721        public MathMLNodeList getContents() {
722            return (MathMLNodeList) this.getChildNodes();
723        }
724    
725        /** {@inheritDoc} */
726        public LayoutContext getChildLayoutContext(final int childNum,
727                final LayoutContext context) {
728            return this.applyLocalAttributesToContext(context);
729        }
730    
731        /**
732         * Retrieve the LayoutContext valid for the current node.
733         * 
734         * @param context
735         *            external context.
736         * @return the current layout context.
737         */
738        public LayoutContext applyLocalAttributesToContext(
739                final LayoutContext context) {
740            // TODO: Theoretically this only applies all to presentation token
741            // elements except mspace and mglyph, and on no other elements except
742            // mstyle 3.2.2
743            return this.applyStyleAttributes(context);
744        }
745    
746        /**
747         * Apply Style attributed specified in 3.2.2 to a layout context.
748         * 
749         * @param applyTo
750         *            the context to apply to
751         * @return a context which has the style attributes changed accordingly. May
752         *         be the original context if nothing has changed.
753         */
754        private LayoutContext applyStyleAttributes(final LayoutContext applyTo) {
755            LayoutContext retVal = applyTo;
756    
757            // Variant is not inherited and therefore not part of the context.
758    
759            final String msize = this.getMathsize();
760    
761            final Color foreground;
762            final String colorString = this.getMathcolor();
763            if (colorString == null) {
764                foreground = null;
765            } else {
766                foreground = AttributesHelper.stringToColor(colorString,
767                        Color.BLACK);
768            }
769    
770            // Background is handled differently and does not need to go into
771            // context.
772    
773            if ((msize != null) || (foreground != null)) {
774                retVal = new StyleAttributeLayoutContext(applyTo, msize, foreground);
775            }
776    
777            return retVal;
778        }
779    
780        /** {@inheritDoc} */
781        public List<LayoutableNode> getChildrenToLayout() {
782            final List<LayoutableNode> l = ElementListSupport
783                    .createListOfLayoutChildren(this);
784            return l;
785        }
786    
787        /** {@inheritDoc} */
788        public List<LayoutableNode> getChildrenToDraw() {
789            final List<LayoutableNode> l = ElementListSupport
790                    .createListOfLayoutChildren(this);
791            return l;
792        }
793    
794        /**
795         * Layout for elements which are stage independent.
796         * <p>
797         * This function will layout an element which is layed out the same no
798         * matter what stage it is in. This is the case for most elements.
799         * <p>
800         * Notable exceptions are mo and tables.
801         * 
802         * @param view
803         *            View Object for this layout.
804         * @param info
805         *            An info object which will be filled during layout.
806         * @param stage
807         *            current layout stage.
808         * @param context
809         *            current LayoutContext.
810         */
811        protected void layoutStageInvariant(final LayoutView view,
812                final LayoutInfo info, final LayoutStage stage,
813                final LayoutContext context) {
814            ElementListSupport.layoutSequential(view, info, this
815                    .getChildrenToLayout(), stage);
816        }
817    
818        /** {@inheritDoc} */
819        public void layoutStage1(final LayoutView view, final LayoutInfo info,
820                final LayoutStage childMinStage, final LayoutContext context) {
821            this.layoutStageInvariant(view, info, LayoutStage.STAGE1, context);
822    
823            // TODO: This should be done in a better way.
824            if (this.getMathbackground() == null) {
825                info.setLayoutStage(childMinStage);
826            } else {
827                info.setLayoutStage(LayoutStage.STAGE1);
828            }
829        }
830    
831        /** {@inheritDoc} */
832        public void layoutStage2(final LayoutView view, final LayoutInfo info,
833                final LayoutContext context) {
834            this.layoutStageInvariant(view, info, LayoutStage.STAGE2, context);
835    
836            // TODO: put in own function, ensure this is also called from
837            // subclasses.
838            final String background = this.getMathbackground();
839            final Color backgroundColor = AttributesHelper.stringToColor(
840                    background, null);
841            ElementListSupport.addBackground(backgroundColor, info, false);
842            info.setLayoutStage(LayoutStage.STAGE2);
843        }
844    
845        static {
846            AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
847                    .add(AbstractJEuclidElement.ATTR_DEPRECATED_COLOR);
848            AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
849                    .add(AbstractJEuclidElement.ATTR_DEPRECATED_BACKGROUND);
850            AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
851                    .add(AbstractJEuclidElement.ATTR_DEPRECATED_FONTSIZE);
852            AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
853                    .add(AbstractJEuclidElement.ATTR_DEPRECATED_FONTWEIGHT);
854            AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
855                    .add(AbstractJEuclidElement.ATTR_DEPRECATED_FONTSTYLE);
856            AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
857                    .add(AbstractJEuclidElement.ATTR_DEPRECATED_FONTFAMILY);
858    
859            AbstractJEuclidElement.DEPRECATED_ATTRIBUTES.add(Mo.ATTR_MOVEABLEWRONG);
860        }
861    
862        /**
863         * Override this function to get notified whenever the contents of this
864         * element have changed.
865         */
866        protected void changeHook() {
867            // Override me!
868        }
869    
870        /** {@inheritDoc} */
871        @Override
872        public boolean dispatchEvent(final Event evt) {
873            if (evt instanceof DOMMutationEvent) {
874                this.changeHook();
875            }
876            return super.dispatchEvent(evt);
877        }
878    }