001 /*
002 * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 /* $Id: Menclose.java,v 353eda94b8dd 2009/10/14 10:04:09 max $ */
018
019 package net.sourceforge.jeuclid.elements.presentation.general;
020
021 import java.awt.Color;
022 import java.awt.geom.Dimension2D;
023 import java.lang.reflect.Constructor;
024 import java.lang.reflect.InvocationTargetException;
025 import java.util.Collections;
026 import java.util.HashMap;
027 import java.util.List;
028 import java.util.Locale;
029 import java.util.Map;
030 import java.util.Stack;
031
032 import net.sourceforge.jeuclid.LayoutContext;
033 import net.sourceforge.jeuclid.context.Parameter;
034 import net.sourceforge.jeuclid.elements.AbstractElementWithDelegates;
035 import net.sourceforge.jeuclid.elements.JEuclidElement;
036 import net.sourceforge.jeuclid.elements.presentation.AbstractContainer;
037 import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
038 import net.sourceforge.jeuclid.elements.support.ElementListSupport;
039 import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
040 import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
041 import net.sourceforge.jeuclid.layout.GraphicsObject;
042 import net.sourceforge.jeuclid.layout.LayoutInfo;
043 import net.sourceforge.jeuclid.layout.LayoutStage;
044 import net.sourceforge.jeuclid.layout.LayoutView;
045 import net.sourceforge.jeuclid.layout.LayoutableNode;
046 import net.sourceforge.jeuclid.layout.LineObject;
047
048 import org.apache.batik.dom.AbstractDocument;
049 import org.apache.commons.logging.Log;
050 import org.apache.commons.logging.LogFactory;
051 import org.w3c.dom.Node;
052 import org.w3c.dom.mathml.MathMLEncloseElement;
053
054 /**
055 * Class for supporting "menclose" elements.
056 *
057 * @version $Revision: 353eda94b8dd $
058 */
059 public final class Menclose extends AbstractElementWithDelegates implements
060 MathMLEncloseElement {
061
062 /**
063 * The XML element from this class.
064 */
065 public static final String ELEMENT = "menclose";
066
067 /** The notation attribute. */
068 public static final String ATTR_NOTATION = "notation";
069
070 private static final String LONGDIV = "longdiv";
071
072 /**
073 * base class for all row-like notations.
074 *
075 */
076 private abstract static class AbstractRowLikeNotation extends
077 AbstractContainer {
078
079 /**
080 * No data, so SUID is does not matter.
081 */
082 private static final long serialVersionUID = 1L;
083
084 /**
085 * Default constructor. Sets MathML Namespace.
086 *
087 * @param qname
088 * Qualified name.
089 * @param odoc
090 * Owner Document.
091 */
092 public AbstractRowLikeNotation(final String qname,
093 final AbstractDocument odoc) {
094 super(qname, odoc);
095 }
096
097 /** {@inheritDoc} */
098 @Override
099 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 }