package com.redhat.ceylon.eclipse.code.correct;
import static com.redhat.ceylon.eclipse.code.correct.ImportProposals.importProposals;
import static com.redhat.ceylon.eclipse.code.editor.Navigation.gotoLocation;
import static com.redhat.ceylon.eclipse.util.EditorUtil.getCurrentEditor;
import static com.redhat.ceylon.eclipse.util.EditorUtil.performChange;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.text.edits.InsertEdit;
import com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.eclipse.code.refactor.CreateUnitChange;
import com.redhat.ceylon.eclipse.code.wizard.SelectNewUnitWizard;
import com.redhat.ceylon.eclipse.util.Highlights;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import ceylon.interop.java.CeylonList;
class CreateInNewUnitProposal implements ICompletionProposal,
ICompletionProposalExtension6 {
private final IFile file;
private final DefinitionGenerator dg;
private final Tree.CompilationUnit rootNode;
CreateInNewUnitProposal(IFile file, DefinitionGenerator dg,
Tree.CompilationUnit rootNode) {
this.file = file;
this.dg = dg;
this.rootNode = rootNode;
}
@Override
public Point getSelection(IDocument doc) {
return null;
}
@Override
public Image getImage() {
return dg.getImage();
}
@Override
public String getDisplayString() {
return "Create toplevel " + dg.getDescription() +
" in a new source file";
}
@Override
public IContextInformation getContextInformation() {
return null;
}
@Override
public String getAdditionalProposalInfo() {
return null;
}
@Override
public void apply(IDocument doc) {
SelectNewUnitWizard w =
new SelectNewUnitWizard("Create in New Source File",
"Create a new Ceylon source file with the missing declaration.",
dg.getBrokenName());
if (w.open(file)) {
IPackageFragment pack = w.getPackageFragment();
String packageName =
pack==null ? "" : pack.getElementName();
boolean samePackage =
packageName.equals(rootNode.getUnit()
.getPackage().getNameAsString());
Change change = createChange(doc, w, samePackage);
if (!samePackage) {
change = new CompositeChange("Create in New Source File",
new Change[] {change, importChange(doc, packageName)});
}
try {
performChange(getCurrentEditor(),
doc, change,
"Create in New Source File");
gotoLocation(w.getFile().getFullPath(), 0);
}
catch (CoreException e) {
e.printStackTrace();
}
}
}
private CreateUnitChange createChange(IDocument doc,
SelectNewUnitWizard w,
boolean samePackage) {
return new CreateUnitChange(w.getFile(),
w.includePreamble(),
getText(doc, samePackage),
w.getProject(),
"Create in New Source File");
}
private TextFileChange importChange(IDocument doc,
String packageName) {
TextFileChange tfc =
new TextFileChange("Add Import", file);
Tree.Import importNode =
importProposals().findImportNode(rootNode, packageName);
if (importNode==null) {
tfc.setEdit(new InsertEdit(
(int) importProposals().getBestImportInsertPosition(rootNode),
"import " + packageName +
" { " + dg.getBrokenName() + " }" +
utilJ2C().indents().getDefaultLineDelimiter(doc)));
}
else {
tfc.setEdit(new InsertEdit(
(int) importProposals().getBestImportMemberInsertPosition(importNode),
"," + utilJ2C().indents().getDefaultLineDelimiter(doc) +
utilJ2C().indents().getDefaultIndent() + dg.getBrokenName()));
}
return tfc;
}
private String getText(IDocument doc, boolean samePackage) {
String delim = utilJ2C().indents().getDefaultLineDelimiter(doc);
String definition = samePackage ?
dg.generate("", delim) :
dg.generateShared("", delim);
List<Declaration> imports = new ArrayList<Declaration>();
resolveImports(imports, dg.getReturnType());
if (dg.getParameters()!=null) {
resolveImports(imports, dg.getParameters().values());
}
String imps = importsJ2C().importCleaner().createImports(
new CeylonList<>(TypeDescriptor.klass(Declaration.class), imports),
new correctJ2C().newDocument(doc));
if (!imps.isEmpty()) {
definition = imps + delim + delim + definition;
}
return definition;
}
static void addCreateInNewUnitProposal(Collection<ICompletionProposal> proposals,
DefinitionGenerator dg, IFile file, Tree.CompilationUnit rootNode) {
proposals.add(new CreateInNewUnitProposal(file, dg, rootNode));
}
private static void resolveImports(List<Declaration> imports,
Collection<Type> producedTypes) {
if (producedTypes!=null) {
for (Type pt : producedTypes) {
resolveImports(imports, pt);
}
}
}
private static void resolveImports(List<Declaration> imports, Type pt) {
if (pt!=null) {
if (pt.isUnknown() || pt.isNothing()) {
//nothing to do
}
else if (pt.isUnion()) {
resolveImports(imports, pt.getCaseTypes());
}
else if (pt.isIntersection()) {
resolveImports(imports, pt.getSatisfiedTypes());
}
else if (pt.isTypeParameter()) {
TypeParameter typeParam =
(TypeParameter)
pt.getDeclaration();
if (typeParam.isConstrained()) {
resolveImports(imports, typeParam.getCaseTypes());
resolveImports(imports, typeParam.getSatisfiedTypes());
}
if (typeParam.isDefaulted()) {
resolveImports(imports, typeParam.getDefaultTypeArgument());
}
}
else {
TypeDeclaration td = pt.getDeclaration();
resolveImports(imports, pt.getTypeArgumentList());
Package p = td.getUnit().getPackage();
if (!p.getQualifiedNameString().isEmpty() &&
!p.getQualifiedNameString().equals(Module.LANGUAGE_MODULE_NAME)) {
if (!imports.contains(td)) {
imports.add(td);
}
}
}
}
}
@Override
public StyledString getStyledDisplayString() {
return Highlights.styleProposal(getDisplayString(), false);
}
}