/* 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 java.util.*;
import sharpen.core.csharp.ast.*;
import org.eclipse.jdt.core.dom.*;
public class CSAnonymousClassBuilder extends AbstractNestedClassBuilder {
private AnonymousClassDeclaration _node;
private CSClass _type;
private CSConstructor _constructor;
private Set<IVariableBinding> _capturedVariables = new LinkedHashSet<IVariableBinding>();
private CSharpBuilder _parent;
private Set<VariableDeclarationFragment> _fieldInitializers = new LinkedHashSet<VariableDeclarationFragment>();
public CSAnonymousClassBuilder(CSharpBuilder builder, AnonymousClassDeclaration node) {
super(builder);
_parent = builder;
_node = node;
run();
}
public CSClass type() {
return _type;
}
public Set<IVariableBinding> capturedVariables() {
return _capturedVariables;
}
public CSExpression createConstructorInvocation() {
CSConstructorInvocationExpression invocation = new CSConstructorInvocationExpression(new CSReferenceExpression(_type.name()));
if (isEnclosingReferenceRequired()) {
invocation.addArgument(new CSThisExpression());
}
addCapturedVariables(invocation);
addBaseConstructorArguments(invocation);
return invocation;
}
private void addCapturedVariables(CSConstructorInvocationExpression invocation) {
for (IVariableBinding variable : _capturedVariables) {
invocation.addArgument(new CSReferenceExpression(identifier(variable.getName())));
}
}
private void addBaseConstructorArguments(CSConstructorInvocationExpression invocation) {
List arguments = classInstanceCreationArguments();
if (arguments.isEmpty()) {
return;
}
final ITypeBinding[] ctorParameterTypes = classInstanceCreation().resolveConstructorBinding().getParameterTypes();
_constructor.chainedConstructorInvocation(new CSConstructorInvocationExpression(new CSBaseExpression()));
for (int i=0; i<ctorParameterTypes.length; ++i) {
ITypeBinding parameterType = ctorParameterTypes[i];
Expression argument = (Expression)arguments.get(i);
String parameterName = "baseArg" + (i + 1);
_constructor.addParameter(parameterName, mappedTypeReference(parameterType));
_constructor.chainedConstructorInvocation().addArgument(new CSReferenceExpression(parameterName));
invocation.addArgument(_parent.mapExpression(argument));
}
}
private List classInstanceCreationArguments() {
return classInstanceCreation().arguments();
}
private ClassInstanceCreation classInstanceCreation() {
return ((ClassInstanceCreation)_node.getParent());
}
public void run() {
captureExternalLocalVariables();
setUpAnonymousType();
setUpConstructor();
processAnonymousBody();
int capturedVariableCount = flushCapturedVariables();
flushFieldInitializers();
flushInstanceInitializers(_type, capturedVariableCount);
}
private void flushFieldInitializers() {
for (VariableDeclarationFragment field : _fieldInitializers) {
addToConstructor(createFieldAssignment(fieldName(field), mapExpression(field.getInitializer())));
}
}
@Override
protected CSExpression mapFieldInitializer(VariableDeclarationFragment fragment) {
if (fragment.getInitializer() != null) {
_fieldInitializers.add(fragment);
}
return null;
}
private void processAnonymousBody() {
CSTypeDeclaration saved = _currentType;
_currentType = _type;
visit(_node.bodyDeclarations());
_currentType = saved;
}
public boolean visit(AnonymousClassDeclaration node) {
CSAnonymousClassBuilder builder = new CSAnonymousClassBuilder(this, node);
if (builder.isEnclosingReferenceRequired()) {
requireEnclosingReference();
}
captureNeededVariables(builder);
pushExpression(builder.createConstructorInvocation());
_currentType.addMember(builder.type());
return false;
}
private void captureNeededVariables(CSAnonymousClassBuilder builder) {
IMethodBinding currentMethod = currentMethodDeclarationBinding();
for (IVariableBinding variable : builder.capturedVariables()) {
IMethodBinding method = variable.getDeclaringMethod();
if (method != currentMethod) {
_capturedVariables.add(variable);
}
}
}
private IMethodBinding currentMethodDeclarationBinding() {
return _currentBodyDeclaration instanceof MethodDeclaration
? ((MethodDeclaration)_currentBodyDeclaration).resolveBinding()
: null;
}
private void addFieldParameter(String name, CSTypeReferenceExpression type) {
addFieldParameter(CSharpCode.newPrivateReadonlyField(name, type));
}
private void addFieldParameter(CSField field) {
_type.addMember(field);
String parameterName = field.name();
_constructor.addParameter(parameterName, field.type());
addToConstructor(createFieldAssignment(field.name(), parameterName));
}
private void addToConstructor(final CSExpression expression) {
_constructor.body().addStatement(expression);
}
private String anonymousBaseTypeName() {
return mappedTypeName(anonymousBaseType());
}
public ITypeBinding anonymousBaseType() {
ITypeBinding binding = nestedTypeBinding();
return binding.getInterfaces().length > 0
? binding.getInterfaces()[0]
: binding.getSuperclass();
}
@Override
protected ITypeBinding nestedTypeBinding() {
return _node.resolveBinding();
}
private String anonymousInnerClassName() {
return "_" + simpleName(anonymousBaseTypeName()) + "_" + lineNumber(_node);
}
private String simpleName(String typeName) {
final int index = typeName.lastIndexOf('.');
if (index < 0) return typeName;
return typeName.substring(index + 1);
}
private void setUpAnonymousType() {
_type = classForAnonymousType();
}
private CSClass classForAnonymousType() {
CSClass type = new CSClass(anonymousInnerClassName(), CSClassModifier.Sealed);
type.visibility(CSVisibility.Private);
type.addBaseType(new CSTypeReference(anonymousBaseTypeName()));
return type;
}
private void setUpConstructor() {
_constructor = new CSConstructor();
_constructor.visibility(CSVisibility.Public);
_type.addMember(_constructor);
}
private int flushCapturedVariables() {
int capturedVariableCount = 0;
if (isEnclosingReferenceRequired()) {
capturedVariableCount++;
addFieldParameter(createEnclosingField());
}
for (IVariableBinding variable : _capturedVariables) {
capturedVariableCount++;
addFieldParameter(identifier(variable.getName()), mappedTypeReference(variable.getType()));
}
return capturedVariableCount;
}
private void captureExternalLocalVariables() {
_node.accept(new ASTVisitor() {
IMethodBinding _currentMethodBinding;
public boolean visit(MethodDeclaration node) {
IMethodBinding saved = _currentMethodBinding;
_currentMethodBinding = node.resolveBinding();
node.getBody().accept(this);
_currentMethodBinding = saved;
return false;
}
public boolean visit(AnonymousClassDeclaration node) {
return node == _node;
}
public boolean visit(SimpleName node) {
IBinding binding = node.resolveBinding();
if (isExternalLocal(binding)) {
_capturedVariables.add((IVariableBinding)binding);
}
return false;
}
boolean isExternalLocal(IBinding binding) {
if (binding instanceof IVariableBinding) {
IVariableBinding variable = (IVariableBinding)binding;
if (!variable.isField()) {
return variable.getDeclaringMethod() != _currentMethodBinding;
}
}
return false;
}
});
}
}