package org.xtest.formatting;
import static com.google.common.collect.Lists.newArrayList;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtend.core.formatting.OrganizeImports;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmMember;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.TextRegion;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XAssignment;
import org.eclipse.xtext.xbase.XBinaryOperation;
import org.eclipse.xtext.xbase.XConstructorCall;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XFeatureCall;
import org.eclipse.xtext.xbase.XInstanceOfExpression;
import org.eclipse.xtext.xbase.XMemberFeatureCall;
import org.eclipse.xtext.xbase.XTypeLiteral;
import org.eclipse.xtext.xbase.XUnaryOperation;
import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation;
import org.xtest.xTest.Body;
import org.xtest.xTest.XTestPackage;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import com.google.inject.Provider;
/**
* Custom import organizer that organizes imports for Xtest files instead of Xtend files
*
* Overridden methods borrowed from Xtend 2.3 implementation, with changes made specifically for
* Xtest. Hopefully in subsequent versions of Xtend this could become more easily extensible
*
* @author Michael Barry
*/
@SuppressWarnings("restriction")
public class XtestOrganizeImports extends OrganizeImports {
@Inject
private Provider<ReferenceAcceptor> referenceAcceptorProvider;
@Override
public void collectAllReferences(XtextResource resource, ReferenceAcceptor acceptor) {
Body xtestFile = getXtestFile(resource);
for (XExpression expression : xtestFile.getExpressions()) {
TreeIterator<EObject> contents = EcoreUtil.getAllContents(expression, true);
while (contents.hasNext()) {
EObject next = contents.next();
if (next instanceof JvmTypeReference) {
acceptor.acceptType((JvmTypeReference) next);
} else if (next instanceof XAnnotation) {
acceptor.acceptType(((XAnnotation) next).getAnnotationType());
} else if (next instanceof XInstanceOfExpression) {
acceptor.acceptType(((XInstanceOfExpression) next).getType());
} else if (next instanceof XConstructorCall) {
acceptor.acceptType(((XConstructorCall) next).getConstructor()
.getDeclaringType());
} else if (next instanceof XTypeLiteral) {
acceptor.acceptType(((XTypeLiteral) next).getType());
} else if (next instanceof XFeatureCall) {
final XFeatureCall featureCall = (XFeatureCall) next;
if (featureCall.getDeclaringType() == null) {
final JvmIdentifiableElement member = featureCall.getFeature();
if (member instanceof JvmOperation) {
if (((JvmOperation) member).isStatic()) {
acceptor.acceptStaticImport((JvmMember) member);
}
}
if (member instanceof JvmField) {
if (((JvmField) member).isStatic()) {
acceptor.acceptStaticImport((JvmMember) member);
}
}
} else {
acceptor.acceptType(featureCall.getDeclaringType());
}
} else if (next instanceof XMemberFeatureCall || next instanceof XBinaryOperation
|| next instanceof XUnaryOperation || next instanceof XAssignment) {
final XAbstractFeatureCall featureCall = (XAbstractFeatureCall) next;
final JvmIdentifiableElement member = featureCall.getFeature();
if (member instanceof JvmOperation) {
if (((JvmOperation) member).isStatic()) {
acceptor.acceptStaticExtensionImport((JvmMember) member);
}
}
if (member instanceof JvmField) {
if (((JvmField) member).isStatic()) {
acceptor.acceptStaticExtensionImport((JvmMember) member);
}
}
}
}
}
}
@Override
public TextRegion computeRegion(XtextResource resource) {
final Body xtestFile = getXtestFile(resource);
if (xtestFile == null) {
return null;
}
List<INode> fileparams = NodeModelUtils.findNodesForFeature(xtestFile,
XTestPackage.Literals.BODY__FILEPARAM);
List<INode> imports = NodeModelUtils.findNodesForFeature(xtestFile,
XTestPackage.Literals.BODY__IMPORTS);
int offset = 0;
if (fileparams.size() >= 1) {
INode last = Iterables.getLast(fileparams);
offset = last.getOffset() + last.getLength();
} else if (imports.size() >= 1) {
INode last = Iterables.getLast(imports);
offset = last.getOffset();
}
EList<XExpression> expressions = xtestFile.getExpressions();
int length = NodeModelUtils.findActualNodeFor(xtestFile).getLength() - offset;
if (expressions.size() > 0) {
XExpression object = expressions.get(0);
ICompositeNode node = NodeModelUtils.getNode(object);
if (node == null) {
throw new IllegalStateException("Cannot find node for clazz "
+ xtestFile.getClass().getName());
}
length = node.getOffset() - offset;
}
return new TextRegion(offset, length);
}
@Override
public String getOrganizedImportSection(XtextResource state) {
String trim = super.getOrganizedImportSection(state).trim();
if (!trim.isEmpty()) {
trim += "\n\n";
}
final Body xtestFile = getXtestFile(state);
List<INode> fileparams = NodeModelUtils.findNodesForFeature(xtestFile,
XTestPackage.Literals.BODY__FILEPARAM);
if (fileparams.size() >= 1) {
trim = "\n" + trim;
}
return trim;
}
@Override
public ReferenceAcceptor intitializeReferenceAcceptor(XtextResource state) {
ReferenceAcceptor acceptor = referenceAcceptorProvider.get();
final Body xtendFile = getXtestFile(state);
if (xtendFile == null) {
return null;
}
collectAllReferences(state, acceptor);
acceptor.addImplicitlyImportedPackages(newArrayList("java.lang"));
return acceptor;
}
private Body getXtestFile(XtextResource resource) {
if (resource.getContents().isEmpty()) {
return null;
}
final EObject eObject = resource.getContents().get(0);
if (!(eObject instanceof Body)) {
return null;
}
final Body xtendFile = (Body) eObject;
return xtendFile;
}
}