/**
Make_Get_and_Set_Functions.bsh - a BeanShell macro for
the jEdit text editor  that creates simple get() and set()
methods for the variables on selected lines.

Copyright (C)  2004 Thomas Galvin - software@thomas-galvin.com
based on Make_Get_and_Set_Methods.bsh by John Gellene

This macro will work on multiple selected lines; for instance,
selecting

<code>
public int foo;
public int bar;
</code>

and running the macro will produce get and set functions for both
variables, along with comments.  This macro produces c-style
functions, unless the buffer is in java mode.

Modifications by Dale Anson, Dec 2008:

1. Allows variable declarations to have an initial assignment, like
<code>
public int foo = 1;
public int bar = 2;
</code>

2. Allows multiple variables on same line, like
<code>
public int foo, bar;
</code>

3. Use line separator as set in buffer properties rather than always using \n.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
*/


boolean JAVA_MODE = buffer.getMode().getName().equals( "java" );

// use line separator from current buffer
String LS = buffer.getStringProperty( "lineSeparator" );
if (LS == null) {
    // otherwise, use default line separator
    LS = jEdit.getProperty("buffer.lineSeparator");   
}

boolean createGetMethods = true;
boolean createSetMethods = true;

void setCaret( int selectionStart, int selectionEnd ) {
    textArea.setCaretPosition( selectionStart );
    textArea.moveCaretPosition( selectionEnd );
}

String getClassName() {
    int selectionStart;
    int selectionEnd;
    if(textArea.getSelection().length != 0){  // if there are selections exists
        selectionStart = textArea.getSelection(0).getStart();
        selectionEnd = textArea.getSelection(0).getEnd();
    }
    else {		// if no selection
        selectionStart = textArea.getCaretPosition();
        selectionEnd = textArea.getCaretPosition();
    }

    String text = textArea.getText();
    int index = text.lastIndexOf( "class", selectionStart );
    if ( index != -1 ) {
        textArea.setCaretPosition( index );
        int lineNumber = textArea.getCaretLine();
        int lineEnd = textArea.getLineEndOffset( lineNumber );
        String lineText = text.substring( index, lineEnd );

        StringTokenizer tokenizer = new StringTokenizer( lineText );
        tokenizer.nextToken(); //eat "class"
        if ( tokenizer.hasMoreTokens() ) {
            setCaret( selectionStart, selectionEnd );
            return tokenizer.nextToken();
        }
    }
    setCaret( selectionStart, selectionEnd );

    String fileClassName = buffer.getName();
    int index = fileClassName.lastIndexOf( '.' );
    if ( index != -1 ) {
        fileClassName = fileClassName.substring( 0, index );
        if ( fileClassName.toLowerCase().indexOf( "untitled" ) == -1 ) {
            return fileClassName;
        }
    }

    return "";
}

String createJavaGetMethod( String type, String variableName ) {
    String uppperVariable = Character.toUpperCase( variableName.charAt( 0 ) ) + variableName.substring( 1, variableName.length() );
    String result =
        "\t/**" + LS +
        "\t * Returns the value of " + variableName + "." + LS +
        "\t */" + LS +
        "\tpublic " + type + " get" + uppperVariable + "() {" + LS +
        "\t\treturn " + variableName + ";" + LS +
        "\t}" + LS;

    return result;
}

String createJavaSetMethod( String type, String variableName ) {
    String uppperVariable = Character.toUpperCase( variableName.charAt( 0 ) ) + variableName.substring( 1, variableName.length() );
    String result =
        "\t/**" + LS +
        "\t * Sets the value of " + variableName + "." + LS +
        "\t * @param " + variableName + " The value to assign " + variableName + "." + LS +
        "\t */" + LS +
        "\tpublic void set" + uppperVariable + "(" + type + " " + variableName + ") {" + LS +
        "\t\tthis." + variableName + " = " + variableName + ";" + LS +
        "\t}" + LS;

    return result;
}

