View Javadoc

1   /*
2    * Copyright 2002 - 2009 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: AbstractJEuclidElement.java,v 62d8defc665e 2009/09/25 14:22:59 max $ */
18  
19  package net.sourceforge.jeuclid.elements;
20  
21  import java.awt.Color;
22  import java.awt.Font;
23  import java.awt.FontMetrics;
24  import java.awt.Graphics2D;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import net.sourceforge.jeuclid.LayoutContext;
32  import net.sourceforge.jeuclid.context.StyleAttributeLayoutContext;
33  import net.sourceforge.jeuclid.elements.presentation.token.Mo;
34  import net.sourceforge.jeuclid.elements.presentation.token.Mtext;
35  import net.sourceforge.jeuclid.elements.support.ElementListSupport;
36  import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
37  import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
38  import net.sourceforge.jeuclid.elements.support.attributes.MathVariant;
39  import net.sourceforge.jeuclid.elements.support.text.TextContent;
40  import net.sourceforge.jeuclid.layout.LayoutInfo;
41  import net.sourceforge.jeuclid.layout.LayoutStage;
42  import net.sourceforge.jeuclid.layout.LayoutView;
43  import net.sourceforge.jeuclid.layout.LayoutableNode;
44  
45  import org.apache.batik.dom.AbstractDocument;
46  import org.apache.batik.dom.GenericElementNS;
47  import org.apache.batik.dom.events.DOMMutationEvent;
48  import org.w3c.dom.Attr;
49  import org.w3c.dom.Node;
50  import org.w3c.dom.events.Event;
51  import org.w3c.dom.mathml.MathMLElement;
52  import org.w3c.dom.mathml.MathMLMathElement;
53  import org.w3c.dom.mathml.MathMLNodeList;
54  
55  /**
56   * The basic class for all math elements. Every element class inherits from this
57   * class. It provides basic functionality for drawing.
58   * 
59   * @version $Revision: 62d8defc665e $
60   */
61  // CHECKSTYLE:OFF
62  public abstract class AbstractJEuclidElement extends
63  // CHECKSTYLE:ON
64          GenericElementNS implements JEuclidElement {
65  
66      /** Constant for mathvariant attribute. */
67      public static final String ATTR_MATHVARIANT = "mathvariant";
68  
69      /** Constant for mathcolor attribute. */
70      public static final String ATTR_MATHCOLOR = "mathcolor";
71  
72      /** Constant for mathsize attribute. */
73      public static final String ATTR_MATHSIZE = "mathsize";
74  
75      /** Constant for fontfamily attribute. */
76      public static final String ATTR_DEPRECATED_FONTFAMILY = "fontfamily";
77  
78      /** Constant for fontstyle attribute. */
79      public static final String ATTR_DEPRECATED_FONTSTYLE = "fontstyle";
80  
81      /** Constant for fontweight attribute. */
82      public static final String ATTR_DEPRECATED_FONTWEIGHT = "fontweight";
83  
84      /** Constant for fontsize attribute. */
85      public static final String ATTR_DEPRECATED_FONTSIZE = "fontsize";
86  
87      /** Constant for color attribute. */
88      public static final String ATTR_DEPRECATED_COLOR = "color";
89  
90      /** Constant for background attribute. */
91      public static final String ATTR_DEPRECATED_BACKGROUND = "background";
92  
93      /** Constant for class attribute. */
94      public static final String ATTR_CLASS = "class";
95  
96      /** Constant for style attribute. */
97      public static final String ATTR_STYLE = "style";
98  
99      /** 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 }