Coverage Report - net.sourceforge.jeuclid.swing.JMathComponent
 
Classes in this File Line Coverage Branch Coverage Complexity
JMathComponent
19%
60/307
15%
6/40
1,31
 
 1  
 /*
 2  
  * Copyright 2002 - 2008 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: JMathComponent.java,v f1695c1926a6 2010/08/09 21:09:55 max $ */
 18  
 
 19  
 package net.sourceforge.jeuclid.swing;
 20  
 
 21  
 import java.awt.Color;
 22  
 import java.awt.Font;
 23  
 import java.io.IOException;
 24  
 import java.util.Arrays;
 25  
 import java.util.Collections;
 26  
 import java.util.List;
 27  
 import java.util.Map;
 28  
 
 29  
 import javax.swing.JComponent;
 30  
 import javax.swing.SwingConstants;
 31  
 import javax.swing.UIManager;
 32  
 import javax.xml.parsers.ParserConfigurationException;
 33  
 
 34  
 import net.sourceforge.jeuclid.DOMBuilder;
 35  
 import net.sourceforge.jeuclid.MathMLParserSupport;
 36  
 import net.sourceforge.jeuclid.MathMLSerializer;
 37  
 import net.sourceforge.jeuclid.MutableLayoutContext;
 38  
 import net.sourceforge.jeuclid.context.LayoutContextImpl;
 39  
 import net.sourceforge.jeuclid.context.Parameter;
 40  
 import net.sourceforge.jeuclid.elements.generic.DocumentElement;
 41  
 import net.sourceforge.jeuclid.elements.support.ClassLoaderSupport;
 42  
 
 43  
 import org.apache.commons.logging.Log;
 44  
 import org.apache.commons.logging.LogFactory;
 45  
 import org.w3c.dom.Document;
 46  
 import org.w3c.dom.Node;
 47  
 import org.xml.sax.SAXException;
 48  
 
 49  
 /**
 50  
  * Displays MathML content in a Swing Component.
 51  
  * <p>
 52  
  * There are two properties which expose the actual content, accessible though
 53  
  * {@link #getDocument()} / {@link #setDocument(org.w3c.dom.Node)} for content
 54  
  * already available as a DOM model, and {@link #getContent()} and
 55  
  * {@link #setContent(String)} for content available as a String.
 56  
  * <p>
 57  
  * This class exposes most of the rendering parameters as standard bean
 58  
  * attributes. If you need to set additional attributes, you may use the
 59  
  * {@link #setParameter(Parameter, Object)} function.
 60  
  * <p>
 61  
  * Please use only the attributes exposed through the attached
 62  
  * {@link JMathComponentBeanInfo} class. Additional attributes, such as
 63  
  * {@link #getFont()} and {@link #setFont(Font)} are provided for Swing
 64  
  * compatibility, but they may not work exactly as expected.
 65  
  * 
 66  
  * @see net.sourceforge.jeuclid.awt.MathComponent
 67  
  * @version $Revision: f1695c1926a6 $
 68  
  */
 69  
 public final class JMathComponent extends JComponent implements
 70  
         SwingConstants {
 71  
 
 72  
     private static final String FONT_SEPARATOR = ",";
 73  
 
 74  
     /**
 75  0
      * Logger for this class
 76  0
      */
 77  4
     private static final Log LOGGER = LogFactory.getLog(JMathComponent.class);
 78  
 
 79  
     /** */
 80  
     private static final long serialVersionUID = 1L;
 81  
 
 82  
     private static String uiClassId;
 83  
 
 84  
     private static Class<?> mathComponentUIClass;
 85  
 
 86  
     private Node document;
 87  0
 
 88  4
     private int horizontalAlignment = SwingConstants.CENTER;
 89  0
 
 90  4
     private final MutableLayoutContext parameters = new LayoutContextImpl(
 91  
             LayoutContextImpl.getDefaultLayoutContext());
 92  0
 
 93  4
     private int verticalAlignment = SwingConstants.CENTER;
 94  
 
 95  
     /**
 96  
      * cursor listener instance.
 97  
      */
 98  
     private final CursorListener cursorListener;
 99  
 
 100  
     /**
 101  
      * Default constructor.
 102  
      */
 103  0
     public JMathComponent() {
 104  4
         this(null);
 105  4
     }
 106  
 
 107  
     /**
 108  
      * Default constructor with cursor listener.
 109  
      * 
 110  
      * @param listener
 111  
      *            cursor listener instance
 112  0
      */
 113  4
     public JMathComponent(final CursorListener listener) {
 114  4
         this.cursorListener = listener;
 115  0
 
 116  4
         final JMathComponentMouseListener mouseListener = new JMathComponentMouseListener(
 117  0
                 this);
 118  4
         this.addMouseListener(mouseListener);
 119  0
 
 120  4
         this.updateUI();
 121  4
         this.fontCompat();
 122  4
         this.setDocument(new DocumentElement());
 123  4
     }
 124  
 
 125  
     /**
 126  
      * gets cursor listener instance.
 127  
      * 
 128  
      * @return cursor listener instance
 129  
      */
 130  0
     public CursorListener getCursorListener() {
 131  0
         return this.cursorListener;
 132  
     }
 133  
 
 134  
     /**
 135  
      * Provide compatibility for standard get/setFont() operations.
 136  
      */
 137  0
     private void fontCompat() {
 138  4
         final String fontName = this.getFontsSerif().split(
 139  0
                 JMathComponent.FONT_SEPARATOR)[0];
 140  4
         final float fontSize = this.getFontSize();
 141  4
         super.setFont(new Font(fontName, 0, (int) fontSize));
 142  4
     }
 143  
 
 144  
     /**
 145  
      * Tries to return the content as a String.
 146  
      * <p>
 147  
      * This transforms the internal DOM tree back into a string, which may is
 148  
      * not guaranteed to be the literally same as the original content.
 149  
      * However, it will represent the same XML document.
 150  0
      * 
 151  
      * @return the content string.
 152  
      */
 153  0
     public String getContent() {
 154  0
         return MathMLSerializer.serializeDocument(this.getDocument(), false,
 155  
                 false);
 156  
     }
 157  
 
 158  
     /**
 159  
      * @return the document
 160  
      */
 161  0
     public Node getDocument() {
 162  12
         return this.document;
 163  0
     }
 164  
 
 165  0
     private static String join(final List<String> list) {
 166  4
         boolean first = true;
 167  4
         final StringBuilder b = new StringBuilder();
 168  4
         for (final String s : list) {
 169  44
             if (first) {
 170  4
                 first = false;
 171  0
             } else {
 172  40
                 b.append(JMathComponent.FONT_SEPARATOR);
 173  0
             }
 174  44
             b.append(s);
 175  0
         }
 176  4
         return b.toString();
 177  0
     }
 178  0
 
 179  0
     /**
 180  
      * Font list for Doublestruck. Please see
 181  0
      * {@link Parameter#FontsDoublestruck} for an explanation of this
 182  
      * parameter.
 183  0
      * 
 184  
      * @return The list for Doublestruck.
 185  0
      * @see Parameter#FontsDoublestruck
 186  
      */
 187  
     @SuppressWarnings("unchecked")
 188  0
     public String getFontsDoublestruck() {
 189  0
         return JMathComponent.join((List<String>) this.parameters
 190  
                 .getParameter(Parameter.FONTS_DOUBLESTRUCK));
 191  
     }
 192  
 
 193  
     /**
 194  
      * Font list for Fraktur. Please see {@link Parameter#FontsFraktur} for an
 195  
      * explanation of this parameter.
 196  
      * 
 197  0
      * @return The list for Fraktur.
 198  
      * @see Parameter#FontsFraktur
 199  
      */
 200  
     @SuppressWarnings("unchecked")
 201  0
     public String getFontsFraktur() {
 202  0
         return JMathComponent.join((List<String>) this.parameters
 203  
                 .getParameter(Parameter.FONTS_FRAKTUR));
 204  
     }
 205  
 
 206  
     /**
 207  
      * @return the fontSize
 208  
      */
 209  0
     public float getFontSize() {
 210  4
         return (Float) this.parameters.getParameter(Parameter.MATHSIZE);
 211  
     }
 212  
 
 213  
     /**
 214  
      * Font list for Monospaced. Please see {@link Parameter#FontsMonospaced}
 215  
      * for an explanation of this parameter.
 216  
      * 
 217  
      * @return The list for monospaced.
 218  0
      * @see Parameter#FontsMonospaced
 219  
      */
 220  
     @SuppressWarnings("unchecked")
 221  0
     public String getFontsMonospaced() {
 222  0
         return JMathComponent.join((List<String>) this.parameters
 223  
                 .getParameter(Parameter.FONTS_MONOSPACED));
 224  
     }
 225  
 
 226  
     /**
 227  
      * Font list for Sans-Serif. Please see {@link Parameter#FontsSanserif}
 228  
      * for an explanation of this parameter.
 229  
      * 
 230  0
      * @return The list for sansserif.
 231  
      * @see Parameter#FontsSanserif
 232  
      */
 233  
     @SuppressWarnings("unchecked")
 234  0
     public String getFontsSanserif() {
 235  0
         return JMathComponent.join((List<String>) this.parameters
 236  
                 .getParameter(Parameter.FONTS_SANSSERIF));
 237  
     }
 238  
 
 239  
     /**
 240  
      * Font list for Script. Please see {@link Parameter#FontsScript} for an
 241  
      * explanation of this parameter.
 242  
      * 
 243  0
      * @return The list for Script.
 244  
      * @see Parameter#FontsScript
 245  
      */
 246  
     @SuppressWarnings("unchecked")
 247  0
     public String getFontsScript() {
 248  0
         return JMathComponent.join((List<String>) this.parameters
 249  
                 .getParameter(Parameter.FONTS_SCRIPT));
 250  
     }
 251  
 
 252  
     /**
 253  
      * Font list for Serif (the default MathML font). Please see
 254  
      * {@link Parameter#FontsSerif} for an explanation of this parameter.
 255  
      * 
 256  0
      * @return The list for serif.
 257  
      * @see Parameter#FontsSerif
 258  
      */
 259  
     @SuppressWarnings("unchecked")
 260  0
     public String getFontsSerif() {
 261  4
         return JMathComponent.join((List<String>) this.parameters
 262  
                 .getParameter(Parameter.FONTS_SERIF));
 263  
     }
 264  
 
 265  
     /** {@inheritDoc} */
 266  
     @Override
 267  0
     public Color getForeground() {
 268  0
         return (Color) this.parameters.getParameter(Parameter.MATHCOLOR);
 269  0
     }
 270  
 
 271  
     /**
 272  
      * Horizontal alignment, as defined by
 273  
      * {@link javax.swing.JLabel#getHorizontalAlignment()}.
 274  
      * <p>
 275  
      * Supported are: {@link SwingConstants#LEADING},
 276  0
      * {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER},
 277  
      * {@link SwingConstants#TRAILING}, {@link SwingConstants#RIGHT}.
 278  
      * 
 279  
      * @return the horizontalAlignment
 280  
      * @see javax.swing.JLabel#getHorizontalAlignment()
 281  
      */
 282  0
     public int getHorizontalAlignment() {
 283  0
         return this.horizontalAlignment;
 284  
     }
 285  
 
 286  
     /**
 287  
      * @return the UI implementation.
 288  
      */
 289  
     public MathComponentUI getUI() {
 290  0
         return (MathComponentUI) this.ui;
 291  0
     }
 292  0
 
 293  
     /**
 294  
      * @return The default UI class
 295  
      */
 296  
     @Override
 297  
     public String getUIClassID() {
 298  4
         return JMathComponent.uiClassId;
 299  0
     }
 300  
 
 301  0
     /**
 302  
      * Vertical alignment, as defined by
 303  
      * {@link javax.swing.JLabel#getVerticalAlignment()}.
 304  
      * <p>
 305  
      * Supported are: {@link SwingConstants#TOP},
 306  
      * {@link SwingConstants#CENTER}, {@link SwingConstants#BOTTOM}.
 307  0
      * 
 308  0
      * @return the verticalAlignment
 309  
      * @see javax.swing.JLabel#getVerticalAlignment()
 310  
      */
 311  
     public int getVerticalAlignment() {
 312  0
         return this.verticalAlignment;
 313  
     }
 314  
 
 315  
     private void reval() {
 316  4
         this.repaint();
 317  4
         this.revalidate();
 318  4
     }
 319  
 
 320  
     /** {@inheritDoc} */
 321  0
     @Override
 322  
     public void setBackground(final Color c) {
 323  0
         super.setBackground(c);
 324  0
         this.reval();
 325  0
     }
 326  0
 
 327  0
     /**
 328  
      * Set the content from a String containing the MathML content.
 329  
      * 
 330  0
      * @param contentString
 331  
      *            the content to set.
 332  0
      */
 333  0
     public void setContent(final String contentString) {
 334  0
         try {
 335  4
             final Document stdDomNode = MathMLParserSupport.parseString(contentString); 
 336  4
             final DocumentElement jEuclidDom = DOMBuilder.getInstance().createJeuclidDom(stdDomNode,
 337  
                     true, true);
 338  4
             this.setDocument(jEuclidDom);
 339  0
         } catch (final SAXException e) {
 340  0
             throw new IllegalArgumentException(e);
 341  0
         } catch (final ParserConfigurationException e) {
 342  0
             throw new IllegalArgumentException(e);
 343  0
         } catch (final IOException e) {
 344  0
             throw new IllegalArgumentException(e);
 345  4
         }
 346  0
 
 347  4
     }
 348  0
 
 349  0
     /**
 350  0
      * Enables, or disables the debug mode.
 351  0
      * 
 352  0
      * @param dbg
 353  0
      *            Debug mode.
 354  
      */
 355  0
     public void setDebug(final boolean dbg) {
 356  0
         this.setParameter(Parameter.DEBUG, dbg);
 357  0
     }
 358  0
 
 359  
     /**
 360  0
      * @param doc
 361  
      *            the document to set
 362  0
      */
 363  0
     public void setDocument(final Node doc) {
 364  8
         final Node oldValue = this.document;
 365  8
         this.firePropertyChange("document", oldValue, doc);
 366  8
         this.document = doc;
 367  8
         if (doc != oldValue) {
 368  8
             this.revalidate();
 369  8
             this.repaint();
 370  0
         }
 371  8
     }
 372  0
 
 373  0
     /**
 374  0
      * Font emulator for standard component behavior.
 375  0
      * <p>
 376  0
      * Emulates the standard setFont function by setting the font Size and
 377  0
      * adding the font to the front of the serif font list.
 378  0
      * <p>
 379  
      * Please use the separate setters if possible.
 380  0
      * 
 381  
      * @param f
 382  0
      *            font to set.
 383  0
      * @see #setFontSize(float)
 384  0
      * @see #setFontsSerif(String)
 385  0
      * @deprecated use separate setters.
 386  
      */
 387  0
     @Deprecated
 388  
     @Override
 389  0
     public void setFont(final Font f) {
 390  0
         super.setFont(f);
 391  0
         this.setFontSize(f.getSize2D());
 392  0
         this.setFontsSerif(f.getFamily() + JMathComponent.FONT_SEPARATOR
 393  
                 + this.getFontsSerif());
 394  0
     }
 395  0
 
 396  0
     private List<String> splitFonts(final String list) {
 397  0
         return Arrays.asList(list.split(JMathComponent.FONT_SEPARATOR));
 398  0
     }
 399  0
 
 400  0
     /**
 401  
      * Font list for Doublestruck. Please see
 402  
      * {@link Parameter#FONTS_DOUBLESTRUCK} for an explanation of this
 403  0
      * parameter.
 404  
      * 
 405  
      * @param newFonts
 406  
      *            new list for Doublestruck (comma seraparated).
 407  
      * @see Parameter#FONTS_DOUBLESTRUCK
 408  
      */
 409  
     public void setFontsDoublestruck(final String newFonts) {
 410  0
         this.setParameter(Parameter.FONTS_DOUBLESTRUCK, this
 411  
                 .splitFonts(newFonts));
 412  0
     }
 413  0
 
 414  
     /**
 415  
      * Font list for Fraktur. Please see {@link Parameter#FONTS_FRAKTUR} for
 416  0
      * an explanation of this parameter.
 417  
      * 
 418  0
      * @param newFonts
 419  
      *            new list for Fraktur (comma seraparated).
 420  0
      * @see Parameter#FONTS_FRAKTUR
 421  0
      */
 422  0
     public void setFontsFraktur(final String newFonts) {
 423  0
         this.setParameter(Parameter.FONTS_FRAKTUR, this.splitFonts(newFonts));
 424  0
     }
 425  0
 
 426  
     /**
 427  0
      * Sets a generic rendering parameter.
 428  
      * 
 429  0
      * @param key
 430  0
      *            Key for the parameter
 431  
      * @param newValue
 432  
      *            newValue
 433  
      */
 434  
     public void setParameter(final Parameter key, final Object newValue) {
 435  0
         this.setParameters(Collections.singletonMap(key, newValue));
 436  0
     }
 437  
 
 438  
     /**
 439  
      * Sets generic rendering parameters.
 440  
      * 
 441  0
      * @param newValues
 442  0
      *            map of parameter keys to new values
 443  
      */
 444  
     public void setParameters(final Map<Parameter, Object> newValues) {
 445  0
         for (final Map.Entry<Parameter, Object> entry : newValues.entrySet()) {
 446  0
             final Parameter key = entry.getKey();
 447  0
             final Object oldValue = this.parameters.getParameter(key);
 448  0
             this.parameters.setParameter(key, entry.getValue());
 449  0
             this.firePropertyChange(key.name(), oldValue, this.parameters
 450  0
                     .getParameter(key));
 451  0
         }
 452  0
         this.revalidate();
 453  0
         this.repaint();
 454  0
     }
 455  0
 
 456  
     /**
 457  0
      * sets the font size used.
 458  0
      * 
 459  0
      * @param fontSize
 460  0
      *            the font size.
 461  
      */
 462  
     public void setFontSize(final float fontSize) {
 463  0
         this.setParameter(Parameter.MATHSIZE, fontSize);
 464  0
     }
 465  
 
 466  0
     /**
 467  
      * Font list for Monospaced. Please see {@link Parameter#FONTS_MONOSPACED}
 468  0
      * for an explanation of this parameter.
 469  0
      * 
 470  0
      * @param newFonts
 471  
      *            new list for Monospaced (comma seraparated).
 472  
      * @see Parameter#FONTS_MONOSPACED
 473  
      */
 474  
     public void setFontsMonospaced(final String newFonts) {
 475  0
         this.setParameter(Parameter.FONTS_MONOSPACED, this
 476  
                 .splitFonts(newFonts));
 477  0
     }
 478  
 
 479  0
     /**
 480  0
      * Font list for Sans-Serif. Please see {@link Parameter#FONTS_SANSSERIF}
 481  0
      * for an explanation of this parameter.
 482  
      * 
 483  0
      * @param newFonts
 484  
      *            new list for sansserif (comma seraparated).
 485  
      * @see Parameter#FONTS_SANSSERIF
 486  
      */
 487  
     public void setFontsSanserif(final String newFonts) {
 488  0
         this.setParameter(Parameter.FONTS_SANSSERIF, this
 489  
                 .splitFonts(newFonts));
 490  0
     }
 491  0
 
 492  0
     /**
 493  
      * Font list for Script. Please see {@link Parameter#FONTS_SCRIPT} for an
 494  0
      * explanation of this parameter.
 495  
      * 
 496  0
      * @param newFonts
 497  
      *            new list for Script (comma seraparated).
 498  
      * @see Parameter#FONTS_SCRIPT
 499  
      */
 500  
     public void setFontsScript(final String newFonts) {
 501  0
         this.setParameter(Parameter.FONTS_SCRIPT, this.splitFonts(newFonts));
 502  0
     }
 503  0
 
 504  0
     /**
 505  0
      * Font list for Serif (the default MathML font). Please see
 506  
      * {@link Parameter#FONTS_SERIF} for an explanation of this parameter.
 507  0
      * 
 508  0
      * @param newFonts
 509  0
      *            new list for serif (comma seraparated).
 510  0
      * @see Parameter#FONTS_SERIF
 511  
      */
 512  
     public void setFontsSerif(final String newFonts) {
 513  0
         this.setParameter(Parameter.FONTS_SERIF, this.splitFonts(newFonts));
 514  0
         this.fontCompat();
 515  0
     }
 516  
 
 517  
     /** {@inheritDoc} */
 518  
     @Override
 519  0
     public void setForeground(final Color fg) {
 520  0
         super.setForeground(fg);
 521  0
         this.setParameter(Parameter.MATHCOLOR, fg);
 522  0
     }
 523  
 
 524  
     /**
 525  
      * Horizontal alignment, as defined by
 526  0
      * {@link javax.swing.JLabel#setHorizontalAlignment(int)}.
 527  0
      * <p>
 528  0
      * Supported are: {@link SwingConstants#LEADING},
 529  
      * {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER},
 530  
      * {@link SwingConstants#TRAILING}, {@link SwingConstants#RIGHT}.
 531  0
      * 
 532  
      * @param hAlignment
 533  
      *            the horizontalAlignment to set
 534  0
      * @see javax.swing.JLabel#setHorizontalAlignment(int)
 535  
      */
 536  
     public void setHorizontalAlignment(final int hAlignment) {
 537  0
         this.horizontalAlignment = hAlignment;
 538  0
     }
 539  
 
 540  
     /** {@inheritDoc} */
 541  
     @Override
 542  
     public void setOpaque(final boolean opaque) {
 543  4
         super.setOpaque(opaque);
 544  4
         this.reval();
 545  4
     }
 546  0
 
 547  
     /**
 548  
      * Vertical alignment, as defined by
 549  0
      * {@link javax.swing.JLabel#setVerticalAlignment(int)}.
 550  0
      * <p>
 551  0
      * Supported are: {@link SwingConstants#TOP},
 552  
      * {@link SwingConstants#CENTER}, {@link SwingConstants#BOTTOM}.
 553  
      * 
 554  
      * @param vAlignment
 555  
      *            the verticalAlignment to set
 556  
      * @see javax.swing.JLabel#setVerticalAlignment(int)
 557  0
      */
 558  0
     public void setVerticalAlignment(final int vAlignment) {
 559  0
         this.verticalAlignment = vAlignment;
 560  0
     }
 561  
 
 562  
     /** {@inheritDoc} */
 563  
     @Override
 564  
     public void updateUI() {
 565  4
         if (UIManager.get(this.getUIClassID()) == null) {
 566  0
             try {
 567  4
                 this
 568  
                         .setUI((MathComponentUI) JMathComponent.mathComponentUIClass
 569  0
                                 .newInstance());
 570  0
             } catch (final InstantiationException e) {
 571  0
                 JMathComponent.LOGGER.warn(e.getMessage());
 572  0
             } catch (final IllegalAccessException e) {
 573  0
                 JMathComponent.LOGGER.warn(e.getMessage());
 574  4
             }
 575  
         } else {
 576  0
             this.setUI(UIManager.getUI(this));
 577  0
         }
 578  4
     }
 579  0
 
 580  0
     /**
 581  
      * @return the parameters
 582  0
      */
 583  
     public MutableLayoutContext getParameters() {
 584  8
         return this.parameters;
 585  
     }
 586  
 
 587  
     /** {@inheritDoc} */
 588  
     @Override
 589  
     public void setSize(final int width, final int height) {
 590  0
         // TODO Auto-generated method stub
 591  0
         super.setSize(width, height);
 592  0
     }
 593  0
 
 594  0
     static {
 595  
         Class<?> uiClass;
 596  
         String id;
 597  0
         try {
 598  4
             uiClass = ClassLoaderSupport.getInstance().loadClass(
 599  0
                     "net.sourceforge.jeuclid.swing.MathComponentUI16");
 600  0
             id = "MathComponentUI16";
 601  4
         } catch (final ClassNotFoundException t) {
 602  4
             uiClass = MathComponentUI.class;
 603  4
             id = "MathComponentUI";
 604  0
         }
 605  4
         JMathComponent.uiClassId = id;
 606  4
         JMathComponent.mathComponentUIClass = uiClass;
 607  4
     }
 608  0
 
 609  0
 }