/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.ins.method;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import com.sun.max.ins.*;
import com.sun.max.ins.gui.*;
import com.sun.max.ins.type.*;
import com.sun.max.ins.util.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.actor.member.MethodKey.DefaultMethodKey;
import com.sun.max.vm.classfile.*;
import com.sun.max.vm.classfile.constant.*;
import com.sun.max.vm.type.*;
/**
* A dialog for obtaining a {@linkplain MethodKey method key} based on a name, signature and declaring class entered by
* the user. This dialog implements input validation so that only a valid method key can be specified.
*/
public class MethodKeyInputDialog extends InspectorDialog implements DocumentListener {
/**
* An action that brings up a dialog for choosing a type available on the Inspector's
* classpath. If a type is selected in the dialog, then an associated field is
* updated with the selected type in {@linkplain TypeDescriptor#toJavaString() Java source format}.
*/
class TypeFieldChooser extends InspectorAction {
final JTextComponent field;
TypeFieldChooser(String name, JTextComponent field) {
super(inspection(), name);
this.field = field;
}
@Override
protected void procedure() {
final TypeDescriptor typeDescriptor = TypeSearchDialog.show(inspection(), "Choose declaring class...", "Select");
if (typeDescriptor != null) {
final String javaString = typeDescriptor.toJavaString();
final String newValue;
if (field instanceof JTextArea) {
newValue = updateSelectedParameter(field.getText(), field.getCaretPosition(), javaString);
} else {
newValue = javaString;
}
field.setText(newValue);
}
}
/**
* Replaces a selected parameter in a string of parameter types separated by commas.
*
* @param parameters a string composed of parameter types separated by commas
* @param caretPosition an index in {@code parameters} denoting the parameter to be replaced
* @param replacementParameter the parameter type to replace the parameter in {@code parameters} denoted by
* {@code caretPosition}
* @return the value of {@code parameters} after the replacement has been made
*/
private String updateSelectedParameter(String parameters, int caretPosition, String replacementParameter) {
final String before = parameters.substring(0, caretPosition);
final int start = before.lastIndexOf(',');
final int end = parameters.indexOf(',', caretPosition);
if (start < 0) {
if (end < 0) {
return replacementParameter;
}
return replacementParameter + parameters.substring(end);
}
if (end < 0) {
return before.substring(0, start + 1) + " " + replacementParameter;
}
return before.substring(0, start + 1) + " " + replacementParameter + parameters.substring(end);
}
}
class MethodKeyMessage extends JLabel {
void clear() {
setText("");
}
void setMessage(boolean isError, String message) {
if (!isError) {
super.setText(message);
} else {
super.setText("<html><i><font color=\"red\">" + message + "</font></i>");
}
}
}
private final MethodKeyMessage methodKeyMessage = new MethodKeyMessage();
private final JButton okButton;
private final JTextField holderField;
private final JTextField nameField;
private final JTextField returnTypeField;
private final JTextArea parametersField;
private DefaultMethodKey methodKey;
public void changedUpdate(DocumentEvent e) {
InspectorError.unexpected();
}
public void insertUpdate(DocumentEvent e) {
okButton.setEnabled(updateMethodKey());
}
public void removeUpdate(DocumentEvent e) {
okButton.setEnabled(updateMethodKey());
}
/**
* Updates the method key based on the values entered by the user.
*
* @return true if the user entered value constitute a valid method key
*/
private boolean updateMethodKey() {
TypeDescriptor holder = null;
Utf8Constant name = null;
String returnType = null;
String parameterTypes = null;
methodKeyMessage.setMessage(false, "");
final String nameString = nameField.getText();
if (!nameString.isEmpty()) {
name = SymbolTable.makeSymbol(nameString);
if (!ClassfileReader.isValidMethodName(name, true)) {
name = null;
methodKeyMessage.setMessage(true, "Name is not a valid Java identifier");
}
}
final String holderName = holderField.getText();
if (!holderName.isEmpty()) {
try {
holder = JavaTypeDescriptor.getDescriptorForJavaString(holderName);
} catch (ClassFormatError e) {
holder = null;
methodKeyMessage.setMessage(true, "Invalid name for declaring class");
}
}
final String returnTypeName = returnTypeField.getText();
if (!returnTypeName.isEmpty()) {
try {
returnType = JavaTypeDescriptor.getDescriptorForJavaString(returnTypeName).string;
} catch (ClassFormatError e) {
returnType = null;
methodKeyMessage.setMessage(true, "Invalid name for return type");
}
}
final String parameterTypeNames = parametersField.getText();
if (parameterTypeNames.isEmpty()) {
parameterTypes = "";
} else {
final String[] names = parameterTypeNames.split("[\\s]*[,\\s][\\s]*");
parameterTypes = "";
for (int i = 0; i != names.length; ++i) {
try {
final String n = names[i];
if (n == null || n.isEmpty()) {
throw new ClassFormatError();
}
parameterTypes += JavaTypeDescriptor.getDescriptorForJavaString(n).string;
} catch (ClassFormatError classFormatError) {
parameterTypes = null;
methodKeyMessage.setMessage(true, "Invalid name for parameter " + (i + 1));
}
}
}
if (holder != null && name != null && returnType != null && parameterTypes != null) {
methodKey = new DefaultMethodKey(holder, name, SignatureDescriptor.create("(" + parameterTypes + ")" + returnType));
methodKeyMessage.setMessage(false, methodKey.toString(true));
return true;
}
return false;
}
public MethodKeyInputDialog(Inspection inspection, String title) {
super(inspection, title, true);
holderField = new JTextField(30);
nameField = new JTextField(30);
returnTypeField = new JTextField(30);
parametersField = new JTextArea(5, 30);
final TextLabel holderLabel = new TextLabel(inspection, "Declaring class:");
final JButton holderButton = new JButton(new TypeFieldChooser("...", holderField));
holderButton.setText(null);
holderButton.setIcon(preference().style().generalFindIcon());
holderButton.setToolTipText("Find declaring class...");
final JLabel nameLabel = new JLabel("Name:");
final TextLabel returnTypeLabel = new TextLabel(inspection, "Return type:");
final JButton returnTypeButton = new JButton(new TypeFieldChooser("...", returnTypeField));
returnTypeButton.setText(null);
returnTypeButton.setIcon(preference().style().generalFindIcon());
returnTypeButton.setToolTipText("Find return type...");
final JScrollPane parametersPane = new InspectorScrollPane(inspection, parametersField);
final TextLabel parametersLabel = new TextLabel(inspection, "Parameters:");
final JButton parametersButton = new JButton(new TypeFieldChooser("...", parametersField));
parametersButton.setText(null);
parametersButton.setIcon(preference().style().generalFindIcon());
parametersButton.setToolTipText("Find parameters....");
okButton = new JButton(new AbstractAction("OK") {
public void actionPerformed(ActionEvent e) {
dispose();
}
});
final JButton cancelButton = new JButton(new AbstractAction("Cancel") {
public void actionPerformed(ActionEvent e) {
methodKey = null;
dispose();
}
});
final JPanel statusPanel = new InspectorPanel(inspection, new FlowLayout(FlowLayout.LEFT));
statusPanel.add(new JLabel("Method key:"));
statusPanel.add(methodKeyMessage);
statusPanel.setBorder(BorderFactory.createEtchedBorder());
final JPanel inputPanel = new InspectorPanel(inspection);
final GroupLayout layout = new GroupLayout(inputPanel);
inputPanel.setLayout(layout);
layout.setAutoCreateContainerGaps(true);
layout.setAutoCreateGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup().
addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING).
addComponent(holderLabel).
addComponent(nameLabel).
addComponent(returnTypeLabel).
addComponent(parametersLabel)).
addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING).
addComponent(holderField).
addComponent(nameField).
addComponent(returnTypeField).
addComponent(parametersPane).
addComponent(okButton)).
addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).
addComponent(holderButton).
addComponent(returnTypeButton).
addComponent(parametersButton).
addComponent(cancelButton)));
layout.setVerticalGroup(layout.createSequentialGroup().
addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).
addComponent(holderLabel).
addComponent(holderField).
addComponent(holderButton)).
addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).
addComponent(nameLabel).
addComponent(nameField)).
addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).
addComponent(returnTypeLabel).
addComponent(returnTypeField).
addComponent(returnTypeButton)).
addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).
addComponent(parametersLabel).
addComponent(parametersPane).
addComponent(parametersButton)).
addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).
addComponent(okButton).
addComponent(cancelButton)));
final JPanel contenPane = new JPanel(new BorderLayout());
contenPane.add(inputPanel, BorderLayout.CENTER);
contenPane.add(statusPanel, BorderLayout.SOUTH);
setContentPane(contenPane);
pack();
inspection.gui().moveToMiddle(this);
holderField.getDocument().addDocumentListener(this);
nameField.getDocument().addDocumentListener(this);
returnTypeField.getDocument().addDocumentListener(this);
parametersField.getDocument().addDocumentListener(this);
// Make pressing "Enter" equivalent to pressing the "Select" button.
getRootPane().setDefaultButton(okButton);
// Make pressing "Escape" equivalent to pressing the "Cancel" button.
getRootPane().registerKeyboardAction(cancelButton.getAction(), KeyStroke.getKeyStroke((char) KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
}
/**
* Gets the method key specified by the user's input to this dialog.
*
* @return null if the dialog was canceled or has never been made {@linkplain #setVisible(boolean) visible}
*/
public MethodKey methodKey() {
return methodKey;
}
/**
* Displays a dialog for specifying a method key.
*
* @param title
* title string for the dialog frame
* @return the reference to the method key input by the user or null if the user canceled the dialog
*/
public static MethodKey show(Inspection inspection, String title) {
final MethodKeyInputDialog dialog = new MethodKeyInputDialog(inspection, title);
dialog.setVisible(true);
return dialog.methodKey();
}
}