String createCppGetMethod( String className, String type, String variableName ) {
    String scopeIndicator = "";
    if ( className != null && className.compareTo( "" ) != 0 ) {
        scopeIndicator = className + "::";
    }
    if (type == null) {
        type = "";   
    }
    System.out.println("7 " + className + ", " + type + ", " + variableName);
    String uppperVariable = Character.toUpperCase( variableName.charAt( 0 ) ) + variableName.substring( 1, variableName.length() );
    System.out.println("8");
    String result =
        "/*" + LS +
        "function: get" + uppperVariable + "()" + LS +
        "Returns the value of " + variableName + "." + LS +
        "*/" + LS +
        type + (type.length() > 0 ? " " : "") + scopeIndicator + "get" + uppperVariable + "()" + "" + LS +
        "{" + "" + LS +
        "  return " + variableName + ";" + "" + LS +
        "}" + LS;

    return result;
}

String createCppSetMethod( String className, String type, String variableName ) {
    String scopeIndicator = "";
    if ( className != null && className.compareTo( "" ) != 0 ) {
        scopeIndicator = className + "::";
    }

    String uppperVariable = Character.toUpperCase( variableName.charAt( 0 ) ) + variableName.substring( 1, variableName.length() );
    String setVariable = variableName + "Value";
    String result =
        "/*" + LS +
        "function: set" + uppperVariable + "()" + LS +
        "Sets the value of " + variableName + "." + LS +
        "Input: " + setVariable + " The value to assign " + variableName + "." + LS +
        "*/" + LS +
        "void " + scopeIndicator + "set" + uppperVariable + "(const " + type + "& " + setVariable + ")" + LS +
        "{" + "" + LS +
        "  " + variableName + " = " + setVariable + ";" + "" + LS +
        "}" + LS;

    return result;
}

void parseSelection() {
    int selectionStart;
    int selectionEnd;
    if(textArea.getSelection().length != 0){  // if there are selections exists
        selectionStart = textArea.getSelection(0).getStart();
        selectionEnd = textArea.getSelection(0).getEnd();
    }
    else {		// if no selection
        selectionStart = textArea.getCaretPosition();
        selectionEnd = textArea.getCaretPosition();
    }
    textArea.setCaretPosition( selectionStart );
    int startLine = textArea.getCaretLine();

    textArea.setCaretPosition( selectionEnd );
    int endLine = textArea.getCaretLine();

    StringBuffer code = new StringBuffer();
    String className = getClassName();

    for ( int i = startLine; i <= endLine; i++ ) {
        // parse each line for variable declaration
        String lineText = textArea.getLineText( i );
        if ( lineText != null && lineText.length() > 0 ) {
            System.out.println("1");
            // remove leading and trailing whitespace
            lineText = lineText.trim();
            if ( lineText.length() == 0 ) {
                continue;   // nothing to do with this line
            }

            // remove semi-colon
            if ( lineText.endsWith( ";" ) ) {
                lineText = lineText.substring( 0, lineText.length() - 1 );
            }
            System.out.println("2");
            // remove initial assignment if present
            if ( lineText.indexOf( "=" ) > 0 ) {
                lineText = lineText.substring( 0, lineText.indexOf( "=" ) );
            }
            System.out.println("3");
            lineText = lineText.trim();
            if ( lineText.length() == 0 ) {
                continue;
            }
            System.out.println("4");
            // list to hold variable names
            ArrayList variables = new ArrayList();

            // could have declaration like int x, y; so split them out into the
            // variables array
            if ( lineText.indexOf( "," ) > 0 ) {
                int index = lineText.indexOf( "," );                        // just after first variable name
                String front = lineText.substring( 0, index );              // up to and including the first variable name
                String back = lineText.substring( index );                  // remaining variable names
                lineText = front.substring( 0, front.lastIndexOf( " " ) );  // adjust remaining line text, this contains the type
                front = front.substring( front.lastIndexOf( " " ) ).trim(); // first variable name
                String[] backs = back.split( "," );                         // remaining variable names
                variables.add( front );                                     // add first variable name to the list
                for ( String back : backs ) {                               // add remaining variable names to the list
                    String maybe = back.trim();
                    if ( maybe.length() > 0 ) {
                        variables.add( maybe );
                    }
                }
            }
            else {
                // just one variable declared
                String var = lineText.substring( lineText.lastIndexOf( " " ) ).trim();
                variables.add( var );
                lineText = lineText.substring( 0, lineText.lastIndexOf( " " ) ).trim();
            }
            System.out.println("5");
            if ( lineText.trim().length() == 0 ) {
                continue;       // no type declared for this variable
            }

            // get the variable type
            String type = "";
            if (lineText.lastIndexOf( " " ) > 0) {
                type = lineText.substring( lineText.lastIndexOf( " " ) );
            }
            type = type.trim();
            System.out.println("5.1");
            
            if ( variables.size() > 0 ) {
                code.append( LS );
            }

            // create the get and set methods for each variable
            for ( String variable : variables ) {
                if ( createGetMethods ) {
                    String tmp = JAVA_MODE ? createJavaGetMethod( type, variable ) : createCppGetMethod( className, type, variable );
                    if ( tmp != null && tmp.length() > 0 ) {
                        code.append( tmp ).append( LS );
                    }
                }

                if ( createSetMethods && lineText.indexOf( "final " ) == -1 && lineText.indexOf( "const " ) == -1 ) {
                    String tmp = JAVA_MODE ? createJavaSetMethod( type, variable ) : createCppSetMethod( className, type, variable );

                    if ( tmp != null && tmp.compareTo( "" ) != 0 ) {
                        code.append( LS ).append( tmp ).append( LS );
                    }
                }
            }
        }
    }

    // move to the end of the selected text
    textArea.setCaretPosition( selectionEnd );

    // insert get/set methods
    textArea.setSelectedText( code.toString() );

    // select the inserted code and indent it
    textArea.setCaretPosition( selectionEnd );
    textArea.moveCaretPosition( selectionEnd + code.length(), true );
    textArea.indentSelectedLines();
}

