1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
57
58
59
60
61
62 public abstract class AbstractJEuclidElement extends
63
64 GenericElementNS implements JEuclidElement {
65
66
67 public static final String ATTR_MATHVARIANT = "mathvariant";
68
69
70 public static final String ATTR_MATHCOLOR = "mathcolor";
71
72
73 public static final String ATTR_MATHSIZE = "mathsize";
74
75
76 public static final String ATTR_DEPRECATED_FONTFAMILY = "fontfamily";
77
78
79 public static final String ATTR_DEPRECATED_FONTSTYLE = "fontstyle";
80
81
82 public static final String ATTR_DEPRECATED_FONTWEIGHT = "fontweight";
83
84
85 public static final String ATTR_DEPRECATED_FONTSIZE = "fontsize";
86
87
88 public static final String ATTR_DEPRECATED_COLOR = "color";
89
90
91 public static final String ATTR_DEPRECATED_BACKGROUND = "background";
92
93
94 public static final String ATTR_CLASS = "class";
95
96
97 public static final String ATTR_STYLE = "style";
98
99
100 public static final String ATTR_ID = "id";
101
102
103 public static final String ATTR_HREF = "xlink:href";
104
105
106 public static final String ATTR_XREF = "xref";
107
108
109 public static final String ATTR_MATHBACKGROUND = "mathbackground";
110
111
112
113
114
115 public static final int TRIVIAL_SPACE_MAX = 0x20;
116
117
118
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
126
127
128
129
130 private static final Set<String> DEPRECATED_ATTRIBUTES = new HashSet<String>();
131
132
133
134
135 private JEuclidElement fakeParent;
136
137 private final Map<String, String> defaultMathAttributes = new HashMap<String, String>();
138
139
140
141
142
143
144
145
146
147 public AbstractJEuclidElement(final String qname,
148 final AbstractDocument odoc) {
149 super(AbstractJEuclidElement.URI, qname, odoc);
150 }
151
152
153
154
155
156
157
158
159
160
161
162 public AbstractJEuclidElement(final String nsUri, final String qname,
163 final AbstractDocument odoc) {
164 super(nsUri, qname, odoc);
165 }
166
167
168
169
170
171
172
173
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
190 public MathVariant getMathvariantAsVariant() {
191
192 String setMv = this.getMathAttribute(
193 AbstractJEuclidElement.ATTR_MATHVARIANT, false);
194
195 JEuclidElement parent = this.getParent();
196 while ((setMv == null) && (parent != null)) {
197
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324 public final void addMathElement(final MathMLElement child) {
325 if (child != null) {
326 this.appendChild(child);
327 }
328 }
329
330
331
332
333
334
335
336
337
338
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
363
364
365
366
367
368
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
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
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
409
410
411
412 public String getText() {
413 return TextContent.getText(this);
414 }
415
416
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
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
449
450
451
452
453 public void setMathvariant(final String mathvariant) {
454 this.setAttribute(AbstractJEuclidElement.ATTR_MATHVARIANT, mathvariant);
455 }
456
457
458
459
460
461
462 public String getMathvariant() {
463 return this.getMathAttribute(AbstractJEuclidElement.ATTR_MATHVARIANT);
464 }
465
466
467
468
469
470
471
472
473
474
475 public FontMetrics getFontMetrics(final Graphics2D g,
476 final LayoutContext context) {
477 return g.getFontMetrics(this.getFont(context));
478 }
479
480
481
482
483
484
485
486 public void setMathcolor(final String mathcolor) {
487 this.setAttribute(AbstractJEuclidElement.ATTR_MATHCOLOR, mathcolor);
488 }
489
490
491
492
493
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
507
508
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
523
524
525
526
527 public void setMathsize(final String mathsize) {
528 this.setAttribute(AbstractJEuclidElement.ATTR_MATHSIZE, mathsize);
529 }
530
531
532
533
534
535
536
537
538
539
540
541 protected void setDefaultMathAttribute(final String key, final String value) {
542 this.defaultMathAttributes.put(key, value);
543 }
544
545
546
547
548
549
550
551
552
553
554 protected String getMathAttribute(final String attrName) {
555 return this.getMathAttribute(attrName, true);
556 }
557
558
559
560
561
562
563
564
565
566
567
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
591
592
593
594
595
596
597 private String getDefaultMathAttribute(final String attrName) {
598 return this.defaultMathAttributes.get(attrName);
599 }
600
601
602
603
604
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
619
620
621
622
623 public void setMathbackground(final String mathbackground) {
624 this.setAttribute(AbstractJEuclidElement.ATTR_MATHBACKGROUND,
625 mathbackground);
626 }
627
628
629
630
631
632
633
634
635
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
643 public String getClassName() {
644 return this.getAttribute(AbstractJEuclidElement.ATTR_CLASS);
645 }
646
647
648 public void setClassName(final String className) {
649 this.setAttribute(AbstractJEuclidElement.ATTR_CLASS, className);
650 }
651
652
653 public String getMathElementStyle() {
654 return this.getAttribute(AbstractJEuclidElement.ATTR_STYLE);
655 }
656
657
658 public void setMathElementStyle(final String mathElementStyle) {
659 this.setAttribute(AbstractJEuclidElement.ATTR_STYLE, mathElementStyle);
660 }
661
662
663 @Override
664 public String getId() {
665 return this.getAttribute(AbstractJEuclidElement.ATTR_ID);
666 }
667
668
669 public void setId(final String id) {
670 this.setAttribute(AbstractJEuclidElement.ATTR_ID, id);
671 }
672
673
674 public String getXref() {
675 return this.getAttribute(AbstractJEuclidElement.ATTR_XREF);
676 }
677
678
679 public void setXref(final String xref) {
680 this.setAttribute(AbstractJEuclidElement.ATTR_XREF, xref);
681 }
682
683
684 public String getHref() {
685 return this.getAttribute(AbstractJEuclidElement.ATTR_HREF);
686 }
687
688
689 public void setHref(final String href) {
690 this.setAttribute(AbstractJEuclidElement.ATTR_HREF, href);
691 }
692
693
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
706 public boolean hasChildPrescripts(final JEuclidElement child) {
707 return false;
708 }
709
710
711 public boolean hasChildPostscripts(final JEuclidElement child,
712 final LayoutContext context) {
713 return false;
714 }
715
716
717
718
719
720
721 public MathMLNodeList getContents() {
722 return (MathMLNodeList) this.getChildNodes();
723 }
724
725
726 public LayoutContext getChildLayoutContext(final int childNum,
727 final LayoutContext context) {
728 return this.applyLocalAttributesToContext(context);
729 }
730
731
732
733
734
735
736
737
738 public LayoutContext applyLocalAttributesToContext(
739 final LayoutContext context) {
740
741
742
743 return this.applyStyleAttributes(context);
744 }
745
746
747
748
749
750
751
752
753
754 private LayoutContext applyStyleAttributes(final LayoutContext applyTo) {
755 LayoutContext retVal = applyTo;
756
757
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
771
772
773 if ((msize != null) || (foreground != null)) {
774 retVal = new StyleAttributeLayoutContext(applyTo, msize, foreground);
775 }
776
777 return retVal;
778 }
779
780
781 public List<LayoutableNode> getChildrenToLayout() {
782 final List<LayoutableNode> l = ElementListSupport
783 .createListOfLayoutChildren(this);
784 return l;
785 }
786
787
788 public List<LayoutableNode> getChildrenToDraw() {
789 final List<LayoutableNode> l = ElementListSupport
790 .createListOfLayoutChildren(this);
791 return l;
792 }
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
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
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
824 if (this.getMathbackground() == null) {
825 info.setLayoutStage(childMinStage);
826 } else {
827 info.setLayoutStage(LayoutStage.STAGE1);
828 }
829 }
830
831
832 public void layoutStage2(final LayoutView view, final LayoutInfo info,
833 final LayoutContext context) {
834 this.layoutStageInvariant(view, info, LayoutStage.STAGE2, context);
835
836
837
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
864
865
866 protected void changeHook() {
867
868 }
869
870
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 }