/* 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 java.util.regex.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.dom.*;
import sharpen.core.Configuration.*;
import sharpen.core.csharp.ast.*;
import sharpen.core.framework.*;
import static sharpen.core.framework.StaticImports.*;
import static sharpen.core.framework.Environments.*;
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;
protected CSMethodBase _currentMethod;
protected BodyDeclaration _currentBodyDeclaration;
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 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>();
protected NamingStrategy namingStrategy() {
return _configuration.getNamingStrategy();
}
protected WarningHandler warningHandler() {
return _configuration.getWarningHandler();
}
public CSharpBuilder() {
_configuration = my(Configuration.class);
_ast = my(CompilationUnit.class);
_resolver = my(ASTResolver.class);
_compilationUnit = my(CSCompilationUnit.class);
}
protected CSharpBuilder(CSharpBuilder other) {
_configuration = other._configuration;
_ast = other._ast;
_resolver = other._resolver;
_compilationUnit = other._compilationUnit;
_currentType = other._currentType;
_currentBlock = other._currentBlock;
_currentExpression = other._currentExpression;
_currentMethod = other._currentMethod;
_currentBodyDeclaration = other._currentBodyDeclaration;
}
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;
}
public boolean visit(ImportDeclaration node) {
return false;
}
public boolean visit(EnumDeclaration node) {
if (SharpenAnnotations.hasIgnoreAnnotation(node)) {
return false;
}
final CSEnum theEnum = new CSEnum(node.getName().getIdentifier());
mapVisibility(node, theEnum);
mapJavadoc(node, theEnum);
addType(theEnum);
for(Object o : node.enumConstants()){
theEnum.addValue(((EnumConstantDeclaration)o).getName().getIdentifier());
}
//throw new IllegalStateException(((EnumConstantDeclaration)node.enumConstants().get(0)).getAnonymousClassDeclaration().toString());
return false;
}
@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;
}
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";
}
public boolean visit(SuperFieldAccess node) {
CSExpression target = new CSMemberReferenceExpression(new CSBaseExpression(), node.getName().getIdentifier());
pushExpression(target);
return false;
}
public boolean visit(MemberRef node) {
notImplemented(node);
return false;
}
@Override
public boolean visit(WildcardType node) {
notImplemented(node);
return false;
}
private void notImplemented(ASTNode node) {
throw new IllegalArgumentException(sourceInformation(node) + ": " + node.toString());
}
public boolean visit(PackageDeclaration node) {
String namespace = node.getName().toString();
_compilationUnit.namespace(mappedNamespace(namespace));
processDisableTags(node, _compilationUnit);
return false;
}
public boolean visit(AnonymousClassDeclaration node) {
CSAnonymousClassBuilder builder = mapAnonymousClass(node);
pushExpression(builder.createConstructorInvocation());
return false;
}
private CSAnonymousClassBuilder mapAnonymousClass(AnonymousClassDeclaration node) {
CSAnonymousClassBuilder builder = new CSAnonymousClassBuilder(this, node);
if(_currentType.isInterface() && _configuration.shouldApplyConversionStrategy(ConversionStrategy.RefactorInterfaceWithTypes, my(NameScope.class).currentType())){
builder.type().visibility(CSVisibility.Public);
_compilationUnit.addType(builder.type());
}
else{
_currentType.addMember(builder.type());
}
return builder;
}
public boolean visit(final TypeDeclaration node) {
if (processIgnoredType(node)) {
return false;
}
if (processEnumType(node)) {
return false;
}
try {
my(NameScope.class).enterTypeDeclaration(node);
_ignoreExtends.using(ignoreExtends(node), new Runnable() {
public void run() {
final ITypeBinding binding = node.resolveBinding();
if (!binding.isNested()) {
processTypeDeclaration(node);
return;
}
if (isNonStaticNestedType(binding)) {
processNonStaticNestedTypeDeclaration(node);
return;
}
new CSharpBuilder(CSharpBuilder.this).processTypeDeclaration(node);
}
});
} finally {
my(NameScope.class).leaveTypeDeclaration(node);
}
return false;
}
private boolean processEnumType(TypeDeclaration node) {
if (!isEnum(node)) {
return false;
}
final CSEnum theEnum = new CSEnum(typeName(node));
mapVisibility(node, theEnum);
mapJavadoc(node, theEnum);
addType(theEnum);
node.accept(new ASTVisitor() {
public boolean visit(VariableDeclarationFragment node) {
theEnum.addValue(identifier(node.getName()));
return false;
}
@Override
public boolean visit(MethodDeclaration node) {
if (node.isConstructor() && isPrivate(node)) {
return false;
}
unsupportedConstruct(node, "Enum can contain only fields and a private constructor.");
return false;
}
});
return true;
}
protected boolean isPrivate(MethodDeclaration node) {
return Modifier.isPrivate(node.getModifiers());
}
private boolean isEnum(TypeDeclaration node) {
return containsJavadoc(node, SharpenAnnotations.SHARPEN_ENUM);
}
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);
}
private void processNonStaticNestedTypeDeclaration(TypeDeclaration node) {
new NonStaticNestedClassBuilder(this, node);
}
protected CSTypeDeclaration processTypeDeclaration(TypeDeclaration node) {
CSTypeDeclaration type = mapTypeDeclaration(node);
processDisabledType(node, isMainType(node) ? _compilationUnit : type);
addType(type);
mapSuperTypes(node, type);
mapVisibility(node, type);
mapDocumentation(node, type);
processConversionJavadocTags(node, type);
mapMembers(node, type);
autoImplementCloneable(node, type);
moveInitializersDependingOnThisReferenceToConstructor(type);
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;
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();
for (CSConstructor ctor : ensureConstructorsFor(type))
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)) {
return;
}
CSMethod clone = new CSMethod("System.ICloneable.Clone");
clone.returnType(OBJECT_TYPE_REFERENCE);
clone.body().addStatement(
new CSReturnStatement(-1,
new CSMethodInvocationExpression(new CSReferenceExpression("MemberwiseClone"))));
type.addMember(clone);
}
private boolean implementsCloneable(CSTypeDeclaration node) {
for (CSTypeReferenceExpression typeRef : node.baseTypes()) {
if (typeRef.toString().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);
}
private boolean ignoreExtends(TypeDeclaration node) {
return containsJavadoc(node, SharpenAnnotations.SHARPEN_IGNORE_EXTENDS);
}
private void processConversionJavadocTags(TypeDeclaration node, CSTypeDeclaration type) {
processPartialTagElement(node, type);
}
private 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) {
return new CSTypeParameter(identifier(item.getName()));
}
private CSTypeDeclaration typeDeclarationFor(TypeDeclaration node) {
if (node.isInterface()) {
return new CSInterface(processInterfaceName(node));
}
final String typeName = typeName(node);
if (isStruct(node)) {
return new CSStruct(typeName);
}
return new CSClass(typeName, mapClassModifier(node.getModifiers()));
}
private String typeName(TypeDeclaration node) {
final String renamed = annotatedRenaming(node);
if (renamed != null)
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());
type.addBaseType(mappedTypeReference(superClassBinding));
}
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 = resolveWellKnownType("java.io.Serializable");
for (Object itf : node.superInterfaceTypes()) {
Type iface = (Type) itf;
if (iface.resolveBinding() == serializable) {
continue;
}
type.addBaseType(mappedTypeReference(iface));
}
if (!type.isInterface() && node.resolveBinding().isSubTypeCompatible(serializable)) {
type.addAttribute(new CSAttribute("System.Serializable"));
}
}
private ITypeBinding resolveWellKnownType(String typeName) {
return _ast.getAST().resolveWellKnownType(typeName);
}
private void mapMembers(TypeDeclaration node, CSTypeDeclaration type) {
CSTypeDeclaration saved = _currentType;
_currentType = type;
try {
if(node.isInterface() && _configuration.shouldApplyConversionStrategy(ConversionStrategy.RefactorInterfaceWithTypes, node)){
for(Object b : node.bodyDeclarations()){
if(b instanceof TypeDeclaration){
_currentType = null;
visit((TypeDeclaration)b);
_currentType = type;
}
else{
((ASTNode)b).accept(this);
}
}
}
else{
visit(node.bodyDeclarations());
}
createInheritedAbstractMemberStubs(node);
flushInstanceInitializers(type, 0);
} finally {
_currentType = saved;
}
}
private void mapVisibility(BodyDeclaration node, CSMember member) {
member.visibility(mapVisibility(node));
}
private boolean isNonStaticNestedType(ITypeBinding binding) {
if (binding.isInterface())
return false;
if (!binding.isNested())
return false;
return !isStatic(binding);
}
private boolean isStatic(ITypeBinding binding) {
return Modifier.isStatic(binding.getModifiers());
}
private void addType(CSType type) {
if (null != _currentType) {
_currentType.addMember(type);
} else {
_compilationUnit.addType(type);
}
}
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 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.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) || TagElement.TAG_EXCEPTION.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 createTagNode(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.size() > 1 && fragments.get(0).toString().equals("<")){
CSDocTagNode tag = new CSDocTagNode("typeparam");
tag.addAttribute("name", fixIdentifierNameFor(fragments.get(1).toString(), element));
collectFragments(tag, fragments, 3);
return tag;
}
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);
case ASTNode.QUALIFIED_NAME:
return new CSDocTextNode(((QualifiedName)node).getFullyQualifiedName());
case ASTNode.SIMPLE_NAME:
return new CSDocTextNode(((SimpleName)node).getIdentifier());
case ASTNode.MEMBER_REF:
return new CSDocTextNode(((MemberRef)node).getName().getIdentifier());
}
warning(node, "Documentation node not supported: " + node.getClass() + ": " + node);
return new CSDocTextNode(node.toString());
}
public boolean visit(FieldDeclaration node) {
if (SharpenAnnotations.hasIgnoreAnnotation(node)) {
return false;
}
CSTypeReferenceExpression typeName = mappedTypeReference(node.getType());
CSVisibility visibility = mapVisibility(node);
for (Object item : node.fragments()) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) item;
CSField field = mapFieldDeclarationFragment(node, fragment, typeName, visibility);
_currentType.addMember(field);
}
return false;
}
private CSField mapFieldDeclarationFragment(FieldDeclaration node, VariableDeclarationFragment fragment,
CSTypeReferenceExpression fieldType, CSVisibility fieldVisibility) {
CSField field = new CSField(fieldName(fragment), fieldType, fieldVisibility, mapFieldInitializer(fragment));
if (isConstField(node, fragment)) {
field.addModifier(CSFieldModifier.Const);
} else {
processFieldModifiers(field, node.getModifiers());
}
mapDocumentation(node, field);
mapAnnotations(node, field);
return field;
}
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();
final CSAttribute attribute = new CSAttribute(mappedTypeName(binding.getAnnotationType()));
member.addAttribute(attribute);
}
protected String fieldName(VariableDeclarationFragment fragment) {
return identifier(fragment.getName());
}
protected CSExpression mapFieldInitializer(VariableDeclarationFragment fragment) {
return mapExpression(fragment.getInitializer());
}
private boolean isConstField(FieldDeclaration node, VariableDeclarationFragment fragment) {
//
return Modifier.isFinal(node.getModifiers()) && node.getType().isPrimitiveType() &&
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");
}
public boolean visit(Initializer node) {
if (Modifier.isStatic(node.getModifiers())) {
CSConstructor ctor = new CSConstructor(CSConstructorModifier.Static);
_currentType.addMember(ctor);
visitBodyDeclarationBlock(node, node.getBody(), ctor);
} else {
_instanceInitializers.add(node);
}
return false;
}
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);
}
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(MethodDeclaration node, final String name) {
mapPropertyDeclaration(node, producePropertyFor(node, name));
}
private CSProperty producePropertyFor(MethodDeclaration node, final String name) {
CSProperty existingProperty = findProperty(node, name);
if (existingProperty != null) {
return existingProperty;
}
CSProperty property = newPropertyFor(node, name);
_currentType.addMember(property);
return property;
}
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;
}
private CSProperty mapPropertyDeclaration(MethodDeclaration node, CSProperty property) {
final CSBlock block = mapBody(node);
if (isGetter(node)) {
property.getter(block);
} else {
property.setter(block);
mapImplicitSetterParameter(node, property);
}
mapMetaMemberAttributes(node, property);
mapParameters(node, property);
return property;
}
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 processMethodDeclaration(MethodDeclaration node) {
if (isDestructor(node)) {
mapMethodParts(node, new CSDestructor());
return;
}
if (node.isConstructor()) {
mapMethodParts(node, new CSConstructor());
return;
}
CSMethod method = new CSMethod(mappedMethodDeclarationName(node));
method.returnType(mappedReturnType(node));
method.modifier(mapMethodModifier(node));
mapTypeParameters(node.typeParameters(), method);
mapMethodParts(node, method);
}
private void mapMethodParts(MethodDeclaration node, CSMethodBase method) {
_currentType.addMember(method);
method.startPosition(node.getStartPosition());
method.isVarArgs(node.isVarargs());
mapVisibility(node, method);
mapParameters(node, method);
mapDocumentation(node, method);
mapAnnotations(node, method);
visitBodyDeclarationBlock(node, node.getBody(), method);
}
private String mappedMethodDeclarationName(MethodDeclaration node) {
final String 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));
}
private void mapMethodParameters(MethodDeclaration node, CSMethod method) {
for (Object o : node.parameters()) {
SingleVariableDeclaration p = (SingleVariableDeclaration) o;
ITypeBinding parameterType = p.getType().resolveBinding();
if (isGenericRuntimeParameterIdiom(node.resolveBinding(), parameterType)) {
// System.Type <p.name> = typeof(<T>);
method.body().addStatement(
new CSDeclarationStatement(p.getStartPosition(), new CSVariableDeclaration(identifier(p
.getName()), new CSTypeReference("System.Type"), new CSTypeofExpression(
genericRuntimeTypeIdiomType(parameterType)))));
} else {
mapParameter(p, method);
}
}
}
private CSTypeReferenceExpression genericRuntimeTypeIdiomType(ITypeBinding parameterType) {
return mappedTypeReference(parameterType.getTypeArguments()[0]);
}
private boolean isGenericRuntimeParameterIdiom(IMethodBinding method, ITypeBinding parameterType) {
if (!parameterType.isParameterizedType()) {
return false;
}
if (!"java.lang.Class".equals(qualifiedName(parameterType))) {
return false;
}
// detecting if the T in Class<T> comes from the method itself
final ITypeBinding classTypeArgument = parameterType.getTypeArguments()[0];
return classTypeArgument.getDeclaringMethod() == method;
}
private CSTypeReferenceExpression mappedReturnType(MethodDeclaration node) {
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) {
mapVisibility(node, metaMember);
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) {
CSMethodBase saved = _currentMethod;
_currentMethod = method;
processDisableTags(node, method);
processBlock(node, block, method.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();
}
public boolean visit(ConstructorInvocation node) {
addChainedConstructorInvocation(new CSThisExpression(), node.arguments());
return false;
}
private void addChainedConstructorInvocation(CSExpression target, List arguments) {
CSConstructorInvocationExpression cie = new CSConstructorInvocationExpression(target);
mapArguments(cie, arguments);
((CSConstructor) _currentMethod).chainedConstructorInvocation(cie);
}
public boolean visit(SuperConstructorInvocation node) {
if (null != node.getExpression()) {
notImplemented(node);
}
addChainedConstructorInvocation(new CSBaseExpression(), node.arguments());
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;
}
public boolean visit(VariableDeclarationExpression node) {
pushExpression(new CSDeclarationExpression(createVariableDeclaration((VariableDeclarationFragment) node
.fragments().get(0))));
return false;
}
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) {
IVariableBinding binding = variable.resolveBinding();
return createVariableDeclaration(binding, mapExpression(variable.getInitializer()));
}
private CSVariableDeclaration createVariableDeclaration(IVariableBinding binding, CSExpression initializer) {
return new CSVariableDeclaration(identifier(binding.getName()), mappedTypeReference(binding.getType()),
initializer);
}
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 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;
}
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;
}
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;
}
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));
}
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;
}
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;
}
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;
}
public boolean visit(SynchronizedStatement node) {
CSLockStatement stmt = new CSLockStatement(node.getStartPosition(), mapExpression(node.getExpression()));
visitBlock(stmt.body(), node.getBody());
addStatement(stmt);
return false;
}
public boolean visit(ReturnStatement node) {
addStatement(new CSReturnStatement(node.getStartPosition(), mapExpression(node.getExpression())));
return false;
}
public boolean visit(AssertStatement node) {
Expression expression = node.getExpression();
Expression message = node.getMessage();
CSReferenceExpression debug = new CSReferenceExpression("Debug");
CSMemberReferenceExpression assertEx = new CSMemberReferenceExpression(debug, "Assert");
CSMethodInvocationExpression method = new CSMethodInvocationExpression(assertEx, mapExpression(expression), mapExpression(message));
if(message == null){
method = new CSMethodInvocationExpression(assertEx, mapExpression(expression));
}
addStatement(new CSExpressionStatement(node.getStartPosition(), method));
return false;
}
public boolean visit(NumberLiteral node) {
String token = node.getToken();
CSNumberLiteralExpression literal = new CSNumberLiteralExpression(token);
if (token.startsWith("0x")) {
if (token.endsWith("l") || token.endsWith("L")) {
pushExpression(uncheckedCast("long", literal));
} else {
pushExpression(uncheckedCast("int", literal));
}
} else {
pushExpression(literal);
}
return false;
}
private CSUncheckedExpression uncheckedCast(String type, CSExpression expression) {
return new CSUncheckedExpression(new CSCastExpression(new CSTypeReference(type), new CSParenthesizedExpression(
expression)));
}
public boolean visit(StringLiteral node) {
String value = node.getLiteralValue();
if (value != null && value.length() == 0) {
pushExpression(new CSReferenceExpression("string.Empty"));
} else {
pushExpression(new CSStringLiteralExpression(mapEscapeSequences(node.getEscapedValue())));
}
return false;
}
public String mapEscapeSequences(String s){
StringBuilder sb = new StringBuilder();
StringBuilder currentOctal = new StringBuilder();
boolean inEscape = false;
for(char c : s.toCharArray()){
if(c == '\\'){
inEscape = !inEscape;
if(currentOctal.length() > 0){
sb.append("x" + Integer.toString(Integer.parseInt(currentOctal.toString(), 8), 16));
currentOctal = new StringBuilder();
inEscape = !inEscape;
}
sb.append(c);
}
else if(inEscape){
if(c == 'u' && sb.charAt(sb.length() - 1) == '\\' && currentOctal.length() == 0){
sb.append('x');
inEscape = false;
}
else if(Character.isLetter(c)){
if(currentOctal.length() > 0){
sb.append("x" + Integer.toString(Integer.parseInt(currentOctal.toString(), 8), 16));
currentOctal = new StringBuilder();
}
sb.append(c);
inEscape = false;
}
else if(Character.isDigit(c)){
currentOctal.append(c);
}
else{
if(currentOctal.length() > 0){
sb.append("x" + Integer.toString(Integer.parseInt(currentOctal.toString(), 8), 16));
currentOctal = new StringBuilder();
}
sb.append(c);
inEscape = false;
}
}
else{
sb.append(c);
}
}
if(inEscape && currentOctal.length() > 0){
sb.append("x" + Integer.toString(Integer.parseInt(currentOctal.toString(), 8), 16));
}
return sb.toString();
}
public boolean visit(CharacterLiteral node) {
pushExpression(new CSCharLiteralExpression(node.getEscapedValue()));
return false;
}
public boolean visit(NullLiteral node) {
pushExpression(new CSNullLiteralExpression());
return false;
}
public boolean visit(BooleanLiteral node) {
pushExpression(new CSBoolLiteralExpression(node.booleanValue()));
return false;
}
public boolean visit(ThisExpression node) {
pushExpression(new CSThisExpression());
return false;
}
public boolean visit(ArrayAccess node) {
pushExpression(new CSIndexedExpression(mapExpression(node.getArray()), mapExpression(node.getIndex())));
return false;
}
public boolean visit(ArrayCreation node) {
if (node.dimensions().size() > 1) {
if (null != node.getInitializer()) {
notImplemented(node);
}
pushExpression(unfoldMultiArrayCreation(node));
} else {
pushExpression(mapSingleArrayCreation(node));
}
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());
}
public boolean visit(ArrayInitializer node) {
if (isImplicitelyTypedArrayInitializer(node)) {
CSArrayCreationExpression ace = new CSArrayCreationExpression(mappedTypeReference(node.resolveTypeBinding()
.getComponentType()));
ace.initializer(mapArrayInitializer(node));
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();
}
@Override
public boolean visit(EnhancedForStatement node) {
CSForEachStatement stmt = new CSForEachStatement(node.getStartPosition(), mapExpression(node.getExpression()));
stmt.variable(createParameter(node.getParameter()));
visitBlock(stmt.body(), node.getBody());
addStatement(stmt);
return false;
}
public boolean visit(final ForStatement node) {
consumeContinueLabel(new Function<CSBlock>() {
public CSBlock apply() {
CSForStatement stmt = new CSForStatement(node.getStartPosition(), mapExpression(node.getExpression()));
for (Object i : node.initializers()) {
stmt.addInitializer(mapExpression((Expression) i));
}
for (Object u : node.updaters()) {
stmt.addUpdater(mapExpression((Expression) u));
}
visitBlock(stmt.body(), node.getBody());
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);
}
}
public boolean visit(SwitchStatement node) {
_currentContinueLabel = null;
CSBlock saved = _currentBlock;
CSSwitchStatement mappedNode = new CSSwitchStatement(node.getStartPosition(), mapExpression(node.getExpression()));
addStatement(mappedNode);
CSCaseClause defaultClause = null;
CSCaseClause current = null;
for (ASTNode element : Types.<Iterable<ASTNode>>cast(node.statements())) {
if (ASTNode.SWITCH_CASE == element.getNodeType()) {
if (null == current) {
current = new CSCaseClause();
mappedNode.addCase(current);
_currentBlock = current.body();
}
SwitchCase sc = (SwitchCase) element;
if (sc.isDefault()) {
defaultClause = current;
current.isDefault(true);
} else {
current.addExpression(mapExpression(node.getExpression().resolveTypeBinding(), sc.getExpression()));
}
} else {
current = null;
element.accept(this);
}
}
if (null != defaultClause) {
List<CSStatement> stats = defaultClause.body().statements();
CSStatement lastStmt = stats.size() > 0 ? stats.get(stats.size()-1) : null;
if( ! ( lastStmt instanceof CSThrowStatement) ) {
defaultClause.body().addStatement(new CSBreakStatement(Integer.MIN_VALUE));
}
}
_currentBlock = saved;
return false;
}
public boolean visit(CastExpression node) {
pushExpression(new CSCastExpression(mappedTypeReference(node.getType()), mapExpression(node.getExpression())));
return false;
}
public boolean visit(PrefixExpression node) {
pushExpression(new CSPrefixExpression(node.getOperator().toString(), mapExpression(node.getOperand())));
return false;
}
public boolean visit(PostfixExpression node) {
pushExpression(new CSPostfixExpression(node.getOperator().toString(), mapExpression(node.getOperand())));
return false;
}
public boolean visit(InfixExpression node) {
CSExpression left = mapExpression(node.getLeftOperand());
CSExpression right = mapExpression(node.getRightOperand());
if (node.getOperator() == InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED) {
String operator = ">>";
pushExpression(new CSInfixExpression("&", right, new CSNumberLiteralExpression("0x1f")));
pushExpression(new CSParenthesizedExpression(popExpression()));
pushExpression(new CSInfixExpression(operator, new CSParenthesizedExpression(left), popExpression()));
} else {
String operator = node.getOperator().toString();
CSInfixExpression infix = new CSInfixExpression(operator, left, right);
for (Object x : node.extendedOperands()) {
infix.addExtendedOperand(mapExpression((Expression) x));
}
pushExpression(infix);
}
return false;
}
public boolean visit(ParenthesizedExpression node) {
pushExpression(new CSParenthesizedExpression(mapExpression(node.getExpression())));
return false;
}
public boolean visit(ConditionalExpression node) {
pushExpression(new CSConditionalExpression(mapExpression(node.getExpression()), mapExpression(node
.getThenExpression()), mapExpression(node.getElseExpression())));
return false;
}
public boolean visit(InstanceofExpression node) {
pushExpression(new CSInfixExpression("is", mapExpression(node.getLeftOperand()), mappedTypeReference(node
.getRightOperand().resolveBinding())));
return false;
}
public boolean visit(Assignment node) {
Expression lhs = node.getLeftHandSide();
pushExpression(new CSInfixExpression(node.getOperator().toString(), mapExpression(lhs), mapExpression(lhs
.resolveTypeBinding(), node.getRightHandSide())));
return false;
}
private CSExpression mapExpression(ITypeBinding expectedType, Expression expression) {
return castIfNeeded(expectedType, expression.resolveTypeBinding(), mapExpression(expression));
}
private CSExpression castIfNeeded(ITypeBinding expectedType, ITypeBinding actualType, CSExpression expression) {
ITypeBinding charType = resolveWellKnownType("char");
if (expectedType != charType)
return expression;
if (actualType == expectedType)
return expression;
return new CSCastExpression(mappedTypeReference(expectedType), expression);
}
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 (isNonStaticNestedTypeCreation(node)) {
expression.addArgument(new CSThisExpression());
}
mapArguments(expression, node.arguments());
pushExpression(expression);
return false;
}
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;
}
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) {
if (_configuration.nativeTypeSystem()) {
pushExpression(new CSTypeofExpression(type));
} else {
pushGetClassForTypeExpression(type);
}
}
private void pushGetClassForTypeExpression(final CSTypeReferenceExpression typeName) {
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(new CSReferenceExpression(
methodName(_configuration.getRuntimeTypeName() + ".getClassForType")));
mie.addArgument(new CSTypeofExpression(typeName));
pushExpression(mie);
}
public boolean visit(MethodInvocation node) {
IMethodBinding binding = originalMethodBinding(node.resolveMethodBinding());
Configuration.MemberMapping mapping = mappingForInvocation(node, binding);
if (null != mapping) {
processMappedMethodInvocation(node, binding, mapping);
} else {
processUnmappedMethodInvocation(node);
}
return false;
}
public boolean visit(SuperMethodInvocation node) {
if (null != node.getQualifier()) {
notImplemented(node);
}
IMethodBinding binding = originalMethodBinding(node.resolveMethodBinding());
Configuration.MemberMapping mapping = mappingForInvocation(node, binding);
CSExpression target = new CSMemberReferenceExpression(new CSBaseExpression(), mappedMethodName(binding));
if (mapping != null && mapping.kind != MemberKind.Method) {
pushExpression(target);
return false;
}
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(target);
mapArguments(mie, node.arguments());
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;
}
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 CSExpression targetExpression = mapMethodTargetExpression(node);
String name = resolveTargetMethodName(node);
CSExpression target = null == targetExpression
? new CSReferenceExpression(name)
: new CSMemberReferenceExpression(targetExpression, name);
CSMethodInvocationExpression mie = new CSMethodInvocationExpression(target);
mapMethodInvocationArguments(mie, node);
mapTypeArguments(mie, node);
pushExpression(mie);
}
private String resolveTargetMethodName(MethodInvocation node) {
final IMethodBinding method = staticImportMethodBinding(node.getName(), _ast.imports());
if(method != 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 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 && isGenericRuntimeParameterIdiom(originalMethod, originalTypes[i])
&& isClassLiteral(arg)) {
mie.addTypeArgument(genericRuntimeTypeIdiomType(actualTypes[i]));
} else {
addArgument(mie, arg);
}
}
}
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, mapping);
return;
}
String name = mappedMethodName(binding);
if (0 == name.length()) {
final Expression expression = node.getExpression();
final CSExpression target = expression != null ? mapExpression(expression) : new CSThisExpression(); // see
// collections/EntrySet1
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) {
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);
pushExpression(mie);
}
private void processIndexerInvocation(MethodInvocation node, IMethodBinding binding, MemberMapping mapping) {
if (node.arguments().size() == 1) {
processIndexerGetter(node);
} else {
processIndexerSetter(node);
}
}
private void processIndexerSetter(MethodInvocation node) {
// target(arg0 ... argN) => target[arg0... argN-1] = argN;
final CSIndexedExpression indexer = new CSIndexedExpression(mapIndexerTarget(node));
final List arguments = node.arguments();
final Expression lastArgument = (Expression)arguments.get(arguments.size() - 1);
for (int i=0; i<arguments.size()-1; ++i) {
indexer.addIndex(mapExpression((Expression) arguments.get(i)));
}
pushExpression(CSharpCode.newAssignment(indexer, mapExpression(lastArgument)));
}
private void processIndexerGetter(MethodInvocation node) {
final Expression singleArgument = (Expression) node.arguments().get(0);
pushExpression(
new CSIndexedExpression(
mapIndexerTarget(node),
mapExpression(singleArgument)));
}
private CSExpression mapIndexerTarget(MethodInvocation node) {
if (node.getExpression() == null) {
return new CSThisExpression();
}
return mapMethodTargetExpression(node);
}
private CSExpression parensIfNeeded(CSExpression expression) {
if (expression instanceof CSInfixExpression || expression instanceof CSPrefixExpression
|| expression instanceof CSPostfixExpression) {
return new CSParenthesizedExpression(expression);
}
return expression;
}
protected CSExpression mapMethodTargetExpression(MethodInvocation node) {
return 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) {
for (Object arg : arguments) {
addArgument(mie, (Expression) arg);
}
}
private void addArgument(CSMethodInvocationExpression mie, Expression arg) {
mie.addArgument(mapExpression(arg));
}
public boolean visit(FieldAccess node) {
String name = mappedFieldName(node);
if (null == node.getExpression()) {
pushExpression(new CSReferenceExpression(name));
} else {
pushExpression(new CSMemberReferenceExpression(mapExpression(node.getExpression()), name));
}
return false;
}
private boolean isBoolLiteral(String name) {
return name.equals("true") || name.equals("false");
}
private String mappedFieldName(FieldAccess node) {
String name = mappedFieldName(node.getName());
if (null != name)
return name;
return identifier(node.getName());
}
public boolean visit(SimpleName node) {
if (isTypeReference(node)) {
pushTypeReference(node.resolveTypeBinding());
} else {
pushExpression(new CSReferenceExpression(identifier(node)));
}
return false;
}
private void addStatement(CSStatement statement) {
_currentBlock.addStatement(statement);
}
private void pushTypeReference(ITypeBinding typeBinding) {
pushExpression(createTypeReference(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();
}
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) {
String mapped = mappedFieldName(node);
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);
}
} else {
Name qualifier = node.getQualifier();
String name = identifier(node.getName().getIdentifier());
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 _configuration.mapByteToSbyte() ?
"sbyte" :
"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;
}
private String mappedFieldName(Name node) {
IVariableBinding binding = variableBinding(node);
return null == binding ? null : 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) {
throw new IllegalArgumentException(sourceInformation(node) + ": " + message, cause);
}
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;
Set<ITypeBinding> interfaces = new LinkedHashSet<ITypeBinding>();
collectInterfaces(interfaces, binding);
for (ITypeBinding baseType : interfaces) {
createInheritedAbstractMemberStubs(binding, baseType);
}
}
private void collectInterfaces(Set<ITypeBinding> interfaceList, ITypeBinding binding) {
ITypeBinding[] interfaces = binding.getInterfaces();
for (int i = 0; i < interfaces.length; ++i) {
ITypeBinding interfaceBinding = interfaces[i];
if (interfaceList.contains(interfaceBinding)) {
continue;
}
collectInterfaces(interfaceList, interfaceBinding);
interfaceList.add(interfaceBinding);
}
}
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.getModifiers()));
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.getModifiers()));
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()) {
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 isOverride(MethodDeclaration method) {
IMethodBinding methodBinding = method.resolveBinding();
ITypeBinding superclass = _ignoreExtends.value() ? resolveWellKnownType("java.lang.Object") : methodBinding
.getDeclaringClass().getSuperclass();
if (null == superclass)
return false;
return null != BindingUtils.findOverriddenMethodInHierarchy(superclass, methodBinding);
}
CSClassModifier mapClassModifier(int modifiers) {
if (Modifier.isAbstract(modifiers)) {
return CSClassModifier.Abstract;
}
if (Modifier.isFinal(modifiers)) {
return CSClassModifier.Sealed;
}
return CSClassModifier.None;
}
CSVisibility mapVisibility(BodyDeclaration node) {
if (containsJavadoc(node, SharpenAnnotations.SHARPEN_INTERNAL)) {
return CSVisibility.Internal;
}
if (containsJavadoc(node, SharpenAnnotations.SHARPEN_PRIVATE)) {
return CSVisibility.Private;
}
if (containsJavadoc(node, SharpenAnnotations.SHARPEN_PROTECTED)) {
return CSVisibility.Protected;
}
if (containsJavadoc(node, SharpenAnnotations.SHARPEN_PUBLIC)) {
return CSVisibility.Public;
}
return mapVisibility(node.getModifiers());
}
CSVisibility mapVisibility(int modifiers) {
if (Modifier.isPublic(modifiers)) {
return CSVisibility.Public;
}
if (Modifier.isProtected(modifiers)) {
return _configuration.mapProtectedToProtectedInternal() ?
CSVisibility.ProtectedInternal :
CSVisibility.Protected;
}
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);
}
protected CSTypeReferenceExpression mappedTypeReference(ITypeBinding type) {
final ASTNode declaration = findDeclaringNode(type);
if (isMacroType(declaration)) {
return mappedMacroTypeReference(type, (TypeDeclaration) declaration);
}
if (type.isArray()) {
return mappedArrayTypeReference(type);
}
if (type.isWildcardType()) {
return mappedWildcardTypeReference(type);
}
final CSTypeReference typeRef = new CSTypeReference(mappedTypeName(type));
if (isJavaLangClass(type)) {
return typeRef;
}
for (ITypeBinding arg : type.getTypeArguments()) {
typeRef.addTypeArgument(mappedTypeReference(arg));
}
return typeRef;
}
private boolean isJavaLangClass(ITypeBinding type) {
return type.getErasure() == javaLangClassBinding();
}
private ITypeBinding javaLangClassBinding() {
return resolveWellKnownType("java.lang.Class");
}
private CSTypeReferenceExpression mappedWildcardTypeReference(ITypeBinding type) {
final ITypeBinding bound = type.getBound();
return bound != null ? mappedTypeReference(bound) : OBJECT_TYPE_REFERENCE;
}
private CSTypeReferenceExpression mappedArrayTypeReference(ITypeBinding type) {
return new CSArrayTypeReference(mappedTypeReference(type.getElementType()), type.getDimensions());
}
protected final String mappedTypeName(ITypeBinding type) {
return my(Mappings.class).mappedTypeName(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 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);
}
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) {
_currentContinueLabel = null;
return super.visit(node);
}
}