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: Mmultiscripts.java,v 9af8f7182adc 2009/09/04 14:21:09 max $ */ 018 019 package net.sourceforge.jeuclid.elements.presentation.script; 020 021 import java.awt.geom.Dimension2D; 022 import java.util.ArrayList; 023 import java.util.List; 024 025 import net.sourceforge.jeuclid.LayoutContext; 026 import net.sourceforge.jeuclid.elements.JEuclidElement; 027 import net.sourceforge.jeuclid.elements.support.Dimension2DImpl; 028 import net.sourceforge.jeuclid.elements.support.ElementListSupport; 029 import net.sourceforge.jeuclid.elements.support.MathMLNodeListImpl; 030 import net.sourceforge.jeuclid.layout.LayoutInfo; 031 import net.sourceforge.jeuclid.layout.LayoutStage; 032 import net.sourceforge.jeuclid.layout.LayoutView; 033 034 import org.apache.batik.dom.AbstractDocument; 035 import org.w3c.dom.Node; 036 import org.w3c.dom.mathml.MathMLElement; 037 import org.w3c.dom.mathml.MathMLMultiScriptsElement; 038 import org.w3c.dom.mathml.MathMLNodeList; 039 040 /** 041 * Prescripts and Tensor Indices. 042 * 043 * @version $Revision: 9af8f7182adc $ 044 */ 045 046 public final class Mmultiscripts extends AbstractScriptElement implements 047 MathMLMultiScriptsElement { 048 /** 049 * The XML element from this class. 050 */ 051 public static final String ELEMENT = "mmultiscripts"; 052 053 private static final long serialVersionUID = 1L; 054 055 // /** 056 // * Logger for this class 057 // */ 058 // currently unused 059 // private static final Log LOGGER = LogFactory 060 // .getLog(MathMultiScripts.class); 061 private static final int STATE_POSTSUB = 0; 062 063 private static final int STATE_POSTSUPER = 1; 064 065 private static final int STATE_PRESUB = 2; 066 067 private static final int STATE_PRESUPER = 3; 068 069 private final List<JEuclidElement> postsubscripts = new ArrayList<JEuclidElement>(); 070 071 private final List<JEuclidElement> postsuperscripts = new ArrayList<JEuclidElement>(); 072 073 private final List<JEuclidElement> presubscripts = new ArrayList<JEuclidElement>(); 074 075 private final List<JEuclidElement> presuperscripts = new ArrayList<JEuclidElement>(); 076 077 private boolean inRewriteChildren; 078 079 /** 080 * Default constructor. Sets MathML Namespace. 081 * 082 * @param qname 083 * Qualified name. 084 * @param odoc 085 * Owner Document. 086 */ 087 public Mmultiscripts(final String qname, final AbstractDocument odoc) { 088 super(qname, odoc); 089 090 this.inRewriteChildren = false; 091 } 092 093 /** {@inheritDoc} */ 094 @Override 095 protected Node newNode() { 096 return new Mmultiscripts(this.nodeName, this.ownerDocument); 097 } 098 099 /** {@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 }