1 /*
   2  * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 package com.sun.hotspot.igv.view;
  25 
  26 import com.sun.hotspot.igv.data.ChangedEvent;
  27 import com.sun.hotspot.igv.data.ChangedListener;
  28 import com.sun.hotspot.igv.data.InputNode;
  29 import com.sun.hotspot.igv.data.Properties;
  30 import com.sun.hotspot.igv.data.Properties.PropertyMatcher;
  31 import com.sun.hotspot.igv.data.services.InputGraphProvider;
  32 import com.sun.hotspot.igv.filter.FilterChain;
  33 import com.sun.hotspot.igv.filter.FilterChainProvider;
  34 import com.sun.hotspot.igv.graph.Diagram;
  35 import com.sun.hotspot.igv.graph.Figure;
  36 import com.sun.hotspot.igv.graph.services.DiagramProvider;
  37 import com.sun.hotspot.igv.svg.BatikSVG;
  38 import com.sun.hotspot.igv.util.LookupHistory;
  39 import com.sun.hotspot.igv.util.RangeSlider;
  40 import com.sun.hotspot.igv.view.actions.*;
  41 import java.awt.*;
  42 import java.awt.event.HierarchyBoundsListener;
  43 import java.awt.event.HierarchyEvent;
  44 import java.awt.event.KeyEvent;
  45 import java.awt.event.KeyListener;
  46 import java.beans.PropertyChangeEvent;
  47 import java.beans.PropertyChangeListener;
  48 import java.io.*;
  49 import java.util.List;
  50 import java.util.*;
  51 import javax.swing.*;
  52 import javax.swing.border.Border;
  53 import org.openide.DialogDisplayer;
  54 import org.openide.NotifyDescriptor;
  55 import org.openide.actions.RedoAction;
  56 import org.openide.actions.UndoAction;
  57 import org.openide.awt.Toolbar;
  58 import org.openide.awt.ToolbarPool;
  59 import org.openide.awt.UndoRedo;
  60 import org.openide.util.Lookup;
  61 import org.openide.util.NbBundle;
  62 import org.openide.util.Utilities;
  63 import org.openide.util.actions.Presenter;
  64 import org.openide.util.lookup.AbstractLookup;
  65 import org.openide.util.lookup.InstanceContent;
  66 import org.openide.util.lookup.ProxyLookup;
  67 import org.openide.windows.Mode;
  68 import org.openide.windows.TopComponent;
  69 import org.openide.windows.WindowManager;
  70 
  71 /**
  72  *
  73  * @author Thomas Wuerthinger
  74  */
  75 public final class EditorTopComponent extends TopComponent implements PropertyChangeListener {
  76 
  77     private DiagramViewer scene;
  78     private InstanceContent content;
  79     private InstanceContent graphContent;
  80     private EnableBlockLayoutAction blockLayoutAction;
  81     private OverviewAction overviewAction;
  82     private HideDuplicatesAction hideDuplicatesAction;
  83     private PredSuccAction predSuccAction;
  84     private SelectionModeAction selectionModeAction;
  85     private PanModeAction panModeAction;
  86     private boolean notFirstTime;
  87     private JComponent satelliteComponent;
  88     private JPanel centerPanel;
  89     private CardLayout cardLayout;
  90     private RangeSlider rangeSlider;
  91     private JToggleButton overviewButton;
  92     private JToggleButton hideDuplicatesButton;
  93     private static final String PREFERRED_ID = "EditorTopComponent";
  94     private static final String SATELLITE_STRING = "satellite";
  95     private static final String SCENE_STRING = "scene";
  96     private DiagramViewModel rangeSliderModel;
  97     private ExportCookie exportCookie = new ExportCookie() {
  98 
  99         @Override
 100         public void export(File f) {
 101 
 102             Graphics2D svgGenerator = BatikSVG.createGraphicsObject();
 103 
 104             if (svgGenerator == null) {
 105                 NotifyDescriptor message = new NotifyDescriptor.Message("For export to SVG files the Batik SVG Toolkit must be intalled.", NotifyDescriptor.ERROR_MESSAGE);
 106                 DialogDisplayer.getDefault().notifyLater(message);
 107             } else {
 108                 scene.paint(svgGenerator);
 109                 FileOutputStream os = null;
 110                 try {
 111                     os = new FileOutputStream(f);
 112                     Writer out = new OutputStreamWriter(os, "UTF-8");
 113                     BatikSVG.printToStream(svgGenerator, out, true);
 114                 } catch (FileNotFoundException e) {
 115                     NotifyDescriptor message = new NotifyDescriptor.Message("For export to SVG files the Batik SVG Toolkit must be intalled.", NotifyDescriptor.ERROR_MESSAGE);
 116                     DialogDisplayer.getDefault().notifyLater(message);
 117 
 118                 } catch (UnsupportedEncodingException e) {
 119                 } finally {
 120                     if (os != null) {
 121                         try {
 122                             os.close();
 123                         } catch (IOException e) {
 124                         }
 125                     }
 126                 }
 127 
 128             }
 129         }
 130     };
 131 
 132     private DiagramProvider diagramProvider = new DiagramProvider() {
 133 
 134         @Override
 135         public Diagram getDiagram() {
 136             return getModel().getDiagramToView();
 137         }
 138 
 139         @Override
 140         public ChangedEvent<DiagramProvider> getChangedEvent() {
 141             return diagramChangedEvent;
 142         }
 143     };
 144 
 145     private ChangedEvent<DiagramProvider> diagramChangedEvent = new ChangedEvent<>(diagramProvider);
 146 
 147 
 148     private void updateDisplayName() {
 149         setDisplayName(getDiagram().getName());
 150         setToolTipText(getDiagram().getGraph().getGroup().getName());
 151     }
 152 
 153     public EditorTopComponent(Diagram diagram) {
 154 
 155         LookupHistory.init(InputGraphProvider.class);
 156         LookupHistory.init(DiagramProvider.class);
 157         this.setFocusable(true);
 158         FilterChain filterChain = null;
 159         FilterChain sequence = null;
 160         FilterChainProvider provider = Lookup.getDefault().lookup(FilterChainProvider.class);
 161         if (provider == null) {
 162             filterChain = new FilterChain();
 163             sequence = new FilterChain();
 164         } else {
 165             filterChain = provider.getFilterChain();
 166             sequence = provider.getSequence();
 167         }
 168 
 169         setName(NbBundle.getMessage(EditorTopComponent.class, "CTL_EditorTopComponent"));
 170         setToolTipText(NbBundle.getMessage(EditorTopComponent.class, "HINT_EditorTopComponent"));
 171 
 172         Action[] actions = new Action[]{
 173             PrevDiagramAction.get(PrevDiagramAction.class),
 174             NextDiagramAction.get(NextDiagramAction.class),
 175             null,
 176             ExtractAction.get(ExtractAction.class),
 177             ShowAllAction.get(HideAction.class),
 178             ShowAllAction.get(ShowAllAction.class),
 179             null,
 180             ZoomInAction.get(ZoomInAction.class),
 181             ZoomOutAction.get(ZoomOutAction.class),
 182         };
 183 
 184 
 185         Action[] actionsWithSelection = new Action[]{
 186             ExtractAction.get(ExtractAction.class),
 187             ShowAllAction.get(HideAction.class),
 188             null,
 189             ExpandPredecessorsAction.get(ExpandPredecessorsAction.class),
 190             ExpandSuccessorsAction.get(ExpandSuccessorsAction.class)
 191         };
 192 
 193         initComponents();
 194 
 195         ToolbarPool.getDefault().setPreferredIconSize(16);
 196         Toolbar toolBar = new Toolbar();
 197         Border b = (Border) UIManager.get("Nb.Editor.Toolbar.border"); //NOI18N
 198         toolBar.setBorder(b);
 199         JPanel container = new JPanel();
 200         this.add(container, BorderLayout.NORTH);
 201         container.setLayout(new BorderLayout());
 202         container.add(BorderLayout.NORTH, toolBar);
 203 
 204         rangeSliderModel = new DiagramViewModel(diagram.getGraph().getGroup(), filterChain, sequence);
 205         rangeSlider = new RangeSlider();
 206         rangeSlider.setModel(rangeSliderModel);
 207         JScrollPane pane = new JScrollPane(rangeSlider, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 208         container.add(BorderLayout.CENTER, pane);
 209 
 210         scene = new DiagramScene(actions, actionsWithSelection, rangeSliderModel);
 211         content = new InstanceContent();
 212         graphContent = new InstanceContent();
 213         this.associateLookup(new ProxyLookup(new Lookup[]{scene.getLookup(), new AbstractLookup(graphContent), new AbstractLookup(content)}));
 214         content.add(exportCookie);
 215         content.add(rangeSliderModel);
 216         content.add(diagramProvider);
 217 
 218         rangeSliderModel.getDiagramChangedEvent().addListener(diagramChangedListener);
 219         rangeSliderModel.selectGraph(diagram.getGraph());
 220         rangeSliderModel.getViewPropertiesChangedEvent().addListener(new ChangedListener<DiagramViewModel>() {
 221                 @Override
 222                 public void changed(DiagramViewModel source) {
 223                     hideDuplicatesButton.setSelected(getModel().getHideDuplicates());
 224                     hideDuplicatesAction.setState(getModel().getHideDuplicates());
 225                 }
 226             });
 227 
 228 
 229         toolBar.add(NextDiagramAction.get(NextDiagramAction.class));
 230         toolBar.add(PrevDiagramAction.get(PrevDiagramAction.class));
 231         toolBar.addSeparator();
 232         toolBar.add(ExtractAction.get(ExtractAction.class));
 233         toolBar.add(ShowAllAction.get(HideAction.class));
 234         toolBar.add(ShowAllAction.get(ShowAllAction.class));
 235         toolBar.addSeparator();
 236         toolBar.add(ShowAllAction.get(ZoomInAction.class));
 237         toolBar.add(ShowAllAction.get(ZoomOutAction.class));
 238 
 239         blockLayoutAction = new EnableBlockLayoutAction();
 240         JToggleButton button = new JToggleButton(blockLayoutAction);
 241         button.setSelected(false);
 242         toolBar.add(button);
 243         blockLayoutAction.addPropertyChangeListener(this);
 244 
 245         overviewAction = new OverviewAction();
 246         overviewButton = new JToggleButton(overviewAction);
 247         overviewButton.setSelected(false);
 248         toolBar.add(overviewButton);
 249         overviewAction.addPropertyChangeListener(this);
 250 
 251         predSuccAction = new PredSuccAction();
 252         button = new JToggleButton(predSuccAction);
 253         button.setSelected(true);
 254         toolBar.add(button);
 255         predSuccAction.addPropertyChangeListener(this);
 256 
 257         hideDuplicatesAction = new HideDuplicatesAction();
 258         hideDuplicatesButton = new JToggleButton(hideDuplicatesAction);
 259         hideDuplicatesButton.setSelected(false);
 260         toolBar.add(hideDuplicatesButton);
 261         hideDuplicatesAction.addPropertyChangeListener(this);
 262 
 263         toolBar.addSeparator();
 264         toolBar.add(UndoAction.get(UndoAction.class));
 265         toolBar.add(RedoAction.get(RedoAction.class));
 266 
 267         toolBar.addSeparator();
 268         ButtonGroup interactionButtons = new ButtonGroup();
 269 
 270         panModeAction = new PanModeAction();
 271         panModeAction.setSelected(true);
 272         button = new JToggleButton(panModeAction);
 273         button.setSelected(true);
 274         interactionButtons.add(button);
 275         toolBar.add(button);
 276         panModeAction.addPropertyChangeListener(this);
 277 
 278         selectionModeAction = new SelectionModeAction();
 279         button = new JToggleButton(selectionModeAction);
 280         interactionButtons.add(button);
 281         toolBar.add(button);
 282         selectionModeAction.addPropertyChangeListener(this);
 283 
 284         toolBar.add(Box.createHorizontalGlue());
 285         Action action = Utilities.actionsForPath("QuickSearchShadow").get(0);
 286         Component quicksearch = ((Presenter.Toolbar) action).getToolbarPresenter();
 287         try {
 288             // (aw) workaround for disappearing search bar due to reparenting one shared component instance.
 289             quicksearch = (Component) quicksearch.getClass().getConstructor(KeyStroke.class).newInstance(new Object[]{null});
 290         } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) {
 291         }
 292         quicksearch.setMinimumSize(quicksearch.getPreferredSize()); // necessary for GTK LAF
 293         toolBar.add(quicksearch);
 294 
 295         centerPanel = new JPanel();
 296         this.add(centerPanel, BorderLayout.CENTER);
 297         cardLayout = new CardLayout();
 298         centerPanel.setLayout(cardLayout);
 299         centerPanel.add(SCENE_STRING, scene.getComponent());
 300         centerPanel.setBackground(Color.WHITE);
 301         satelliteComponent = scene.createSatelliteView();
 302         satelliteComponent.setSize(200, 200);
 303         centerPanel.add(SATELLITE_STRING, satelliteComponent);
 304 
 305         // TODO: Fix the hot key for entering the satellite view
 306         this.addKeyListener(keyListener);
 307 
 308         scene.getComponent().addHierarchyBoundsListener(new HierarchyBoundsListener() {
 309 
 310             @Override
 311             public void ancestorMoved(HierarchyEvent e) {
 312             }
 313 
 314             @Override
 315             public void ancestorResized(HierarchyEvent e) {
 316                 if (!notFirstTime && scene.getComponent().getBounds().width > 0) {
 317                     notFirstTime = true;
 318                     SwingUtilities.invokeLater(new Runnable() {
 319 
 320                         @Override
 321                         public void run() {
 322                             EditorTopComponent.this.scene.initialize();
 323                         }
 324                     });
 325                 }
 326             }
 327         });
 328 
 329         if (diagram.getGraph().getGroup().getGraphsCount() == 1) {
 330             rangeSlider.setVisible(false);
 331         }
 332 
 333         updateDisplayName();
 334     }
 335     private KeyListener keyListener = new KeyListener() {
 336 
 337         @Override
 338         public void keyTyped(KeyEvent e) {
 339         }
 340 
 341         @Override
 342         public void keyPressed(KeyEvent e) {
 343             if (e.getKeyCode() == KeyEvent.VK_S) {
 344                 EditorTopComponent.this.overviewButton.setSelected(true);
 345                 EditorTopComponent.this.overviewAction.setState(true);
 346             }
 347         }
 348 
 349         @Override
 350         public void keyReleased(KeyEvent e) {
 351             if (e.getKeyCode() == KeyEvent.VK_S) {
 352                 EditorTopComponent.this.overviewButton.setSelected(false);
 353                 EditorTopComponent.this.overviewAction.setState(false);
 354             }
 355         }
 356     };
 357 
 358     public DiagramViewModel getDiagramModel() {
 359         return rangeSliderModel;
 360     }
 361 
 362     private void showSatellite() {
 363         cardLayout.show(centerPanel, SATELLITE_STRING);
 364         satelliteComponent.requestFocus();
 365 
 366     }
 367 
 368     private void showScene() {
 369         cardLayout.show(centerPanel, SCENE_STRING);
 370         scene.getComponent().requestFocus();
 371     }
 372 
 373     public void zoomOut() {
 374         scene.zoomOut();
 375     }
 376 
 377     public void zoomIn() {
 378         scene.zoomIn();
 379     }
 380 
 381     public void showPrevDiagram() {
 382         int fp = getModel().getFirstPosition();
 383         int sp = getModel().getSecondPosition();
 384         if (fp != 0) {
 385             fp--;
 386             sp--;
 387             getModel().setPositions(fp, sp);
 388         }
 389     }
 390 
 391     public DiagramViewModel getModel() {
 392         return rangeSliderModel;
 393     }
 394 
 395     public FilterChain getFilterChain() {
 396         return getModel().getFilterChain();
 397     }
 398 
 399     public static EditorTopComponent getActive() {
 400         Set<? extends Mode> modes = WindowManager.getDefault().getModes();
 401         for (Mode m : modes) {
 402             TopComponent tc = m.getSelectedTopComponent();
 403             if (tc instanceof EditorTopComponent) {
 404                 return (EditorTopComponent) tc;
 405             }
 406         }
 407         return null;
 408     }
 409 
 410     /** This method is called from within the constructor to
 411      * initialize the form.
 412      * WARNING: Do NOT modify this code. The content of this method is
 413      * always regenerated by the Form Editor.
 414      */
 415         // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
 416         private void initComponents() {
 417                 jCheckBox1 = new javax.swing.JCheckBox();
 418 
 419                 org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, "jCheckBox1");
 420                 jCheckBox1.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
 421                 jCheckBox1.setMargin(new java.awt.Insets(0, 0, 0, 0));
 422 
 423                 setLayout(new java.awt.BorderLayout());
 424 
 425         }// </editor-fold>//GEN-END:initComponents
 426         // Variables declaration - do not modify//GEN-BEGIN:variables
 427         private javax.swing.JCheckBox jCheckBox1;
 428         // End of variables declaration//GEN-END:variables
 429 
 430     @Override
 431     public int getPersistenceType() {
 432         return TopComponent.PERSISTENCE_NEVER;
 433     }
 434 
 435     @Override
 436     public void componentOpened() {
 437     }
 438 
 439     @Override
 440     public void componentClosed() {
 441         rangeSliderModel.close();
 442     }
 443 
 444     @Override
 445     protected String preferredID() {
 446         return PREFERRED_ID;
 447     }
 448 
 449     private ChangedListener<DiagramViewModel> diagramChangedListener = new ChangedListener<DiagramViewModel>() {
 450 
 451         @Override
 452         public void changed(DiagramViewModel source) {
 453             updateDisplayName();
 454             Collection<Object> list = new ArrayList<>();
 455             list.add(new EditorInputGraphProvider(EditorTopComponent.this));
 456             graphContent.set(list, null);
 457             diagramProvider.getChangedEvent().fire();
 458         }
 459 
 460     };
 461 
 462     public boolean showPredSucc() {
 463         return (Boolean) predSuccAction.getValue(PredSuccAction.STATE);
 464     }
 465 
 466     public void setSelection(PropertyMatcher matcher) {
 467 
 468         Properties.PropertySelector<Figure> selector = new Properties.PropertySelector<>(getModel().getDiagramToView().getFigures());
 469         List<Figure> list = selector.selectMultiple(matcher);
 470         setSelectedFigures(list);
 471     }
 472 
 473     public void setSelectedFigures(List<Figure> list) {
 474         scene.setSelection(list);
 475         scene.centerFigures(list);
 476     }
 477 
 478     public void setSelectedNodes(Set<InputNode> nodes) {
 479 
 480         List<Figure> list = new ArrayList<>();
 481         Set<Integer> ids = new HashSet<>();
 482         for (InputNode n : nodes) {
 483             ids.add(n.getId());
 484         }
 485 
 486         for (Figure f : getModel().getDiagramToView().getFigures()) {
 487             for (InputNode n : f.getSource().getSourceNodes()) {
 488                 if (ids.contains(n.getId())) {
 489                     list.add(f);
 490                     break;
 491                 }
 492             }
 493         }
 494 
 495         setSelectedFigures(list);
 496     }
 497 
 498     @Override
 499     public void propertyChange(PropertyChangeEvent evt) {
 500         if (evt.getSource() == this.predSuccAction) {
 501             boolean b = (Boolean) predSuccAction.getValue(PredSuccAction.STATE);
 502             this.getModel().setShowNodeHull(b);
 503         } else if (evt.getSource() == this.overviewAction) {
 504             boolean b = (Boolean) overviewAction.getValue(OverviewAction.STATE);
 505             if (b) {
 506                 showSatellite();
 507             } else {
 508                 showScene();
 509             }
 510         } else if (evt.getSource() == this.blockLayoutAction) {
 511             boolean b = (Boolean) blockLayoutAction.getValue(EnableBlockLayoutAction.STATE);
 512             this.getModel().setShowBlocks(b);
 513         } else if (evt.getSource() == this.hideDuplicatesAction) {
 514             boolean b = (Boolean) hideDuplicatesAction.getValue(HideDuplicatesAction.STATE);
 515             this.getModel().setHideDuplicates(b);
 516         } else if (evt.getSource() == this.selectionModeAction || evt.getSource() == this.panModeAction) {
 517             if (panModeAction.isSelected()) {
 518                 scene.setInteractionMode(DiagramViewer.InteractionMode.PANNING);
 519             } else if (selectionModeAction.isSelected()) {
 520                 scene.setInteractionMode(DiagramViewer.InteractionMode.SELECTION);
 521             }
 522         } else {
 523             assert false : "Unknown event source";
 524         }
 525     }
 526 
 527     public void extract() {
 528         getModel().showOnly(getModel().getSelectedNodes());
 529     }
 530 
 531     public void hideNodes() {
 532         Set<Integer> selectedNodes = this.getModel().getSelectedNodes();
 533         HashSet<Integer> nodes = new HashSet<>(getModel().getHiddenNodes());
 534         nodes.addAll(selectedNodes);
 535         this.getModel().showNot(nodes);
 536     }
 537 
 538     public void expandPredecessors() {
 539         Set<Figure> oldSelection = getModel().getSelectedFigures();
 540         Set<Figure> figures = new HashSet<>();
 541 
 542         for (Figure f : this.getDiagramModel().getDiagramToView().getFigures()) {
 543             boolean ok = false;
 544             if (oldSelection.contains(f)) {
 545                 ok = true;
 546             } else {
 547                 for (Figure pred : f.getSuccessors()) {
 548                     if (oldSelection.contains(pred)) {
 549                         ok = true;
 550                         break;
 551                     }
 552                 }
 553             }
 554 
 555             if (ok) {
 556                 figures.add(f);
 557             }
 558         }
 559 
 560         getModel().showAll(figures);
 561     }
 562 
 563     public void expandSuccessors() {
 564         Set<Figure> oldSelection = getModel().getSelectedFigures();
 565         Set<Figure> figures = new HashSet<>();
 566 
 567         for (Figure f : this.getDiagramModel().getDiagramToView().getFigures()) {
 568             boolean ok = false;
 569             if (oldSelection.contains(f)) {
 570                 ok = true;
 571             } else {
 572                 for (Figure succ : f.getPredecessors()) {
 573                     if (oldSelection.contains(succ)) {
 574                         ok = true;
 575                         break;
 576                     }
 577                 }
 578             }
 579 
 580             if (ok) {
 581                 figures.add(f);
 582             }
 583         }
 584 
 585         getModel().showAll(figures);
 586     }
 587 
 588     public void showAll() {
 589         getModel().showNot(new HashSet<Integer>());
 590     }
 591 
 592     public Diagram getDiagram() {
 593         return getDiagramModel().getDiagramToView();
 594     }
 595 
 596     @Override
 597     protected void componentHidden() {
 598         super.componentHidden();
 599         scene.componentHidden();
 600 
 601     }
 602 
 603     @Override
 604     protected void componentShowing() {
 605         super.componentShowing();
 606         scene.componentShowing();
 607     }
 608 
 609     @Override
 610     public void requestActive() {
 611         super.requestActive();
 612         scene.getComponent().requestFocus();
 613     }
 614 
 615     @Override
 616     public UndoRedo getUndoRedo() {
 617         return scene.getUndoRedo();
 618     }
 619 
 620     @Override
 621     protected Object writeReplace() throws ObjectStreamException {
 622         throw new NotSerializableException();
 623 }
 624 }