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: Menclose.java,v 353eda94b8dd 2009/10/14 10:04:09 max $ */
18  
19  package net.sourceforge.jeuclid.elements.presentation.general;
20  
21  import java.awt.Color;
22  import java.awt.geom.Dimension2D;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.InvocationTargetException;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Locale;
29  import java.util.Map;
30  import java.util.Stack;
31  
32  import net.sourceforge.jeuclid.LayoutContext;
33  import net.sourceforge.jeuclid.context.Parameter;
34  import net.sourceforge.jeuclid.elements.AbstractElementWithDelegates;
35  import net.sourceforge.jeuclid.elements.JEuclidElement;
36  import net.sourceforge.jeuclid.elements.presentation.AbstractContainer;
37  import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
38  import net.sourceforge.jeuclid.elements.support.ElementListSupport;
39  import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
40  import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
41  import net.sourceforge.jeuclid.layout.GraphicsObject;
42  import net.sourceforge.jeuclid.layout.LayoutInfo;
43  import net.sourceforge.jeuclid.layout.LayoutStage;
44  import net.sourceforge.jeuclid.layout.LayoutView;
45  import net.sourceforge.jeuclid.layout.LayoutableNode;
46  import net.sourceforge.jeuclid.layout.LineObject;
47  
48  import org.apache.batik.dom.AbstractDocument;
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  import org.w3c.dom.Node;
52  import org.w3c.dom.mathml.MathMLEncloseElement;
53  
54  /**
55   * Class for supporting "menclose" elements.
56   * 
57   * @version $Revision: 353eda94b8dd $
58   */
59  public final class Menclose extends AbstractElementWithDelegates implements
60          MathMLEncloseElement {
61  
62      /**
63       * The XML element from this class.
64       */
65      public static final String ELEMENT = "menclose";
66  
67      /** The notation attribute. */
68      public static final String ATTR_NOTATION = "notation";
69  
70      private static final String LONGDIV = "longdiv";
71  
72      /**
73       * base class for all row-like notations.
74       * 
75       */
76      private abstract static class AbstractRowLikeNotation extends
77              AbstractContainer {
78  
79          /**
80           * No data, so SUID is does not matter.
81           */
82          private static final long serialVersionUID = 1L;
83  
84          /**
85           * Default constructor. Sets MathML Namespace.
86           * 
87           * @param qname
88           *            Qualified name.
89           * @param odoc
90           *            Owner Document.
91           */
92          public AbstractRowLikeNotation(final String qname,
93                  final AbstractDocument odoc) {
94              super(qname, odoc);
95          }
96  
97          /** {@inheritDoc} */
98          @Override
99          protected void layoutStageInvariant(final LayoutView view,
100                 final LayoutInfo info, final LayoutStage stage,
101                 final LayoutContext context) {
102             final LayoutContext now = this
103                     .applyLocalAttributesToContext(context);
104             final Dimension2D borderLeftTop = this.getBorderLeftTop(now);
105             final float borderLeft = (float) borderLeftTop.getWidth();
106             if (borderLeft > 0.0f) {
107                 view.getInfo((LayoutableNode) this.getFirstChild()).moveTo(
108                         borderLeft, 0, stage);
109             }
110             ElementListSupport.fillInfoFromChildren(view, info, this, stage,
111                     borderLeftTop, this.getBorderRightBottom(now));
112             this.enclHook(info, stage, this.applyLocalAttributesToContext(now));
113         }
114 
115         /**
116          * Add left / top border to enclosed object.
117          * 
118          * @param now
119          *            current Layout Context
120          * @return Left and Top border
121          */
122         protected Dimension2D getBorderLeftTop(final LayoutContext now) {
123             return new Dimension2DImpl(0.0f, 0.0f);
124         }
125 
126         /**
127          * Add right / bottom border to enclosed object.
128          * 
129          * @param now
130          *            current Layout Context
131          * @return Right and Bottom border
132          */
133         protected Dimension2D getBorderRightBottom(final LayoutContext now) {
134             return new Dimension2DImpl(0.0f, 0.0f);
135         }
136 
137         /**
138          * Add Graphic objects to enclosed object.
139          * 
140          * @param info
141          *            Layout Info
142          * @param stage
143          *            current Layout Stage
144          * @param now
145          *            current Layout Context
146          */
147         protected abstract void enclHook(final LayoutInfo info,
148                 final LayoutStage stage, final LayoutContext now);
149     }
150 
151     /**
152      * Represents the US long-division notation, to support the notation
153      * "longdiv".
154      * 
155      */
156     private static final class Longdiv extends Menclose.AbstractRowLikeNotation {
157         private static final long serialVersionUID = 1L;
158 
159         /**
160          * Default constructor. Sets MathML Namespace.
161          * 
162          * @param qname
163          *            Qualified name.
164          * @param odoc
165          *            Owner Document.
166          */
167         public Longdiv(final String qname, final AbstractDocument odoc) {
168             super(qname, odoc);
169         }
170 
171         @Override
172         protected Node newNode() {
173             return new Longdiv(this.nodeName, this.ownerDocument);
174         }
175 
176         /** {@inheritDoc} */
177         @Override
178         protected Dimension2D getBorderLeftTop(final LayoutContext now) {
179             final float lineSpace = GraphicsSupport.lineWidth(now) * 2;
180             return new Dimension2DImpl(AttributesHelper.convertSizeToPt(
181                     "0.25em", now, null)
182                     + lineSpace, lineSpace);
183         }
184 
185         /** {@inheritDoc} */
186         @Override
187         protected void enclHook(final LayoutInfo info, final LayoutStage stage,
188                 final LayoutContext now) {
189             final List<GraphicsObject> graphicObjects = info
190                     .getGraphicObjects();
191             graphicObjects.clear();
192             final float lineWidth = GraphicsSupport.lineWidth(now);
193             final float top = info.getAscentHeight(stage) + lineWidth;
194             final Color color = (Color) now.getParameter(Parameter.MATHCOLOR);
195             graphicObjects.add(new LineObject(lineWidth, -top, lineWidth, info
196                     .getDescentHeight(stage), lineWidth, color));
197             graphicObjects.add(new LineObject(lineWidth, -top, info
198                     .getWidth(stage), -top, lineWidth, color));
199         }
200     }
201 
202     /**
203      * Up-Diagonal Strike.
204      * 
205      */
206     private static final class Updiagonalstrike extends
207             Menclose.AbstractRowLikeNotation {
208 
209         private static final long serialVersionUID = 1L;
210 
211         /**
212          * Default constructor. Sets MathML Namespace.
213          * 
214          * @param qname
215          *            Qualified name.
216          * @param odoc
217          *            Owner Document.
218          */
219         public Updiagonalstrike(final String qname, final AbstractDocument odoc) {
220             super(qname, odoc);
221         }
222 
223         @Override
224         protected Node newNode() {
225             return new Updiagonalstrike(this.nodeName, this.ownerDocument);
226         }
227 
228         /** {@inheritDoc} */
229         @Override
230         protected void enclHook(final LayoutInfo info, final LayoutStage stage,
231                 final LayoutContext now) {
232 
233             final Color color = (Color) now.getParameter(Parameter.MATHCOLOR);
234             final float lineWidth = GraphicsSupport.lineWidth(now);
235             info.setGraphicsObject(new LineObject(0, info
236                     .getDescentHeight(stage), info.getWidth(stage), -info
237                     .getAscentHeight(stage), lineWidth, color));
238         }
239 
240     }
241 
242     /**
243      * Down-Diagonal Strike.
244      * 
245      */
246     private static final class Downdiagonalstrike extends
247             Menclose.AbstractRowLikeNotation {
248 
249         private static final long serialVersionUID = 1L;
250 
251         /**
252          * Default constructor. Sets MathML Namespace.
253          * 
254          * @param qname
255          *            Qualified name.
256          * @param odoc
257          *            Owner Document.
258          */
259         public Downdiagonalstrike(final String qname,
260                 final AbstractDocument odoc) {
261             super(qname, odoc);
262         }
263 
264         @Override
265         protected Node newNode() {
266             return new Downdiagonalstrike(this.nodeName, this.ownerDocument);
267         }
268 
269         /** {@inheritDoc} */
270         @Override
271         protected void enclHook(final LayoutInfo info, final LayoutStage stage,
272                 final LayoutContext now) {
273 
274             final Color color = (Color) now.getParameter(Parameter.MATHCOLOR);
275             final float lineWidth = GraphicsSupport.lineWidth(now);
276             info.setGraphicsObject(new LineObject(0, -info
277                     .getAscentHeight(stage), info.getWidth(stage), info
278                     .getDescentHeight(stage), lineWidth, color));
279         }
280     }
281 
282     private static final class Actuarial extends
283             Menclose.AbstractRowLikeNotation {
284         private static final long serialVersionUID = 1L;
285 
286         /**
287          * Default constructor. Sets MathML Namespace.
288          * 
289          * @param qname
290          *            Qualified name.
291          * @param odoc
292          *            Owner Document.
293          */
294         public Actuarial(final String qname, final AbstractDocument odoc) {
295             super(qname, odoc);
296         }
297 
298         @Override
299         protected Node newNode() {
300             return new Actuarial(this.nodeName, this.ownerDocument);
301         }
302 
303         /** {@inheritDoc} */
304         @Override
305         protected Dimension2D getBorderLeftTop(final LayoutContext now) {
306             final float lineSpace = GraphicsSupport.lineWidth(now) * 2;
307             return new Dimension2DImpl(0.0f, lineSpace);
308         }
309 
310         /** {@inheritDoc} */
311         @Override
312         protected Dimension2D getBorderRightBottom(final LayoutContext now) {
313             final float lineSpace = GraphicsSupport.lineWidth(now) * 2;
314             return new Dimension2DImpl(lineSpace, 0.0f);
315         }
316 
317         /** {@inheritDoc} */
318         @Override
319         protected void enclHook(final LayoutInfo info, final LayoutStage stage,
320                 final LayoutContext now) {
321             final List<GraphicsObject> graphicObjects = info
322                     .getGraphicObjects();
323             graphicObjects.clear();
324             final float lineWidth = GraphicsSupport.lineWidth(now);
325             final float top = info.getAscentHeight(stage) + lineWidth;
326             final Color color = (Color) now.getParameter(Parameter.MATHCOLOR);
327             graphicObjects.add(new LineObject(0, -top, info.getWidth(stage)
328                     - lineWidth, -top, lineWidth, color));
329             graphicObjects.add(new LineObject(info.getWidth(stage) - lineWidth,
330                     -top, info.getWidth(stage) - lineWidth, info
331                             .getDescentHeight(stage), lineWidth, color));
332         }
333 
334     }
335 
336     /**
337      * Logger for this class
338      */
339     private static final Log LOGGER = LogFactory.getLog(Menclose.class);
340 
341     private static final Map<String, Constructor<?>> IMPL_CLASSES = new HashMap<String, Constructor<?>>();;
342 
343     private static final long serialVersionUID = 1L;
344 
345     /**
346      * Default constructor. Sets MathML Namespace.
347      * 
348      * @param qname
349      *            Qualified name.
350      * @param odoc
351      *            Owner Document.
352      */
353     public Menclose(final String qname, final AbstractDocument odoc) {
354         super(qname, odoc);
355 
356         this.setDefaultMathAttribute(Menclose.ATTR_NOTATION, Menclose.LONGDIV);
357     }
358 
359     /** {@inheritDoc} */
360     @Override
361     protected Node newNode() {
362         return new Menclose(this.nodeName, this.ownerDocument);
363     }
364 
365     /**
366      * @return notation of menclose element
367      */
368     public String getNotation() {
369         return this.getMathAttribute(Menclose.ATTR_NOTATION);
370     }
371 
372     /**
373      * Sets notation for menclose element.
374      * 
375      * @param notation
376      *            Notation
377      */
378     public void setNotation(final String notation) {
379         this.setAttribute(Menclose.ATTR_NOTATION, notation);
380     }
381 
382     /** {@inheritDoc} */
383     @Override
384     protected List<LayoutableNode> createDelegates() {
385         final Stack<Constructor<?>> notationImpls = this.parseNotations();
386         JEuclidElement lastChild = this.ensureSingleChild();
387         lastChild = this.createStackOfDelegates(notationImpls, lastChild);
388         return Collections.singletonList((LayoutableNode) lastChild);
389     }
390 
391     private JEuclidElement createStackOfDelegates(
392             final Stack<Constructor<?>> notationImpls,
393             final JEuclidElement oldChild) {
394         JEuclidElement lastChild = oldChild;
395         while (!notationImpls.isEmpty()) {
396             final Constructor<?> con = notationImpls.pop();
397             try {
398                 final JEuclidElement element = (JEuclidElement) con
399                         .newInstance("saklsdiwet:menclosechild",
400                                 this.ownerDocument);
401                 element.appendChild(lastChild);
402                 lastChild = element;
403             } catch (final InstantiationException e) {
404                 Menclose.LOGGER.warn(e);
405             } catch (final IllegalAccessException e) {
406                 Menclose.LOGGER.warn(e);
407             } catch (final InvocationTargetException e) {
408                 Menclose.LOGGER.warn(e);
409             }
410         }
411         return lastChild;
412     }
413 
414     /**
415      * This is just to make sure that there is at least one delegate, and that
416      * each of the standard delegates has exactly one child.
417      * 
418      * @return a single JEuclidElement.
419      */
420     private JEuclidElement ensureSingleChild() {
421         final JEuclidElement lastChild;
422         if (this.getMathElementCount() == 1) {
423             lastChild = this.getMathElement(0);
424         } else {
425             lastChild = (JEuclidElement) this.ownerDocument
426                     .createElement(Mrow.ELEMENT);
427             for (final Node child : ElementListSupport
428                     .createListOfChildren(this)) {
429                 lastChild.appendChild(child);
430             }
431         }
432         return lastChild;
433     }
434 
435     private Stack<Constructor<?>> parseNotations() {
436         final String[] notations = this.getNotation().split(" ");
437         final Stack<Constructor<?>> notationImpls = new Stack<Constructor<?>>();
438         for (final String curNotation : notations) {
439             final Constructor<?> con = Menclose.IMPL_CLASSES.get(curNotation
440                     .toLowerCase(Locale.ENGLISH));
441             if (con == null) {
442                 if (curNotation.length() > 0) {
443                     Menclose.LOGGER.info("Unsupported notation for menclose: "
444                             + curNotation);
445                 }
446             } else {
447                 notationImpls.push(con);
448             }
449         }
450         return notationImpls;
451     }
452 
453     static {
454         try {
455             Menclose.IMPL_CLASSES.put("radical", Msqrt.class.getConstructor(
456                     String.class, AbstractDocument.class));
457             Menclose.IMPL_CLASSES.put(Menclose.LONGDIV, Menclose.Longdiv.class
458                     .getConstructor(String.class, AbstractDocument.class));
459             Menclose.IMPL_CLASSES.put("updiagonalstrike",
460                     Menclose.Updiagonalstrike.class.getConstructor(
461                             String.class, AbstractDocument.class));
462             Menclose.IMPL_CLASSES.put("downdiagonalstrike",
463                     Menclose.Downdiagonalstrike.class.getConstructor(
464                             String.class, AbstractDocument.class));
465             Menclose.IMPL_CLASSES.put("actuarial", Menclose.Actuarial.class
466                     .getConstructor(String.class, AbstractDocument.class));
467         } catch (final NoSuchMethodException e) {
468             Menclose.LOGGER.fatal(e);
469         }
470     }
471 }