package com.redhat.ceylon.eclipse.code.correct;
import static com.redhat.ceylon.eclipse.code.correct.LinkedModeCompletionProposal.getNameProposals;
import static com.redhat.ceylon.eclipse.util.Nodes.addNameProposals;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.link.ProposalPosition;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.ltk.core.refactoring.DocumentChange;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.eclipse.code.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.util.LinkedMode;
import com.redhat.ceylon.ide.common.util.escaping_;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.Unit;
class DestructureProposal extends LocalProposal {
private List<String[]> nameProposals = new ArrayList<String[]>();
private static escaping_ escaping = escaping_.get_();
private boolean isTupleType() {
Class td = node.getUnit().getTupleDeclaration();
return type.getDeclaration().inherits(td);
}
protected DocumentChange createChange(IDocument document, Node expanse,
int endIndex) {
DocumentChange change =
new DocumentChange("Destructure", document);
change.setEdit(new MultiTextEdit());
Unit unit = expanse.getUnit();
String text;
if (isTupleType()) {
List<Type> elementTypes =
unit.getTupleElementTypes(type);
StringBuilder builder = new StringBuilder();
Set<String> used = new HashSet<String>();
for (int i = 0; i<elementTypes.size(); i++) {
Type elementType = elementTypes.get(i);
if (builder.length()!=0) builder.append(", ");
String name = escaping.toInitialLowercase(
elementType.getDeclaration().getName(unit));
if (!used.add(name)) {
name = name + i;
}
Set<String> more = new LinkedHashSet<String>();
more.add(name);
addNameProposals(more, false, name);
nameProposals.add(more.toArray(new String[0]));
builder.append(name);
}
text = "value [" + builder + "] = ";
}
else {
nameProposals.add(getKeyProposals(unit, type));
nameProposals.add(getItemProposals(unit, type));
text = "value key -> item = ";
}
change.addEdit(new InsertEdit(offset, text));
String terminal = expanse.getEndToken().getText();
if (!terminal.equals(";")) {
change.addEdit(new InsertEdit(endIndex, ";"));
exitPos = endIndex+1+text.length();
}
else {
exitPos = endIndex+text.length();
}
return change;
}
static String[] getItemProposals(Unit unit, Type type) {
Type itemType = unit.getValueType(type);
String itemName = escaping.toInitialLowercase(
itemType.getDeclaration().getName(unit));
Set<String> itemMore = new LinkedHashSet<String>();
itemMore.add("item");
itemMore.add(itemName);
addNameProposals(itemMore, false, itemName);
return itemMore.toArray(new String[0]);
}
static String[] getKeyProposals(Unit unit, Type type) {
Type keyType = unit.getKeyType(type);
String keyName = escaping.toInitialLowercase(
keyType.getDeclaration().getName(unit));
Set<String> keyMore = new LinkedHashSet<String>();
keyMore.add("key");
keyMore.add(keyName);
addNameProposals(keyMore, false, keyName);
return keyMore.toArray(new String[0]);
}
@Override
protected int getExitPosition() {
return exitPos;
}
@Override
int getExitSequenceNumber() {
return nameProposals.size()+1;
}
public DestructureProposal(CeylonEditor ceylonEditor, Tree.CompilationUnit cu,
Node node, int currentOffset) {
super(ceylonEditor, cu, node, currentOffset);
}
protected void addLinkedPositions(IDocument document, Unit unit)
throws BadLocationException {
int startOffset = isTupleType() ? offset+7 : offset+6;
int nameOffset = startOffset;
for (int i=0; i<nameProposals.size(); i++) {
String[] proposalList = nameProposals.get(i);
int length = proposalList[0].length();
ProposalPosition namePosition =
new ProposalPosition(document, nameOffset, length, i,
getNameProposals(startOffset, i, proposalList));
LinkedMode.addLinkedPosition(linkedModeModel, namePosition);
nameOffset += isTupleType() ? length + 2 : length + 4;
}
}
@Override
boolean isEnabled(Type resultType) {
if (node==null || node.getUnit()==null) return false;
Class td = node.getUnit().getTupleDeclaration();
Class ed = node.getUnit().getEntryDeclaration();
TypeDeclaration rtd = resultType.getDeclaration();
return rtd.inherits(td) || rtd.inherits(ed);
}
@Override
public String getDisplayString() {
return "Destructure expression";
}
@Override
public StyledString getStyledDisplayString() {
String hint =
CorrectionUtil.shortcut(
"com.redhat.ceylon.eclipse.ui.action.destructure");
return new StyledString(getDisplayString())
.append(hint, StyledString.QUALIFIER_STYLER);
}
static void addDestructureProposal(CeylonEditor ceylonEditor, Tree.CompilationUnit cu,
Collection<ICompletionProposal> proposals,
Node node, int currentOffset) {
DestructureProposal prop =
new DestructureProposal(ceylonEditor, cu, node, currentOffset);
if (prop.isEnabled()) {
proposals.add(prop);
}
}
}