void displayPrompt() {
    String DONE = "Generate Code";
    String CANCEL = "Cancel";

    JCheckBox getCheckbox = new JCheckBox( "Create Get Methods", true );
    JCheckBox setCheckbox = new JCheckBox( "Create Set Methods", true );

    JPanel checkBoxPanel = new JPanel(new BorderLayout());
    checkBoxPanel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
    checkBoxPanel.add( getCheckbox, BorderLayout.NORTH );
    checkBoxPanel.add( setCheckbox, BorderLayout.SOUTH );

    JButton createButton = new JButton( DONE );
    JButton cancelButton = new JButton( CANCEL );

    JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 6, 0));
    buttonPanel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
    buttonPanel.add( createButton, BorderLayout.WEST );
    buttonPanel.add( cancelButton, BorderLayout.EAST );

    JPanel mainPanel = new JPanel();
    mainPanel.setLayout( new BorderLayout() );
    mainPanel.add( checkBoxPanel, BorderLayout.NORTH );
    mainPanel.add( buttonPanel, BorderLayout.SOUTH );

    String title = "Create Get and Set Methods";
    JDialog dialog = new JDialog( view, title, false );
    dialog.setContentPane( mainPanel );

    actionPerformed( ActionEvent e ) {
        if ( e.getSource() == createButton ) {
            createGetMethods = getCheckbox.isSelected();
            createSetMethods = setCheckbox.isSelected();
            parseSelection();
        }
        this.dialog.dispose();
        return ;
    }

    createButton.addActionListener( this );
    cancelButton.addActionListener( this );

    dialog.pack();
    dialog.setLocationRelativeTo( view );
    dialog.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE );
    dialog.setVisible( true );
    createButton.requestFocus();
}

if ( buffer.isReadOnly() )
    Macros.error( view, "Buffer is read-only." );
else
    displayPrompt();