View Javadoc

1   /*
2    * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: Mmultiscripts.java 783 2008-06-07 14:12:27Z maxberger $ */
18  
19  package net.sourceforge.jeuclid.elements.presentation.script;
20  
21  import java.awt.geom.Dimension2D;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import net.sourceforge.jeuclid.LayoutContext;
26  import net.sourceforge.jeuclid.elements.JEuclidElement;
27  import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
28  import net.sourceforge.jeuclid.elements.support.ElementListSupport;
29  import net.sourceforge.jeuclid.elements.support.MathMLNodeListImpl;
30  import net.sourceforge.jeuclid.layout.LayoutInfo;
31  import net.sourceforge.jeuclid.layout.LayoutStage;
32  import net.sourceforge.jeuclid.layout.LayoutView;
33  
34  import org.w3c.dom.Node;
35  import org.w3c.dom.mathml.MathMLElement;
36  import org.w3c.dom.mathml.MathMLMultiScriptsElement;
37  import org.w3c.dom.mathml.MathMLNodeList;
38  
39  /**
40   * Prescripts and Tensor Indices.
41   * 
42   * @version $Revision: 783 $
43   */
44  
45  public final class Mmultiscripts extends AbstractScriptElement implements
46          MathMLMultiScriptsElement {
47      /**
48       * The XML element from this class.
49       */
50      public static final String ELEMENT = "mmultiscripts";
51  
52      private static final long serialVersionUID = 1L;
53  
54      // /**
55      // * Logger for this class
56      // */
57      // currently unused
58      // private static final Log LOGGER = LogFactory
59      // .getLog(MathMultiScripts.class);
60      private static final int STATE_POSTSUB = 0;
61  
62      private static final int STATE_POSTSUPER = 1;
63  
64      private static final int STATE_PRESUB = 2;
65  
66      private static final int STATE_PRESUPER = 3;
67  
68      private final List<JEuclidElement> postsubscripts = new ArrayList<JEuclidElement>();
69  
70      private final List<JEuclidElement> postsuperscripts = new ArrayList<JEuclidElement>();
71  
72      private final List<JEuclidElement> presubscripts = new ArrayList<JEuclidElement>();
73  
74      private final List<JEuclidElement> presuperscripts = new ArrayList<JEuclidElement>();
75  
76      private boolean inRewriteChildren;
77  
78      /**
79       * Default constructor.
80       */
81      public Mmultiscripts() {
82          super();
83          this.inRewriteChildren = false;
84      }
85  
86      /** {@inheritDoc} */
87      @Override
88      protected Node newNode() {
89          return new Mmultiscripts();
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      public void changeHook() {
95          super.changeHook();
96          if (!this.inRewriteChildren) {
97              this.parseChildren();
98          }
99      }
100 
101     private void parseChildren() {
102         this.presubscripts.clear();
103         this.presuperscripts.clear();
104         this.postsubscripts.clear();
105         this.postsuperscripts.clear();
106         final org.w3c.dom.NodeList childList = this.getChildNodes();
107         int state = Mmultiscripts.STATE_POSTSUB;
108         final int len = childList.getLength();
109         for (int i = 1; i < len; i++) {
110             final Node child = childList.item(i);
111             if (child instanceof Mprescripts) {
112                 state = Mmultiscripts.STATE_PRESUB;
113             } else if (child instanceof JEuclidElement) {
114                 final JEuclidElement jchild = (JEuclidElement) child;
115                 if (state == Mmultiscripts.STATE_POSTSUB) {
116                     this.postsubscripts.add(jchild);
117                     state = Mmultiscripts.STATE_POSTSUPER;
118                 } else if (state == Mmultiscripts.STATE_POSTSUPER) {
119                     this.postsuperscripts.add(jchild);
120                     state = Mmultiscripts.STATE_POSTSUB;
121                 } else if (state == Mmultiscripts.STATE_PRESUB) {
122                     this.presubscripts.add(jchild);
123                     state = Mmultiscripts.STATE_PRESUPER;
124                 } else {
125                     this.presuperscripts.add(jchild);
126                     state = Mmultiscripts.STATE_PRESUB;
127                 }
128             }
129         }
130         if (this.postsuperscripts.size() < this.postsubscripts.size()) {
131             this.postsuperscripts.add((JEuclidElement) this.getOwnerDocument()
132                     .createElement(None.ELEMENT));
133         }
134         if (this.presuperscripts.size() < this.presubscripts.size()) {
135             this.presuperscripts.add((JEuclidElement) this.getOwnerDocument()
136                     .createElement(None.ELEMENT));
137         }
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     protected void layoutStageInvariant(final LayoutView view,
143             final LayoutInfo info, final LayoutStage stage,
144             final LayoutContext context) {
145         final JEuclidElement base = this.getBase();
146         final LayoutInfo baseInfo = view.getInfo(base);
147         final LayoutContext now = this.applyLocalAttributesToContext(context);
148 
149         final String subScriptshift = this.getSubscriptshift();
150         final String superScriptshift = this.getSuperscriptshift();
151 
152         final ScriptSupport.ShiftInfo totalShiftInfo = this
153                 .calculateTotalShift(view, stage, baseInfo, now,
154                         subScriptshift, superScriptshift);
155         float posX = 0.0f;
156         final float subBaselineShift = totalShiftInfo.getSubShift();
157         final float superBaselineShift = totalShiftInfo.getSuperShift();
158         for (int i = 0; i < this.presubscripts.size(); i++) {
159             final LayoutInfo subInfo = view.getInfo(this.presubscripts.get(i));
160             final LayoutInfo superInfo = view.getInfo(this.presuperscripts
161                     .get(i));
162             subInfo.moveTo(posX, subBaselineShift, stage);
163             superInfo.moveTo(posX, -superBaselineShift, stage);
164             posX += Math
165                     .max(subInfo.getWidth(stage), superInfo.getWidth(stage));
166         }
167         baseInfo.moveTo(posX, 0.0f, stage);
168         posX += baseInfo.getWidth(stage);
169         for (int i = 0; i < this.postsubscripts.size(); i++) {
170             final LayoutInfo subInfo = view.getInfo(this.postsubscripts.get(i));
171             final LayoutInfo superInfo = view.getInfo(this.postsuperscripts
172                     .get(i));
173             subInfo.moveTo(posX, subBaselineShift, stage);
174             superInfo.moveTo(posX, -superBaselineShift, stage);
175             posX += Math
176                     .max(subInfo.getWidth(stage), superInfo.getWidth(stage));
177         }
178 
179         final Dimension2D noborder = new Dimension2DImpl(0.0f, 0.0f);
180         ElementListSupport.fillInfoFromChildren(view, info, this, stage,
181                 noborder, noborder);
182     }
183 
184     private ScriptSupport.ShiftInfo calculateTotalShift(final LayoutView view,
185             final LayoutStage stage, final LayoutInfo baseInfo,
186             final LayoutContext now, final String subScriptshift,
187             final String superScriptshift) {
188         final ScriptSupport.ShiftInfo totalShiftInfo = new ScriptSupport.ShiftInfo(
189                 0.0f, 0.0f);
190 
191         for (int i = 0; i < this.presubscripts.size(); i++) {
192             final LayoutInfo subInfo = view.getInfo(this.presubscripts.get(i));
193             final LayoutInfo superInfo = view.getInfo(this.presuperscripts
194                     .get(i));
195             final ScriptSupport.ShiftInfo shiftInfo = ScriptSupport
196                     .calculateScriptShfits(stage, now, subScriptshift,
197                             superScriptshift, baseInfo, subInfo, superInfo);
198             totalShiftInfo.max(shiftInfo);
199         }
200         for (int i = 0; i < this.postsubscripts.size(); i++) {
201             final LayoutInfo subInfo = view.getInfo(this.postsubscripts.get(i));
202             final LayoutInfo superInfo = view.getInfo(this.postsuperscripts
203                     .get(i));
204             final ScriptSupport.ShiftInfo shiftInfo = ScriptSupport
205                     .calculateScriptShfits(stage, now, subScriptshift,
206                             superScriptshift, baseInfo, subInfo, superInfo);
207             totalShiftInfo.max(shiftInfo);
208         }
209         return totalShiftInfo;
210     }
211 
212     /** {@inheritDoc} */
213     @Override
214     public boolean hasChildPrescripts(final JEuclidElement child) {
215         return child.isSameNode(this.getBase())
216                 && this.getNumprescriptcolumns() > 0;
217     }
218 
219     /** {@inheritDoc} */
220     @Override
221     public boolean hasChildPostscripts(final JEuclidElement child,
222             final LayoutContext context) {
223         return child.isSameNode(this.getBase())
224                 && this.getNumscriptcolumns() > 0;
225     }
226 
227     /** {@inheritDoc} */
228     public JEuclidElement getBase() {
229         final JEuclidElement base = this.getMathElement(0);
230         if (base == null) {
231             return (JEuclidElement) this.getOwnerDocument().createElement(
232                     None.ELEMENT);
233         } else {
234             return base;
235         }
236     }
237 
238     /** {@inheritDoc} */
239     public void setBase(final MathMLElement base) {
240         this.setMathElement(0, base);
241     }
242 
243     /** {@inheritDoc} */
244     public int getNumprescriptcolumns() {
245         return this.presubscripts.size();
246     }
247 
248     /** {@inheritDoc} */
249     public int getNumscriptcolumns() {
250         return this.postsubscripts.size();
251     }
252 
253     /** {@inheritDoc} */
254     public MathMLElement getPreSubScript(final int colIndex) {
255         return this.presubscripts.get(colIndex - 1);
256     }
257 
258     /** {@inheritDoc} */
259     public MathMLElement getPreSuperScript(final int colIndex) {
260         return this.presuperscripts.get(colIndex - 1);
261     }
262 
263     /** {@inheritDoc} */
264     public MathMLNodeList getPrescripts() {
265         final int presubsize = this.presubscripts.size();
266         final List<Node> list = new ArrayList<Node>(2 * presubsize);
267         for (int i = 0; i < presubsize; i++) {
268             list.add(this.presubscripts.get(i));
269             list.add(this.presuperscripts.get(i));
270         }
271         return new MathMLNodeListImpl(list);
272     }
273 
274     /** {@inheritDoc} */
275     public MathMLNodeList getScripts() {
276         final int postsubsize = this.postsubscripts.size();
277         final List<Node> list = new ArrayList<Node>(2 * postsubsize);
278         for (int i = 0; i < postsubsize; i++) {
279             list.add(this.postsubscripts.get(i));
280             list.add(this.postsuperscripts.get(i));
281         }
282         return new MathMLNodeListImpl(list);
283     }
284 
285     /** {@inheritDoc} */
286     public MathMLElement getSubScript(final int colIndex) {
287         if ((colIndex < 1) || (colIndex > this.postsubscripts.size())) {
288             return null;
289         }
290         return this.postsubscripts.get(colIndex - 1);
291     }
292 
293     /** {@inheritDoc} */
294     public MathMLElement getSuperScript(final int colIndex) {
295         if ((colIndex < 1) || (colIndex > this.postsuperscripts.size())) {
296             return null;
297         }
298         return this.postsuperscripts.get(colIndex - 1);
299     }
300 
301     private void rewriteChildren() {
302         this.inRewriteChildren = true;
303 
304         final org.w3c.dom.NodeList childList = this.getChildNodes();
305         final int len = childList.getLength();
306         // start at 1 since 0 is the base!
307         for (int i = 1; i < len; i++) {
308             this.removeChild(childList.item(1));
309         }
310         if (len == 0) {
311             this.addMathElement((JEuclidElement) this.getOwnerDocument()
312                     .createElement(None.ELEMENT));
313         }
314         for (int i = 0; i < this.postsubscripts.size(); i++) {
315             this.addMathElement(this.postsubscripts.get(i));
316             this.addMathElement(this.postsuperscripts.get(i));
317         }
318         final int numprescripts = this.presubscripts.size();
319         if (numprescripts > 0) {
320             this.addMathElement((Mprescripts) this.getOwnerDocument()
321                     .createElement(Mprescripts.ELEMENT));
322             for (int i = 0; i < numprescripts; i++) {
323                 this.addMathElement(this.presubscripts.get(i));
324                 this.addMathElement(this.presuperscripts.get(i));
325             }
326         }
327         this.inRewriteChildren = false;
328     }
329 
330     /** {@inheritDoc} */
331     public MathMLElement insertPreSubScriptBefore(final int colIndex,
332             final MathMLElement newScript) {
333         final int targetIndex;
334         if (colIndex == 0) {
335             targetIndex = this.presubscripts.size();
336         } else {
337             targetIndex = colIndex - 1;
338         }
339         this.presubscripts.add(targetIndex, (JEuclidElement) newScript);
340         this.presuperscripts.add(targetIndex, (JEuclidElement) this
341                 .getOwnerDocument().createElement(None.ELEMENT));
342         this.rewriteChildren();
343         return newScript;
344     }
345 
346     /** {@inheritDoc} */
347     public MathMLElement insertPreSuperScriptBefore(final int colIndex,
348             final MathMLElement newScript) {
349         final int targetIndex;
350         if (colIndex == 0) {
351             targetIndex = this.presubscripts.size();
352         } else {
353             targetIndex = colIndex - 1;
354         }
355         this.presubscripts.add(targetIndex, (JEuclidElement) this
356                 .getOwnerDocument().createElement(None.ELEMENT));
357         this.presuperscripts.add(targetIndex, (JEuclidElement) newScript);
358         this.rewriteChildren();
359         return newScript;
360     }
361 
362     /** {@inheritDoc} */
363     public MathMLElement insertSubScriptBefore(final int colIndex,
364             final MathMLElement newScript) {
365         final int targetIndex;
366         if (colIndex == 0) {
367             targetIndex = this.postsubscripts.size();
368         } else {
369             targetIndex = colIndex - 1;
370         }
371         this.postsubscripts.add(targetIndex, (JEuclidElement) newScript);
372         this.postsuperscripts.add(targetIndex, (JEuclidElement) this
373                 .getOwnerDocument().createElement(None.ELEMENT));
374         this.rewriteChildren();
375         return newScript;
376     }
377 
378     /** {@inheritDoc} */
379     public MathMLElement insertSuperScriptBefore(final int colIndex,
380             final MathMLElement newScript) {
381         final int targetIndex;
382         if (colIndex == 0) {
383             targetIndex = this.postsubscripts.size();
384         } else {
385             targetIndex = colIndex - 1;
386         }
387         this.postsubscripts.add(targetIndex, (JEuclidElement) this
388                 .getOwnerDocument().createElement(None.ELEMENT));
389         this.postsuperscripts.add(targetIndex, (JEuclidElement) newScript);
390         this.rewriteChildren();
391         return newScript;
392     }
393 
394     /** {@inheritDoc} */
395     public MathMLElement setPreSubScriptAt(final int colIndex,
396             final MathMLElement newScript) {
397         final int targetCol = colIndex - 1;
398         if (targetCol == this.presubscripts.size()) {
399             return this.insertPreSubScriptBefore(0, newScript);
400         } else {
401             this.presubscripts.set(targetCol, (JEuclidElement) newScript);
402             this.rewriteChildren();
403             return newScript;
404         }
405     }
406 
407     /** {@inheritDoc} */
408     public MathMLElement setPreSuperScriptAt(final int colIndex,
409             final MathMLElement newScript) {
410         final int targetCol = colIndex - 1;
411         if (targetCol == this.presuperscripts.size()) {
412             return this.insertPreSuperScriptBefore(0, newScript);
413         } else {
414             this.presuperscripts.set(targetCol, (JEuclidElement) newScript);
415             this.rewriteChildren();
416             return newScript;
417         }
418     }
419 
420     /** {@inheritDoc} */
421     public MathMLElement setSubScriptAt(final int colIndex,
422             final MathMLElement newScript) {
423         final int targetCol = colIndex - 1;
424         if (targetCol == this.postsubscripts.size()) {
425             return this.insertSubScriptBefore(0, newScript);
426         } else {
427             this.postsubscripts.set(targetCol, (JEuclidElement) newScript);
428             this.rewriteChildren();
429             return newScript;
430         }
431     }
432 
433     /** {@inheritDoc} */
434     public MathMLElement setSuperScriptAt(final int colIndex,
435             final MathMLElement newScript) {
436         final int targetCol = colIndex - 1;
437         if (targetCol == this.postsuperscripts.size()) {
438             return this.insertSuperScriptBefore(0, newScript);
439         } else {
440             this.postsuperscripts.set(targetCol, (JEuclidElement) newScript);
441             this.rewriteChildren();
442             return newScript;
443         }
444     }
445 
446 }