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 }