/**
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.

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");
boolean createGetMethods = true;
boolean createSetMethods = true;

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

String getClassName()
{
  int selectionStart = textArea.getSelectionStart();
  int selectionEnd = textArea.getSelectionEnd();
  
  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/**\n" + 
  "\t * Returns the value of " + variableName + ".\n" + 
  "\t */\n" + 
  "\tpublic " + type + " get" + uppperVariable + "()" + "\n" +
  "\t{" + "\n" + 
  "\t\treturn " + variableName + ";" + "\n" + 
  "\t}\n";
  
  return result;
}

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

String createCppGetMethod(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 result = 
  "/*\n" + 
  "function: get" + uppperVariable + "()\n" +
  "Returns the value of " + variableName + ".\n" + 
  "*/\n" + 
  type + " " + scopeIndicator + "get" + uppperVariable + "()" + "\n" +
  "{" + "\n" + 
    "  return " + variableName + ";" + "\n" + 
  "}\n";
  
  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 = 
  "/*\n" + 
  "function: set" + uppperVariable + "()\n" +
  "Sets the value of " + variableName + ".\n" + 
  "Input: " + setVariable + " The value to assign " + variableName + ".\n" +
  "*/\n" + 
  "void " + scopeIndicator + "set" + uppperVariable + "(const " + type + "& " + setVariable + ")\n" + 
  "{" + "\n" + 
    "  " + variableName + " = " + setVariable + ";" + "\n" +
  "}\n";
  
  return result;
}

void parseSelection()
{
  int selectionStart = textArea.getSelectionStart();
  int selectionEnd = textArea.getSelectionEnd();
  
  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++)
  {
    String lineText = textArea.getLineText(i);
    if( lineText != null && !lineText.equals("") )
    {
      lineText = lineText.trim();
      if( lineText.endsWith(";") )
      {
        lineText = lineText.substring( 0, lineText.length() -1 );
      }
      
      StringTokenizer tokenizer = new StringTokenizer(lineText);
      int tokenCount = tokenizer.countTokens();
      if(tokenCount >= 2)
      {
        int numGarbage = tokenCount - 2;
        for (int i = 0; i < numGarbage; i++)
        {
          tokenizer.nextToken();
        }
        
        String type = tokenizer.nextToken();
        String variable = tokenizer.nextToken();
        
        if(createGetMethods)
        {
          String tmp = "";
          if(JAVA_MODE)
          {
            tmp =  createJavaGetMethod(type, variable);
          }
          else
          {
            tmp =  createCppGetMethod(className, type, variable);
          }
          
          if(tmp != null && tmp.compareTo("") != 0)
          {
            code.append(tmp + "\n");
          }
        }
        
        if(createSetMethods && lineText.indexOf("final ") == -1 && lineText.indexOf("const ") == -1)
        {
          String tmp = "\n";
          if(JAVA_MODE)
          {
            tmp =  createJavaSetMethod(type, variable);
          }
          else
          {
            tmp =  createCppSetMethod(className, type, variable);
          }
          
          if(tmp != null && tmp.compareTo("") != 0)
          {
            code.append(tmp + "\n");
          }
        }
      }
    }
  }
  
  textArea.setCaretPosition(selectionEnd);
  textArea.setSelectedText(code.toString());
  
  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();
  checkBoxPanel.setLayout(new BorderLayout());
  checkBoxPanel.add(getCheckbox, BorderLayout.NORTH);
  checkBoxPanel.add(setCheckbox, BorderLayout.SOUTH);
  
  JButton createButton = new JButton(DONE);
  JButton cancelButton = new JButton(CANCEL);
  
  JPanel buttonPanel = new JPanel();
  buttonPanel.setLayout(new BorderLayout());
  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();
