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