package com.redhat.ceylon.eclipse.code.refactor;
import java.util.List;
import java.util.Set;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import com.redhat.ceylon.compiler.typechecker.analyzer.ExpressionVisitor;
import com.redhat.ceylon.compiler.typechecker.analyzer.TypeVisitor;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonParser;
import com.redhat.ceylon.compiler.typechecker.parser.LexError;
import com.redhat.ceylon.compiler.typechecker.parser.ParseError;
import com.redhat.ceylon.compiler.typechecker.tree.Message;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.StaticType;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.eclipse.ui.CeylonResources;
import com.redhat.ceylon.eclipse.util.ErrorVisitor;
import com.redhat.ceylon.model.typechecker.model.Cancellable;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.Value;
import com.redhat.ceylon.model.typechecker.model.Unit;
public class AddParameterDialog extends Dialog /*TitleAreaDialog*/ {
private String name;
private Type type;
private String argument;
private Node node;
private Set<String> parameterNames;
private Unit unit;
public AddParameterDialog(Shell parentShell,
Node node, final IProject project,
Set<String> parameterNames) {
super(parentShell);
this.parameterNames = parameterNames;
this.node = node;
name = "something";
unit = node.getUnit();
type = unit.getAnythingDeclaration().getType();
argument = "nothing";
}
@Override
protected boolean isResizable() {
return true;
}
public String getName() {
return name;
}
public Type getType() {
return type;
}
public String getArgument() {
return argument;
}
@Override
protected Control createDialogArea(Composite parent) {
getShell().setText("Add Parameter");
parent = (Composite) super.createDialogArea(parent);
new Label(parent, SWT.NONE).setText("Enter the name and type of the new parameter.");
new Label(parent, SWT.SEPARATOR|SWT.HORIZONTAL).setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(GridLayoutFactory.swtDefaults().numColumns(2).create());
composite.setLayoutData(GridDataFactory.fillDefaults().hint(500, SWT.DEFAULT).grab(true, true).create());
Label typeLabel = new Label(composite, SWT.NONE);
typeLabel.setText("Type:");
final Text typeText = new Text(composite, SWT.SINGLE | SWT.BORDER);
typeText.setText("Anything");
Label nameLabel = new Label(composite, SWT.NONE);
nameLabel.setText("Name:");
final Text nameText = new Text(composite, SWT.SINGLE | SWT.BORDER);
nameText.setText("something");
nameText.selectAll();
nameText.setLayoutData(GridDataFactory.fillDefaults().create());
Label valLabel = new Label(composite, SWT.NONE);
valLabel.setText("Argument:");
final Text argumentText = new Text(composite, SWT.SINGLE | SWT.BORDER);
argumentText.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
argumentText.setText("nothing");
final CLabel errorLabel = new CLabel(composite, SWT.NONE);
errorLabel.setLayoutData(GridDataFactory.fillDefaults().span(4, 1).grab(true, true).create());
errorLabel.setVisible(false);
errorLabel.setImage(CeylonResources.ERROR);
nameText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
if (updateName(nameText, errorLabel)) {
if (updateType(typeText, errorLabel)) {
updateArgument(argumentText, errorLabel);
}
}
}
});
argumentText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
argument = argumentText.getText();
if (updateArgument(argumentText, errorLabel)) {
if (updateType(typeText, errorLabel)) {
updateName(nameText, errorLabel);
}
}
}
});
typeText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent event) {
if (updateType(typeText, errorLabel)) {
if (updateName(nameText, errorLabel)) {
updateArgument(argumentText, errorLabel);
}
}
}
});
typeText.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(300, SWT.DEFAULT).create());
return parent;
}
private boolean updateType(final Text typeText,
final CLabel errorLabel) {
try {
String text = typeText.getText();
// TODO replace with parseTypeExpression() in common
CeylonLexer lexer = new CeylonLexer(new ANTLRStringStream(text));
CommonTokenStream ts = new CommonTokenStream(lexer);
ts.fill();
List<LexError> lexErrors = lexer.getErrors();
if (!lexErrors.isEmpty()) {
errorLabel.setText(lexErrors.get(0).getMessage());
errorLabel.setVisible(true);
return false;
}
CeylonParser parser = new CeylonParser(ts);
StaticType staticType = parser.type();
if (ts.index()<ts.size()-1) {
errorLabel.setText("extra tokens in type expression");
errorLabel.setVisible(true);
return false;
}
List<ParseError> parseErrors = parser.getErrors();
if (!parseErrors.isEmpty()) {
errorLabel.setText(parseErrors.get(0).getMessage());
errorLabel.setVisible(true);
return false;
}
staticType.visit(new Visitor() {
@Override
public void visitAny(Node that) {
that.setUnit(unit);
that.setScope(node.getScope());
super.visitAny(that);
}
});
staticType.visit(new TypeVisitor(unit, Cancellable.ALWAYS_CANCELLED));
staticType.visit(new ExpressionVisitor(unit, Cancellable.ALWAYS_CANCELLED));
errorLabel.setVisible(false);
new ErrorVisitor() {
@Override
protected void handleMessage(int startOffset, int endOffset,
int startCol, int startLine, Message error) {
errorLabel.setText(error.getMessage());
errorLabel.setVisible(true);
}
}.visit(staticType);
if (errorLabel.isVisible()) {
return false;
}
else {
type = staticType.getTypeModel();
return true;
}
}
catch (Exception e) {
errorLabel.setText("Could not parse type expression");
errorLabel.setVisible(true);
return false;
}
}
private boolean updateArgument(final Text argumentText,
final CLabel errorLabel) {
try {
String text = argumentText.getText();
if (text.isEmpty()) {
errorLabel.setText("Missing argument expression");
errorLabel.setVisible(true);
return false;
}
String typeExpression = type==null ? "Anything" : type.asString(unit);
String parameterName = name==null ? "something" : name;
String paramDeclaration = "(" + typeExpression + " " + parameterName + " = " + text + ")";
CeylonLexer lexer = new CeylonLexer(new ANTLRStringStream(paramDeclaration));
CommonTokenStream ts = new CommonTokenStream(lexer);
ts.fill();
List<LexError> lexErrors = lexer.getErrors();
if (!lexErrors.isEmpty()) {
errorLabel.setText(lexErrors.get(0).getMessage());
errorLabel.setVisible(true);
return false;
}
CeylonParser parser = new CeylonParser(ts);
Tree.ParameterList parameters = parser.parameters();
if (ts.index()<ts.size()-1) {
errorLabel.setText("extra tokens in argument expression");
errorLabel.setVisible(true);
return false;
}
List<ParseError> parseErrors = parser.getErrors();
if (!parseErrors.isEmpty()) {
errorLabel.setText(parseErrors.get(0).getMessage());
errorLabel.setVisible(true);
return false;
}
final Value value = new Value();
value.setName(name);
final Parameter param = new Parameter();
param.setName(name);
param.setModel(value);
parameters.visit(new Visitor() {
@Override
public void visitAny(Node that) {
that.setUnit(unit);
that.setScope(node.getScope());
super.visitAny(that);
}
@Override
public void visit(Tree.AttributeDeclaration that) {
that.setDeclarationModel(value);
super.visit(that);
}
@Override public void visit(Tree.ParameterDeclaration that) {
that.setParameterModel(param);
super.visit(that);
}
});
parameters.visit(new TypeVisitor(unit, Cancellable.ALWAYS_CANCELLED));
parameters.visit(new ExpressionVisitor(unit, Cancellable.ALWAYS_CANCELLED));
errorLabel.setVisible(false);
new ErrorVisitor() {
@Override
protected void handleMessage(int startOffset, int endOffset,
int startCol, int startLine, Message error) {
errorLabel.setText(error.getMessage());
errorLabel.setVisible(true);
}
}.visit(parameters);
if (errorLabel.isVisible()) {
return false;
}
else {
this.argument = text;
return true;
}
}
catch (Exception e) {
errorLabel.setText("Could not parse argument expression");
errorLabel.setVisible(true);
return false;
}
}
private boolean updateName(final Text nameText,
final CLabel errorLabel) {
String text = nameText.getText();
if (text.isEmpty()) {
errorLabel.setText("Missing name");
errorLabel.setVisible(true);
return false;
}
else if (!text.matches("^[a-z_]\\w*$")) {
errorLabel.setText("Illegal parameter name");
errorLabel.setVisible(true);
return false;
}
else if (parameterNames.contains(text)) {
errorLabel.setText("Duplicate parameter name");
errorLabel.setVisible(true);
return false;
}
else {
name = text;
errorLabel.setVisible(false);
return true;
}
}
}