/* * [SurrogatePair.java] * * Summary: Converts 32-bit Unicode codePoints to 16-bit surrogate pairs for Java String literals. * * Copyright: (c) 2009-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2009-12-30 initial version * 1.1 2011-12-01 configurable L&F */ package com.mindprod.surrogatepair; import com.mindprod.common18.Build; import com.mindprod.common18.CMPAboutJBox; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.Laf; import com.mindprod.common18.ST; import com.mindprod.common18.VersionCheck; import com.mindprod.spinner.HexNumberEditor; import javax.swing.JApplet; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.Document; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.lang.System.*; /** * Converts 32-bit Unicode codePoints to 16-bit surrogate pairs for Java String literals. *

* * @author Roedy Green, Canadian Mind Products * @version 1.1 2011-12-01 configurable L&F * @noinspection FieldCanBeLocal * @since 2009-12-30 */ public final class SurrogatePair extends JApplet { /** * height of Applet box in pixels. Does not include surrounding frame. */ private static final int APPLET_HEIGHT = 440; /** * Width of Applet box in pixels. */ private static final int APPLET_WIDTH = 600; /** * basic font size */ private static final int basicFontSize = 12; /** * font for data entry fields */ private static final Font FONT_FOR_EDITABLE_FIELDS = FontFactory.build( "DialogInput", Font.PLAIN, basicFontSize + 1 ); /** * font for instructions */ private static final Font FONT_FOR_INSTRUCTIONS = FontFactory.build( "Dialog", Font.PLAIN, basicFontSize ); /** * font for result fields */ private static final Font FONT_FOR_RESULTS = FontFactory.build( "Dialog", Font.PLAIN, basicFontSize + 1 ); /** * largest codepoint we can represent with a surrogate pair. * 10 bits in each char + 0x10000 16-bit codes we don't need to represent * 2 ** (10+10) + 0x10000 - 1 = 0x10ffff, a bit more than 20 bits. */ private static final int BIGGEST_REPRESENTABLE_CODEPOINT = 0x10ffff; private static final int FIRST_COPYRIGHT_YEAR = 2009; /** * copyright not displayed * * @noinspection UnusedDeclaration */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2009-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; private static final String RELEASE_DATE = "2011-12-01"; private static final String TITLE_STRING = "Surrogate Pairs"; private static final String VERSION_STRING = "1.1"; private static final Color BACKGROUND_FOR_APPLET = new Color( 0xfaf0e6 )/* linen */; /** * background where for data entry where user enters a value. */ private static final Color BACKGROUND_FOR_EDITABLE = Color.WHITE; /** * background colour for instructions. Default grey is too dark */ private static final Color BACKGROUND_FOR_INSTRUCTIONS = new Color( 0xf8f8f8 ); /** * background where for data entry where user enters a value. */ private static final Color BACKGROUND_FOR_RESULT = new Color( 0xf6fff6 ); // pale green /** * data entry text field colour */ private static final Color FOREGROUND_FOR_ENTER = Color.BLACK; /** * color for instructions foreground */ private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x006400 )/* dark green */; /** * color for label foreground */ private static final Color FOREGROUND_FOR_LABEL = new Color( 0x0000b0 ); /** * data entry text field colour */ private static final Color FOREGROUND_FOR_RESULT = new Color( 0x000090 ); // dark blue /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * for for titles and About buttons */ private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 ); /** * for for title second line */ private static final Font FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 ); /** * pattern to find C code codepoint of form \Uxxxxxxxx */ private static final Pattern CU8 = Pattern.compile( "\\\\U(\\p{XDigit}{8})", Pattern.CASE_INSENSITIVE ); /** * pattern to find Java or C code with codepoint of form \Uxxxxxxxx or \\uxxxx */ private static final Pattern CU8U4 = Pattern.compile( "\\\\U(\\p{XDigit}{8})|\\\\u(\\p{XDigit}{4})", Pattern.CASE_INSENSITIVE ); /** * space to leave around the Applet fields when there are on the outer rim. */ private static final int[] margin = { 12, 15, 12, 15 }/* t l b r */; /** * label for input text box */ private JLabel inputTextLabel; /** * label for renderNext */ private JLabel renderTextLabel; /** * label for output text box */ private JLabel surrogatePairLiteralsLabel; /** * Displayed title */ private JLabel title; /** * title, second line */ private JLabel title2; /** * how long a password do you want */ private JSpinner codePointSpinner; /** * input java literal with embedded \Uxxxxxxxx. */ private JTextArea inputText; /** * displayed instructions on how to use */ private JTextArea instructions; /** * how string appears on your platform: */ private JTextArea renderText; /** * Generated text expanded with surrogate pairs */ private JTextArea surrogatePairLiterals; /** * helper for codePointSpinner. */ private SpinnerNumberModel codePointNumberModel; /** * Convert 32-bit codePoint to \ U x x x x x x x x C-style string literal. * e.g. 0x1d509 to \U0001d509 * * @param codePoint number 32-bit code point to convert to literal. * Must be in in range 0 to {@value #BIGGEST_REPRESENTABLE_CODEPOINT}. * * @return as a C-style 10-char literal without quotes. */ private static String toCLiteral( final int codePoint ) { if ( !( 0 <= codePoint && codePoint <= BIGGEST_REPRESENTABLE_CODEPOINT ) ) { throw new IllegalArgumentException( "toSurrogatePair toCLiteral must be in range 0x0000.." + BIGGEST_REPRESENTABLE_CODEPOINT ); } StringBuilder sb = new StringBuilder( 10 ); sb.append( "\\U" ); sb.append( ST.toLZHexString( codePoint, 8 ) ); return sb.toString(); } /** * Convert 32-bit codePoint to a 2-character Unicode-16 string surrogate pair string. * e.g. 0x1d509 to "\ud835\udd09" * * @param codePoint number 32-bit code point to convert to literal * Must be in in range 0 to {@value #BIGGEST_REPRESENTABLE_CODEPOINT}. * * @return pair of surrogate characters as a 2-character string. */ private static String toSurrogatePair( final int codePoint ) { if ( !( 0 <= codePoint && codePoint <= BIGGEST_REPRESENTABLE_CODEPOINT ) ) { throw new IllegalArgumentException( "codePoint parameter for toSurrogatePair must be in range 0x0000.." + BIGGEST_REPRESENTABLE_CODEPOINT ); } if ( codePoint <= 0xffff ) { // ordinary 16-bit char return String.valueOf( ( char ) codePoint ); } else { // offset since we don't need to represent low chars 0x0000..0xffff. // This lets us represent 64K more chars on the high end. final int extract = codePoint - 0x10000; // strip out the high 10 bits and embed in high surrogate range. final int high = ( extract >>> 10 ) + 0xd800; // strip out the lowe 10 bits and embed in low surrogate range. final int low = ( extract & 0x3ff ) + 0xdc00; // emit the surrogate pair in big endian order. StringBuilder sb = new StringBuilder( 2 ); sb.append( ( char ) high ); sb.append( ( char ) low ); return sb.toString(); } } /** * Convert 32-bit codePoint to a pair of \ u x x x x surrogate literals. * e.g. 0x1d509 to "\ud835\udd09" * * @param codePoint number 32-bit code point to convert to literal * Must be in in range 0 to {@value #BIGGEST_REPRESENTABLE_CODEPOINT}. * * @return pair of surrogate characters as a 12-character Java literal without quotes. */ private static String toSurrogatePairLiteral( final int codePoint ) { if ( !( 0 <= codePoint && codePoint <= BIGGEST_REPRESENTABLE_CODEPOINT ) ) { throw new IllegalArgumentException( "codePoint parameter for toSurrogatePairLiteral must be in range 0x0000.." + BIGGEST_REPRESENTABLE_CODEPOINT ); } if ( codePoint <= 0xffff ) { // ordinary 16-bit char StringBuilder sb = new StringBuilder( 6 ); sb.append( "\\u" ); sb.append( ST.toLZHexString( codePoint, 4 ) ); return sb.toString(); } else { final int extract = codePoint - 0x10000; final int high = ( extract >>> 10 ) + 0xd800; final int low = ( extract & 0x3ff ) + 0xdc00; StringBuilder sb = new StringBuilder( 12 ); sb.append( "\\u" ); sb.append( ST.toLZHexString( high, 4 ) ); sb.append( "\\u" ); sb.append( ST.toLZHexString( low, 4 ) ); return sb.toString(); } } /** * allocate all components */ private void buildComponents() { title = new JLabel( TITLE_STRING + " " + VERSION_STRING ); title.setForeground( FOREGROUND_FOR_TITLE ); title.setFont( FONT_FOR_TITLE ); title2 = new JLabel( "released:" + RELEASE_DATE + " build:" + Build.BUILD_NUMBER ); title2.setFont( FONT_FOR_TITLE2 ); title2.setForeground( FOREGROUND_FOR_TITLE ); codePointSpinner = new JSpinner(); codePointSpinner.setForeground( FOREGROUND_FOR_ENTER ); codePointSpinner.setBackground( BACKGROUND_FOR_EDITABLE ); codePointSpinner.setFont( FONT_FOR_EDITABLE_FIELDS ); Dimension d = new Dimension( 100, 25 ); codePointSpinner.setMinimumSize( d ); codePointSpinner.setPreferredSize( d ); codePointSpinner.setMaximumSize( d ); codePointNumberModel = new SpinnerNumberModel( 0x1d504 /* initial value */, 0x0000 /* min */, BIGGEST_REPRESENTABLE_CODEPOINT /* max */, 1 /* step */ ); codePointSpinner.setModel( codePointNumberModel ); final HexNumberEditor hne = new HexNumberEditor( codePointSpinner, 8 ); codePointSpinner.setEditor( hne ); // Java has bug. setBackground on JSpinner has no effect, must set inner field. final JFormattedTextField innerHexSpinner = hne.getTextField(); innerHexSpinner.setBackground( BACKGROUND_FOR_EDITABLE ); innerHexSpinner.setForeground( FOREGROUND_FOR_ENTER ); innerHexSpinner.setMargin( new Insets( 1, 1, 1, 1 ) ); inputTextLabel = new JLabel( "Enter String literal using C-style \\Uxxxxxxxx or \\uxxxx Java-style notation:" ); inputTextLabel.setForeground( FOREGROUND_FOR_LABEL ); inputTextLabel.setFont( FontFactory.build( "Dialog", Font.PLAIN, basicFontSize ) ); inputText = new JTextArea(); inputText.setText( "Sample String literal containing Java-style escape \\u00b5 and unsupported C style code point " + "\\U0001d504." ); inputText.setEditable( true ); inputText.setForeground( FOREGROUND_FOR_ENTER ); inputText.setBackground( BACKGROUND_FOR_EDITABLE ); inputText.setFont( FONT_FOR_EDITABLE_FIELDS ); // automatically wrap lines inputText.setLineWrap( true ); // break lines on word, rather than character boundaries. inputText.setWrapStyleWord( true ); inputText.setOpaque( true ); inputText.setMargin( new Insets( 2, 2, 2, 2 ) ); // we don't need a GO button. We repaint on every keystroke. surrogatePairLiteralsLabel = new JLabel( "As Java String literal using surrogate pairs:" ); surrogatePairLiteralsLabel.setForeground( FOREGROUND_FOR_LABEL ); surrogatePairLiteralsLabel.setFont( FontFactory.build( "Dialog", Font.PLAIN, basicFontSize ) ); surrogatePairLiterals = new JTextArea(); surrogatePairLiterals.setEditable( false ); surrogatePairLiterals.setForeground( FOREGROUND_FOR_RESULT ); surrogatePairLiterals.setBackground( BACKGROUND_FOR_RESULT ); surrogatePairLiterals.setFont( FONT_FOR_RESULTS ); // automatically wrap lines surrogatePairLiterals.setLineWrap( true ); // break lines on word, rather than character boundaries. surrogatePairLiterals.setWrapStyleWord( true ); surrogatePairLiterals.setOpaque( true ); surrogatePairLiterals.setMargin( new Insets( 2, 2, 2, 2 ) ); renderTextLabel = new JLabel( "How that literal will render on your platform:" ); renderTextLabel.setForeground( FOREGROUND_FOR_LABEL ); renderTextLabel.setFont( FontFactory.build( "Dialog", Font.PLAIN, basicFontSize ) ); renderText = new JTextArea(); renderText.setEditable( false ); renderText.setForeground( FOREGROUND_FOR_RESULT ); renderText.setBackground( BACKGROUND_FOR_RESULT ); renderText.setFont( FONT_FOR_RESULTS ); // automatically wrap lines renderText.setLineWrap( true ); // break lines on word, rather than character boundaries. renderText.setWrapStyleWord( true ); renderText.setOpaque( true ); renderText.setMargin( new Insets( 2, 2, 2, 2 ) ); instructions = new JTextArea( "Either:\n" + "(1) Use the spinner to see a single hex codePoint rendered as a surrogate pair" + ".\n" + "(2) Type a Java string literal using embedded C-style \\Uxxxxxxxx notation for" + " the codePoints in the top box.\n" ); instructions.setEditable( false ); instructions.setMargin( new Insets( 4, 4, 4, 4 ) ); instructions.setBackground( BACKGROUND_FOR_INSTRUCTIONS ); instructions.setFont( FONT_FOR_INSTRUCTIONS ); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); } /** * build a menu with Look & Feel and About across the top */ private void buildMenu() { // turn on anti-alias System.setProperty( "swing.aatext", "true" ); final JMenuBar menubar = new JMenuBar(); setJMenuBar( menubar ); final JMenu lafMenu = Laf.buildLookAndFeelMenu(); if ( lafMenu != null ) { menubar.add( lafMenu ); } final JMenu menuHelp = new JMenu( "Help" ); menubar.add( menuHelp ); final JMenuItem aboutItem = new JMenuItem( "About" ); menuHelp.add( aboutItem ); aboutItem.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { // open aboutbox frame new CMPAboutJBox( TITLE_STRING, VERSION_STRING, "Converts codePoints to surrogate pair literals", "", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "SURROGATEPAIR", "1.8" ); } } ); } /** * invoked when spinner changes value */ private void inputTextChanged() { // Leave input as is final String text = inputText.getText(); { // search for \Uxxxxxxxx patterns, replacing them. With surrogate pairs for surrogate Pair display. // must be StringBuffer to be compatible with Matcher. StringBuffer sb = new StringBuffer( text.length() + 40 ); final Matcher m = CU8.matcher( text ); while ( m.find() ) { // will not fail. Regex pattern ensures valid // regexDump( m, "cu8" ); final String codePointString = m.group( 1 );// without leading \U int codePoint; try { codePoint = Integer.parseInt( codePointString, 16 ); // without leading \U or \\u } catch ( NumberFormatException e ) { err.println( "program bug: problem parsing " + codePointString ); codePoint = '?'; } // patch replacement string to ensure $ and \ treated literally. m.appendReplacement( sb, Matcher.quoteReplacement( toSurrogatePairLiteral( codePoint ) ) ); } m.appendTail( sb ); surrogatePairLiterals.setText( sb.toString() ); } { // search for \\Uxxxxxxxx and \\uxxxx patterns, replacing them. With surrogate pairs for render display. // must be StringBuffer to be compatible with Matcher. StringBuffer sb = new StringBuffer( text.length() ); final Matcher m = CU8U4.matcher( text ); while ( m.find() ) { // should not fail. Regex pattern ensures valid // regexDump( m, "cu8u4" ); // result will show up in slot 2 for \Uxxxxxxxx and slot 4 for \\uxxxx final String codePointString = ( m.group( 1 ) != null ) ? m.group( 1 ) : m.group( 2 ); int codePoint; try { codePoint = Integer.parseInt( codePointString, 16 ); // without leading \U or \\u } catch ( NumberFormatException e ) { err.println( "program bug: problem parsing " + codePointString ); codePoint = '?'; } // patch replacement string to ensure $ and \ treated literally. m.appendReplacement( sb, Matcher.quoteReplacement( toSurrogatePair( codePoint ) ) ); // as 1 or 2 chars. } m.appendTail( sb ); renderText.setText( sb.toString() ); } } /** * Install listeners */ private void installHooks() { codePointSpinner.addChangeListener( new ChangeListener() { /** * spinner changed value * @param e event from spinner */ public void stateChanged( ChangeEvent e ) { spinnerChanged(); } // end stateChanged } ); // textArea.InputMethodListener does not work unless you override // getInputMethodRequests. // Must add DocumentListener to the associated document, not the // JTextArea. Document document = inputText.getDocument(); document.addDocumentListener( new DocumentListener() { /** * Gives notification that an attribute or set of attributes * changed. * @param e the document event */ public void changedUpdate( DocumentEvent e ) { inputTextChanged(); } /** * Gives notification that there was an insert into the * document. The range given by the DocumentEvent bounds the * freshly inserted region. * @param e the document event */ public void insertUpdate( DocumentEvent e ) { inputTextChanged(); } /** * Gives notification that a portion of the document has * been removed. The range is given in terms of what the * view last saw (that is, before updating sticky * positions). * @param e the document event */ public void removeUpdate( DocumentEvent e ) { inputTextChanged(); } } ); } /** * Layout components in a GridBag * * @param contentPane JApplet contentPane. */ private void layoutComponents( Container contentPane ) { // layout // ---0--------------------------1--- // 0 ----------- title ------title2- 0 // 1 inputTextLabel codePointSpinner 1 // 2 ----------inputText------------- 2 // 3 outputTextLabel----------------- 3 // 4 --------- outputText ----------- 4 // 5 renderTextLabel----------------- 5 // 6 --------- renderText ----------- 6 // 7 ----------instructions --------- 7 int line = 0; // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title, new GridBagConstraints( 0, line, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( margin[ 0 ], margin[ 1 ], 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title2, new GridBagConstraints( 1, line++, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( margin[ 0 ], 5, 5, margin[ 3 ] ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( inputTextLabel, new GridBagConstraints( 0, line, 2, 1, 0.0, 0.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.HORIZONTAL, new Insets( 2, margin[ 1 ], 0, 2 ), 40, 20 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( codePointSpinner, new GridBagConstraints( 1, line++, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 2, 2, 2, margin[ 3 ] ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( inputText, new GridBagConstraints( 0, line++, 2, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets( 0, margin[ 1 ], 5, margin[ 3 ] ), 40, 20 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( surrogatePairLiteralsLabel, new GridBagConstraints( 0, line++, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( 2, margin[ 1 ], 0, margin[ 3 ] ), 40, 20 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( surrogatePairLiterals, new GridBagConstraints( 0, line++, 2, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets( 0, margin[ 1 ], 5, margin[ 3 ] ), 40, 20 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( renderTextLabel, new GridBagConstraints( 0, line++, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( 2, margin[ 1 ], 0, margin[ 3 ] ), 40, 20 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( renderText, new GridBagConstraints( 0, line++, 2, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets( 0, margin[ 1 ], 5, margin[ 3 ] ), 40, 20 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( instructions, new GridBagConstraints( 0, line, 2, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( 5, margin[ 1 ], margin[ 2 ], margin[ 3 ] ), 0, 0 ) ); } /** * debugging tool for looking and regex find results * * @param m Matcher used for find. * @param desc description of regex */ @SuppressWarnings( { "UnusedDeclaration" } ) private void regexDump( final Matcher m, final String desc ) { final int count = m.groupCount(); err.println( ">>> " + desc + " start:" + m.start() + " end:" + m.end() + " length:" + ( m.end() - m.start() ) + " groups:" + count ); for ( int i = 0; i <= count; i++ ) { err.println( " " + i + ". " + m.group( i ) ); } } /** * invoked when spinner changes value */ private void spinnerChanged() { int codePoint = codePointNumberModel.getNumber().intValue(); final StringBuilder sb = new StringBuilder( 12 ); sb.append( '\"' ); sb.append( toCLiteral( codePoint ) ); sb.append( "\"" ); // change event will render other two fields inputText.setText( sb.toString() ); } /** * Allow JApplet to be run as an Application * * @param args not used. */ public static void main( String[] args ) { HybridJ.fireup( new SurrogatePair(), TITLE_STRING + " " + VERSION_STRING, APPLET_WIDTH, APPLET_HEIGHT ); } /** * Called by the browser or Applet viewer to inform * this Applet that it is being reclaimed and that it should destroy * any resources that it has allocated. */ @Override public void destroy() { codePointNumberModel = null; codePointSpinner = null; inputText = null; inputTextLabel = null; instructions = null; renderText = null; renderTextLabel = null; surrogatePairLiterals = null; surrogatePairLiteralsLabel = null; title2 = null; title = null; } /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ @Override public void init() { Container contentPane = this.getContentPane(); if ( !VersionCheck.isJavaVersionOK( 1, 7, 0, contentPane ) ) { // effectively abort return; } buildMenu(); contentPane.setLayout( new GridBagLayout() ); contentPane.setBackground( BACKGROUND_FOR_APPLET ); buildComponents(); layoutComponents( contentPane ); installHooks(); this.validate(); this.setVisible( true ); } /** * Applet has scrolled onscreen. */ public void start() { inputTextChanged(); validate(); } /** * Applet scrolled offscreen */ public void stop() { } }