/* Copyright (C) 2004 - 2008 Versant Inc. http://www.db4o.com
This file is part of the sharpen open source java to c# translator.
sharpen is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.
sharpen 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
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
package sharpen.core;
import static sharpen.core.framework.Environments.my;
import static sharpen.core.framework.StaticImports.isStaticImport;
import static sharpen.core.framework.StaticImports.staticImportMethodBinding;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.*;
import sharpen.core.Configuration.MappingFlags;
import sharpen.core.Configuration.MemberMapping;
import sharpen.core.SharpenProblem.ProblemKind;
import sharpen.core.csharp.ast.*;
import sharpen.core.framework.*;
import sharpen.core.framework.CSharpDriver.IAnonymousClassBuilderDelegate;
import sharpen.core.framework.CSharpDriver.IEnumBuilderDelegate;
import sharpen.core.framework.CSharpDriver.IFieldBuilderDelegate;
import sharpen.core.framework.CSharpDriver.IMemberFilter;
import sharpen.core.framework.CSharpDriver.IMethodBuilderDelegate;
import sharpen.core.framework.CSharpDriver.IPropertyBuilderDelegate;
import sharpen.core.framework.CSharpDriver.ITypeBuilderDelegate;
import sharpen.core.framework.IBindingManager.IExtractedEnumInfo;
import sharpen.core.framework.IBindingManager.ITypeInfo;
import sharpen.core.framework.IBindingManager.IVariableInfo;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CSharpBuilder extends ASTVisitor {
private static final String JAVA_LANG_VOID_TYPE = "java.lang.Void.TYPE";
private static final String JAVA_LANG_BOOLEAN_TYPE = "java.lang.Boolean.TYPE";
private static final String JAVA_LANG_CHARACTER_TYPE = "java.lang.Character.TYPE";
private static final String JAVA_LANG_INTEGER_TYPE = "java.lang.Integer.TYPE";
private static final String JAVA_LANG_LONG_TYPE = "java.lang.Long.TYPE";
private static final String JAVA_LANG_BYTE_TYPE = "java.lang.Byte.TYPE";
private static final String JAVA_LANG_SHORT_TYPE = "java.lang.Short.TYPE";
private static final String JAVA_LANG_FLOAT_TYPE = "java.lang.Float.TYPE";
private static final String JAVA_LANG_DOUBLE_TYPE = "java.lang.Double.TYPE";
private static final CSTypeReference OBJECT_TYPE_REFERENCE = new CSTypeReference("object");
private final CSCompilationUnit _compilationUnit;
protected CSTypeDeclaration _currentType;
private CSBlock _currentBlock;
private CSExpression _currentExpression;
private CSMethodBase _currentMethod;
protected BodyDeclaration _currentBodyDeclaration;
protected TypeDeclaration _currentTypeDeclaration;
private CSLabelStatement _currentContinueLabel;
private static final Pattern SUMMARY_CLOSURE_PATTERN = Pattern.compile("\\.(\\s|$)");
private static final Pattern HTML_ANCHOR_PATTERN = Pattern.compile("<([aA])\\s+.+>");
protected CompilationUnitPair _pair;
protected CompilationUnit _ast;
protected Configuration _configuration;
private ASTResolver _resolver;
private IVariableBinding _currentExceptionVariable;
private final DynamicVariable<Boolean> _ignoreExtends = new DynamicVariable<Boolean>(Boolean.FALSE);
private List<Initializer> _instanceInitializers = new ArrayList<Initializer>();
private HashMap<ITypeBinding, String> _currentWildcardParams;
private Stack<ITypeBinding> _currentExpectedType = new Stack<ITypeBinding>();
private final ITypeBinding _objectType;
private final ITypeBinding _classType;
protected NamingStrategy namingStrategy() {
return _configuration.getNamingStrategy();
}
protected WarningHandler warningHandler() {
return _configuration.getWarningHandler();
}
public CSharpBuilder() {
_configuration = my(Configuration.class);
_pair = my(CompilationUnitPair.class);
_ast = my(CompilationUnit.class);
_resolver = my(ASTResolver.class);
_compilationUnit = my(CSCompilationUnit.class);
_compilationUnit.addUsing(new CSUsing("Sharpen"));
_objectType = my(IBindingManager.class).getObjectType();
_classType = my(IBindingManager.class).getClassType();
}
protected CSharpBuilder(CSharpBuilder other) {
_configuration = other._configuration;
_pair = other._pair;
_ast = other._ast;
_resolver = other._resolver;
_compilationUnit = other._compilationUnit;
_currentType = other._currentType;
_currentBlock = other._currentBlock;
_currentExpression = other._currentExpression;
_currentMethod = other._currentMethod;
_currentBodyDeclaration = other._currentBodyDeclaration;
_objectType = other._objectType;
_classType = other._classType;
}
public void setSourceCompilationUnit(CompilationUnit ast) {
_ast = ast;
}
public void run() {
if (null == warningHandler() || null == _ast) {
throw new IllegalStateException();
}
_ast.accept(this);
visit(_ast.getCommentList());
}
@Override
public boolean visit(LineComment node) {
_compilationUnit.addComment(new CSLineComment(node.getStartPosition(), getText(node.getStartPosition(),
node
.getLength())));
return false;
}
private String getText(int startPosition, int length) {
try {
return ((ICompilationUnit) _ast.getJavaElement()).getBuffer().getText(startPosition, length);
} catch (JavaModelException e) {
throw new RuntimeException(e);
}
}
public CSCompilationUnit compilationUnit() {
return _compilationUnit;
}
@Override
public boolean visit(ImportDeclaration node) {
return false;
}
@Override
public boolean visit(final EnumDeclaration node) {
if (SharpenAnnotations.hasIgnoreAnnotation(node))
return false;
final ITypeBinding typeBinding = node.resolveBinding();
final CSTypeContainer current = _currentType != null ? _currentType : _compilationUnit;
IExtractedEnumInfo info = my(IBindingManager.class).getExtractedEnumInfo(typeBinding);
if (info != null) {
processExtractedEnum(node, info);
return false;
}
my(CSharpDriver.class).processEnumDeclaration(this, current, node, new IEnumBuilderDelegate() {
public CSEnum create() {
return new CSEnum(typeName(node));
}
public void map(final CSEnum theEnum) {
theEnum.visibility(mapVisibility(typeBinding));
node.accept(new ASTVisitor() {
@Override
public boolean visit(EnumConstantDeclaration node) {
final String name = identifier(node.getName());
CSEnumValue value;
if (node.arguments().size() == 0)
value = new CSEnumValue(name);
else if (node.arguments().size() != 1) {
addProblem(node, ProblemKind.PARSING_ERROR,
"Enum value can not have more than one initializer.");
return false;
} else {
Object arg = node.arguments().get(0);
if (!(arg instanceof NumberLiteral)) {
addProblem(node, ProblemKind.PARSING_ERROR,
"Invalid enum initializer.");
return false;
}
NumberLiteral literal = (NumberLiteral) arg;
value = new CSEnumValue(name, mapExpression(literal));
}
mapJavadoc(node, value);
theEnum.addValue(value);
return false;
}
@Override
public boolean visit(MethodDeclaration node) {
if (node.isConstructor()) {
int mods = node.getModifiers();
if ((mods == 0) || Modifier.isPrivate(mods))
return false;
}
addProblem(node, ProblemKind.WARNING,
"Enum can contain only fields and a private constructor.");
return false;
}
});
}
public void document(CSEnum theEnum) {
mapJavadoc(node, theEnum);
}
public void fixup(CSEnum member) {
}
});
return false;
}
private CSTypeDeclaration processExtractedEnum(final EnumDeclaration node, final IExtractedEnumInfo info) {
final ITypeBinding typeBinding = node.resolveBinding();
final CSTypeContainer current = getCurrentType(typeBinding);
final String typeName = typeName(node);
final String valueName = info.valueField();
return my(CSharpDriver.class).processExtractedEnumDeclaration(this, current, node,
new ITypeBuilderDelegate() {
CSConstructor _ctor;
CSField _valueField;
int _nextId;
public CSTypeDeclaration create() {
CSClass klass = new CSClass(typeName, CSClassModifier.None);
klass.startPosition(node.getStartPosition());
klass.sourceLength(node.getLength());
return klass;
}
public void map(CSTypeDeclaration klass) {
CSTypeReference intRef = new CSTypeReference("int");
_valueField = new CSField(valueName, intRef, CSVisibility.Public);
_valueField.addModifier(CSFieldModifier.Readonly);
klass.addMember(_valueField);
_ctor = new CSConstructor();
_ctor.visibility(CSVisibility.Protected);
_ctor.addParameter(valueName, intRef);
CSExpression paramRef = new CSReferenceExpression(valueName);
CSExpression valueRef = new CSMemberReferenceExpression(
new CSThisExpression(), valueName);
CSExpression init = new CSInfixExpression("=", valueRef, paramRef);
_ctor.body().addStatement(init);
klass.addMember(_ctor);
klass.visibility(mapVisibility(typeBinding));
}
public void mapMembers(CSTypeDeclaration klass, final IMemberFilter filter) {
List<EnumConstantDeclaration> constants = node.enumConstants();
for (final EnumConstantDeclaration ec : constants) {
int id = ++_nextId;
CSTypeReference typeRef = new CSTypeReference(typeName);
CSharpBuilder.this.mapEnumConstant(klass, id, typeRef, ec);
}
CSharpBuilder.this.mapMembers(node, klass, new IMemberFilter() {
public boolean includeMember(ASTNode member) {
if ((filter != null) && !filter.includeMember(member))
return false;
return true;
}
});
}
public void document(CSTypeDeclaration klass) {
CSharpBuilder.this.mapDocumentation(node, klass);
}
public void fixup(CSTypeDeclaration type) {
CSClass klass = (CSClass) type;
for (CSMember member : klass.members()) {
if (member instanceof CSMethod) {
CSMethod method = (CSMethod) member;
if (method.isAbstract())
klass.modifier(CSClassModifier.Abstract);
}
}
}
});
}
private void mapEnumConstant(CSTypeDeclaration klass, int id, CSTypeReference typeRef,
EnumConstantDeclaration node) {
final String name = identifier(node.getName());
final CSReferenceExpression idRef = new CSReferenceExpression(name + "_ID");
CSExpression initializer;
AnonymousClassDeclaration anon = node.getAnonymousClassDeclaration();
if (anon != null) {
CSAnonymousClassBuilder anonBuilder = mapAnonymousClass(anon);
if (anonBuilder == null)
initializer = new CSNullLiteralExpression();
else {
initializer = anonBuilder.createConstructorInvocation();
CSConstructor ctor = anonBuilder.constructor();
CSConstructorInvocationExpression cie;
if (ctor.chainedConstructorInvocation() == null) {
cie = new CSConstructorInvocationExpression(new CSBaseExpression());
cie.addArgument(idRef);
ctor.chainedConstructorInvocation(cie);
} else {
CSConstructorInvocationExpression oldCie = ctor.chainedConstructorInvocation();
cie = new CSConstructorInvocationExpression(new CSBaseExpression());
ctor.chainedConstructorInvocation(cie);
cie.addArgument(idRef);
for (CSExpression arg : oldCie.arguments())
cie.addArgument(arg);
}
}
} else {
CSConstructorInvocationExpression cie = new CSConstructorInvocationExpression(typeRef);
cie.addArgument(idRef);
for (Object o : node.arguments()) {
Expression expr = (Expression) o;
cie.addArgument(mapExpression(expr));
}
initializer = cie;
}
CSField field = new CSField(name, typeRef, CSVisibility.Public);
field.initializer(initializer);
field.addModifier(CSFieldModifier.Static);
field.addModifier(CSFieldModifier.Readonly);
klass.addMember(field);
CSTypeReference intRef = new CSTypeReference("int");
CSField idField = new CSField(name + "_ID", intRef, CSVisibility.Public);
idField.initializer(new CSNumberLiteralExpression(Integer.toString(id)));
idField.addModifier(CSFieldModifier.Const);
klass.addMember(idField);
}
@Override
public boolean visit(AnnotationTypeDeclaration node) {
// TODO: SHA-51
return false;
}
@Override
public boolean visit(MarkerAnnotation node) {
// TODO: SHA-51
return false;
}
@Override
public boolean visit(NormalAnnotation node) {
// TODO: SHA-51
return false;
}
@Override
public boolean visit(final LabeledStatement node) {
String identifier = node.getLabel().getIdentifier();
_currentContinueLabel = new CSLabelStatement(continueLabel(identifier));
try {
node.getBody().accept(this);
} finally {
_currentContinueLabel = null;
}
addStatement(new CSLabelStatement(breakLabel(identifier)));
return false;
}
private String breakLabel(String identifier) {
return identifier + "_break";
}
private String continueLabel(String identifier) {
return identifier + "_continue";
}
@Override
public boolean visit(SuperFieldAccess node) {
IVariableBinding binding = node.resolveFieldBinding();
String name = mappedFieldName(binding);
if (name == null)
name = identifier(node.getName());
pushExpression(new CSMemberReferenceExpression(new CSBaseExpression(), name));
return false;
}
@Override
public boolean visit(MemberRef node) {
notImplemented(node);
return false;
}
@Override
public boolean visit(WildcardType node) {
notImplemented(node);
return false;
}
private void notImplemented(ASTNode node) {
addProblem(node, ProblemKind.PARSING_ERROR, "Not implemented: %s", node.getClass().getSimpleName());
}
@Override
public boolean visit(PackageDeclaration node) {
String namespace = node.getName().toString();
_compilationUnit.namespace(mappedNamespace(namespace));
processDisableTags(node, _compilationUnit);
return false;
}
@Override
public boolean visit(AnonymousClassDeclaration node) {
CSAnonymousClassBuilder builder = mapAnonymousClass(node);
if (builder == null)
pushExpression(new CSNullLiteralExpression());
else
pushExpression(builder.createConstructorInvocation());
return false;
}
private CSAnonymousClassBuilder mapAnonymousClass(final AnonymousClassDeclaration node) {
final ByRef<CSAnonymousClassBuilder> builder = new ByRef<CSAnonymousClassBuilder>(null);
my(CSharpDriver.class).processAnonymousClass(this, _currentType, node,
new IAnonymousClassBuilderDelegate() {
public CSAnonymousClass create() {
builder.value = new CSAnonymousClassBuilder(CSharpBuilder.this, node);
return new CSAnonymousClass(builder.value.type());
}
public void map(CSAnonymousClass member) {
}
public void document(CSAnonymousClass member) {
}
public void fixup(CSAnonymousClass member) {
}
});
return builder.value;
}
@Override
public boolean visit(final TypeDeclaration node) {
if (processIgnoredType(node)) {
return false;
}
try {
my(NameScope.class).enterTypeDeclaration(node);
final ITypeBinding binding = node.resolveBinding();
if (!binding.isNested()) {
processTypeDeclaration(node);
return false;
}
if (isNonStaticNestedType(binding))
NonStaticNestedClassBuilder.build(CSharpBuilder.this, node);
else
StaticNestedClassBuilder.build(CSharpBuilder.this, node);
} finally {
my(NameScope.class).leaveTypeDeclaration(node);
}
return false;
}
protected boolean isPrivate(MethodDeclaration node) {
return Modifier.isPrivate(node.getModifiers());
}
private boolean processIgnoredType(TypeDeclaration node) {
if (!hasIgnoreOrRemoveAnnotation(node)) {
return false;
}
if (isMainType(node)) {
compilationUnit().ignore(true);
}
return true;
}
private boolean hasIgnoreOrRemoveAnnotation(TypeDeclaration node) {
return SharpenAnnotations.hasIgnoreAnnotation(node) || hasRemoveAnnotation(node);
}
protected CSTypeDeclaration processTypeDeclaration(final TypeDeclaration node) {
final ITypeBinding typeBinding = node.resolveBinding();
CSTypeDeclaration type;
TypeDeclaration oldCurrentTypeDeclaration = _currentTypeDeclaration;
_currentTypeDeclaration = node;
if (node.isInterface() && !isValidCSInterface(typeBinding)) {
type = processExtractedInterface(node);
} else if (mustExtractRawType(typeBinding)) {
type = processExtractedRawType(node);
} else {
type = processOrdinaryTypeDeclaration(node);
}
_currentTypeDeclaration = oldCurrentTypeDeclaration;
return type;
}
private CSTypeDeclaration processOrdinaryTypeDeclaration(final TypeDeclaration node) {
final ITypeBinding typeBinding = node.resolveBinding();
final CSTypeContainer current = getCurrentType(typeBinding);
CSTypeDeclaration type = my(CSharpDriver.class).processTypeDeclaration(this, current, node,
new CSharpDriver.ITypeBuilderDelegate() {
public CSTypeDeclaration create() {
return mapTypeDeclaration(node);
}
public void map(CSTypeDeclaration type) {
processDisabledType(node, isMainType(node) ? _compilationUnit : type);
if (_configuration.shouldMakePartial(BindingUtils
.qualifiedName(typeBinding)))
type.partial(true);
mapSuperTypes(node, type);
type.visibility(mapVisibility(typeBinding));
}
public void mapMembers(CSTypeDeclaration type, IMemberFilter filter) {
CSharpBuilder.this.mapMembers(node, type, filter);
}
public void document(CSTypeDeclaration type) {
CSharpBuilder.this.mapDocumentation(node, type);
processConversionJavadocTags(node, type);
}
public void fixup(CSTypeDeclaration type) {
adjustMemberVisibility(node, type);
autoImplementCloneable(node, type);
moveInitializersDependingOnThisReferenceToConstructor(type);
if (_configuration.junitConversion() && hasTests(type))
type.addAttribute(new CSAttribute("NUnit.Framework.TestFixture"));
type.cleanupStaticConstructor();
}
});
return type;
}
private void processDisabledType(TypeDeclaration node, CSNode type) {
final String expression = _configuration.conditionalCompilationExpressionFor(packageNameFor(node));
if (null != expression) {
compilationUnit().addEnclosingIfDef(expression);
}
processDisableTags(node, type);
}
private String packageNameFor(TypeDeclaration node) {
ITypeBinding type = node.resolveBinding();
return type.getPackage().getName();
}
protected void flushInstanceInitializers(CSTypeDeclaration type, int startStatementIndex) {
if (_instanceInitializers.isEmpty()) {
return;
}
ensureConstructorsFor(type);
int initializerIndex = startStatementIndex;
for (Initializer node : _instanceInitializers) {
final CSBlock body = mapInitializer(node);
for (CSConstructor ctor : type.constructors()) {
if (ctor.isStatic() || hasChainedThisInvocation(ctor)) {
continue;
}
ctor.body().addStatement(initializerIndex, body);
}
++initializerIndex;
}
_instanceInitializers.clear();
}
private CSBlock mapInitializer(Initializer node) {
final CSConstructor template = new CSConstructor();
visitBodyDeclarationBlock(node, node.getBody(), template);
final CSBlock body = template.body();
return body;
}
private boolean hasChainedThisInvocation(CSConstructor ctor) {
final CSConstructorInvocationExpression chained = ctor.chainedConstructorInvocation();
return chained != null && chained.expression() instanceof CSThisExpression;
}
private void moveInitializersDependingOnThisReferenceToConstructor(CSTypeDeclaration type) {
final HashSet<String> memberNames = memberNameSetFor(type);
int index = 0;
for (CSMember member : copy(type.members())) {
if (!(member instanceof CSField))
continue;
final CSField field = (CSField) member;
if (!isDependentOnThisOrMember(field, memberNames))
continue;
moveFieldInitializerToConstructors(field, type, index++);
}
}
private HashSet<String> memberNameSetFor(CSTypeDeclaration type) {
final HashSet<String> members = new HashSet<String>();
for (CSMember member : type.members()) {
if (member instanceof CSType)
continue;
if (isStatic(member))
continue;
members.add(member.name());
}
return members;
}
private boolean isStatic(CSMember member) {
if (member instanceof CSField)
return isStatic((CSField) member);
if (member instanceof CSMethod)
return isStatic((CSMethod) member);
return false;
}
private boolean isStatic(CSMethod method) {
return method.modifier() == CSMethodModifier.Static;
}
private boolean isStatic(CSField member) {
final Set<CSFieldModifier> fieldModifiers = member.modifiers();
return fieldModifiers.contains(CSFieldModifier.Static)
|| fieldModifiers.contains(CSFieldModifier.Const);
}
private CSMember[] copy(final List<CSMember> list) {
return list.toArray(new CSMember[0]);
}
private boolean isDependentOnThisOrMember(CSField field, final Set<String> fields) {
if (null == field.initializer())
return false;
if (this instanceof AbstractNestedClassBuilder) {
if (!field.isConst() && !field.isStatic())
return true;
}
final ByRef<Boolean> foundThisReference = new ByRef<Boolean>(false);
field.initializer().accept(new CSExpressionVisitor() {
@Override
public void visit(CSThisExpression node) {
foundThisReference.value = true;
}
@Override
public void visit(CSReferenceExpression node) {
if (fields.contains(node.name())) {
foundThisReference.value = true;
}
}
});
return foundThisReference.value;
}
private void moveFieldInitializerToConstructors(CSField field, CSTypeDeclaration type, int index) {
final CSExpression initializer = field.initializer();
if (field.isStatic()) {
CSConstructor ctor = type.ensureStaticConstructor();
if (!ctor.isStub())
ctor.body().addStatement(index, newAssignment(field, initializer));
} else {
for (CSConstructor ctor : ensureConstructorsFor(type)) {
if (ctor.isStatic() || ctor.isStub())
continue;
ctor.body().addStatement(index, newAssignment(field, initializer));
}
}
field.initializer(null);
}
private CSExpression newAssignment(CSField field, final CSExpression initializer) {
return CSharpCode.newAssignment(CSharpCode.newReference(field), initializer);
}
private Iterable<CSConstructor> ensureConstructorsFor(CSTypeDeclaration type) {
final List<CSConstructor> ctors = type.constructors();
if (!ctors.isEmpty())
return ctors;
return Collections.singletonList(addDefaultConstructor(type));
}
private CSConstructor addDefaultConstructor(CSTypeDeclaration type) {
final CSConstructor ctor = CSharpCode.newPublicConstructor();
type.addMember(ctor);
return ctor;
}
private void autoImplementCloneable(TypeDeclaration node, CSTypeDeclaration type) {
if (!implementsCloneable(type) || node.isInterface()) {
return;
}
final String name;
if (type.getMember("Clone") instanceof CSMethod)
name = "Clone";
else
name = "MemberwiseClone";
CSMethod clone = new CSMethod("System.ICloneable.Clone");
clone.returnType(OBJECT_TYPE_REFERENCE);
clone.body().addStatement(
new CSReturnStatement(-1,
new CSMethodInvocationExpression(new CSReferenceExpression(name))));
type.addMember(clone);
}
private boolean implementsCloneable(CSTypeDeclaration node) {
for (CSTypeReferenceExpression typeRef : node.baseTypes()) {
if (typeRef.getTypeName().equals("System.ICloneable")) {
return true;
}
}
return false;
}
private void mapSuperTypes(TypeDeclaration node, CSTypeDeclaration type) {
if (!_ignoreExtends.value()) {
mapSuperClass(node, type);
}
if (!ignoreImplements(node)) {
mapSuperInterfaces(node, type);
}
}
private boolean ignoreImplements(TypeDeclaration node) {
return containsJavadoc(node, SharpenAnnotations.SHARPEN_IGNORE_IMPLEMENTS);
}
@SuppressWarnings("unused")
private boolean ignoreExtends(TypeDeclaration node) {
return containsJavadoc(node, SharpenAnnotations.SHARPEN_IGNORE_EXTENDS);
}
private void processConversionJavadocTags(TypeDeclaration node, CSTypeDeclaration type) {
processPartialTagElement(node, type);
}
public CSTypeDeclaration mapTypeDeclaration(TypeDeclaration node) {
CSTypeDeclaration type = typeDeclarationFor(node);
type.startPosition(node.getStartPosition());
type.sourceLength(node.getLength());
mapTypeParameters(node.typeParameters(), type);
return checkForMainType(node, type);
}
private void mapTypeParameters(final List typeParameters, CSTypeParameterProvider type) {
for (Object item : typeParameters) {
type.addTypeParameter(mapTypeParameter((TypeParameter) item));
}
}
private CSTypeParameter mapTypeParameter(TypeParameter item) {
CSTypeParameter tp = new CSTypeParameter(identifier(item.getName()));
ITypeBinding tb = item.resolveBinding();
if (tb != null) {
ITypeBinding superc = mapTypeParameterExtendedType(tb);
if (superc != null)
tp.superClass(mappedTypeReference(superc));
}
return tp;
}
private CSTypeDeclaration typeDeclarationFor(TypeDeclaration node) {
final String typeName = typeName(node);
if (node.isInterface()) {
if (!isValidCSInterface(node.resolveBinding()))
addProblem(node, ProblemKind.INTERNAL_ERROR, "Must extract interface");
return new CSInterface(processInterfaceName(node));
}
if (isStruct(node)) {
return new CSStruct(typeName);
}
return new CSClass(typeName, mapClassModifier(node.getModifiers()));
}
private CSTypeDeclaration processExtractedInterface(final TypeDeclaration node) {
final ITypeBinding typeBinding = node.resolveBinding();
final CSTypeContainer current = getCurrentType(typeBinding);
final String typeName = typeName(node);
final String extractedName = typeName + "Class";
my(CSharpDriver.class).processTypeDeclaration(this, current, node,
new ITypeBuilderDelegate() {
public CSTypeDeclaration create() {
CSInterface iface = new CSInterface(typeName);
iface.startPosition(node.getStartPosition());
iface.sourceLength(node.getLength());
CSharpBuilder.this.mapTypeParameters(node.typeParameters(), iface);
return iface;
}
public void map(CSTypeDeclaration iface) {
mapSuperTypes(node, iface);
iface.visibility(mapVisibility(typeBinding));
}
public void mapMembers(CSTypeDeclaration iface, final IMemberFilter filter) {
CSharpBuilder.this.mapMembers(node, iface, new IMemberFilter() {
public boolean includeMember(ASTNode member) {
if ((filter != null) && !filter.includeMember(member))
return false;
if (!(member instanceof MethodDeclaration))
return false;
MethodDeclaration method = (MethodDeclaration) member;
if (method.isConstructor())
return false;
if (Modifier.isStatic(method.getModifiers()))
return false;
return true;
}
});
}
public void document(CSTypeDeclaration iface) {
CSharpBuilder.this.mapDocumentation(node, iface);
processConversionJavadocTags(node, iface);
}
public void fixup(CSTypeDeclaration iface) {
}
});
return my(CSharpDriver.class).processTypeDeclaration(this, current, node,
new ITypeBuilderDelegate() {
public CSTypeDeclaration create() {
CSClass klass = new CSClass(extractedName, CSClassModifier.Static);
klass.startPosition(node.getStartPosition());
klass.sourceLength(node.getLength());
return klass;
}
public void map(CSTypeDeclaration klass) {
klass.visibility(mapVisibility(typeBinding));
}
public void mapMembers(CSTypeDeclaration klass, final IMemberFilter filter) {
CSharpBuilder.this.mapMembers(node, klass, new IMemberFilter() {
public boolean includeMember(ASTNode member) {
if ((filter != null) && !filter.includeMember(member))
return false;
if (member instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) member;
if (!Modifier.isStatic(method.getModifiers()))
return false;
}
return true;
}
});
}
public void document(CSTypeDeclaration klass) {
CSharpBuilder.this.mapDocumentation(node, klass);
processConversionJavadocTags(node, klass);
}
public void fixup(CSTypeDeclaration klass) {
for (CSMember member : klass.members()) {
member.visibility(CSVisibility.Public);
}
autoImplementCloneable(node, klass);
adjustMemberVisibility(node, klass);
moveInitializersDependingOnThisReferenceToConstructor(klass);
}
});
}
private CSTypeDeclaration processExtractedRawType(final TypeDeclaration node) {
final ITypeBinding typeBinding = node.resolveBinding();
final CSTypeContainer current = getCurrentType(typeBinding);
final String typeName = typeName(node);
my(CSharpDriver.class).processTypeDeclaration(this, current, node,
new ITypeBuilderDelegate() {
public CSTypeDeclaration create() {
return new CSClass(typeName, CSClassModifier.Static);
}
public void map(CSTypeDeclaration raw) {
raw.visibility(mapVisibility(typeBinding));
}
public void mapMembers(CSTypeDeclaration raw, final IMemberFilter filter) {
CSharpBuilder.this.mapMembers(node, raw, new IMemberFilter() {
public boolean includeMember(ASTNode member) {
if ((filter != null) && !filter.includeMember(member))
return false;
if (member instanceof TypeDeclaration) {
TypeDeclaration tdecl = (TypeDeclaration) member;
ITypeBinding binding = tdecl.resolveBinding();
return extractIntoRawType(binding);
} else if (member instanceof EnumDeclaration) {
return true;
} else if (member instanceof FieldDeclaration) {
FieldDeclaration fdecl = (FieldDeclaration) member;
if (fdecl.fragments().size() != 1)
return false;
VariableDeclarationFragment fragment = (VariableDeclarationFragment) fdecl
.fragments().get(0);
return isConstField(fdecl, fragment);
}
return false;
}
});
}
public void document(CSTypeDeclaration raw) {
CSharpBuilder.this.mapDocumentation(node, raw);
}
public void fixup(CSTypeDeclaration raw) {
for (CSMember member : raw.members()) {
if (member.visibility() != CSVisibility.Public)
member.visibility(CSVisibility.Internal);
}
}
});
CSTypeDeclaration type = my(CSharpDriver.class).processTypeDeclaration(this, current, node,
new CSharpDriver.ITypeBuilderDelegate() {
public CSTypeDeclaration create() {
CSTypeDeclaration type = typeDeclarationFor(node);
type.startPosition(node.getStartPosition());
type.sourceLength(node.getLength());
mapTypeParameters(node.typeParameters(), type);
return type;
}
public void map(CSTypeDeclaration type) {
mapSuperTypes(node, type);
type.visibility(mapVisibility(typeBinding));
}
public void mapMembers(CSTypeDeclaration type, final IMemberFilter filter) {
CSharpBuilder.this.mapMembers(node, type, new IMemberFilter() {
public boolean includeMember(ASTNode member) {
if ((filter != null) && !filter.includeMember(member))
return false;
if (member instanceof TypeDeclaration) {
TypeDeclaration tdecl = (TypeDeclaration) member;
ITypeBinding binding = tdecl.resolveBinding();
return !extractIntoRawType(binding);
} else if (member instanceof EnumDeclaration) {
return false;
} else if (member instanceof FieldDeclaration) {
FieldDeclaration fdecl = (FieldDeclaration) member;
if (fdecl.fragments().size() != 1)
return true;
VariableDeclarationFragment fragment = (VariableDeclarationFragment) fdecl
.fragments().get(0);
return !isConstField(fdecl, fragment);
}
return true;
}
});
}
public void document(CSTypeDeclaration type) {
CSharpBuilder.this.mapDocumentation(node, type);
processConversionJavadocTags(node, type);
}
public void fixup(CSTypeDeclaration type) {
adjustMemberVisibility(node, type);
autoImplementCloneable(node, type);
moveInitializersDependingOnThisReferenceToConstructor(type);
}
});
return type;
}
private String typeName(AbstractTypeDeclaration node) {
String renamed = annotatedRenaming(node);
if (renamed != null)
return renamed;
renamed = mappedTypeName(node.resolveBinding());
if (renamed != null) {
int i = renamed.lastIndexOf('.');
if (i != -1)
return renamed.substring(i + 1);
else
return renamed;
}
return node.getName().toString();
}
private boolean isStruct(TypeDeclaration node) {
return containsJavadoc(node, SharpenAnnotations.SHARPEN_STRUCT);
}
private CSTypeDeclaration checkForMainType(TypeDeclaration node, CSTypeDeclaration type) {
if (isMainType(node)) {
setCompilationUnitElementName(type.name());
}
return type;
}
private void setCompilationUnitElementName(String name) {
_compilationUnit.elementName(name + ".cs");
}
private String processInterfaceName(TypeDeclaration node) {
String name = node.getName().getFullyQualifiedName();
return interfaceName(name);
}
private boolean isMainType(TypeDeclaration node) {
return node.isPackageMemberTypeDeclaration() && Modifier.isPublic(node.getModifiers());
}
private void mapSuperClass(TypeDeclaration node, CSTypeDeclaration type) {
if (handledExtends(node, type))
return;
if (null == node.getSuperclassType())
return;
final ITypeBinding superClassBinding = node.getSuperclassType().resolveBinding();
if (null == superClassBinding)
unresolvedTypeBinding(node.getSuperclassType());
if (!isLegacyTestFixtureClass(superClassBinding))
type.addBaseType(mappedTypeReference(superClassBinding));
else {
type.addAttribute(new CSAttribute("NUnit.Framework.TestFixture"));
}
}
private boolean isLegacyTestFixtureClass(ITypeBinding type)
{
return (_configuration.junitConversion() && type.getQualifiedName().equals("junit.framework.TestCase"));
}
private boolean isLegacyTestFixture(ITypeBinding type) {
if (!_configuration.junitConversion())
return false;
if (isLegacyTestFixtureClass(type))
return true;
ITypeBinding base = type.getSuperclass();
return (base != null) && isLegacyTestFixture(base);
}
private boolean hasTests(CSTypeDeclaration type) {
for (CSMember m : type.members()) {
if (m instanceof CSMethod) {
CSMethod met = (CSMethod) m;
for (CSAttribute at : met.attributes()) {
if (at.name().equals("Test") || at.name().equals("NUnit.Framework.Test"))
return true;
}
}
}
return false;
}
private boolean handledExtends(TypeDeclaration node, CSTypeDeclaration type) {
final TagElement replaceExtendsTag = javadocTagFor(node, SharpenAnnotations.SHARPEN_EXTENDS);
if (null == replaceExtendsTag)
return false;
final String baseType = JavadocUtility.singleTextFragmentFrom(replaceExtendsTag);
type.addBaseType(new CSTypeReference(baseType));
return true;
}
private void mapSuperInterfaces(TypeDeclaration node, CSTypeDeclaration type) {
final ITypeBinding serializable = my(IBindingManager.class).getSerializableType();
for (Object itf : node.superInterfaceTypes()) {
Type iface = (Type) itf;
ITypeBinding binding = iface.resolveBinding();
if (binding == serializable) {
continue;
} else {
type.addBaseType(mappedTypeReference(iface));
}
}
if (!type.isInterface() && node.resolveBinding().isSubTypeCompatible(serializable)) {
type.addAttribute(new CSAttribute("System.Serializable"));
}
}
private boolean isJavaLangCharSequence(ITypeBinding binding) {
return binding.getQualifiedName().equals("java.lang.CharSequence");
}
private boolean isJavaLangString(ITypeBinding binding) {
return binding.getQualifiedName().equals("java.lang.String");
}
private boolean isJavaLangNumber(ITypeBinding binding) {
return binding.getQualifiedName().equals("java.lang.Number");
}
private ITypeBinding resolveWellKnownType(String typeName) {
return _ast.getAST().resolveWellKnownType(typeName);
}
private void mapMembers(TypeDeclaration node, CSTypeDeclaration type, CSharpDriver.IMemberFilter filter) {
CSTypeDeclaration saved = _currentType;
_currentType = type;
try {
List<ASTNode> members = node.bodyDeclarations();
for (final ASTNode member : members) {
if ((filter != null) && !filter.includeMember(member))
continue;
member.accept(this);
}
createInheritedAbstractMemberStubs(node);
flushInstanceInitializers(type, 0);
} finally {
_currentType = saved;
}
}
private void mapMembers(EnumDeclaration node, CSTypeDeclaration type, CSharpDriver.IMemberFilter filter) {
CSTypeDeclaration saved = _currentType;
_currentType = type;
try {
List<ASTNode> members = node.bodyDeclarations();
for (final ASTNode member : members) {
if ((filter != null) && !filter.includeMember(member))
continue;
member.accept(this);
}
flushInstanceInitializers(type, 0);
} finally {
_currentType = saved;
}
}
private void adjustMemberVisibility(TypeDeclaration node, CSTypeDeclaration type) {
ITypeBinding binding = node.resolveBinding();
for (final CSMember member : type.members()) {
if (member instanceof CSMethod)
continue;
if (binding.isNested() && Modifier.isPrivate(binding.getModifiers())) {
if (member.visibility() == CSVisibility.Private)
member.visibility(CSVisibility.Internal);
}
}
}
private boolean containsNonPublicTypes(ITypeBinding binding, boolean privateOnly) {
if (binding.isPrimitive())
return false;
if (binding.isTypeVariable())
return false;
if (binding.isWildcardType())
return false;
if (binding.isArray())
return containsNonPublicTypes(binding.getElementType(), privateOnly);
if (binding.isParameterizedType()) {
for (final ITypeBinding tp : binding.getTypeArguments()) {
if (containsNonPublicTypes(tp, privateOnly))
return true;
}
}
if (privateOnly)
return Modifier.isPrivate(binding.getModifiers());
else
return !Modifier.isPublic(binding.getModifiers());
}
private boolean containsNonPublicTypes(IMethodBinding binding, boolean privateOnly) {
ITypeBinding returnType = binding.getReturnType();
if ((returnType != null) && containsNonPublicTypes(returnType, privateOnly))
return true;
for (final ITypeBinding type : binding.getParameterTypes()) {
if (containsNonPublicTypes(type, privateOnly))
return true;
}
return false;
}
protected boolean isNonStaticNestedType(ITypeBinding binding) {
if (binding.isInterface())
return false;
if (!binding.isNested())
return false;
return !isStatic(binding);
}
private boolean isGenericInstance(ITypeBinding type) {
if (type.isArray())
return isGenericInstance(type.getElementType());
if (type.isParameterizedType())
return true;
if (type.isCapture()) {
ITypeBinding erasure = type.getErasure();
return erasure.isGenericType();
}
return false;
}
private boolean isRawOrGenericType(ITypeBinding type) {
if (type.isArray())
return isRawOrGenericType(type.getElementType());
return type.isRawType() || type.isGenericType();
}
/*
* Only use this for comparisons.
*/
private ITypeBinding getUnderlyingGenericType(ITypeBinding type) {
if (type.isGenericType())
return type;
if (type.isParameterizedType())
return type.getTypeDeclaration();
if (type.isRawType())
return type.getTypeDeclaration();
if (type.isCapture()) {
ITypeBinding erasure = type.getErasure();
return isRawOrGenericType(erasure) ? erasure : null;
}
if (type.isArray())
return getUnderlyingGenericType(type.getElementType());
return null;
}
private boolean isStatic(ITypeBinding binding) {
return Modifier.isStatic(binding.getModifiers());
}
private CSTypeContainer getCurrentType(ITypeBinding binding) {
if (null != _currentType && !isExtractedNestedType(binding)) {
return _currentType;
} else {
return _compilationUnit;
}
}
private void mapDocumentation(final BodyDeclaration bodyDecl, final CSMember member) {
my(PreserveFullyQualifiedNamesState.class).using(true, new Runnable() {
public void run() {
if (processDocumentationOverlay(member)) {
return;
}
mapJavadoc(bodyDecl, member);
mapDeclaredExceptions(bodyDecl, member);
}
});
}
private void mapDeclaredExceptions(BodyDeclaration bodyDecl, CSMember member) {
if (!(bodyDecl instanceof MethodDeclaration))
return;
MethodDeclaration method = (MethodDeclaration) bodyDecl;
mapThrownExceptions(method.thrownExceptions(), member);
}
private void mapThrownExceptions(List thrownExceptions, CSMember member) {
for (Object exception : thrownExceptions) {
mapThrownException((Name) exception, member);
}
}
private void mapThrownException(Name exception, CSMember member) {
final String typeName = mappedTypeName(exception.resolveTypeBinding());
if (containsExceptionTagWithCRef(member, typeName))
return;
member.addDoc(newTagWithCRef("exception", typeName));
}
private boolean containsExceptionTagWithCRef(CSMember member, String cref) {
for (CSDocNode node : member.docs()) {
if (!(node instanceof CSDocTagNode))
continue;
if (cref.equals(((CSDocTagNode) node).getAttribute("cref"))) {
return true;
}
}
return false;
}
private void mapJavadoc(final BodyDeclaration bodyDecl, final CSMember member) {
final Javadoc javadoc = bodyDecl.getJavadoc();
if (null == javadoc) {
return;
}
mapJavadocTags(javadoc, member);
}
private boolean processDocumentationOverlay(CSMember node) {
if (node instanceof CSTypeDeclaration) {
return processTypeDocumentationOverlay((CSTypeDeclaration) node);
}
return processMemberDocumentationOverlay((CSMember) node);
}
private boolean processMemberDocumentationOverlay(CSMember node) {
String overlay = documentationOverlay().forMember(currentTypeQName(), node.signature());
return processDocumentationOverlay(node, overlay);
}
private String currentTypeQName() {
return qualifiedName(_currentType);
}
private boolean processTypeDocumentationOverlay(CSTypeDeclaration node) {
String overlay = documentationOverlay().forType(qualifiedName(node));
return processDocumentationOverlay(node, overlay);
}
private boolean processDocumentationOverlay(CSMember node, String overlay) {
if (null == overlay) {
return false;
}
node.addDoc(new CSDocTextOverlay(overlay.trim()));
return true;
}
private DocumentationOverlay documentationOverlay() {
return _configuration.documentationOverlay();
}
private String qualifiedName(CSTypeDeclaration node) {
if (currentNamespace() == null) {
return node.name();
}
return currentNamespace() + "." + node.name();
}
private String currentNamespace() {
return _compilationUnit.namespace();
}
private IMethodBinding currentMethodBinding() {
if (_currentBodyDeclaration == null)
return null;
else if (_currentBodyDeclaration instanceof MethodDeclaration)
return ((MethodDeclaration) _currentBodyDeclaration).resolveBinding();
return null;
}
private void mapJavadocTags(final Javadoc javadoc, final CSMember member) {
for (Object tag : javadoc.tags()) {
try {
TagElement element = (TagElement) tag;
String tagName = element.getTagName();
if (null == tagName) {
mapJavadocSummary(member, element);
} else {
processTagElement(member, element);
}
} catch (Exception x) {
warning((ASTNode) tag, x.getMessage());
x.printStackTrace();
}
}
}
private void processTagElement(final CSMember member, TagElement element) {
if (processSemanticallySignificantTagElement(member, element)) {
return;
}
if (!isConversionTag(element.getTagName())) {
member.addDoc(mapTagElement(element));
}
else if (isAttributeAnnotation(element)) {
processAttribute(member, element);
}
else if (isNewAnnotation(element)) {
member.setNewModifier(true);
}
}
private boolean isAttributeAnnotation(TagElement element) {
return element.getTagName().equals(SharpenAnnotations.SHARPEN_ATTRIBUTE);
}
private boolean isNewAnnotation(TagElement element) {
return element.getTagName().equals(SharpenAnnotations.SHARPEN_NEW);
}
private void processAttribute(CSMember member, TagElement element) {
String attrType = mappedTypeName(JavadocUtility.singleTextFragmentFrom(element));
CSAttribute attribute = new CSAttribute(attrType);
member.addAttribute(attribute);
}
private boolean processSemanticallySignificantTagElement(CSMember member, TagElement element) {
if (element.getTagName().equals("@deprecated")) {
member.removeAttribute("System.Obsolete");
member.removeAttribute("System.ObsoleteAttribute");
member.addAttribute(obsoleteAttributeFromDeprecatedTagElement(element));
return true;
}
return false;
}
private CSAttribute obsoleteAttributeFromDeprecatedTagElement(TagElement element) {
CSAttribute attribute = new CSAttribute(mappedTypeName("System.ObsoleteAttribute"));
if (element.fragments().isEmpty()) {
return attribute;
}
attribute.addArgument(new CSStringLiteralExpression(toLiteralStringForm(getWholeText(element))));
return attribute;
}
private String getWholeText(TagElement element) {
StringBuilder builder = new StringBuilder();
for (ASTNode fragment : (List<ASTNode>) element.fragments()) {
if (fragment instanceof TextElement) {
TextElement textElement = (TextElement) fragment;
String text = textElement.getText();
appendWithSpaceIfRequired(builder, text);
} else if (fragment instanceof TagElement) {
builder.append(getWholeText((TagElement) fragment));
} else if (fragment instanceof MethodRef) {
builder.append(mapCRefTarget(fragment));
} else if (fragment instanceof MemberRef) {
builder.append(mapCRefTarget(fragment));
} else if (fragment instanceof Name) {
builder.append(mapCRefTarget(fragment));
} else {
break;
}
}
return builder.toString().trim();
}
private void appendWithSpaceIfRequired(StringBuilder builder, String text) {
if (builder.length() > 0 && builder.charAt(builder.length() - 1) != ' '
&& text.startsWith(" ") == false) {
builder.append(" ");
}
builder.append(text);
}
private String toLiteralStringForm(String s) {
// TODO: deal with escaping sequences here
return "@\"" + s.replace("\"", "\"\"") + "\"";
}
private boolean isConversionTag(String tagName) {
return tagName.startsWith("@sharpen.");
}
private void processPartialTagElement(TypeDeclaration node, CSTypeDeclaration member) {
TagElement element = javadocTagFor(node, SharpenAnnotations.SHARPEN_PARTIAL);
if (null == element)
return;
((CSTypeDeclaration) member).partial(true);
}
private TagElement javadocTagFor(PackageDeclaration node, final String withName) {
return JavadocUtility.getJavadocTag(node, withName);
}
private TagElement javadocTagFor(BodyDeclaration node, final String withName) {
return JavadocUtility.getJavadocTag(node, withName);
}
private void mapJavadocSummary(final CSMember member, TagElement element) {
List<String> summary = getFirstSentence(element);
if (null != summary) {
CSDocTagNode summaryNode = new CSDocTagNode("summary");
for (String fragment : summary) {
summaryNode.addFragment(new CSDocTextNode(fragment));
}
member.addDoc(summaryNode);
member.addDoc(createTagNode("remarks", element));
} else {
member.addDoc(createTagNode("summary", element));
}
}
private List<String> getFirstSentence(TagElement element) {
List<String> fragments = new LinkedList<String>();
for (Object fragment : element.fragments()) {
if (fragment instanceof TextElement) {
TextElement textElement = (TextElement) fragment;
String text = textElement.getText();
int index = findSentenceClosure(text);
if (index > -1) {
fragments.add(text.substring(0, index + 1));
return fragments;
} else {
fragments.add(text);
}
} else {
break;
}
}
return null;
}
private int findSentenceClosure(String text) {
Matcher matcher = SUMMARY_CLOSURE_PATTERN.matcher(text);
return matcher.find() ? matcher.start() : -1;
}
private CSDocNode mapTagElement(TagElement element) {
String tagName = element.getTagName();
if (TagElement.TAG_PARAM.equals(tagName)) {
return mapTagParam(element);
} else if (TagElement.TAG_RETURN.equals(tagName)) {
return createTagNode("returns", element);
} else if (TagElement.TAG_LINK.equals(tagName)) {
return mapTagLink(element);
} else if (TagElement.TAG_THROWS.equals(tagName)) {
return mapTagThrows(element);
} else if (TagElement.TAG_SEE.equals(tagName)) {
return mapTagWithCRef("seealso", element);
}
return createTagNode(tagName.substring(1), element);
}
private CSDocNode mapTagThrows(TagElement element) {
return mapTagWithCRef("exception", element);
}
private CSDocNode mapTagLink(TagElement element) {
return mapTagWithCRef("see", element);
}
private CSDocNode mapTagWithCRef(String tagName, TagElement element) {
final List fragments = element.fragments();
if (fragments.isEmpty()) {
return invalidTagWithCRef(element, tagName, element);
}
final ASTNode linkTarget = (ASTNode) fragments.get(0);
String cref = mapCRefTarget(linkTarget);
if (null == cref) {
return invalidTagWithCRef(linkTarget, tagName, element);
}
CSDocTagNode node = newTagWithCRef(tagName, cref);
if (fragments.size() > 1) {
if (isLinkWithSimpleLabel(fragments, linkTarget)) {
node.addTextFragment(unqualifiedName(cref));
} else {
collectFragments(node, fragments, 1);
}
} else {
// TODO: Move the XML encoding to the right place
// (CSharpPrinter)
node.addTextFragment(cref.replace("{", "<").replace("}", ">"));
}
return node;
}
private ASTNode documentedNodeAttachedTo(TagElement element) {
ASTNode attachedToNode = element;
while (attachedToNode instanceof TagElement || attachedToNode instanceof Javadoc) {
attachedToNode = attachedToNode.getParent();
}
return attachedToNode;
}
private CSDocNode invalidTagWithCRef(final ASTNode linkTarget, String tagName, TagElement element) {
warning(linkTarget, "Tag '" + element.getTagName() + "' demands a valid cref target.");
CSDocNode newTag = createTagNode(tagName, element);
return newTag;
}
private CSDocTagNode newTagWithCRef(String tagName, String cref) {
CSDocTagNode node = new CSDocTagNode(tagName);
node.addAttribute("cref", cref);
return node;
}
private boolean isLinkWithSimpleLabel(List<ASTNode> fragments, final ASTNode linkTarget) {
if (fragments.size() != 2)
return false;
if (!JavadocUtility.isTextFragment(fragments, 1))
return false;
final String link = linkTarget.toString();
final String label = JavadocUtility.textFragment(fragments, 1);
return label.equals(link) || label.equals(unqualifiedName(link));
}
private String mapCRefTarget(final ASTNode crefTarget) {
return new CRefBuilder(crefTarget).build();
}
private CSDocNode mapTagParam(TagElement element) {
List fragments = element.fragments();
if (!(fragments.get(0) instanceof SimpleName))
return new CSDocTagNode("?");
SimpleName name = (SimpleName) fragments.get(0);
if (null == name.resolveBinding()) {
warning(name, "Parameter '" + name + "' not found.");
}
CSDocTagNode param = isPropertyNode(documentedNodeAttachedTo(element))
? new CSDocTagNode("value")
: newCSDocTag(fixIdentifierNameFor(identifier(name), element));
collectFragments(param, fragments, 1);
return param;
}
private CSDocTagNode newCSDocTag(final String paramName) {
CSDocTagNode param;
param = new CSDocTagNode("param");
param.addAttribute("name", paramName);
return param;
}
private boolean isPropertyNode(ASTNode node) {
if (node.getNodeType() != ASTNode.METHOD_DECLARATION) {
return false;
}
return isProperty((MethodDeclaration) node);
}
private String fixIdentifierNameFor(String identifier, TagElement element) {
return removeAtSign(identifier);
}
private String removeAtSign(String identifier) {
return identifier.startsWith("@")
? identifier.substring(1)
: identifier;
}
private void collectFragments(CSDocTagNode node, List fragments, int index) {
for (int i = index; i < fragments.size(); ++i) {
node.addFragment(mapTagElementFragment((ASTNode) fragments.get(i)));
}
}
private CSDocNode mapTextElement(TextElement element) {
final String text = element.getText();
if (HTML_ANCHOR_PATTERN.matcher(text).find()) {
warning(element,
"Caution: HTML anchors can result in broken links. Consider using @link instead.");
}
return new CSDocTextNode(text);
}
private CSDocNode createTagNode(String tagName, TagElement element) {
CSDocTagNode summary = new CSDocTagNode(tagName);
for (Object f : element.fragments()) {
summary.addFragment(mapTagElementFragment((ASTNode) f));
}
return summary;
}
private CSDocNode mapTagElementFragment(ASTNode node) {
switch (node.getNodeType()) {
case ASTNode.TAG_ELEMENT:
return mapTagElement((TagElement) node);
case ASTNode.TEXT_ELEMENT:
return mapTextElement((TextElement) node);
}
warning(node, "Documentation node not supported: " + node.getClass() + ": " + node);
return new CSDocTextNode(node.toString());
}
@Override
public boolean visit(final FieldDeclaration node) {
if (SharpenAnnotations.hasIgnoreAnnotation(node)) {
return false;
}
for (Object item : node.fragments()) {
final VariableDeclarationFragment fragment = (VariableDeclarationFragment) item;
my(CSharpDriver.class).processFieldDeclaration(this, _currentType, node, fragment,
new IFieldBuilderDelegate() {
public CSField create() {
final IVariableBinding binding = fragment.resolveBinding();
final ITypeBinding fieldType = binding.getType();
CSTypeReferenceExpression fieldTypeRef = mappedVariableType(binding);
CSVisibility visibility = mapVisibility(binding);
pushExpectedType(fieldType);
CSField field = mapFieldDeclarationFragment(node, fragment,
fieldTypeRef, visibility);
popExpectedType();
return field;
}
public void map(CSField member) {
// Nothing to do
}
public void document(CSField member) {
// Nothing to do
}
public void fixup(CSField member) {
// Nothing to do
}
});
}
return false;
}
private CSField mapFieldDeclarationFragment(FieldDeclaration node, VariableDeclarationFragment fragment,
CSTypeReferenceExpression fieldType, CSVisibility fieldVisibility) {
final String mappedName = mappedFieldDeclarationName(node, fragment);
CSExpression initializer = mapFieldInitializer(fragment);
CSField field = new CSField(mappedName, fieldType, fieldVisibility, initializer);
if (isConstField(node, fragment)) {
ITypeBinding type = fragment.resolveBinding().getType();
boolean useStaticReadonly;
if (type.equals(my(IBindingManager.class).getStringType()))
useStaticReadonly = !(initializer instanceof CSStringLiteralExpression)
&& !(initializer instanceof CSNullLiteralExpression);
else
useStaticReadonly = type.isArray() || !type.isPrimitive();
if (useStaticReadonly) {
field.addModifier(CSFieldModifier.Static);
field.addModifier(CSFieldModifier.Readonly);
} else {
field.addModifier(CSFieldModifier.Const);
if (field.visibility() != CSVisibility.Public)
field.visibility(CSVisibility.Internal);
}
} else {
processFieldModifiers(field, node.getModifiers());
}
mapDocumentation(node, field);
mapAnnotations(node, field);
return field;
}
private String mappedFieldDeclarationName(FieldDeclaration node, VariableDeclarationFragment fragment) {
final IVariableBinding binding = fragment.resolveBinding();
final String mappedName = null == binding ? null : my(Mappings.class).mappedFieldName(binding);
if (null == mappedName || 0 == mappedName.length() || mappedName.contains(".")) {
return fieldName(fragment);
}
return mappedName;
}
private CSVisibility mapVisibility(IVariableBinding binding) {
CSVisibility vis = my(CSharpDriver.class).mapVisibility(binding);
if (vis != null)
return vis;
if (binding.getDeclaringClass().isInterface())
return CSVisibility.Public;
vis = mapVisibility(binding.getModifiers());
vis = adjustVisibility(binding.getDeclaringClass(), binding.getType(), vis);
return vis;
}
private void mapAnnotations(BodyDeclaration node, CSMember member) {
for (Object m : node.modifiers()) {
if (!(m instanceof Annotation)) {
continue;
}
if (isIgnoredAnnotation((Annotation) m)) {
continue;
}
if (m instanceof MarkerAnnotation) {
mapMarkerAnnotation((MarkerAnnotation) m, member);
}
}
}
private boolean isIgnoredAnnotation(Annotation m) {
return _configuration.isIgnoredAnnotation(qualifiedName(m.resolveAnnotationBinding()
.getAnnotationType()));
}
private void mapMarkerAnnotation(MarkerAnnotation annotation, CSMember member) {
final IAnnotationBinding binding = annotation.resolveAnnotationBinding();
String name = mappedTypeName(binding.getAnnotationType());
if (name.equals("System.Obsolete") || name.equals("System.ObsoleteAttribute")) {
for (CSAttribute old : member.attributes()) {
if (old.name().equals("System.Obsolete")
|| old.name().equals("System.ObsoleteAttribute"))
return;
}
}
final CSAttribute attribute = new CSAttribute(name);
member.addAttribute(attribute);
}
protected String fieldName(VariableDeclarationFragment fragment) {
return identifier(fragment.getName());
}
protected CSExpression mapFieldInitializer(VariableDeclarationFragment fragment) {
return mapExpression(fragment.resolveBinding().getType(), fragment.getInitializer());
}
private boolean isConstField(FieldDeclaration node, VariableDeclarationFragment fragment) {
IVariableBinding binding = fragment.resolveBinding();
if (binding.getDeclaringClass().isInterface())
return true;
ITypeBinding type = binding.getType();
boolean isPrimitive = type.isPrimitive() || type.equals(my(IBindingManager.class).getStringType());
return Modifier.isFinal(node.getModifiers()) && isPrimitive &&
hasConstValue(fragment) && Modifier.isStatic(node.getModifiers());
}
private boolean hasConstValue(VariableDeclarationFragment fragment) {
return null != fragment.resolveBinding().getConstantValue();
}
private void processFieldModifiers(CSField field, int modifiers) {
if (Modifier.isStatic(modifiers)) {
field.addModifier(CSFieldModifier.Static);
}
if (Modifier.isFinal(modifiers)) {
field.addModifier(CSFieldModifier.Readonly);
}
if (Modifier.isTransient(modifiers)) {
field.addAttribute(new CSAttribute(mappedTypeName("System.NonSerialized")));
}
if (Modifier.isVolatile(modifiers)) {
field.addModifier(CSFieldModifier.Volatile);
}
}
private boolean isDestructor(MethodDeclaration node) {
return node.getName().toString().equals("finalize");
}
@Override
public boolean visit(Initializer node) {
if (Modifier.isStatic(node.getModifiers())) {
CSConstructor ctor = _currentType.ensureStaticConstructor();
CSBlock block = new CSBlock();
ctor.body().addStatement(block);
visitBodyDeclarationBlock(node, node.getBody(), ctor, block);
} else {
_instanceInitializers.add(node);
}
return false;
}
@Override
public boolean visit(MethodDeclaration node) {
if (SharpenAnnotations.hasIgnoreAnnotation(node) || isRemoved(node)) {
return false;
}
if (isEvent(node)) {
processEventDeclaration(node);
return false;
}
if (isMappedToProperty(node)) {
processMappedPropertyDeclaration(node);
return false;
}
if (isTaggedAsProperty(node)) {
processPropertyDeclaration(node);
return false;
}
if (isIndexer(node)) {
processIndexerDeclaration(node);
return false;
}
processMethodDeclaration(node);
return false;
}
private void processIndexerDeclaration(MethodDeclaration node) {
processPropertyDeclaration(node, CSProperty.INDEXER);
}
private boolean isIndexer(MethodDeclaration node) {
return isTaggedDeclaration(node, SharpenAnnotations.SHARPEN_INDEXER)
|| isMappedToIndexer(node);
}
private boolean isRemoved(MethodDeclaration node) {
return hasRemoveAnnotation(node) || isRemoved(node.resolveBinding());
}
private boolean hasRemoveAnnotation(BodyDeclaration node) {
return containsJavadoc(node, SharpenAnnotations.SHARPEN_REMOVE);
}
private boolean isRemoved(final IMethodBinding binding) {
return _configuration.isRemoved(qualifiedName(binding));
}
public static boolean containsJavadoc(BodyDeclaration node, final String tag) {
return JavadocUtility.containsJavadoc(node, tag);
}
private void processPropertyDeclaration(MethodDeclaration node) {
processPropertyDeclaration(node, propertyName(node));
}
private void processMappedPropertyDeclaration(MethodDeclaration node) {
processPropertyDeclaration(node, mappedMethodName(node));
}
private void processPropertyDeclaration(final MethodDeclaration node, final String name) {
final CSProperty existingProperty = findProperty(node, name);
my(CSharpDriver.class).processPropertyDeclaration(this, _currentType, node, name, existingProperty,
new IPropertyBuilderDelegate() {
public boolean isGetter() {
return CSharpBuilder.this.isGetter(node);
}
public CSProperty create() {
if (existingProperty != null)
return existingProperty;
return newPropertyFor(node, name);
}
public void map(CSProperty property) {
if (!CSharpBuilder.this.isGetter(node))
mapImplicitSetterParameter(node, property);
mapMetaMemberAttributes(node, property);
mapParameters(node, property);
}
public void document(CSProperty member) {
}
public void fixup(CSProperty member) {
}
public void mapBody(CSProperty property) {
final CSBlock block = CSharpBuilder.this.mapBody(node);
if (isGetter()) {
property.getter(block);
} else {
property.setter(block);
}
}
});
}
private CSProperty findProperty(MethodDeclaration node, final String name) {
CSMember existingProperty = _currentType.getMember(name);
if (existingProperty != null) {
if (!(existingProperty instanceof CSProperty)) {
throw new IllegalArgumentException(sourceInformation(node)
+ ": Previously declared member redeclared as property.");
}
}
return (CSProperty) existingProperty;
}
@Override
public boolean visit(AssertStatement node) {
// Ignore
return false;
}
private void mapImplicitSetterParameter(MethodDeclaration node, CSProperty property) {
final String parameterName = parameter(node, 0).getName().toString();
if (parameterName.equals("value")) {
return;
}
property.setter().addStatement(
0,
newVariableDeclarationExpression(parameterName, property.type(),
new CSReferenceExpression("value")));
}
private CSDeclarationExpression newVariableDeclarationExpression(final String name,
final CSTypeReferenceExpression type, final CSReferenceExpression initializer) {
return new CSDeclarationExpression(
new CSVariableDeclaration(name, type, initializer));
}
private CSProperty newPropertyFor(MethodDeclaration node, final String propName) {
final CSTypeReferenceExpression propertyType = isGetter(node)
? mappedReturnType(node)
: mappedTypeReference(lastParameter(node).getType());
CSProperty p = new CSProperty(propName, propertyType);
return p;
}
private CSBlock mapBody(MethodDeclaration node) {
final CSBlock block = new CSBlock();
processBlock(node, node.getBody(), block);
return block;
}
private boolean isGetter(MethodDeclaration node) {
return !"void".equals(node.getReturnType2().toString());
}
private SingleVariableDeclaration lastParameter(MethodDeclaration node) {
return parameter(node, node.parameters().size() - 1);
}
private String propertyName(MethodDeclaration node) {
return my(Annotations.class).annotatedPropertyName(node);
}
private String propertyName(IMethodBinding binding) {
return propertyName(declaringNode(binding));
}
private boolean isProperty(MethodDeclaration node) {
return isTaggedAsProperty(node)
|| isMappedToProperty(node);
}
private boolean isTaggedAsProperty(MethodDeclaration node) {
return isTaggedDeclaration(node, SharpenAnnotations.SHARPEN_PROPERTY);
}
private boolean isTaggedDeclaration(MethodDeclaration node, final String tag) {
return effectiveAnnotationFor(node, tag) != null;
}
private void cleanBaseSetupCalls(CSMethod method) {
ArrayList<CSStatement> toDelete = new ArrayList<CSStatement>();
for (CSStatement st : method.body().statements()) {
if (st instanceof CSExpressionStatement) {
CSExpressionStatement es = (CSExpressionStatement) st;
if (es.expression() instanceof CSMethodInvocationExpression) {
CSMethodInvocationExpression mie = (CSMethodInvocationExpression) es
.expression();
if (mie.expression() instanceof CSMemberReferenceExpression) {
CSMemberReferenceExpression mr = (CSMemberReferenceExpression) mie
.expression();
if ((mr.expression() instanceof CSBaseExpression)
&& (mr.name().equals("SetUp") || mr.name().equals(
"TearDown")))
toDelete.add(st);
}
}
}
}
for (CSStatement st : toDelete)
method.body().removeStatement(st);
}
protected CSMethodBase processMethodDeclaration(final MethodDeclaration node) {
final IMethodBinding binding = node.resolveBinding();
final ASTNode parent = node.getParent();
final ITypeBinding declaringType;
if (parent instanceof TypeDeclaration) {
declaringType = ((TypeDeclaration) parent).resolveBinding();
} else if (parent instanceof AnonymousClassDeclaration) {
declaringType = ((AnonymousClassDeclaration) parent).resolveBinding();
} else {
declaringType = null;
}
_currentWildcardParams = new HashMap<ITypeBinding, String>();
CSMethodBase method = my(CSharpDriver.class).processMethodDeclaration(this, _currentType, node,
new IMethodBuilderDelegate() {
public ITypeBinding getDeclaringType() {
return declaringType;
}
public IMethodBinding getBaseMethod(boolean overrideOnly, boolean allowStatic) {
return CSharpBuilder.this.getBaseMethod(binding, overrideOnly,
allowStatic);
}
public CSMethodBase create() {
if (isDestructor(node))
return new CSDestructor();
if (node.isConstructor())
return new CSConstructor();
final String name = mappedMethodDeclarationName(node);
CSMethod method = new CSMethod(name);
method.returnType(mappedReturnType(node));
method.modifier(mapMethodModifier(node));
mapTypeParameters(node.typeParameters(), method);
return method;
}
public void map(CSMethodBase method) {
method.startPosition(node.getStartPosition());
method.isVarArgs(node.isVarargs());
mapParameters(node, method);
mapAnnotations(node, method);
}
public void mapBody(CSMethodBase method) {
visitBodyDeclarationBlock(node, node.getBody(), method);
}
public void document(CSMethodBase method) {
CSharpBuilder.this.mapDocumentation(node, method);
}
public void fixup(CSMethodBase method) {
CSharpBuilder.this.fixupMethod(node, method);
}
public void fixup(CSTypeDeclaration parent, CSMethodBase method) {
if (method instanceof CSMethod)
CSharpBuilder.this.fixupMethod(node, parent, (CSMethod) method);
}
});
_currentWildcardParams = null;
return method;
}
private CSVisibility mapVisibility(IMethodBinding binding) {
CSVisibility vis = my(CSharpDriver.class).mapVisibility(binding);
if (vis != null)
return vis;
IMethodBinding overriden = getBaseMethod(binding, false, false);
if (overriden != null) {
vis = mapVisibility(overriden);
if (vis == CSVisibility.ProtectedInternal && !overriden.getDeclaringClass().isFromSource())
vis = CSVisibility.Protected;
return vis;
} else if (binding.getDeclaringClass().isInterface()) {
return CSVisibility.Public;
}
vis = mapVisibility(binding.getModifiers());
final ITypeBinding declaringClass = binding.getDeclaringClass();
final boolean isNestedPrivate = declaringClass.isNested()
&& Modifier.isPrivate(declaringClass.getModifiers());
final IMethodBinding declaration = binding.getMethodDeclaration();
if (!isNestedPrivate) {
if (containsNonPublicTypes(declaration, true)) {
vis = CSVisibility.Private;
} else if (containsNonPublicTypes(declaration, false))
vis = CSVisibility.Internal;
} else {
if (vis == CSVisibility.Private)
vis = CSVisibility.Internal;
}
return vis;
}
private void fixupMethod(MethodDeclaration node, CSMethodBase method) {
final IMethodBinding binding = node.resolveBinding();
method.visibility(mapVisibility(binding));
if (method instanceof CSMethod)
fixupMethod(node, (CSMethod) method);
}
private void fixupMethod(MethodDeclaration node, CSMethod method) {
final IMethodBinding binding = node.resolveBinding();
final ITypeBinding declaringClass = binding.getDeclaringClass();
IMethodBinding overriden = getBaseMethod(binding, true, true);
ITypeBinding interfaceImpl = null;
if (overriden != null) {
if (Modifier.isStatic(binding.getModifiers())) {
method.setNewModifier(true);
} else if (Modifier.isPrivate(overriden.getModifiers())) {
method.setNewModifier(true);
if (method.modifier() == CSMethodModifier.Override)
method.modifier(CSMethodModifier.None);
} else {
ITypeBinding declaringType = overriden.getDeclaringClass();
String overrideName = BindingUtils.qualifiedName(declaringType);
if (declaringType.isInterface())
interfaceImpl = declaringType;
else {
CSAttribute attr = new CSAttribute("Sharpen.OverridesMethod");
attr.addArgument(new CSStringLiteralExpression(
toLiteralStringForm(overrideName)));
method.addAttribute(attr);
}
}
}
if ((interfaceImpl == null) && !declaringClass.isInterface()) {
overriden = getBaseMethod(binding, false, false);
if (overriden != null)
interfaceImpl = overriden.getDeclaringClass();
}
if ((interfaceImpl != null) && interfaceImpl.isInterface()) {
ITypeBinding parentType = null;
ASTNode parent = node.getParent();
if (parent instanceof TypeDeclaration)
parentType = ((TypeDeclaration) parent).resolveBinding();
else if (parent instanceof AnonymousClassDeclaration)
parentType = ((AnonymousClassDeclaration) parent).resolveBinding();
if (parentType != null)
parentType = parentType.getSuperclass();
if (Modifier.isAbstract(node.getModifiers())) {
if (BindingUtils.findInterfaceInClassHierarchy(interfaceImpl, parentType))
method.modifier(CSMethodModifier.AbstractOverride);
else
method.modifier(CSMethodModifier.Abstract);
} else {
if (BindingUtils.findInterfaceInClassHierarchy(interfaceImpl, parentType))
method.modifier(CSMethodModifier.Override);
else if (_currentType.isSealed())
method.modifier(CSMethodModifier.None);
else
method.modifier(CSMethodModifier.Virtual);
}
CSAttribute attr = new CSAttribute("Sharpen.ImplementsInterface");
String overrideName = BindingUtils.qualifiedName(interfaceImpl);
attr.addArgument(new CSStringLiteralExpression(toLiteralStringForm(overrideName)));
method.addAttribute(attr);
}
if ((method.modifier() == CSMethodModifier.Virtual) && (method.visibility() == CSVisibility.Private))
method.modifier(CSMethodModifier.None);
if (_configuration.junitConversion() && isLegacyTestFixture(node.resolveBinding().getDeclaringClass())) {
if (method.name().startsWith("Test") && method.visibility() == CSVisibility.Public)
method.addAttribute(new CSAttribute("NUnit.Framework.Test"));
if (isLegacyTestFixtureClass(node.resolveBinding().getDeclaringClass().getSuperclass())) {
if (method.name().equals("SetUp")) {
method.addAttribute(new CSAttribute("NUnit.Framework.SetUp"));
method.modifier(CSMethodModifier.Virtual);
cleanBaseSetupCalls(method);
} else if (method.name().equals("TearDown")) {
method.addAttribute(new CSAttribute("NUnit.Framework.TearDown"));
method.modifier(CSMethodModifier.Virtual);
cleanBaseSetupCalls(method);
}
}
}
}
private void fixupMethod(MethodDeclaration node, CSTypeDeclaration typeDecl, CSMethod method) {
final IMethodBinding binding = node.resolveBinding();
final ITypeBinding returnType = binding.getReturnType();
if ((returnType == null) || binding.getDeclaringClass().isInterface())
return;
for (final ITypeBinding iface : BindingUtils.getAllInterfaces(binding.getDeclaringClass())) {
IMethodBinding ifaceMethod = BindingUtils.findOverriddenMethodInType(iface, binding);
if (ifaceMethod == null)
continue;
ITypeBinding ifaceReturnType = ifaceMethod.getReturnType();
if (!returnType.equals(ifaceReturnType)) {
CSMethod proxy = createInterfaceProxy(node, method, ifaceMethod);
typeDecl.addMember(proxy);
}
}
}
private CSMethod createInterfaceProxy(MethodDeclaration node, CSMethod method, IMethodBinding ifaceMethod) {
final IMethodBinding binding = node.resolveBinding();
final ITypeBinding returnType = binding.getReturnType();
final ITypeBinding ifaceReturnType = ifaceMethod.getReturnType();
CSTypeReferenceExpression ifaceName = mappedTypeReference(ifaceMethod.getDeclaringClass());
CSMethod proxy = new CSMethod(ifaceName.getTypeName() + '.' + ifaceMethod.getName());
proxy.visibility(CSVisibility.Private);
proxy.modifier(CSMethodModifier.None);
proxy.returnType(mappedTypeReference(ifaceMethod.getReturnType()));
mapTypeParameters(node.typeParameters(), proxy);
mapParameters(node, proxy);
CSAttribute attr = new CSAttribute("Sharpen.Proxy");
proxy.addAttribute(attr);
CSExpression name = new CSReferenceExpression(method.name());
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(name);
for (CSVariableDeclaration param : proxy.parameters())
mie.addArgument(new CSReferenceExpression(param.name()));
for (CSTypeParameter param : proxy.typeParameters())
mie.addTypeArgument(new CSTypeReference(param.name()));
CSExpression retval = castIfNeeded(ifaceReturnType, returnType, mie);
proxy.body().addStatement(new CSReturnStatement(-1, retval));
return proxy;
}
protected void processMethodBody(MethodDeclaration node, CSMethodBase method) {
visitBodyDeclarationBlock(node, node.getBody(), method);
}
private String mappedMethodDeclarationName(MethodDeclaration node) {
final IMethodBinding binding = node.resolveBinding();
final IMethodBinding baseMethod = getBaseMethod(binding, false, false);
String mappedName;
if (baseMethod != null)
mappedName = mappedMethodName(baseMethod);
else
mappedName = mappedMethodName(node);
if (null == mappedName || 0 == mappedName.length() || mappedName.contains(".")) {
return methodName(node.getName().toString());
}
return mappedName;
}
private void mapParameters(MethodDeclaration node, CSParameterized method) {
if (method instanceof CSMethod) {
mapMethodParameters(node, (CSMethod) method);
return;
}
for (Object p : node.parameters()) {
mapParameter((SingleVariableDeclaration) p, method);
}
}
private void mapParameter(SingleVariableDeclaration parameter, CSParameterized method) {
method.addParameter(createParameter(parameter));
}
ITypeBinding mapTypeParameterExtendedType(ITypeBinding tb) {
ITypeBinding superc = tb.getSuperclass();
if (superc != null && !superc.equals(_objectType)
&& !superc.getQualifiedName().equals("java.lang.Enum<?>")) {
return superc;
}
ITypeBinding[] ints = tb.getInterfaces();
if (ints.length > 0)
return ints[0];
return null;
}
private void mapMethodParameters(MethodDeclaration node, CSMethod method) {
final IMethodBinding methodBinding = node.resolveBinding();
final IMethodBinding baseMethod = my(IBindingManager.class).getBaseMethod(methodBinding);
final IMethodBinding baseOrThis = baseMethod != null ? baseMethod : methodBinding;
final ITypeBinding[] baseTypes = baseMethod != null ? baseMethod.getParameterTypes() : null;
int first = 0;
final List<SingleVariableDeclaration> params = node.parameters();
ITypeInfo typeInfo = my(IBindingManager.class).getTypeInfo(baseOrThis.getDeclaringClass());
if ((typeInfo != null) && typeInfo.isEventInterface()) {
IVariableBinding vb = params.get(0).resolveBinding();
method.addParameter(createVariableDeclaration(vb, OBJECT_TYPE_REFERENCE, null));
first = 1;
}
for (int i = first; i < params.size(); i++) {
ITypeBinding baseType = !node.isVarargs() && (baseMethod != null) ? baseTypes[i] : null;
mapMethodParameter(node, baseType, params.get(i), method);
}
}
private void mapMethodParameter(MethodDeclaration node, ITypeBinding baseType, SingleVariableDeclaration p,
CSMethod method) {
final IMethodBinding methodBinding = node.resolveBinding();
final ITypeBinding parameterType = p.getType().resolveBinding();
final IVariableBinding vb = p.resolveBinding();
final ITypeBinding[] ta = vb.getType().getTypeArguments();
if (isGenericRuntimeParameterIdiom(methodBinding, parameterType)) {
// System.Type <p.name> = typeof(<T>);
CSTypeofExpression to = new CSTypeofExpression(genericRuntimeTypeIdiomType(parameterType));
CSTypeReference str = new CSTypeReference("System.Type");
CSVariableDeclaration vdecl = new CSVariableDeclaration(identifier(p.getName()), str, to);
method.body().addStatement(new CSDeclarationStatement(p.getStartPosition(), vdecl));
return;
}
final List<CSTypeReferenceExpression> args = new ArrayList<CSTypeReferenceExpression>();
if (ta.length > 0)
mapWildcardType(methodBinding, method, args, parameterType);
else if (baseType != null)
mapWildcardType(methodBinding, method, args, baseType);
if (isJavaLangClass(parameterType) && (args.size() == 1)) {
CSExpression typeof = new CSTypeofExpression(args.get(0));
CSTypeReference str = new CSTypeReference("System.Type");
CSVariableDeclaration vdecl = createVariableDeclaration(vb, str, typeof);
method.body().addStatement(new CSDeclarationStatement(p.getStartPosition(), vdecl));
} else if (args.size() > 0) {
CSTypeReference tr = new CSTypeReference(mappedTypeName(parameterType.getTypeDeclaration()));
for (CSTypeReferenceExpression arg : args)
tr.addTypeArgument(arg);
method.addParameter(createVariableDeclaration(vb, tr, null));
} else {
method.addParameter(createVariableDeclaration(vb, null));
}
}
private void mapWildcardType(IMethodBinding binding, CSMethod method, List<CSTypeReferenceExpression> list,
ITypeBinding type) {
final ITypeBinding[] args = type.getTypeArguments();
for (int i = 0; i < args.length; i++) {
final ITypeBinding arg = args[i];
if (!arg.isWildcardType()) {
list.add(mappedTypeReference(arg));
continue;
}
final ITypeBinding bound = arg.getBound();
if (bound != null) {
IMethodBinding boundDecl = bound.getDeclaringMethod();
if ((boundDecl != null) && boundDecl.equals(binding)) {
list.add(mappedTypeReference(bound));
continue;
}
}
final ITypeBinding extended = mapTypeParameterExtendedType(arg);
String genericArg = "_T" + method.typeParameters().size();
CSTypeParameter tp = new CSTypeParameter(genericArg);
if (extended != null)
tp.superClass(mappedTypeReference(extended));
else if (bound != null) {
list.add(mappedTypeReference(bound));
continue;
}
method.addTypeParameter(tp);
_currentWildcardParams.put(arg, genericArg);
list.add(new CSTypeReference(genericArg));
}
}
private CSTypeReferenceExpression genericRuntimeTypeIdiomType(ITypeBinding parameterType) {
return mappedTypeReference(parameterType.getTypeArguments()[0]);
}
private boolean isGenericRuntimeParameterIdiom(IMethodBinding method, ITypeBinding parameterType) {
if (!parameterType.isParameterizedType()) {
return false;
}
if (!parameterType.getTypeDeclaration().equals(_classType)) {
return false;
}
// detecting if the T in Class<T> comes from the method itself
final ITypeBinding arg = parameterType.getTypeArguments()[0];
final IMethodBinding decl = getTypeParameterDeclaringMethod(arg);
return method.equals(decl);
}
private IMethodBinding getTypeParameterDeclaringMethod(ITypeBinding type) {
if (type.isWildcardType()) {
ITypeBinding bound = type.getBound();
if (bound != null)
return getTypeParameterDeclaringMethod(bound);
}
if (type.isArray())
return getTypeParameterDeclaringMethod(type.getElementType());
return type.getDeclaringMethod();
}
private CSTypeReferenceExpression mappedReturnType(MethodDeclaration node) {
IMethodBinding overriden = getOverridedMethod(node);
if (overriden != null)
return mappedTypeReference(overriden.getReturnType());
return mappedTypeReference(node.getReturnType2());
}
private void processEventDeclaration(MethodDeclaration node) {
CSTypeReference eventHandlerType = new CSTypeReference(getEventHandlerTypeName(node));
CSEvent event = createEventFromMethod(node, eventHandlerType);
mapMetaMemberAttributes(node, event);
if (_currentType.isInterface())
return;
VariableDeclarationFragment field = getEventBackingField(node);
CSField backingField = (CSField) _currentType.getMember(field.getName().toString());
backingField.type(eventHandlerType);
// clean field
backingField.initializer(null);
backingField.removeModifier(CSFieldModifier.Readonly);
final CSBlock addBlock = createEventBlock(backingField, "System.Delegate.Combine");
String onAddMethod = getEventOnAddMethod(node);
if (onAddMethod != null) {
addBlock.addStatement(new CSMethodInvocationExpression(new CSReferenceExpression(onAddMethod)));
}
event.setAddBlock(addBlock);
event.setRemoveBlock(createEventBlock(backingField, "System.Delegate.Remove"));
}
private String getEventOnAddMethod(MethodDeclaration node) {
final TagElement onAddTag = javadocTagFor(node, SharpenAnnotations.SHARPEN_EVENT_ON_ADD);
if (null == onAddTag)
return null;
return methodName(JavadocUtility.singleTextFragmentFrom(onAddTag));
}
private String getEventHandlerTypeName(MethodDeclaration node) {
final String eventArgsType = getEventArgsType(node);
return buildEventHandlerTypeName(node, eventArgsType);
}
private void mapMetaMemberAttributes(MethodDeclaration node, CSMetaMember metaMember) {
final IMethodBinding binding = node.resolveBinding();
metaMember.visibility(mapVisibility(binding));
metaMember.modifier(mapMethodModifier(node));
mapDocumentation(node, metaMember);
}
private CSBlock createEventBlock(CSField backingField, String delegateMethod) {
CSBlock block = new CSBlock();
block.addStatement(new CSInfixExpression("=", new CSReferenceExpression(backingField.name()),
new CSCastExpression(backingField.type(), new CSMethodInvocationExpression(
new CSReferenceExpression(
delegateMethod), new CSReferenceExpression(backingField
.name()), new CSReferenceExpression(
"value")))));
return block;
}
private static final class CheckVariableUseVisitor extends ASTVisitor {
private final IVariableBinding _var;
private boolean _used;
private CheckVariableUseVisitor(IVariableBinding var) {
this._var = var;
}
@Override
public boolean visit(SimpleName name) {
IBinding binding = name.resolveBinding();
if (binding == null) {
return false;
}
if (binding.equals(_var)) {
_used = true;
}
return false;
}
public boolean used() {
return _used;
}
}
private static final class FieldAccessFinder extends ASTVisitor {
public IBinding field;
@Override
public boolean visit(SimpleName node) {
field = node.resolveBinding();
return false;
}
}
private VariableDeclarationFragment getEventBackingField(MethodDeclaration node) {
FieldAccessFinder finder = new FieldAccessFinder();
node.accept(finder);
return findDeclaringNode(finder.field);
}
private CSEvent createEventFromMethod(MethodDeclaration node, CSTypeReference eventHandlerType) {
String eventName = methodName(node);
CSEvent event = new CSEvent(eventName, eventHandlerType);
_currentType.addMember(event);
return event;
}
private String methodName(MethodDeclaration node) {
return methodName(node.getName().toString());
}
private String unqualifiedName(String typeName) {
int index = typeName.lastIndexOf('.');
if (index < 0)
return typeName;
return typeName.substring(index + 1);
}
private String buildEventHandlerTypeName(ASTNode node, String eventArgsTypeName) {
if (!eventArgsTypeName.endsWith("EventArgs")) {
warning(node, SharpenAnnotations.SHARPEN_EVENT + " type name must end with 'EventArgs'");
return eventArgsTypeName + "EventHandler";
}
return "System.EventHandler<" + eventArgsTypeName + ">";
}
private String getEventArgsType(MethodDeclaration node) {
TagElement tag = eventTagFor(node);
if (null == tag)
return null;
return mappedTypeName(JavadocUtility.singleTextFragmentFrom(tag));
}
private TagElement eventTagFor(MethodDeclaration node) {
return effectiveAnnotationFor(node, SharpenAnnotations.SHARPEN_EVENT);
}
private TagElement effectiveAnnotationFor(MethodDeclaration node, final String annotation) {
return my(Annotations.class).effectiveAnnotationFor(node, annotation);
}
private <T extends ASTNode> T findDeclaringNode(IBinding binding) {
return (T) my(Bindings.class).findDeclaringNode(binding);
}
private void visitBodyDeclarationBlock(BodyDeclaration node, Block block, CSMethodBase method) {
visitBodyDeclarationBlock(node, block, method, method.body());
}
private void visitBodyDeclarationBlock(BodyDeclaration node, Block block, CSMethodBase method, CSBlock body) {
CSMethodBase saved = _currentMethod;
_currentMethod = method;
processDisableTags(node, method);
processBlock(node, block, body);
_currentMethod = saved;
}
private void processDisableTags(PackageDeclaration packageDeclaration, CSNode csNode) {
TagElement tag = javadocTagFor(packageDeclaration, SharpenAnnotations.SHARPEN_IF);
if (null == tag)
return;
csNode.addEnclosingIfDef(JavadocUtility.singleTextFragmentFrom(tag));
}
private void processDisableTags(BodyDeclaration node, CSNode csNode) {
TagElement tag = javadocTagFor(node, SharpenAnnotations.SHARPEN_IF);
if (null == tag)
return;
csNode.addEnclosingIfDef(JavadocUtility.singleTextFragmentFrom(tag));
}
private void processBlock(BodyDeclaration node, Block block, final CSBlock targetBlock) {
if (containsJavadoc(node, SharpenAnnotations.SHARPEN_REMOVE_FIRST)) {
block.statements().remove(0);
}
BodyDeclaration savedDeclaration = _currentBodyDeclaration;
_currentBodyDeclaration = node;
if (Modifier.isSynchronized(node.getModifiers())) {
CSLockStatement lock = new CSLockStatement(node.getStartPosition(), getLockTarget(node));
targetBlock.addStatement(lock);
visitBlock(lock.body(), block);
} else {
visitBlock(targetBlock, block);
}
_currentBodyDeclaration = savedDeclaration;
}
private CSExpression getLockTarget(BodyDeclaration node) {
return Modifier.isStatic(node.getModifiers()) ? new CSTypeofExpression(new CSTypeReference(
_currentType.name()))
: new CSThisExpression();
}
@Override
public boolean visit(ConstructorInvocation node) {
addChainedConstructorInvocation(new CSThisExpression(), node.arguments(),
node.resolveConstructorBinding());
return false;
}
private void addChainedConstructorInvocation(CSExpression target, List arguments, IMethodBinding binding) {
CSConstructorInvocationExpression cie = mapChainedConstructorInvocation(target, arguments, binding);
((CSConstructor) _currentMethod).chainedConstructorInvocation(cie);
}
public CSConstructorInvocationExpression mapChainedConstructorInvocation(CSExpression target, List args,
IMethodBinding binding) {
CSConstructorInvocationExpression cie = new CSConstructorInvocationExpression(target);
mapArguments(cie, args, binding);
return cie;
}
@Override
public boolean visit(SuperConstructorInvocation node) {
if (null != node.getExpression()) {
notImplemented(node);
}
addChainedConstructorInvocation(new CSBaseExpression(), node.arguments(),
node.resolveConstructorBinding());
return false;
}
private <T extends ASTNode> void visitBlock(CSBlock block, T node) {
if (null == node) {
return;
}
CSBlock saved = _currentBlock;
_currentBlock = block;
_currentContinueLabel = null;
node.accept(this);
_currentBlock = saved;
}
@Override
public boolean visit(VariableDeclarationExpression node) {
pushExpression(new CSDeclarationExpression(createVariableDeclaration((VariableDeclarationFragment) node
.fragments().get(0))));
return false;
}
@Override
public boolean visit(VariableDeclarationStatement node) {
for (Object f : node.fragments()) {
VariableDeclarationFragment variable = (VariableDeclarationFragment) f;
addStatement(new CSDeclarationStatement(node.getStartPosition(),
createVariableDeclaration(variable)));
}
return false;
}
private CSVariableDeclaration createVariableDeclaration(VariableDeclarationFragment variable) {
final IVariableBinding binding = variable.resolveBinding();
final ITypeBinding type = binding.getType();
final Expression initializer = variable.getInitializer();
if (initializer == null)
return createVariableDeclaration(binding, null);
if (isZeroLiteral(initializer)) {
CSExpression nullPointer = my(CSharpDriver.class).mappedNullPointer(binding);
if (nullPointer != null)
return createVariableDeclaration(binding, nullPointer);
}
final ITypeBinding initType = initializer.resolveTypeBinding();
ITypeBinding expected = type;
if (isGenericInstance(initType)) {
ITypeBinding underlyingLeftType = getUnderlyingGenericType(type);
ITypeBinding underlyingRightType = getUnderlyingGenericType(initType);
if ((underlyingLeftType != null) && underlyingLeftType.equals(underlyingRightType))
expected = initType;
}
pushExpectedType(expected);
CSExpression mapped = mapExpression(type, initializer);
CSVariableDeclaration vdecl = createVariableDeclaration(binding, mapped);
popExpectedType();
return vdecl;
}
public static boolean isZeroLiteral(Expression expr) {
if (expr instanceof NumberLiteral) {
NumberLiteral literal = (NumberLiteral) expr;
Object value = literal.resolveConstantExpressionValue();
return value.equals(0);
}
if (expr instanceof NullLiteral)
return true;
return false;
}
private CSVariableDeclaration createVariableDeclaration(IVariableBinding binding, CSExpression initializer) {
CSTypeReferenceExpression type = mappedVariableType(binding);
return createVariableDeclaration(binding, type, initializer);
}
private CSVariableDeclaration createVariableDeclaration(IVariableBinding binding,
CSTypeReferenceExpression type, CSExpression initializer) {
String name = my(CSharpDriver.class).mappedVariableName(binding);
if (name == null)
name = binding.getName();
return new CSVariableDeclaration(identifier(name), type, initializer);
}
@Override
public boolean visit(ExpressionStatement node) {
if (isRemovedMethodInvocation(node.getExpression())) {
return false;
}
addStatement(new CSExpressionStatement(node.getStartPosition(), mapExpression(node.getExpression())));
return false;
}
private boolean isRemovedMethodInvocation(Expression expression) {
if (!(expression instanceof MethodInvocation)) {
return false;
}
MethodInvocation invocation = (MethodInvocation) expression;
return isTaggedMethodInvocation(invocation, SharpenAnnotations.SHARPEN_REMOVE)
|| isRemoved(invocation.resolveMethodBinding());
}
public boolean isEnumOrdinalMethodInvocation(MethodInvocation node) {
return node.getName().getIdentifier().equals("ordinal") &&
node.getExpression() != null &&
node.getExpression().resolveTypeBinding().isEnum();
}
public boolean isEnumNameMethodInvocation(MethodInvocation node) {
return node.getName().getIdentifier().equals("name") &&
node.getExpression() != null &&
node.getExpression().resolveTypeBinding().isEnum();
}
public boolean isEnumValuesMethodInvocation(MethodInvocation node) {
return node.getName().getIdentifier().equals("values") &&
node.getExpression() != null &&
node.getExpression().resolveTypeBinding().isEnum();
}
@Override
public boolean visit(IfStatement node) {
Expression expression = node.getExpression();
Object constValue = constValue(expression);
if (null != constValue) {
// dead branch elimination
if (isTrue(constValue)) {
node.getThenStatement().accept(this);
} else {
if (null != node.getElseStatement()) {
node.getElseStatement().accept(this);
}
}
} else {
CSIfStatement stmt = new CSIfStatement(node.getStartPosition(), mapExpression(expression));
visitBlock(stmt.trueBlock(), node.getThenStatement());
visitBlock(stmt.falseBlock(), node.getElseStatement());
addStatement(stmt);
}
return false;
}
private boolean isTrue(Object constValue) {
return ((Boolean) constValue).booleanValue();
}
private Object constValue(Expression expression) {
switch (expression.getNodeType()) {
case ASTNode.PREFIX_EXPRESSION:
return constValue((PrefixExpression) expression);
case ASTNode.SIMPLE_NAME:
case ASTNode.QUALIFIED_NAME:
return constValue((Name) expression);
}
return null;
}
public Object constValue(PrefixExpression expression) {
if (PrefixExpression.Operator.NOT == expression.getOperator()) {
Object value = constValue(expression.getOperand());
if (null != value) {
return isTrue(value) ? Boolean.FALSE : Boolean.TRUE;
}
}
return null;
}
public Object constValue(Name expression) {
IBinding binding = expression.resolveBinding();
if (IBinding.VARIABLE == binding.getKind()) {
return ((IVariableBinding) binding).getConstantValue();
}
return null;
}
@Override
public boolean visit(final WhileStatement node) {
consumeContinueLabel(new Function<CSBlock>() {
public CSBlock apply() {
CSWhileStatement stmt = new CSWhileStatement(node.getStartPosition(),
mapExpression(node.getExpression()));
visitBlock(stmt.body(), node.getBody());
addStatement(stmt);
return stmt.body();
}
});
return false;
}
@Override
public boolean visit(final DoStatement node) {
consumeContinueLabel(new Function<CSBlock>() {
public CSBlock apply() {
CSDoStatement stmt = new CSDoStatement(node.getStartPosition(),
mapExpression(node.getExpression()));
visitBlock(stmt.body(), node.getBody());
addStatement(stmt);
return stmt.body();
}
});
return false;
}
@Override
public boolean visit(TryStatement node) {
CSTryStatement stmt = new CSTryStatement(node.getStartPosition());
visitBlock(stmt.body(), node.getBody());
for (Object o : node.catchClauses()) {
CatchClause clause = (CatchClause) o;
if (!_configuration.isIgnoredExceptionType(qualifiedName(clause.getException().getType()
.resolveBinding()))) {
stmt.addCatchClause(mapCatchClause(clause));
}
}
if (null != node.getFinally()) {
CSBlock finallyBlock = new CSBlock();
visitBlock(finallyBlock, node.getFinally());
stmt.finallyBlock(finallyBlock);
}
if (null != stmt.finallyBlock() || !stmt.catchClauses().isEmpty()) {
addStatement(stmt);
} else {
_currentBlock.addAll(stmt.body());
}
return false;
}
private CSCatchClause mapCatchClause(CatchClause node) {
IVariableBinding oldExceptionVariable = _currentExceptionVariable;
_currentExceptionVariable = node.getException().resolveBinding();
try {
CheckVariableUseVisitor check = new CheckVariableUseVisitor(_currentExceptionVariable);
node.getBody().accept(check);
CSCatchClause clause;
if (isEmptyCatch(node, check)) {
clause = new CSCatchClause();
} else {
clause = new CSCatchClause(createVariableDeclaration(_currentExceptionVariable, null));
}
clause.anonymous(!check.used());
visitBlock(clause.body(), node.getBody());
return clause;
} finally {
_currentExceptionVariable = oldExceptionVariable;
}
}
private boolean isEmptyCatch(CatchClause clause, CheckVariableUseVisitor check) {
if (check.used())
return false;
return isThrowable(clause.getException().resolveBinding().getType());
}
private boolean isThrowable(ITypeBinding declaringClass) {
return "java.lang.Throwable".equals(qualifiedName(declaringClass));
}
@Override
public boolean visit(ThrowStatement node) {
addStatement(mapThrowStatement(node));
return false;
}
private CSThrowStatement mapThrowStatement(ThrowStatement node) {
Expression exception = node.getExpression();
if (isCurrentExceptionVariable(exception)) {
return new CSThrowStatement(node.getStartPosition(), null);
}
return new CSThrowStatement(node.getStartPosition(), mapExpression(exception));
}
private boolean isCurrentExceptionVariable(Expression exception) {
if (!(exception instanceof SimpleName)) {
return false;
}
return ((SimpleName) exception).resolveBinding() == _currentExceptionVariable;
}
@Override
public boolean visit(BreakStatement node) {
SimpleName labelName = node.getLabel();
if (labelName != null) {
addStatement(new CSGotoStatement(node.getStartPosition(), breakLabel(labelName.getIdentifier())));
return false;
}
addStatement(new CSBreakStatement(node.getStartPosition()));
return false;
}
@Override
public boolean visit(ContinueStatement node) {
SimpleName labelName = node.getLabel();
if (labelName != null) {
addStatement(new CSGotoStatement(node.getStartPosition(),
continueLabel(labelName.getIdentifier())));
return false;
}
addStatement(new CSContinueStatement(node.getStartPosition()));
return false;
}
@Override
public boolean visit(SynchronizedStatement node) {
CSLockStatement stmt = new CSLockStatement(node.getStartPosition(), mapExpression(node.getExpression()));
visitBlock(stmt.body(), node.getBody());
addStatement(stmt);
return false;
}
@Override
public boolean visit(ReturnStatement node) {
CSExpression expr;
IMethodBinding binding = currentMethodBinding();
if (binding != null)
expr = mapExpression(binding.getReturnType(), node.getExpression());
else
expr = mapExpression(node.getExpression());
addStatement(new CSReturnStatement(node.getStartPosition(), expr));
return false;
}
@Override
public boolean visit(NumberLiteral node) {
String token = node.getToken();
CSExpression literal = new CSNumberLiteralExpression(token);
if (expectingType("byte") && token.startsWith("-")) {
literal = uncheckedCast("byte", literal);
}
else if (token.startsWith("0x")) {
if (token.endsWith("l") || token.endsWith("L")) {
literal = uncheckedCast("long", literal);
} else {
literal = uncheckedCast("int", literal);
}
} else if (token.startsWith("0") && token.indexOf('.') == -1
&& Character.isDigit(token.charAt(token.length() - 1))) {
try {
int n = Integer.parseInt(token, 8);
if (n != 0)
literal = new CSNumberLiteralExpression("0x" + Integer.toHexString(n));
} catch (NumberFormatException ex) {
}
} else if (token.endsWith(".f")) {
literal = new CSNumberLiteralExpression(token.substring(0, token.length() - 2) + ".0f");
}
pushExpression(literal);
return false;
}
private CSUncheckedExpression uncheckedCast(String type, CSExpression expression) {
return new CSUncheckedExpression(new CSCastExpression(new CSTypeReference(type),
new CSParenthesizedExpression(
expression)));
}
@Override
public boolean visit(StringLiteral node) {
String value = node.getLiteralValue();
if (value != null && value.length() == 0) {
pushExpression(new CSReferenceExpression("string.Empty"));
} else {
pushExpression(new CSStringLiteralExpression(fixEscapedNumbers(node.getEscapedValue())));
}
return false;
}
String fixEscapedNumbers(String literal) {
StringBuffer s = new StringBuffer();
for (int n = 0; n < literal.length(); n++) {
if (literal.charAt(n) == '\\') {
int i = n + 1;
if (i < literal.length() && literal.charAt(i) == '\\') {
s.append("\\\\");
n = i;
continue;
}
while (i < literal.length() && Character.isDigit(literal.charAt(i)))
i++;
if (i != n + 1) {
int num = Integer.parseInt(literal.substring(n + 1, i));
s.append("\\x" + Integer.toHexString(num));
n = i - 1;
continue;
}
}
s.append(literal.charAt(n));
}
return s.toString();
}
@Override
public boolean visit(CharacterLiteral node) {
CSExpression expr = new CSCharLiteralExpression(node.getEscapedValue());
if (expectingType("byte")) {
expr = new CSCastExpression(new CSTypeReference("byte"), new CSParenthesizedExpression(
expr));
}
pushExpression(expr);
return false;
}
private boolean expectingType(String name) {
ITypeBinding expected = getExpectedType();
return (expected != null && expected.getName().equals(name));
}
@Override
public boolean visit(NullLiteral node) {
pushExpression(new CSNullLiteralExpression());
return false;
}
@Override
public boolean visit(BooleanLiteral node) {
pushExpression(new CSBoolLiteralExpression(node.booleanValue()));
return false;
}
@Override
public boolean visit(ThisExpression node) {
pushExpression(new CSThisExpression());
return false;
}
@Override
public boolean visit(ArrayAccess node) {
pushExpression(new CSIndexedExpression(mapExpression(node.getArray()), mapExpression(node.getIndex())));
return false;
}
@Override
public boolean visit(ArrayCreation node) {
ITypeBinding expected = getExpectedType();
if ((expected != null) && expected.isArray())
pushExpectedType(expected.getElementType());
else
pushExpectedType(node.getType().getElementType().resolveBinding());
if (node.dimensions().size() > 1) {
if (null != node.getInitializer()) {
notImplemented(node);
}
pushExpression(unfoldMultiArrayCreation(node));
} else {
pushExpression(mapSingleArrayCreation(node));
}
popExpectedType();
return false;
}
/**
* Unfolds java multi array creation shortcut "new String[2][3][2]" into
* explicitly array creation "new string[][][] { new string[][] { new
* string[2], new string[2], new string[2] }, new string[][] { new
* string[2], new string[2], new string[2] } }"
*/
private CSArrayCreationExpression unfoldMultiArrayCreation(ArrayCreation node) {
return unfoldMultiArray((ArrayType) node.getType().getComponentType(), node.dimensions(), 0);
}
private CSArrayCreationExpression unfoldMultiArray(ArrayType type, List dimensions, int dimensionIndex) {
final CSArrayCreationExpression expression = new CSArrayCreationExpression(mappedTypeReference(type));
expression.initializer(new CSArrayInitializerExpression());
int length = resolveIntValue(dimensions.get(dimensionIndex));
if (dimensionIndex < lastIndex(dimensions) - 1) {
for (int i = 0; i < length; ++i) {
expression.initializer().addExpression(
unfoldMultiArray((ArrayType) type.getComponentType(), dimensions,
dimensionIndex + 1));
}
} else {
Expression innerLength = (Expression) dimensions.get(dimensionIndex + 1);
CSTypeReferenceExpression innerType = mappedTypeReference(type.getComponentType());
for (int i = 0; i < length; ++i) {
expression.initializer().addExpression(
new CSArrayCreationExpression(innerType, mapExpression(innerLength)));
}
}
return expression;
}
private int lastIndex(List<?> dimensions) {
return dimensions.size() - 1;
}
private int resolveIntValue(Object expression) {
return ((Number) ((Expression) expression).resolveConstantExpressionValue()).intValue();
}
private CSArrayCreationExpression mapSingleArrayCreation(ArrayCreation node) {
CSArrayCreationExpression expression = new CSArrayCreationExpression(
mappedTypeReference(componentType(node
.getType())));
if (!node.dimensions().isEmpty()) {
expression.length(mapExpression((Expression) node.dimensions().get(0)));
}
expression.initializer(mapArrayInitializer(node));
return expression;
}
private CSArrayInitializerExpression mapArrayInitializer(ArrayCreation node) {
return (CSArrayInitializerExpression) mapExpression(node.getInitializer());
}
@Override
public boolean visit(ArrayInitializer node) {
if (isImplicitelyTypedArrayInitializer(node)) {
CSArrayCreationExpression ace = new CSArrayCreationExpression(mappedTypeReference(node
.resolveTypeBinding()
.getComponentType()));
pushExpectedType(node.resolveTypeBinding().getElementType());
ace.initializer(mapArrayInitializer(node));
popExpectedType();
pushExpression(ace);
return false;
}
pushExpression(mapArrayInitializer(node));
return false;
}
private CSArrayInitializerExpression mapArrayInitializer(ArrayInitializer node) {
CSArrayInitializerExpression initializer = new CSArrayInitializerExpression();
for (Object e : node.expressions()) {
initializer.addExpression(mapExpression((Expression) e));
}
return initializer;
}
private boolean isImplicitelyTypedArrayInitializer(ArrayInitializer node) {
return !(node.getParent() instanceof ArrayCreation);
}
public ITypeBinding componentType(ArrayType type) {
return type.getComponentType().resolveBinding();
}
private boolean isIterableType(ITypeBinding type) {
final String fullName = BindingUtils.qualifiedName(type);
if (_configuration.typeHasMapping(fullName))
return false;
if (fullName.equals("java.lang.Iterable"))
return true;
for (ITypeBinding iface : type.getInterfaces()) {
if (isIterableType(iface))
return true;
}
ITypeBinding parent = type.getSuperclass();
if (parent != null)
return isIterableType(parent);
return false;
}
@Override
public boolean visit(EnhancedForStatement node) {
Expression expr = node.getExpression();
ITypeBinding exprType = expr.resolveTypeBinding();
CSExpression mappedExpr = mapExpression(expr);
if (isIterableType(exprType)) {
CSReferenceExpression proxy = new CSReferenceExpression("Sharpen.IterableProxy.Create");
CSMethodInvocationExpression cie = new CSMethodInvocationExpression(proxy);
cie.addArgument(mappedExpr);
mappedExpr = cie;
}
CSForEachStatement stmt = new CSForEachStatement(node.getStartPosition(), mappedExpr);
stmt.variable(createParameter(node.getParameter()));
visitBlock(stmt.body(), node.getBody());
addStatement(stmt);
return false;
}
@Override
public boolean visit(final ForStatement node) {
consumeContinueLabel(new Function<CSBlock>() {
public CSBlock apply() {
List<CSExpression> initializers = new ArrayList<CSExpression>();
int count = 0;
for (final Object i : node.initializers()) {
if (i instanceof VariableDeclarationExpression) {
VariableDeclarationExpression vde = (VariableDeclarationExpression) i;
count += vde.fragments().size();
}
}
CSBlock declBlock = null;
if (count > 0)
declBlock = new CSBlock();
for (final Object i : node.initializers()) {
if (!(i instanceof VariableDeclarationExpression)) {
initializers.add(mapExpression((Expression) i));
continue;
}
VariableDeclarationExpression vde = (VariableDeclarationExpression) i;
for (final Object f : vde.fragments()) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) f;
CSVariableDeclaration decl = createVariableDeclaration(fragment);
CSDeclarationExpression expr = new CSDeclarationExpression(decl);
if (count > 1)
declBlock.addStatement(new CSExpressionStatement(-1, expr));
else
initializers.add(expr);
}
}
CSForStatement stmt = new CSForStatement(node.getStartPosition(),
mapExpression(node.getExpression()));
for (CSExpression i : initializers) {
stmt.addInitializer(i);
}
for (Object u : node.updaters()) {
stmt.addUpdater(mapExpression((Expression) u));
}
visitBlock(stmt.body(), node.getBody());
if (declBlock != null) {
declBlock.addStatement(stmt);
addStatement(declBlock);
return declBlock;
} else {
addStatement(stmt);
return stmt.body();
}
}
});
return false;
}
private void consumeContinueLabel(Function<CSBlock> func) {
CSLabelStatement label = _currentContinueLabel;
_currentContinueLabel = null;
CSBlock body = func.apply();
if (label != null) {
body.addStatement(label);
}
}
@Override
public boolean visit(SwitchStatement node) {
_currentContinueLabel = null;
CSBlock saved = _currentBlock;
final ITypeBinding switchType = node.getExpression().resolveTypeBinding();
final ITypeBinding effectiveSwitchType;
final IExtractedEnumInfo eei = my(IBindingManager.class).getExtractedEnumInfo(switchType);
if (eei != null)
effectiveSwitchType = my(IBindingManager.class).getIntType();
else
effectiveSwitchType = switchType;
pushExpectedType(effectiveSwitchType);
CSExpression expression = mapExpression(node.getExpression());
if (eei != null)
expression = new CSMemberReferenceExpression(expression, eei.valueField());
popExpectedType();
CSSwitchStatement mappedNode = new CSSwitchStatement(node.getStartPosition(), expression);
addStatement(mappedNode);
CSCaseClause current = null;
_currentBlock = null;
for (ASTNode element : Types.<ASTNode> cast(node.statements())) {
if (ASTNode.SWITCH_CASE == element.getNodeType()) {
CSBlock openCaseBlock = null;
if (null == current) {
if ((_currentBlock != null) && FlowAnalysis.isReachable(_currentBlock))
openCaseBlock = _currentBlock;
current = new CSCaseClause();
mappedNode.addCase(current);
_currentBlock = current.body();
}
SwitchCase sc = (SwitchCase) element;
if (sc.isDefault()) {
current.isDefault(true);
if (openCaseBlock != null)
openCaseBlock.addStatement(new CSGotoStatement(Integer.MIN_VALUE,
"default"));
} else {
pushExpectedType(effectiveSwitchType);
CSExpression caseExpression = mapExpression(sc.getExpression());
current.addExpression(caseExpression);
popExpectedType();
if (openCaseBlock != null)
openCaseBlock.addStatement(new CSGotoStatement(Integer.MIN_VALUE,
caseExpression));
}
openCaseBlock = null;
} else {
element.accept(this);
current = null;
}
}
if ((_currentBlock != null) && FlowAnalysis.isReachable(_currentBlock)) {
_currentBlock.addStatement(new CSBreakStatement(Integer.MIN_VALUE));
}
_currentBlock = saved;
return false;
}
private boolean isTypeVariable(ITypeBinding type) {
if (type.isTypeVariable())
return true;
if (type.isWildcardType()) {
ITypeBinding bound = type.getBound();
if (bound != null)
return isTypeVariable(bound);
}
return false;
}
@Override
public boolean visit(CastExpression node) {
Expression expr = node.getExpression();
ITypeBinding exprType = expr.resolveTypeBinding();
ITypeBinding type = node.getType().resolveBinding();
if (isJavaLangCharSequence(exprType) && isJavaLangString(type)) {
CSReferenceExpression re = new CSReferenceExpression("java.lang.CharSequenceProxy.UnWrap");
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(re);
mie.addArgument(mapExpression(expr));
pushExpression(mie);
return false;
} else if (isJavaLangString(exprType) && isJavaLangCharSequence(type)) {
CSReferenceExpression re = new CSReferenceExpression("java.lang.CharSequenceProxy.Wrap");
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(re);
mie.addArgument(mapExpression(expr));
pushExpression(mie);
return false;
} else if (isJavaLangNumber(type)) {
pushExpression(mapExpression(expr));
return false;
}
final CSTypeReferenceExpression target = mappedTypeReference(type);
final CSExpression mappedExpr = mapExpression(expr);
if (isTypeVariable(type) && isTypeVariable(exprType)) {
CSExpression objectCast = new CSCastExpression(OBJECT_TYPE_REFERENCE, mappedExpr);
pushExpression(new CSCastExpression(target, objectCast));
return false;
}
pushExpression(new CSCastExpression(target, mappedExpr));
// Make all byte casts unchecked
if (type.equals(my(IBindingManager.class).getByteType()))
pushExpression(new CSUncheckedExpression(popExpression()));
return false;
}
@Override
public boolean visit(PrefixExpression node) {
CSExpression expr;
expr = new CSPrefixExpression(node.getOperator().toString(), mapExpression(node.getOperand()));
if (expectingType("byte") && node.getOperator() == PrefixExpression.Operator.MINUS) {
expr = uncheckedCast("byte", expr);
}
pushExpression(expr);
return false;
}
@Override
public boolean visit(PostfixExpression node) {
pushExpression(new CSPostfixExpression(node.getOperator().toString(), mapExpression(node.getOperand())));
return false;
}
@Override
public boolean visit(InfixExpression node) {
final Expression left = node.getLeftOperand();
final Expression right = node.getRightOperand();
final ITypeBinding leftType = left.resolveTypeBinding();
final ITypeBinding rightType = right.resolveTypeBinding();
CSExpression mappedLeft = mapExpression(left);
CSExpression mappedRight = mapExpression(right);
final String type = left.resolveTypeBinding().getQualifiedName();
final InfixExpression.Operator op = node.getOperator();
final ITypeBinding byteType = my(IBindingManager.class).getByteType();
final ITypeBinding longType = my(IBindingManager.class).getLongType();
if (op == InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED) {
if (leftType.equals(byteType)) {
pushExpression(new CSInfixExpression(">>", mappedLeft, mappedRight));
} else {
CSExpression cast = new CSCastExpression(new CSTypeReference("u" + type), mappedLeft);
cast = new CSParenthesizedExpression(cast);
CSExpression shiftResult = new CSInfixExpression(">>", cast, mappedRight);
shiftResult = new CSParenthesizedExpression(shiftResult);
pushExpression(new CSCastExpression(new CSTypeReference(type), shiftResult));
}
return false;
} else if ((op == InfixExpression.Operator.RIGHT_SHIFT_SIGNED)
|| (op == InfixExpression.Operator.LEFT_SHIFT)) {
if (rightType.equals(longType))
mappedRight = new CSCastExpression(new CSTypeReference("int"), mappedRight);
} else if ((op == InfixExpression.Operator.EQUALS) || (op == InfixExpression.Operator.NOT_EQUALS)) {
if (isZeroLiteral(right)) {
CSExpression mapped = my(CSharpDriver.class).mappedNullPointer(left);
if (mapped != null)
mappedRight = mapped;
if (leftType.isTypeVariable())
mappedLeft = new CSCastExpression(OBJECT_TYPE_REFERENCE, mappedLeft);
} else if (isZeroLiteral(left)) {
CSExpression mapped = my(CSharpDriver.class).mappedNullPointer(right);
if (mapped != null)
mappedLeft = mapped;
if (rightType.isTypeVariable())
mappedRight = new CSCastExpression(OBJECT_TYPE_REFERENCE, mappedRight);
}
if ((leftType.equals(_objectType) && isTypeVariable(rightType))
|| ((rightType.equals(_objectType) && isTypeVariable(leftType)))) {
CSTypeReference helperRef = new CSTypeReference("Sharpen.Util");
CSMemberReferenceExpression mr = new CSMemberReferenceExpression(helperRef, "Equals");
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(mr);
if (isTypeVariable(leftType)) {
mie.addArgument(mappedLeft);
mie.addArgument(mappedRight);
} else {
mie.addArgument(mappedRight);
mie.addArgument(mappedLeft);
}
pushExpression(mie);
return false;
}
}
if (leftType.equals(byteType)
&& ((op == InfixExpression.Operator.LESS) || (op == InfixExpression.Operator.LESS_EQUALS))) {
mappedLeft = new CSCastExpression(new CSTypeReference("sbyte"), mappedLeft);
mappedLeft = new CSParenthesizedExpression(mappedLeft);
}
String operator = node.getOperator().toString();
pushExpression(new CSInfixExpression(operator, mappedLeft, mappedRight));
pushExtendedOperands(operator, node);
return false;
}
private void pushExtendedOperands(String operator, InfixExpression node) {
for (Object x : node.extendedOperands()) {
pushExpression(new CSInfixExpression(operator, popExpression(), mapExpression((Expression) x)));
}
}
@Override
public boolean visit(ParenthesizedExpression node) {
pushExpression(new CSParenthesizedExpression(mapExpression(node.getExpression())));
return false;
}
@Override
public boolean visit(ConditionalExpression node) {
final Expression thenExpr = node.getThenExpression();
final Expression elseExpr = node.getElseExpression();
final ITypeBinding type = node.resolveTypeBinding();
final ITypeBinding thenType = thenExpr.resolveTypeBinding();
final ITypeBinding elseType = elseExpr.resolveTypeBinding();
CSExpression mappedCondition = mapExpression(node.getExpression());
CSExpression mappedThen = mapExpression(type, thenExpr);
CSExpression mappedElse = mapExpression(type, elseExpr);
boolean oneIsNull = thenExpr instanceof NullLiteral || elseExpr instanceof NullLiteral;
// HACK: If 'then' is a field that's been mapped to "IntPtr" and
// 'else' is a "0" literal, map it to "IntPtr.Zero"
if (isZeroLiteral(elseExpr)) {
CSExpression mapped = my(CSharpDriver.class).mappedNullPointer(thenExpr);
if (mapped != null)
mappedElse = mapped;
oneIsNull = true;
}
if (isZeroLiteral(thenExpr)) {
CSExpression mapped = my(CSharpDriver.class).mappedNullPointer(elseExpr);
if (mapped != null)
mappedThen = mapped;
oneIsNull = true;
}
if (oneIsNull) {
pushExpression(new CSConditionalExpression(mappedCondition, mappedThen, mappedElse));
return false;
}
final ITypeBinding stringType = my(IBindingManager.class).getStringType();
final ITypeBinding serializableType = my(IBindingManager.class).getSerializableType();
if ((type.equals(stringType) || type.equals(serializableType))
&& (thenType.equals(stringType) || elseType.equals(stringType))) {
if (!thenType.equals(stringType)) {
CSExpression toStringRef = new CSMemberReferenceExpression(mappedThen, "ToString");
mappedThen = new CSMethodInvocationExpression(toStringRef);
}
if (!elseType.equals(stringType)) {
CSExpression toStringRef = new CSMemberReferenceExpression(mappedElse, "ToString");
mappedElse = new CSMethodInvocationExpression(toStringRef);
}
}
if (type.isClass() && thenType.isAssignmentCompatible(type) && elseType.isAssignmentCompatible(type)
&& !thenType.isAssignmentCompatible(elseType)
&& !elseType.isAssignmentCompatible(thenType)) {
if (!thenType.equals(type))
mappedThen = new CSCastExpression(mappedTypeReference(type), mappedThen);
if (!elseType.equals(type))
mappedElse = new CSCastExpression(mappedTypeReference(type), mappedElse);
}
pushExpression(new CSConditionalExpression(mappedCondition, mappedThen, mappedElse));
return false;
}
@Override
public boolean visit(InstanceofExpression node) {
Expression lo = node.getLeftOperand();
ITypeBinding loType = lo.resolveTypeBinding();
ITypeBinding type = node.getRightOperand().resolveBinding();
if (isJavaLangCharSequence(loType) && isJavaLangString(type)) {
CSReferenceExpression re = new CSReferenceExpression(
"java.lang.CharSequenceProxy.IsStringProxy");
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(re);
mie.addArgument(mapExpression(lo));
pushExpression(mie);
return false;
} else if (isJavaLangString(loType) && isJavaLangCharSequence(type)) {
pushExpression(new CSBoolLiteralExpression(true));
return false;
}
pushExpression(new CSInfixExpression("is", mapExpression(lo), mappedTypeReference(type)));
return false;
}
@Override
public boolean visit(Assignment node) {
final Expression lhs = node.getLeftHandSide();
final Expression rhs = node.getRightHandSide();
final ITypeBinding lhsType = lhs.resolveTypeBinding();
pushExpectedType(lhsType);
final Assignment.Operator op = node.getOperator();
if (op == Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN) {
String type = lhsType.getQualifiedName();
if (type == "byte") {
pushExpression(new CSInfixExpression(">>", mapExpression(lhs), mapExpression(lhs
.resolveTypeBinding(), rhs)));
} else {
CSExpression mappedLhs = new CSParenthesizedExpression(mapExpression(lhs));
CSExpression cast = new CSCastExpression(new CSTypeReference("u" + type), mappedLhs);
CSExpression shiftResult = new CSInfixExpression(">>", cast, mapExpression(rhs));
shiftResult = new CSParenthesizedExpression(shiftResult);
shiftResult = new CSCastExpression(new CSTypeReference(type), shiftResult);
pushExpression(new CSInfixExpression("=", mappedLhs, shiftResult));
}
} else if ((op == Assignment.Operator.PLUS_ASSIGN) || (op == Assignment.Operator.MINUS_ASSIGN)
|| (op == Assignment.Operator.TIMES_ASSIGN)
|| (op == Assignment.Operator.DIVIDE_ASSIGN)) {
ITypeBinding rhsType = rhs.resolveTypeBinding();
CSExpression mappedLhs = mapExpression(lhs);
CSExpression mappedRhs = mapExpression(lhsType, rhs);
final ITypeBinding intType = my(IBindingManager.class).getIntType();
final ITypeBinding longType = my(IBindingManager.class).getLongType();
final ITypeBinding floatType = my(IBindingManager.class).getFloatType();
if (lhsType.equals(intType) && (rhsType.equals(longType) || rhsType.equals(floatType))) {
mappedRhs = new CSParenthesizedExpression(mappedRhs);
mappedRhs = new CSCastExpression(new CSTypeReference("int"), mappedRhs);
}
pushExpression(new CSInfixExpression(op.toString(), mappedLhs, mappedRhs));
} else if ((op == Assignment.Operator.ASSIGN) && isZeroLiteral(rhs)) {
CSExpression mappedLhs = mapExpression(lhs);
CSExpression mappedRhs = mapExpression(lhsType, rhs);
CSExpression mapped = my(CSharpDriver.class).mappedNullPointer(lhs);
if (mapped != null)
mappedRhs = mapped;
pushExpression(new CSInfixExpression(op.toString(), mappedLhs, mappedRhs));
} else {
CSExpression mappedLhs = mapExpression(lhs);
CSExpression mappedRhs = mapExpression(lhsType, rhs);
pushExpression(new CSInfixExpression(op.toString(), mappedLhs, mappedRhs));
}
popExpectedType();
return false;
}
public CSExpression mapExpression(ITypeBinding expectedType, Expression expression) {
if (expression instanceof NullLiteral) {
if ((expectedType != null) && isTypeVariable(expectedType))
return new CSDefaultExpression(expectedType.getName());
else
return new CSNullLiteralExpression();
}
if ((expression != null) && (expectedType != null)) {
final ITypeBinding type = expression.resolveTypeBinding();
pushExpectedType(expectedType);
CSExpression mappedExpr = mapExpression(expression);
popExpectedType();
return castIfNeeded(expectedType, type, mappedExpr);
} else {
return mapExpression(expression);
}
}
private CSExpression castIfNeeded(ITypeBinding expectedType, ITypeBinding actualType, CSExpression expression) {
if (expectedType != actualType && isSubclassOf(expectedType, actualType))
return new CSCastExpression(mappedTypeReference(expectedType), expression);
if (actualType == expectedType)
return expression;
if (isJavaLangCharSequence(expectedType) && isJavaLangString(actualType)) {
CSReferenceExpression proxy = new CSReferenceExpression("java.lang.CharSequenceProxy.Wrap");
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(proxy);
mie.addArgument(expression);
return mie;
}
if (actualType.equals(_objectType))
return new CSCastExpression(mappedTypeReference(expectedType), expression);
CSExpression casted = my(CSharpDriver.class).castIfNeeded(this, expectedType, actualType, expression);
if (casted != null)
return casted;
if (expectedType != my(IBindingManager.class).getCharType())
return expression;
if (actualType == expectedType)
return expression;
return new CSCastExpression(mappedTypeReference(expectedType), expression);
}
private boolean isSubclassOf(ITypeBinding t, ITypeBinding tbase) {
while (t != null) {
if (t.isEqualTo(tbase))
return true;
t = t.getSuperclass();
}
return false;
}
@Override
public boolean visit(ClassInstanceCreation node) {
if (null != node.getAnonymousClassDeclaration()) {
node.getAnonymousClassDeclaration().accept(this);
return false;
}
CSMethodInvocationExpression expression = mapConstructorInvocation(node);
if (null == expression) {
return false;
}
if (node.getExpression() != null) {
expression.addArgument(mapExpression(node.getExpression()));
} else if (isNonStaticNestedTypeCreation(node)) {
ITypeBinding target = node.resolveTypeBinding();
expression.addArgument(createEnclosingThisReference(target.getDeclaringClass()));
}
mapArguments(expression, node.arguments(), node.resolveConstructorBinding());
pushExpression(expression);
return false;
}
protected CSExpression createEnclosingThisReference(ITypeBinding enclosingClassBinding) {
return new CSThisExpression();
}
private boolean isNonStaticNestedTypeCreation(ClassInstanceCreation node) {
return isNonStaticNestedType(node.resolveTypeBinding());
}
private CSMethodInvocationExpression mapConstructorInvocation(ClassInstanceCreation node) {
Configuration.MemberMapping mappedConstructor = effectiveMappingFor(node.resolveConstructorBinding());
if (null == mappedConstructor) {
return new CSConstructorInvocationExpression(mappedTypeReference(node.resolveTypeBinding()));
}
final String mappedName = mappedConstructor.name;
if (mappedName.length() == 0) {
pushExpression(mapExpression((Expression) node.arguments().get(0)));
return null;
}
if (mappedName.startsWith("System.Convert.To")) {
if (optimizeSystemConvert(mappedName, node)) {
return null;
}
}
return new CSMethodInvocationExpression(new CSReferenceExpression(methodName(mappedName)));
}
private boolean optimizeSystemConvert(String mappedConstructor, ClassInstanceCreation node) {
String typeName = _configuration.getConvertRelatedWellKnownTypeName(mappedConstructor);
if (null != typeName) {
assert 1 == node.arguments().size();
Expression arg = (Expression) node.arguments().get(0);
if (arg.resolveTypeBinding() == resolveWellKnownType(typeName)) {
arg.accept(this);
return true;
}
}
return false;
}
@Override
public boolean visit(TypeLiteral node) {
if (isReferenceToRemovedType(node.getType())) {
pushExpression(new CSRemovedExpression(node.toString()));
return false;
}
pushTypeOfExpression(mappedTypeReference(node.getType()));
return false;
}
private boolean isReferenceToRemovedType(Type node) {
BodyDeclaration typeDeclaration = findDeclaringNode(node.resolveBinding());
if (null == typeDeclaration)
return false;
return hasRemoveAnnotation(typeDeclaration);
}
private void pushTypeOfExpression(CSTypeReferenceExpression type) {
pushExpression(new CSTypeofExpression(type));
}
@Override
public boolean visit(MethodInvocation node) {
final IMethodBinding binding = node.resolveMethodBinding();
final ITypeBinding declaringType = binding.getDeclaringClass();
if ((declaringType == _objectType) && (binding.getName().equals("clone"))) {
final Expression expr = node.getExpression();
final ITypeBinding exprType = expr.resolveTypeBinding();
if (exprType.isArray()) {
CSExpression mapped = mapExpression(expr);
CSExpression target = new CSMemberReferenceExpression(parensIfNeeded(mapped), "Clone");
CSExpression mie = new CSMethodInvocationExpression(target);
ITypeBinding expected = getExpectedType();
if (expected == null)
expected = exprType;
pushExpression(new CSCastExpression(mappedTypeReference(expected), mie));
return false;
}
}
CSExpression mapped = my(CSharpDriver.class).mappedMethodInvocation(this, node);
if (mapped != null) {
pushExpression(mapped);
return false;
}
IMethodBinding originalBinding = originalMethodBinding(node.resolveMethodBinding());
Configuration.MemberMapping mapping = mappingForInvocation(node, originalBinding);
if (null != mapping) {
processMappedMethodInvocation(node, originalBinding, mapping);
} else {
processUnmappedMethodInvocation(node);
}
return false;
}
@Override
public boolean visit(SuperMethodInvocation node) {
if (null != node.getQualifier()) {
notImplemented(node);
}
final IMethodBinding binding = originalMethodBinding(node.resolveMethodBinding());
final ITypeBinding declaringType = binding.getDeclaringClass();
if (declaringType.equals(_objectType) && node.getName().toString().equals("finalize")) {
pushExpression(new CSEmptyExpression());
return false;
}
Configuration.MemberMapping mapping = mappingForInvocation(node, binding);
CSExpression target = new CSMemberReferenceExpression(new CSBaseExpression(), mappedMethodName(binding));
if (mapping != null) {
if (mapping.kind == MemberKind.Indexer) {
processIndexerInvocation(node, binding);
return false;
} else if (mapping.kind != MemberKind.Method) {
pushExpression(target);
return false;
}
}
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(target);
mapArguments(mie, node.arguments(), binding);
pushExpression(mie);
return false;
}
private Configuration.MemberMapping mappingForInvocation(ASTNode node, IMethodBinding binding) {
Configuration.MemberMapping mapping = effectiveMappingFor(binding);
if (null == mapping) {
if (isIndexer(binding)) {
mapping = new MemberMapping(null, MemberKind.Indexer);
} else if (isTaggedMethodInvocation(binding, SharpenAnnotations.SHARPEN_EVENT)) {
mapping = new MemberMapping(binding.getName(), MemberKind.Property);
} else if (isTaggedMethodInvocation(binding, SharpenAnnotations.SHARPEN_PROPERTY)) {
mapping = new MemberMapping(propertyName(binding), MemberKind.Property);
}
}
return mapping;
}
private boolean isIndexer(final IMethodBinding binding) {
return isTaggedMethod(binding, SharpenAnnotations.SHARPEN_INDEXER);
}
private boolean isTaggedMethod(final IMethodBinding binding, final String tag) {
final MethodDeclaration declaration = declaringNode(binding);
if (null == declaration) {
return false;
}
return isTaggedDeclaration(declaration, tag);
}
private IMethodBinding originalMethodBinding(IMethodBinding binding) {
IMethodBinding original = BindingUtils.findMethodDefininition(binding, my(CompilationUnit.class)
.getAST());
if (null != original)
return original;
return binding;
}
private void processUnmappedMethodInvocation(MethodInvocation node) {
if (isMappedEventSubscription(node)) {
processMappedEventSubscription(node);
return;
}
if (isEventSubscription(node)) {
processEventSubscription(node);
return;
}
if (isRemovedMethodInvocation(node)) {
processRemovedInvocation(node);
return;
}
if (isUnwrapInvocation(node)) {
processUnwrapInvocation(node);
return;
}
if (isMacro(node)) {
processMacroInvocation(node);
return;
}
if (isEnumOrdinalMethodInvocation(node)) {
processEnumOrdinalMethodInvocation(node);
return;
}
if (isEnumNameMethodInvocation(node)) {
processEnumNameMethodInvocation(node);
return;
}
if (isEnumValuesMethodInvocation(node)) {
processEnumValuesMethodInvocation(node);
return;
}
processOrdinaryMethodInvocation(node);
}
private boolean isMacro(MethodInvocation node) {
return isTaggedMethodInvocation(node, SharpenAnnotations.SHARPEN_MACRO);
}
private void processMacroInvocation(MethodInvocation node) {
final MethodDeclaration declaration = declaringNode(node.resolveMethodBinding());
final TagElement macro = effectiveAnnotationFor(declaration, SharpenAnnotations.SHARPEN_MACRO);
final CSMacro code = new CSMacro(JavadocUtility.singleTextFragmentFrom(macro));
code.addVariable("expression", mapExpression(node.getExpression()));
code.addVariable("arguments", mapExpressions(node.arguments()));
pushExpression(new CSMacroExpression(code));
}
private List<CSExpression> mapExpressions(List expressions) {
final ArrayList<CSExpression> result = new ArrayList<CSExpression>(expressions.size());
for (Object expression : expressions) {
result.add(mapExpression((Expression) expression));
}
return result;
}
private boolean isUnwrapInvocation(MethodInvocation node) {
return isTaggedMethodInvocation(node, SharpenAnnotations.SHARPEN_UNWRAP);
}
private void processUnwrapInvocation(MethodInvocation node) {
final List arguments = node.arguments();
if (arguments.size() != 1) {
unsupportedConstruct(node, SharpenAnnotations.SHARPEN_UNWRAP
+ " only works against single argument methods.");
}
pushExpression(mapExpression((Expression) arguments.get(0)));
}
private void processOrdinaryMethodInvocation(MethodInvocation node) {
final IMethodBinding method = node.resolveMethodBinding();
final IMethodBinding methodDecl = method.getMethodDeclaration();
CSExpression targetExpression = mapMethodTargetExpression(node);
if ((method.getModifiers() & Modifier.STATIC) != 0
&& !(targetExpression instanceof CSTypeReferenceExpression)
&& node.getExpression() != null)
targetExpression = mappedTypeReference(node.getExpression().resolveTypeBinding());
final String name = resolveTargetMethodName(targetExpression, node);
final String qualifiedName = BindingUtils.qualifiedName(method);
if (qualifiedName.equals("com.android.internal.util.ArrayUtils.emptyArray")) {
final ITypeBinding instanceType = method.getParameterTypes()[0].getTypeArguments()[0];
CSArrayCreationExpression ace = new CSArrayCreationExpression(mappedTypeReference(instanceType));
ace.length(new CSNumberLiteralExpression("0"));
pushExpression(ace);
return;
}
CSExpression target = null == targetExpression ? new CSReferenceExpression(name)
: new CSMemberReferenceExpression(targetExpression, name);
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(target);
mapMethodInvocationArguments(mie, node);
mapTypeArguments(mie, node);
if (methodDecl.isGenericMethod() && (mie.typeArguments().size() == 0)) {
IMethodBinding current = null;
if (_currentBodyDeclaration instanceof MethodDeclaration)
current = ((MethodDeclaration) _currentBodyDeclaration).resolveBinding();
final ITypeBinding expected = getExpectedType();
final ITypeBinding retType = methodDecl.getReturnType();
final ITypeBinding[] mtp = methodDecl.getTypeParameters();
if ((expected != null) && expected.isParameterizedType() && retType.isParameterizedType()) {
ITypeBinding retDecl = methodDecl.getReturnType().getTypeDeclaration();
ITypeBinding expDecl = expected.getTypeDeclaration();
if (retDecl.equals(expDecl) && (expected.getTypeArguments().length == mtp.length)) {
for (ITypeBinding tp : expected.getTypeArguments())
mie.addTypeArgument(mappedTypeReference(tp));
}
} else if ((current != null) && (current.getTypeParameters().length == mtp.length)) {
for (ITypeBinding tp : current.getTypeParameters()) {
mie.addTypeArgument(new CSTypeReference(tp.getName()));
}
}
}
IMethodBinding base = getOverridedMethod(method);
if (base != null && base.getReturnType() != method.getReturnType()
&& !(node.getParent() instanceof ExpressionStatement))
pushExpression(new CSParenthesizedExpression(new CSCastExpression(
mappedTypeReference(method.getReturnType()), mie)));
else
pushExpression(mie);
}
private String resolveTargetMethodName(CSExpression targetExpression, MethodInvocation node) {
final IMethodBinding method = staticImportMethodBinding(node.getName(), _ast.imports());
if (method != null && targetExpression == null) {
return mappedTypeName(method.getDeclaringClass()) + "."
+ mappedMethodName(node.resolveMethodBinding());
}
return mappedMethodName(node.resolveMethodBinding());
}
private void mapTypeArguments(CSMethodInvocationExpression mie, MethodInvocation node) {
for (Object o : node.typeArguments()) {
mie.addTypeArgument(mappedTypeReference((Type) o));
}
}
private void processMappedEventSubscription(MethodInvocation node) {
final MethodInvocation event = (MethodInvocation) node.getExpression();
final String eventArgsType = _configuration.mappedEvent(qualifiedName(event));
final String eventHandlerType = buildEventHandlerTypeName(node, eventArgsType);
mapEventSubscription(node, eventArgsType, eventHandlerType);
}
private void processRemovedInvocation(MethodInvocation node) {
TagElement element = javadocTagFor(declaringNode(node.resolveMethodBinding()),
SharpenAnnotations.SHARPEN_REMOVE);
String exchangeValue = JavadocUtility.singleTextFragmentFrom(element);
pushExpression(new CSReferenceExpression(exchangeValue));
}
private void processEnumOrdinalMethodInvocation(MethodInvocation node)
{
CSExpression exp = mapExpression(node.getExpression());
pushExpression(new CSCastExpression(new CSTypeReference("int"), new CSParenthesizedExpression(exp)));
}
private void processEnumNameMethodInvocation(MethodInvocation node)
{
CSExpression exp = mapExpression(node.getExpression());
pushExpression(new CSMethodInvocationExpression(new CSMemberReferenceExpression(exp, "ToString")));
}
private void processEnumValuesMethodInvocation(MethodInvocation node)
{
CSExpression exp = mapExpression(node.getExpression());
CSExpression type = new CSTypeofExpression((CSTypeReferenceExpression) exp);
CSExpression mref = new CSMemberReferenceExpression(new CSTypeReference("System.Enum"), "GetValues");
pushExpression(new CSMethodInvocationExpression(mref, type));
}
private void mapMethodInvocationArguments(CSMethodInvocationExpression mie, MethodInvocation node) {
final List arguments = node.arguments();
final IMethodBinding actualMethod = node.resolveMethodBinding();
final ITypeBinding[] actualTypes = actualMethod.getParameterTypes();
final IMethodBinding originalMethod = actualMethod.getMethodDeclaration();
final ITypeBinding[] originalTypes = originalMethod.getParameterTypes();
for (int i = 0; i < arguments.size(); ++i) {
final Expression arg = (Expression) arguments.get(i);
if (i >= originalTypes.length) {
addArgument(mie, arg, null);
continue;
}
if (isGenericRuntimeParameterIdiom(originalMethod, originalTypes[i])) {
if (isClassLiteral(arg)) {
mie.addTypeArgument(genericRuntimeTypeIdiomType(actualTypes[i]));
continue;
}
ITypeBinding argType = arg.resolveTypeBinding();
if (isJavaLangClass(argType) && argType.getTypeArguments().length == 1) {
mie.addTypeArgument(genericRuntimeTypeIdiomType(argType));
continue;
}
} else if (isJavaLangClass(originalTypes[i])) {
ITypeBinding[] ota = originalTypes[i].getTypeArguments();
ITypeBinding[] ata = actualTypes[i].getTypeArguments();
if (ota.length == 1 && ata.length == 1 && ota[0].getName().startsWith("?")) {
mie.addTypeArgument(mappedTypeReference(ata[0]));
continue;
}
}
CSExpression expr = my(CSharpDriver.class).mappedMethodInvocationArgument(this, node, i, arg);
if (expr != null)
mie.addArgument(expr);
}
adjustJUnitArguments(mie, node);
}
private void adjustJUnitArguments(CSMethodInvocationExpression mie, MethodInvocation node) {
if (!_configuration.junitConversion())
return;
ITypeBinding t = node.resolveMethodBinding().getDeclaringClass();
if (t.getQualifiedName().equals("junit.framework.Assert")
|| t.getQualifiedName().equals("org.junit.Assert")) {
String method = node.getName().getIdentifier();
int np = -1;
if (method.equals("assertTrue") || method.equals("assertFalse")
|| method.equals("assertNull") || method.equals("assertNotNull"))
np = 1;
else if (method.equals("fail"))
np = 0;
else if (method.startsWith("assert"))
np = 2;
if (np == -1)
return;
if (mie.arguments().size() == np + 1) {
// Move the comment argument to the end
mie.addArgument(mie.arguments().get(0));
mie.removeArgument(0);
}
if (method.equals("assertSame")) {
boolean useEquals = false;
final List arguments = node.arguments();
for (int i = 0; i < arguments.size(); ++i) {
final Expression arg = (Expression) arguments.get(i);
ITypeBinding b = arg.resolveTypeBinding();
if (b.isEnum()) {
useEquals = true;
break;
}
}
if (useEquals) {
CSReferenceExpression mref = (CSReferenceExpression) mie.expression();
mref.name("NUnit.Framework.Assert.AreEqual");
}
}
}
}
private boolean isClassLiteral(Expression arg) {
return arg.getNodeType() == ASTNode.TYPE_LITERAL;
}
private void processEventSubscription(MethodInvocation node) {
final MethodDeclaration addListener = declaringNode(node.resolveMethodBinding());
assertValidEventAddListener(node, addListener);
final MethodInvocation eventInvocation = (MethodInvocation) node.getExpression();
final MethodDeclaration eventDeclaration = declaringNode(eventInvocation.resolveMethodBinding());
mapEventSubscription(node, getEventArgsType(eventDeclaration),
getEventHandlerTypeName(eventDeclaration));
}
private void mapEventSubscription(MethodInvocation node, final String eventArgsType,
final String eventHandlerType) {
final CSAnonymousClassBuilder listenerBuilder = mapAnonymousEventListener(node);
final CSMemberReferenceExpression handlerMethodRef = new CSMemberReferenceExpression(listenerBuilder
.createConstructorInvocation(), eventListenerMethodName(listenerBuilder));
final CSReferenceExpression delegateType = new CSReferenceExpression(eventHandlerType);
patchEventListener(listenerBuilder, eventArgsType);
CSConstructorInvocationExpression delegateConstruction = new CSConstructorInvocationExpression(
delegateType);
delegateConstruction.addArgument(handlerMethodRef);
pushExpression(new CSInfixExpression("+=", mapMethodTargetExpression(node), delegateConstruction));
}
private CSAnonymousClassBuilder mapAnonymousEventListener(MethodInvocation node) {
ClassInstanceCreation creation = (ClassInstanceCreation) node.arguments().get(0);
return mapAnonymousClass(creation.getAnonymousClassDeclaration());
}
private String eventListenerMethodName(final CSAnonymousClassBuilder listenerBuilder) {
return mappedMethodName(getFirstMethod(listenerBuilder.anonymousBaseType()));
}
private void patchEventListener(CSAnonymousClassBuilder listenerBuilder, String eventArgsType) {
final CSClass type = listenerBuilder.type();
type.clearBaseTypes();
final CSMethod handlerMethod = (CSMethod) type.getMember(eventListenerMethodName(listenerBuilder));
handlerMethod.parameters().get(0).type(OBJECT_TYPE_REFERENCE);
handlerMethod.parameters().get(0).name("sender");
handlerMethod.parameters().get(1).type(new CSTypeReference(eventArgsType));
}
private IMethodBinding getFirstMethod(ITypeBinding listenerType) {
return listenerType.getDeclaredMethods()[0];
}
private void assertValidEventAddListener(ASTNode source, MethodDeclaration addListener) {
if (isValidEventAddListener(addListener))
return;
unsupportedConstruct(source, SharpenAnnotations.SHARPEN_EVENT_ADD
+ " must take lone single method interface argument");
}
private boolean isValidEventAddListener(MethodDeclaration addListener) {
if (1 != addListener.parameters().size())
return false;
final ITypeBinding type = getFirstParameterType(addListener);
if (!type.isInterface())
return false;
return type.getDeclaredMethods().length == 1;
}
private ITypeBinding getFirstParameterType(MethodDeclaration addListener) {
return parameter(addListener, 0).getType().resolveBinding();
}
private SingleVariableDeclaration parameter(MethodDeclaration method, final int index) {
return (SingleVariableDeclaration) method.parameters().get(index);
}
private boolean isEventSubscription(MethodInvocation node) {
return isTaggedMethodInvocation(node, SharpenAnnotations.SHARPEN_EVENT_ADD);
}
private boolean isMappedEventSubscription(MethodInvocation node) {
return _configuration.isMappedEventAdd(qualifiedName(node));
}
private String qualifiedName(MethodInvocation node) {
return qualifiedName(node.resolveMethodBinding());
}
private boolean isTaggedMethodInvocation(MethodInvocation node, final String tag) {
return isTaggedMethodInvocation(node.resolveMethodBinding(), tag);
}
private boolean isTaggedMethodInvocation(final IMethodBinding binding, final String tag) {
final MethodDeclaration method = declaringNode(originalMethodBinding(binding));
if (null == method) {
return false;
}
return containsJavadoc(method, tag);
}
@SuppressWarnings("unchecked")
private void processMappedMethodInvocation(MethodInvocation node, IMethodBinding binding,
Configuration.MemberMapping mapping) {
if (mapping.kind == MemberKind.Indexer) {
processIndexerInvocation(node, binding);
return;
}
String name = mappedMethodName(binding);
if (0 == name.length()) {
final Expression expression = node.getExpression();
final CSExpression target = expression != null ? mapExpression(expression)
: new CSThisExpression();
pushExpression(target);
return;
}
boolean isMappingToStaticMethod = isMappingToStaticMember(name);
List<Expression> arguments = node.arguments();
CSExpression expression = mapMethodTargetExpression(node);
CSExpression target = null;
if (null == expression || isMappingToStaticMethod) {
target = new CSReferenceExpression(name);
} else {
if (BindingUtils.isStatic(binding) && arguments.size() > 0) {
// mapping static method to instance member
// typical example is String.valueOf(arg) =>
// arg.ToString()
target = new CSMemberReferenceExpression(
parensIfNeeded(mapExpression(arguments.get(0))), name);
arguments = arguments.subList(1, arguments.size());
} else {
target = new CSMemberReferenceExpression(expression, name);
}
}
if (mapping.kind != MemberKind.Method) {
IMethodBinding originalBinding = node.resolveMethodBinding();
if (binding != originalBinding && originalBinding.getReturnType() != binding.getReturnType()
&& !(node.getParent() instanceof ExpressionStatement))
target = new CSParenthesizedExpression(new CSCastExpression(
mappedTypeReference(originalBinding.getReturnType()), target));
switch (arguments.size()) {
case 0:
pushExpression(target);
break;
case 1:
pushExpression(new CSInfixExpression("=", target, mapExpression(arguments.get(0))));
break;
default:
unsupportedConstruct(node,
"Method invocation with more than 1 argument mapped to property");
break;
}
return;
}
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(target);
if (isMappingToStaticMethod && isInstanceMethod(binding)) {
if (null == expression) {
mie.addArgument(new CSThisExpression());
} else {
mie.addArgument(expression);
}
}
mapArguments(mie, arguments, null);
adjustJUnitArguments(mie, node);
if (mapping.flags == MappingFlags.CastResult) {
pushExpression(new CSCastExpression(mappedTypeReference(binding.getReturnType()), mie));
} else {
pushExpression(mie);
}
}
private void processIndexerInvocation(MethodInvocation node, IMethodBinding binding) {
CSExpression target;
if (node.getExpression() == null)
target = new CSThisExpression();
else
target = mapMethodTargetExpression(node);
List<Expression> arguments = new ArrayList<Expression>();
arguments.addAll(node.arguments());
processIndexerInvocation(binding, target, arguments);
}
private void processIndexerInvocation(SuperMethodInvocation node, IMethodBinding binding) {
CSExpression target = new CSBaseExpression();
List<Expression> arguments = new ArrayList<Expression>();
arguments.addAll(node.arguments());
processIndexerInvocation(binding, target, arguments);
}
private void processIndexerInvocation(IMethodBinding binding, CSExpression target, List<Expression> arguments) {
if (arguments.size() == 1)
processIndexerGetter(binding, target, arguments);
else
processIndexerSetter(binding, target, arguments);
}
private void processIndexerSetter(IMethodBinding binding, CSExpression target, List<Expression> arguments) {
// target(arg0 ... argN) => target[arg0... argN-1] = argN;
final CSIndexedExpression indexer = new CSIndexedExpression(target);
final Expression lastArgument = arguments.get(arguments.size() - 1);
for (int i = 0; i < arguments.size() - 1; ++i) {
indexer.addIndex(mapExpression(arguments.get(i)));
}
pushExpression(CSharpCode.newAssignment(indexer, mapExpression(lastArgument)));
}
private void processIndexerGetter(IMethodBinding binding, CSExpression target, List<Expression> arguments) {
final Expression singleArgument = arguments.get(0);
pushExpression(new CSIndexedExpression(target, mapExpression(singleArgument)));
}
private CSExpression parensIfNeeded(CSExpression expression) {
if (expression instanceof CSInfixExpression || expression instanceof CSPrefixExpression
|| expression instanceof CSPostfixExpression || expression instanceof CSCastExpression) {
return new CSParenthesizedExpression(expression);
}
return expression;
}
protected CSExpression mapMethodTargetExpression(MethodInvocation node) {
return parensIfNeeded(mapExpression(node.getExpression()));
}
private boolean isInstanceMethod(IMethodBinding binding) {
return !BindingUtils.isStatic(binding);
}
private boolean isMappingToStaticMember(String name) {
return -1 != name.indexOf('.');
}
protected void mapArguments(CSMethodInvocationExpression mie, List arguments, IMethodBinding binding) {
if (binding != null) {
ITypeBinding[] ptypes = binding.getParameterTypes();
for (int i = 0; i < ptypes.length; i++) {
Expression expr = (Expression) arguments.get(i);
mie.addArgument(mapExpression(ptypes[i], expr));
}
} else {
for (Object arg : arguments) {
addArgument(mie, (Expression) arg, null);
}
}
}
private void addArgument(CSMethodInvocationExpression mie, Expression arg, ITypeBinding expectedType) {
mie.addArgument(mapExpression(expectedType, arg));
}
@Override
public boolean visit(FieldAccess node) {
IVariableBinding vb = node.resolveFieldBinding();
String name = mappedFieldName(vb);
if (name == null)
name = identifier(node.getName());
if (Modifier.isStatic(vb.getModifiers())) {
Expression expr = node.getExpression();
ITypeBinding type = expr.resolveTypeBinding();
pushExpression(new CSMemberReferenceExpression(mappedTypeReference(type), name));
return false;
} else if (null == node.getExpression()) {
pushExpression(new CSReferenceExpression(name));
} else {
pushExpression(new CSMemberReferenceExpression(mapExpression(node.getExpression()), name));
}
return false;
}
protected String mappedVariableName(IVariableBinding binding) {
return my(CSharpDriver.class).mappedVariableName(binding);
}
private boolean isBoolLiteral(String name) {
return name.equals("true") || name.equals("false");
}
@Override
public boolean visit(SimpleName node) {
CSExpression expr = createSimpleNameReference(node, null);
if (expr != null)
pushExpression(expr);
return false;
}
protected CSExpression createSimpleNameReference(SimpleName node, CSExpression target) {
if (isTypeReference(node)) {
return mappedTypeReference(node.resolveTypeBinding());
} else if (_currentExpression != null) {
return null;
}
final IBinding b = node.resolveBinding();
final IVariableBinding vb = b instanceof IVariableBinding ? (IVariableBinding) b : null;
if (vb == null) {
if (target != null)
return new CSMemberReferenceExpression(target, identifier(node));
else
return new CSReferenceExpression(identifier(node));
}
final String ident = identifier(vb.getName());
final ITypeBinding cls = vb.getDeclaringClass();
if (cls != null) {
ITypeBinding intType = my(IBindingManager.class).getIntType();
IExtractedEnumInfo eei = my(IBindingManager.class).getExtractedEnumInfo(cls);
if ((eei != null) && (intType.equals(getExpectedType()))) {
return new CSMemberReferenceExpression(mappedTypeReference(cls), ident + "_ID");
}
if (isStaticImport(vb, _ast.imports()))
return new CSMemberReferenceExpression(mappedTypeReference(cls), ident);
else if (cls.isEnum() && ident.indexOf('.') == -1) {
CSExpression expr = my(CSharpDriver.class).mappedEnumAccess(this, node, vb);
if (expr != null)
return expr;
return new CSMemberReferenceExpression(mappedTypeReference(cls), ident);
}
}
final String mapped;
if (vb.isField())
mapped = mappedFieldName(vb);
else
mapped = mappedVariableName(vb);
if (null != mapped) {
if (isBoolLiteral(mapped)) {
return new CSBoolLiteralExpression(Boolean.parseBoolean(mapped));
} else if ((target == null) || (mapped.indexOf('.') > 0)) {
return new CSReferenceExpression(mapped);
} else {
return new CSMemberReferenceExpression(target, mapped);
}
}
final CSExpression expr;
if (target != null) {
expr = new CSMemberReferenceExpression(target, ident);
} else {
expr = new CSReferenceExpression(ident);
}
final IVariableInfo info = my(IBindingManager.class).getVariableInfo(vb);
if (info != null) {
CSTypeReferenceExpression autoCast = info.autoCast();
if (autoCast != null)
return new CSCastExpression(autoCast, expr);
}
return expr;
}
private void addStatement(CSStatement statement) {
_currentBlock.addStatement(statement);
}
private void pushTypeReference(ITypeBinding typeBinding) {
pushExpression(mappedTypeReference(typeBinding));
}
/*
* protected CSReferenceExpression createTypeReference(ITypeBinding
* typeBinding) { return new
* CSReferenceExpression(mappedTypeName(typeBinding)); }
*/private boolean isTypeReference(Name node) {
final IBinding binding = node.resolveBinding();
if (null == binding) {
unresolvedTypeBinding(node);
return false;
}
return IBinding.TYPE == binding.getKind();
}
@Override
public boolean visit(QualifiedName node) {
if (isTypeReference(node)) {
pushTypeReference(node.resolveTypeBinding());
} else {
String primitiveTypeRef = checkForPrimitiveTypeReference(node);
if (primitiveTypeRef != null) {
pushTypeOfExpression(new CSTypeReference(primitiveTypeRef));
} else {
handleRegularQualifiedName(node);
}
}
return false;
}
private void handleRegularQualifiedName(QualifiedName node) {
IVariableBinding vb = variableBinding(node);
if (vb != null) {
String mapped = mappedFieldName(vb);
if (null != mapped) {
if (isBoolLiteral(mapped)) {
pushExpression(new CSBoolLiteralExpression(Boolean.parseBoolean(mapped)));
return;
}
if (isMappingToStaticMember(mapped)) {
pushExpression(new CSReferenceExpression(mapped));
} else {
pushMemberReferenceExpression(node.getQualifier(), mapped);
}
return;
}
}
Name qualifier = node.getQualifier();
String name = identifier(node.getName().getIdentifier());
if ((vb != null) && vb.getDeclaringClass().isEnum()) {
ITypeBinding type = vb.getDeclaringClass();
CSExpression expr = my(CSharpDriver.class).mappedEnumAccess(this, qualifier, vb);
if (expr != null) {
pushExpression(expr);
return;
}
if (my(IBindingManager.class).getExtractedEnumInfo(type) != null) {
final ITypeBinding intType = my(IBindingManager.class).getIntType();
if (intType.equals(getExpectedType())) {
CSExpression typeRef = mappedTypeReference(type);
pushExpression(new CSMemberReferenceExpression(typeRef, name + "_ID"));
return;
}
}
}
if ((vb != null) && Modifier.isStatic(vb.getModifiers())) {
ITypeBinding type = vb.getDeclaringClass();
pushExpression(new CSMemberReferenceExpression(mappedTypeReference(type), name));
} else {
pushMemberReferenceExpression(qualifier, name);
}
}
private String checkForPrimitiveTypeReference(QualifiedName node) {
String name = qualifiedName(node);
if (name.equals(JAVA_LANG_VOID_TYPE))
return "void";
if (name.equals(JAVA_LANG_BOOLEAN_TYPE))
return "bool";
if (name.equals(JAVA_LANG_BYTE_TYPE))
return "byte";
if (name.equals(JAVA_LANG_CHARACTER_TYPE))
return "char";
if (name.equals(JAVA_LANG_SHORT_TYPE))
return "short";
if (name.equals(JAVA_LANG_INTEGER_TYPE))
return "int";
if (name.equals(JAVA_LANG_LONG_TYPE))
return "long";
if (name.equals(JAVA_LANG_FLOAT_TYPE))
return "float";
if (name.equals(JAVA_LANG_DOUBLE_TYPE))
return "double";
return null;
}
private String qualifiedName(QualifiedName node) {
IVariableBinding binding = variableBinding(node);
if (binding == null)
return node.toString();
return BindingUtils.qualifiedName(binding);
}
private void pushMemberReferenceExpression(Name qualifier, String name) {
pushExpression(new CSMemberReferenceExpression(mapExpression(qualifier), name));
}
private IVariableBinding variableBinding(Name node) {
if (node.resolveBinding() instanceof IVariableBinding) {
return (IVariableBinding) node.resolveBinding();
}
return null;
}
protected String mappedFieldName(IVariableBinding binding) {
return my(Mappings.class).mappedFieldName(binding);
}
protected CSExpression mapExpression(Expression expression) {
if (null == expression)
return null;
try {
expression.accept(this);
return popExpression();
} catch (Exception e) {
unsupportedConstruct(expression, e);
return null; // we'll never get here
}
}
private void unsupportedConstruct(ASTNode node, Exception cause) {
unsupportedConstruct(node, "failed to map: '" + node + "'", cause);
}
private void unsupportedConstruct(ASTNode node, String message) {
unsupportedConstruct(node, message, null);
}
private void unsupportedConstruct(ASTNode node, final String message, Exception cause) {
addProblem(node, ProblemKind.INTERNAL_ERROR, message + ": " + cause);
throw new IllegalArgumentException(sourceInformation(node) + ": " + message, cause);
}
public void addProblem(ASTNode node, ProblemKind kind, String message) {
_pair.addProblem(new SharpenProblem(_ast, node, kind, message));
}
public void addProblem(ASTNode node, ProblemKind kind, String message, Object... args) {
addProblem(node, kind, String.format(message, args));
}
private ITypeBinding getExpectedType() {
if (_currentExpectedType.size() > 0)
return _currentExpectedType.peek();
return null;
}
private void pushExpectedType(ITypeBinding type) {
_currentExpectedType.push(type);
}
private void popExpectedType() {
_currentExpectedType.pop();
}
protected void pushExpression(CSExpression expression) {
if (null != _currentExpression) {
throw new IllegalStateException();
}
_currentExpression = expression;
}
private CSExpression popExpression() {
if (null == _currentExpression) {
throw new IllegalStateException();
}
CSExpression found = _currentExpression;
_currentExpression = null;
return found;
}
private CSVariableDeclaration createParameter(SingleVariableDeclaration declaration) {
return createVariableDeclaration(declaration.resolveBinding(), null);
}
protected void visit(List nodes) {
for (Object node : nodes) {
((ASTNode) node).accept(this);
}
}
private void createInheritedAbstractMemberStubs(TypeDeclaration node) {
if (node.isInterface())
return;
ITypeBinding binding = node.resolveBinding();
if (!Modifier.isAbstract(node.getModifiers()))
return;
for (ITypeBinding baseType : BindingUtils.getAllInterfaces(binding)) {
createInheritedAbstractMemberStubs(binding, baseType);
}
}
private void createInheritedAbstractMemberStubs(ITypeBinding type, ITypeBinding baseType) {
IMethodBinding[] methods = baseType.getDeclaredMethods();
for (int i = 0; i < methods.length; ++i) {
IMethodBinding method = methods[i];
if (!Modifier.isAbstract(method.getModifiers())) {
continue;
}
if (null != BindingUtils.findOverriddenMethodInTypeOrSuperclasses(type, method)) {
continue;
}
if (isIgnored(originalMethodBinding(method))) {
continue;
}
if (stubIsProperty(method)) {
_currentType.addMember(createAbstractPropertyStub(method));
} else {
CSMethod newMethod = createAbstractMethodStub(method);
// the same method might be defined in multiple
// interfaces
// but only a single stub must be created for
// those
if (!_currentType.members().contains(newMethod)) {
_currentType.addMember(newMethod);
}
}
}
}
private boolean isIgnored(IMethodBinding binding) {
final MethodDeclaration dec = declaringNode(binding);
return dec != null && SharpenAnnotations.hasIgnoreAnnotation(dec);
}
private boolean stubIsProperty(IMethodBinding method) {
final MethodDeclaration dec = declaringNode(method);
return dec != null && isProperty(dec);
}
private MethodDeclaration declaringNode(IMethodBinding method) {
return findDeclaringNode(method);
}
private CSProperty createAbstractPropertyStub(IMethodBinding method) {
CSProperty stub = newAbstractPropertyStubFor(method);
safeProcessDisableTags(method, stub);
return stub;
}
private CSProperty newAbstractPropertyStubFor(IMethodBinding method) {
CSProperty stub = new CSProperty(mappedMethodName(method), mappedTypeReference(method.getReturnType()));
stub.modifier(CSMethodModifier.Abstract);
stub.visibility(mapVisibility(method));
stub.getter(new CSBlock());
return stub;
}
private CSMethod createAbstractMethodStub(IMethodBinding method) {
CSMethod stub = newAbstractMethodStubFor(method);
safeProcessDisableTags(method, stub);
return stub;
}
private CSMethod newAbstractMethodStubFor(IMethodBinding method) {
CSMethod stub = new CSMethod(mappedMethodName(method));
stub.modifier(CSMethodModifier.Abstract);
stub.visibility(mapVisibility(method));
stub.returnType(mappedTypeReference(method.getReturnType()));
ITypeBinding[] parameters = method.getParameterTypes();
for (int i = 0; i < parameters.length; ++i) {
stub.addParameter(new CSVariableDeclaration("arg" + (i + 1), mappedTypeReference(parameters[i])));
}
return stub;
}
private void safeProcessDisableTags(IMethodBinding method, CSMember member) {
final MethodDeclaration node = declaringNode(method);
if (node == null)
return;
processDisableTags(node, member);
}
CSMethodModifier mapMethodModifier(MethodDeclaration method) {
if (_currentType.isInterface() || method.resolveBinding().getDeclaringClass().isInterface()) {
return CSMethodModifier.Abstract;
}
int modifiers = method.getModifiers();
if (Modifier.isStatic(modifiers)) {
return CSMethodModifier.Static;
}
if (Modifier.isPrivate(modifiers)) {
return CSMethodModifier.None;
}
boolean override = isOverride(method);
if (Modifier.isAbstract(modifiers)) {
return override ? CSMethodModifier.AbstractOverride : CSMethodModifier.Abstract;
}
boolean isFinal = Modifier.isFinal(modifiers);
if (override) {
return isFinal ? CSMethodModifier.Sealed : modifierIfNewAnnotationNotApplied(method,
CSMethodModifier.Override);
}
return isFinal || _currentType.isSealed() ? CSMethodModifier.None : CSMethodModifier.Virtual;
}
private CSMethodModifier modifierIfNewAnnotationNotApplied(MethodDeclaration method, CSMethodModifier modifier) {
return containsJavadoc(method, SharpenAnnotations.SHARPEN_NEW)
? CSMethodModifier.None
: modifier;
}
private boolean isExtractedNestedType(ITypeBinding type) {
return _configuration.typeHasMapping(BindingUtils.typeMappingKey(type));
}
private boolean mustExtractRawType(ITypeBinding type) {
if (!type.isGenericType())
return false;
for (final ITypeBinding nested : type.getDeclaredTypes()) {
if (nested.isInterface() || nested.isEnum())
return true;
if (nested.isClass() && Modifier.isStatic(nested.getModifiers()))
return true;
if (mustExtractRawType(nested))
return true;
}
for (final IVariableBinding field : type.getDeclaredFields()) {
if (field.getConstantValue() != null)
return true;
}
return false;
}
private boolean extractIntoRawType(ITypeBinding type) {
if (type.isInterface() || type.isEnum())
return true;
if (Modifier.isStatic(type.getModifiers()))
return true;
return false;
}
private boolean isOverride(MethodDeclaration method) {
return null != getOverridedMethod(method);
}
private IMethodBinding getOverridedMethod(MethodDeclaration method) {
return getBaseMethod(method.resolveBinding(), true, false);
}
private IMethodBinding getOverridedMethod(IMethodBinding methodBinding) {
return getBaseMethod(methodBinding, true, false);
}
public IMethodBinding getBaseMethod(IMethodBinding methodBinding, boolean overrideOnly, boolean allowStatic) {
return BindingUtils.getBaseMethod(methodBinding, overrideOnly, allowStatic);
}
private boolean isValidCSInterface(ITypeBinding type) {
return BindingUtils.isValidCSInterface(type);
}
CSClassModifier mapClassModifier(int modifiers) {
if (Modifier.isAbstract(modifiers)) {
return CSClassModifier.Abstract;
}
if (Modifier.isFinal(modifiers)) {
return CSClassModifier.Sealed;
}
return CSClassModifier.None;
}
private CSVisibility mapVisibility(ITypeBinding type, boolean recursive) {
CSVisibility vis = my(CSharpDriver.class).mapVisibility(type);
if (vis != null)
return vis;
if (type.isPrimitive())
return CSVisibility.Public;
if (type.isArray())
vis = mapVisibility(type.getElementType());
else
vis = mapVisibility(type.getModifiers());
if (recursive)
return vis;
if (type.getSuperclass() != null)
vis = adjustVisibility(type, type.getSuperclass(), vis);
return vis;
}
private CSVisibility mapVisibility(ITypeBinding type) {
return mapVisibility(type, false);
}
private CSVisibility adjustVisibility(ITypeBinding type, ITypeBinding memberType, CSVisibility vis) {
CSVisibility typeVisibility = mapVisibility(memberType, true);
if (typeVisibility == CSVisibility.Protected && vis == CSVisibility.Internal)
vis = CSVisibility.Protected;
else if (typeVisibility == CSVisibility.Private)
vis = CSVisibility.Private;
else if (typeVisibility == CSVisibility.Internal) {
if ((vis == CSVisibility.Protected) || (vis == CSVisibility.ProtectedInternal)
|| (vis == CSVisibility.Public))
vis = CSVisibility.Internal;
}
if (memberType.isParameterizedType()) {
List<ITypeBinding> tp = new ArrayList<ITypeBinding>();
for (ITypeBinding ta : type.getTypeParameters())
tp.add(ta);
for (ITypeBinding ta : memberType.getTypeArguments()) {
if (tp.contains(ta))
continue;
vis = adjustVisibility(type, ta, vis);
}
}
return vis;
}
private CSVisibility mapVisibility(int modifiers) {
if (Modifier.isPublic(modifiers)) {
return CSVisibility.Public;
}
if (Modifier.isProtected(modifiers)) {
return CSVisibility.ProtectedInternal;
}
if (Modifier.isPrivate(modifiers)) {
return CSVisibility.Private;
}
return CSVisibility.Internal;
}
protected CSTypeReferenceExpression mappedTypeReference(Type type) {
return mappedTypeReference(type.resolveBinding());
}
private CSTypeReferenceExpression mappedMacroTypeReference(ITypeBinding typeUsage,
final TypeDeclaration typeDeclaration) {
final CSMacro macro = new CSMacro(JavadocUtility.singleTextFragmentFrom(javadocTagFor(typeDeclaration,
SharpenAnnotations.SHARPEN_MACRO)));
final ITypeBinding[] typeArguments = typeUsage.getTypeArguments();
if (typeArguments.length > 0) {
final ITypeBinding[] typeParameters = typeUsage.getTypeDeclaration().getTypeParameters();
for (int i = 0; i < typeParameters.length; i++) {
macro.addVariable(typeParameters[i].getName(), mappedTypeReference(typeArguments[i]));
}
}
return new CSMacroTypeReference(macro);
}
private boolean isMacroType(final ASTNode declaration) {
return declaration instanceof TypeDeclaration
&& containsJavadoc((TypeDeclaration) declaration, SharpenAnnotations.SHARPEN_MACRO);
}
public CSTypeReferenceExpression mappedTypeReference(ITypeBinding type) {
CSTypeReferenceExpression mapped = my(CSharpDriver.class).mappedTypeReference(this, type);
if (mapped != null)
return mapped;
final ASTNode declaration = findDeclaringNode(type);
if (isMacroType(declaration)) {
return mappedMacroTypeReference(type, (TypeDeclaration) declaration);
}
final ITypeBinding expected = getExpectedType();
if (type.isTypeVariable()) {
return new CSTypeReference(type.getName());
}
if (type.isArray()) {
if ((expected != null) && expected.isArray())
pushExpectedType(expected.getElementType());
else
pushExpectedType(null);
CSTypeReferenceExpression array = mappedArrayTypeReference(type);
popExpectedType();
return array;
}
if (type.isWildcardType()) {
return mappedWildcardTypeReference(type);
}
if (isJavaLangClass(type)) {
return new CSTypeReference(mappedTypeName(type));
}
if (type.isCapture()) {
return mappedWildcardTypeReference(type.getWildcard());
}
final String fullName = BindingUtils.qualifiedName(type);
if (fullName.equals("java.lang.reflect.Constructor")) {
return new CSTypeReference("System.Reflection.ConstructorInfo");
}
if ((expected != null) && isGenericInstance(expected)) {
ITypeBinding underlying = getUnderlyingGenericType(type);
ITypeBinding underlyingExpected = getUnderlyingGenericType(expected);
if ((underlying != null) && underlying.equals(underlyingExpected)) {
pushExpectedType(null);
CSTypeReferenceExpression retval = mappedTypeReference(expected);
popExpectedType();
return retval;
}
}
final ITypeBinding declaring = type.getDeclaringClass();
final boolean hasMapping = _configuration.typeHasMapping(BindingUtils.typeMappingKey(type));
if (type.isNested() && !type.isAnonymous() && !hasMapping && (declaring != null)) {
final String childName = identifier(type.getTypeDeclaration().getName());
CSTypeReferenceExpression parentRef;
if (declaring.isInterface()) {
final String parentName = extractedInterfaceName(declaring);
parentRef = new CSTypeReference(parentName);
} else if (extractIntoRawType(type)) {
final String parentName = mappedTypeName(declaring);
parentRef = new CSTypeReference(parentName);
} else {
parentRef = mappedTypeReference(declaring);
}
final CSTypeReferenceExpression childRef = mappedSimpleTypeReference(type, childName);
CSNestedTypeReference nestedRef = new CSNestedTypeReference(parentRef, childRef);
return nestedRef;
}
return mappedSimpleTypeReference(type, mappedTypeName(type));
}
private CSTypeReferenceExpression mappedVariableType(IVariableBinding binding) {
CSTypeReferenceExpression type = my(CSharpDriver.class).mappedVariableType(binding);
if (type != null)
return type;
return mappedTypeReference(binding.getType());
}
private boolean isJavaLangClass(ITypeBinding type) {
return type.getErasure() == _classType;
}
private CSTypeReferenceExpression mappedWildcardTypeReference(ITypeBinding type) {
final ITypeBinding bound = type.getBound();
if (_currentWildcardParams != null) {
String tp = _currentWildcardParams.get(type);
if (tp != null)
return new CSTypeReference(tp);
}
if (bound != null)
return mappedTypeReference(bound);
if (_currentType != null) {
if (_currentType.typeParameters().size() == 1)
return new CSTypeReference(_currentType.typeParameters().get(0).name());
}
return OBJECT_TYPE_REFERENCE;
}
private CSTypeReferenceExpression mappedArrayTypeReference(ITypeBinding type) {
return new CSArrayTypeReference(mappedTypeReference(type.getElementType()), type.getDimensions());
}
public CSTypeReferenceExpression mappedSimpleTypeReference(ITypeBinding type, String name) {
final CSTypeReference typeRef = new CSTypeReference(name);
addTypeArguments(typeRef, type);
return typeRef;
}
private static class GenericContext {
public final ITypeBinding type;
public final ITypeBinding[] tparams;
private GenericContext(ITypeBinding type, ITypeBinding[] tparams) {
this.type = type;
this.tparams = tparams;
}
public GenericContext(IMethodBinding method) {
this(method.getDeclaringClass(), method.getTypeParameters());
}
public static GenericContext create(ITypeBinding type) {
while (type.isNested() && !type.isGenericType() && !type.isParameterizedType()) {
type = type.getDeclaringClass();
}
while ((type != null) && !type.isGenericType() && !type.isParameterizedType()) {
type = type.getSuperclass();
}
if (type == null)
return null;
if (type.isParameterizedType())
return new GenericContext(type, type.getTypeArguments());
else if (type.isGenericType())
return new GenericContext(type, type.getTypeParameters());
return null;
}
}
private GenericContext getGenericContext() {
final IMethodBinding method;
if (_currentBodyDeclaration instanceof MethodDeclaration)
method = ((MethodDeclaration) _currentBodyDeclaration).resolveBinding();
else
method = null;
if ((method != null) && method.isGenericMethod())
return new GenericContext(method);
if (_currentTypeDeclaration != null)
return GenericContext.create(_currentTypeDeclaration.resolveBinding());
else if (method != null)
return GenericContext.create(method.getDeclaringClass());
else
return null;
}
private void addTypeArguments(CSTypeReference typeRef, ITypeBinding type) {
if (_addTypeArguments(typeRef, type))
return;
final ITypeBinding[] tparams = type.getTypeDeclaration().getTypeParameters();
for (ITypeBinding tparam : tparams) {
ITypeBinding sclass = mapTypeParameterExtendedType(tparam);
if (sclass == null)
typeRef.addTypeArgument(OBJECT_TYPE_REFERENCE);
else
typeRef.addTypeArgument(mappedTypeReference(sclass));
}
}
private boolean _addTypeArguments(CSTypeReference typeRef, ITypeBinding type) {
final ITypeBinding[] targs = type.getTypeArguments();
if (targs.length > 0) {
for (ITypeBinding arg : targs) {
typeRef.addTypeArgument(mappedTypeReference(arg));
}
return true;
}
GenericContext context = getGenericContext();
if (context == null)
return false;
ITypeBinding declType = type.getTypeDeclaration();
if (context.type.isGenericType() && context.type.isEqualTo(declType)
|| isNestedInside(context.type, declType)
|| (declType.getTypeParameters().length == context.tparams.length)) {
for (ITypeBinding arg : context.tparams) {
typeRef.addTypeArgument(mappedTypeReference(arg));
}
return true;
}
return false;
}
protected boolean isNestedInside(ITypeBinding parent, ITypeBinding child) {
if (!child.isNested())
return false;
while ((child = child.getSuperclass()) != null) {
if (child.isEqualTo(parent))
return true;
}
return false;
}
protected final String mappedTypeName(ITypeBinding type) {
final String mapped = my(CSharpDriver.class).mappedTypeName(this, type);
if (mapped != null)
return mapped;
return my(Mappings.class).mappedTypeName(type);
}
private final String extractedInterfaceName(ITypeBinding type) {
return my(Mappings.class).extractedInterfaceName(type);
}
private static String qualifiedName(ITypeBinding type) {
return BindingUtils.qualifiedName(type);
}
private String interfaceName(String name) {
return my(Configuration.class).toInterfaceName(name);
}
private String mappedTypeName(String typeName) {
return mappedTypeName(typeName, typeName);
}
private String mappedTypeName(String typeName, String defaultValue) {
return _configuration.mappedTypeName(typeName, defaultValue);
}
private String annotatedRenaming(BodyDeclaration node) {
return my(Annotations.class).annotatedRenaming(node);
}
protected String mappedMethodName(MethodDeclaration node) {
return mappedMethodName(node.resolveBinding());
}
protected final String mappedMethodName(IMethodBinding binding) {
return my(Mappings.class).mappedMethodName(binding);
}
private String qualifiedName(IMethodBinding actual) {
return BindingUtils.qualifiedName(actual);
}
private boolean isEvent(MethodDeclaration declaring) {
return eventTagFor(declaring) != null;
}
private boolean isMappedToProperty(MethodDeclaration original) {
final MemberMapping mapping = effectiveMappingFor(original.resolveBinding());
if (null == mapping)
return false;
return mapping.kind == MemberKind.Property;
}
private boolean isMappedToIndexer(MethodDeclaration original) {
final MemberMapping mapping = effectiveMappingFor(original.resolveBinding());
if (null == mapping)
return false;
return mapping.kind == MemberKind.Indexer;
}
private MemberMapping effectiveMappingFor(IMethodBinding binding) {
return my(Mappings.class).effectiveMappingFor(binding);
}
private String methodName(String name) {
return namingStrategy().methodName(name);
}
protected String identifier(SimpleName name) {
return identifier(name.toString());
}
protected String identifier(String name) {
return namingStrategy().identifier(name);
}
private void unresolvedTypeBinding(ASTNode node) {
warning(node, "unresolved type binding for node: " + node);
}
@Override
public boolean visit(CompilationUnit node) {
return true;
}
private void warning(ASTNode node, String message) {
warningHandler().warning(node, message);
}
protected final String sourceInformation(ASTNode node) {
return ASTUtility.sourceInformation(_ast, node);
}
@SuppressWarnings("deprecation")
protected int lineNumber(ASTNode node) {
return _ast.lineNumber(node.getStartPosition());
}
public void setASTResolver(ASTResolver resolver) {
_resolver = resolver;
}
private String mappedNamespace(String namespace) {
return _configuration.mappedNamespace(namespace);
}
@Override
public boolean visit(Block node) {
if (isBlockInsideBlock(node)) {
CSBlock parent = _currentBlock;
_currentBlock = new CSBlock();
_currentBlock.parent(parent);
parent.addStatement(_currentBlock);
}
_currentContinueLabel = null;
return super.visit(node);
}
@Override
public void endVisit(Block node) {
if (isBlockInsideBlock(node)) {
_currentBlock = (CSBlock) _currentBlock.parent();
}
super.endVisit(node);
}
boolean isBlockInsideBlock(Block node) {
return node.getParent() instanceof Block;
}
}