Java tutorial
/* 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.security.DomainCombiner; 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>(); private Stack<Set<String>> _blockVariables = new Stack<Set<String>>(); private Stack<Set<String>> _localBlockVariables = new Stack<Set<String>>(); private Stack<HashMap<String, String>> _renamedVariables = new Stack<HashMap<String, String>>(); private ITypeBinding _currentExpectedType; 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); _compilationUnit.addUsing(new CSUsing("Sharpen")); } 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)) { if (node.bodyDeclarations().isEmpty()) { return convSimpleEnum(node); } else { new CSharpBuilder(CSharpBuilder.this).processTypeDeclaration(node); return false; } } return false; } private boolean convSimpleEnum(EnumDeclaration node) { final CSEnum theEnum = new CSEnum(typeName(node)); mapVisibility(node, theEnum); mapJavadoc(node, theEnum); addType(node.resolveBinding(), theEnum); node.accept(new ASTVisitor() { public boolean visit(EnumConstantDeclaration 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 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) { notImplemented(node); 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); _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 (processInterfaceWithFields(node, binding)) { return; } processMappableTypeDeclaration(node, binding); } }); } finally { my(NameScope.class).leaveTypeDeclaration(node); } return false; } private void processMappableTypeDeclaration(final TypeDeclaration node, final ITypeBinding binding) { if (!binding.isNested()) { processTypeDeclaration(node); return; } if (isNonStaticNestedType(binding)) { processNonStaticNestedTypeDeclaration(node); return; } new CSharpBuilder(CSharpBuilder.this).processTypeDeclaration(node); } private boolean processInterfaceWithFields(final TypeDeclaration node, final ITypeBinding binding) { if (binding.isInterface()) { IVariableBinding[] declaredFields = binding.getDeclaredFields(); if (declaredFields.length > 0 || binding.getDeclaredTypes().length > 0) { String name = typeName(node); CSClass constantsType = new CSClass(interfaceStaticsClassName(name, true, binding), CSClassModifier.Static); addType(binding, constantsType); constantsType.startPosition(node.getStartPosition()); constantsType.sourceLength(node.getLength()); mapTypeParameters(node.typeParameters(), constantsType); mapVisibility(node, constantsType); adjustVisibility(binding.getSuperclass(), constantsType); mapDocumentation(node, constantsType); processConversionJavadocTags(node, constantsType); CSTypeDeclaration saved = _currentType; _currentType = constantsType; try { for (Object memberNodeObj : node.bodyDeclarations()) { ASTNode memberNode = (ASTNode) memberNodeObj; if (memberNode.getNodeType() != ASTNode.METHOD_DECLARATION) { memberNode.accept(this); } } //flushInstanceInitializers(type, 0); } finally { _currentType = saved; } if (binding.getDeclaredMethods().length != 0 || !name.equals(constantsType.name())) { processMappableTypeDeclaration(node, binding); } return true; } } 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(node.resolveBinding(), 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(AbstractTypeDeclaration node) { CSTypeDeclaration type = mapTypeDeclaration(node); processDisabledType(node, isMainType(node) ? _compilationUnit : type); if (_configuration.shouldMakePartial(node.getName().getFullyQualifiedName())) type.partial(true); ITypeBinding typeBinding = node.resolveBinding(); addType(typeBinding, type); TypeDeclaration tNode = node instanceof TypeDeclaration ? (TypeDeclaration) node : null; if (tNode != null) mapSuperTypes(tNode, type); mapVisibility(node, type); adjustVisibility(typeBinding.getSuperclass(), type); mapDocumentation(node, type); if (tNode != null) processConversionJavadocTags(tNode, type); if (node instanceof EnumDeclaration) { EnumDeclaration eNode = (EnumDeclaration) node; CSTypeReferenceExpression typeName = mappedTypeReference(typeBinding); for (Object item : eNode.enumConstants()) { EnumConstantDeclaration ecd = (EnumConstantDeclaration) item; CSMethodInvocationExpression initializer = mapEnumInitializer(ecd, typeName); mapArguments(initializer, ecd.arguments()); CSField field = new CSField(identifier(ecd.getName()), typeName, CSVisibility.Public, initializer); field.addModifier(CSFieldModifier.Static); field.addModifier(CSFieldModifier.Readonly); type.addMember(field); } } mapMembers(node, type); if (node instanceof EnumDeclaration) { EnumDeclaration eNode = (EnumDeclaration) node; CSTypeReferenceExpression typeName = mappedTypeReference(typeBinding); CSArrayCreationExpression newArrayExpr = new CSArrayCreationExpression(typeName); CSArrayInitializerExpression initializer = new CSArrayInitializerExpression(); for (Object item : eNode.enumConstants()) { EnumConstantDeclaration ecd = (EnumConstantDeclaration) item; initializer.addExpression(new CSReferenceExpression(identifier(ecd.getName()))); } newArrayExpr.initializer(initializer); CSReturnStatement returnStatment = new CSReturnStatement(0, newArrayExpr); CSMethod valuesMethod = new CSMethod(methodName("values")); valuesMethod.modifier(CSMethodModifier.Static); valuesMethod.visibility(CSVisibility.Public); valuesMethod.returnType(new CSArrayTypeReference(typeName, 1)); valuesMethod.body().addStatement(returnStatment); type.addMember(valuesMethod); } if (tNode != null) autoImplementCloneable(tNode, type); moveInitializersDependingOnThisReferenceToConstructor(type); if (_configuration.junitConversion() && hasTests(type)) type.addAttribute(new CSAttribute("NUnit.Framework.TestFixture")); return type; } private CSMethodInvocationExpression mapEnumInitializer(EnumConstantDeclaration node, CSTypeReferenceExpression typeName) { Configuration.MemberMapping mappedConstructor = effectiveMappingFor(node.resolveConstructorBinding()); if (null == mappedConstructor) { return new CSConstructorInvocationExpression(typeName); } 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, EnumConstantDeclaration 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; } private void processDisabledType(AbstractTypeDeclaration node, CSNode type) { final String expression = _configuration.conditionalCompilationExpressionFor(packageNameFor(node)); if (null != expression) { compilationUnit().addEnclosingIfDef(expression); } processDisableTags(node, type); } private String packageNameFor(AbstractTypeDeclaration 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)) { if (ctor.isStatic() || hasChainedThisInvocation(ctor)) continue; ctor.body().addStatement(index, newAssignment(field, initializer)); } field.initializer(null); } private CSExpression newAssignment(CSField field, final CSExpression initializer) { return CSharpCode.newAssignment(CSharpCode.newReference(field), initializer); } private Iterable<CSConstructor> ensureConstructorsFor(CSTypeDeclaration type) { final List<CSConstructor> ctors = type.constructors(); if (!ctors.isEmpty()) { for (CSConstructor ctor : ctors) { if (!ctor.isStatic()) return ctors; } addDefaultConstructor(type); return type.constructors(); } 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(AbstractTypeDeclaration node) { CSTypeDeclaration type = typeDeclarationFor(node); type.startPosition(node.getStartPosition()); type.sourceLength(node.getLength()); if (node instanceof TypeDeclaration) mapTypeParameters(((TypeDeclaration) node).typeParameters(), type); return checkForMainType(node, type); } private void mapTypeParameters(final List typeParameters, CSTypeParameterProvider type) { for (Object item : typeParameters) { type.addTypeParameter(mapTypeParameter((TypeParameter) item)); } } private CSTypeParameter mapTypeParameter(TypeParameter item) { CSTypeParameter tp = new CSTypeParameter(identifier(item.getName())); ITypeBinding tb = item.resolveBinding(); if (tb != null) { ITypeBinding superc = mapTypeParameterExtendedType(tb); if (superc != null) tp.superClass(mappedTypeReference(superc)); } return tp; } private CSTypeDeclaration typeDeclarationFor(AbstractTypeDeclaration node) { final String typeName = typeName(node); if (node instanceof EnumDeclaration) return new CSClass(typeName, CSClassModifier.None); TypeDeclaration tNode = (TypeDeclaration) node; if (tNode.isInterface()) { if (isValidCSInterface(tNode.resolveBinding())) return new CSInterface(typeName(tNode, true)); else return new CSClass(typeName, CSClassModifier.Abstract); } if (isStruct(tNode)) { return new CSStruct(typeName); } return new CSClass(typeName, mapClassModifier(tNode.getModifiers())); } private String typeName(AbstractTypeDeclaration node) { return typeName(node, false); } private String typeName(AbstractTypeDeclaration node, boolean isInterface) { String renamed = annotatedRenaming(node); if (renamed != null) return renamed; renamed = mappedTypeName(node.resolveBinding()); if (renamed != null) { int i = renamed.lastIndexOf('.'); if (i != -1) return renamed.substring(i + 1); else return renamed; } renamed = node.getName().toString(); if (isInterface) renamed = interfaceName(renamed); return renamed; } private boolean isStruct(TypeDeclaration node) { return containsJavadoc(node, SharpenAnnotations.SHARPEN_STRUCT); } private CSTypeDeclaration checkForMainType(AbstractTypeDeclaration node, CSTypeDeclaration type) { if (isMainType(node)) { setCompilationUnitElementName(type.name()); } return type; } private void setCompilationUnitElementName(String name) { _compilationUnit.elementName(name + ".cs"); } private boolean isMainType(AbstractTypeDeclaration node) { return node.isPackageMemberTypeDeclaration() && Modifier.isPublic(node.getModifiers()); } private void mapSuperClass(TypeDeclaration node, CSTypeDeclaration type) { if (handledExtends(node, type)) return; if (null == node.getSuperclassType()) return; final ITypeBinding superClassBinding = node.getSuperclassType().resolveBinding(); if (null == superClassBinding) unresolvedTypeBinding(node.getSuperclassType()); if (!isLegacyTestFixtureClass(superClassBinding)) type.addBaseType(mappedTypeReference(superClassBinding)); else { type.addAttribute(new CSAttribute("NUnit.Framework.TestFixture")); } } private boolean isLegacyTestFixtureClass(ITypeBinding type) { return (_configuration.junitConversion() && type.getQualifiedName().equals("junit.framework.TestCase")); } private boolean isLegacyTestFixture(ITypeBinding type) { if (!_configuration.junitConversion()) return false; if (isLegacyTestFixtureClass(type)) return true; ITypeBinding base = type.getSuperclass(); return (base != null) && isLegacyTestFixture(base); } private boolean hasTests(CSTypeDeclaration type) { for (CSMember m : type.members()) { if (m instanceof CSMethod) { CSMethod met = (CSMethod) m; for (CSAttribute at : met.attributes()) { if (at.name().equals("Test") || at.name().equals("NUnit.Framework.Test")) return true; } } } return false; } private boolean handledExtends(TypeDeclaration node, CSTypeDeclaration type) { final TagElement replaceExtendsTag = javadocTagFor(node, SharpenAnnotations.SHARPEN_EXTENDS); if (null == replaceExtendsTag) return false; final String baseType = JavadocUtility.singleTextFragmentFrom(replaceExtendsTag); type.addBaseType(new CSTypeReference(baseType)); return true; } private void mapSuperInterfaces(TypeDeclaration node, CSTypeDeclaration type) { final ITypeBinding serializable = resolveWellKnownType("java.io.Serializable"); for (Object itf : node.superInterfaceTypes()) { Type iface = (Type) itf; ITypeBinding binding = iface.resolveBinding(); if (binding == serializable) { continue; } if (binding != null) { String identifier = binding.getName(); if (identifier.charAt(0) != 'I' && identifier.endsWith("Constants") && binding.getDeclaredMethods().length == 0) { 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(AbstractTypeDeclaration node, CSTypeDeclaration type) { CSTypeDeclaration saved = _currentType; _currentType = type; try { if (type.isInterface()) { for (Object obj : node.bodyDeclarations()) { ASTNode astNode = (ASTNode) obj; if (astNode.getNodeType() == ASTNode.METHOD_DECLARATION) astNode.accept(this); } } else { visit(node.bodyDeclarations()); if (node instanceof TypeDeclaration) createInheritedAbstractMemberStubs((TypeDeclaration) node); flushInstanceInitializers(type, 0); } } finally { _currentType = saved; } } private void mapVisibility(BodyDeclaration node, CSMember member) { member.visibility(mapVisibility(node)); } protected 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(ITypeBinding binding, CSType type) { if (null != _currentType && !isExtractedNestedType(binding)) { _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.removeAttribute("System.Obsolete"); member.removeAttribute("System.ObsoleteAttribute"); member.addAttribute(obsoleteAttributeFromDeprecatedTagElement(element)); return true; } return false; } private CSAttribute obsoleteAttributeFromDeprecatedTagElement(TagElement element) { CSAttribute attribute = new CSAttribute(mappedTypeName("System.ObsoleteAttribute")); if (element.fragments().isEmpty()) { return attribute; } attribute.addArgument(new CSStringLiteralExpression(toLiteralStringForm(getWholeText(element)))); return attribute; } private String getWholeText(TagElement element) { StringBuilder builder = new StringBuilder(); for (ASTNode fragment : (List<ASTNode>) element.fragments()) { if (fragment instanceof TextElement) { TextElement textElement = (TextElement) fragment; String text = textElement.getText(); appendWithSpaceIfRequired(builder, text); } else if (fragment instanceof TagElement) { builder.append(getWholeText((TagElement) fragment)); } else if (fragment instanceof MethodRef) { builder.append(mapCRefTarget(fragment)); } else if (fragment instanceof MemberRef) { builder.append(mapCRefTarget(fragment)); } else if (fragment instanceof Name) { builder.append(mapCRefTarget(fragment)); } else { break; } } return builder.toString().trim(); } private void appendWithSpaceIfRequired(StringBuilder builder, String text) { if (builder.length() > 0 && builder.charAt(builder.length() - 1) != ' ' && text.startsWith(" ") == false) { builder.append(" "); } builder.append(text); } private String toLiteralStringForm(String s) { // TODO: deal with escaping sequences here return "@\"" + s.replace("\"", "\"\"") + "\""; } private boolean isConversionTag(String tagName) { return tagName.startsWith("@sharpen."); } private void processPartialTagElement(TypeDeclaration node, CSTypeDeclaration member) { TagElement element = javadocTagFor(node, SharpenAnnotations.SHARPEN_PARTIAL); if (null == element) return; ((CSTypeDeclaration) member).partial(true); } private TagElement javadocTagFor(PackageDeclaration node, final String withName) { return JavadocUtility.getJavadocTag(node, withName); } private TagElement javadocTagFor(BodyDeclaration node, final String withName) { return JavadocUtility.getJavadocTag(node, withName); } private void mapJavadocSummary(final CSMember member, TagElement element) { List<String> summary = getFirstSentence(element); if (null != summary) { CSDocTagNode summaryNode = new CSDocTagNode("summary"); for (String fragment : summary) { summaryNode.addFragment(new CSDocTextNode(fragment)); } member.addDoc(summaryNode); member.addDoc(createTagNode("remarks", element)); } else { member.addDoc(createTagNode("summary", element)); } } private List<String> getFirstSentence(TagElement element) { List<String> fragments = new LinkedList<String>(); for (Object fragment : element.fragments()) { if (fragment instanceof TextElement) { TextElement textElement = (TextElement) fragment; String text = textElement.getText(); int index = findSentenceClosure(text); if (index > -1) { fragments.add(text.substring(0, index + 1)); return fragments; } else { fragments.add(text); } } else { break; } } return null; } private int findSentenceClosure(String text) { Matcher matcher = SUMMARY_CLOSURE_PATTERN.matcher(text); return matcher.find() ? matcher.start() : -1; } private CSDocNode mapTagElement(TagElement element) { String tagName = element.getTagName(); if (TagElement.TAG_PARAM.equals(tagName)) { return mapTagParam(element); } else if (TagElement.TAG_RETURN.equals(tagName)) { return createTagNode("returns", element); } else if (TagElement.TAG_LINK.equals(tagName)) { return mapTagLink(element); } else if (TagElement.TAG_THROWS.equals(tagName)) { return mapTagThrows(element); } else if (TagElement.TAG_SEE.equals(tagName)) { return mapTagWithCRef("seealso", element); } return createTagNode(tagName.substring(1), element); } private CSDocNode mapTagThrows(TagElement element) { return mapTagWithCRef("exception", element); } private CSDocNode mapTagLink(TagElement element) { return mapTagWithCRef("see", element); } private CSDocNode mapTagWithCRef(String tagName, TagElement element) { final List fragments = element.fragments(); if (fragments.isEmpty()) { return invalidTagWithCRef(element, tagName, element); } final ASTNode linkTarget = (ASTNode) fragments.get(0); String cref = mapCRefTarget(linkTarget); if (null == cref) { return invalidTagWithCRef(linkTarget, tagName, element); } CSDocTagNode node = newTagWithCRef(tagName, cref); if (fragments.size() > 1) { if (isLinkWithSimpleLabel(fragments, linkTarget)) { node.addTextFragment(unqualifiedName(cref)); } else { collectFragments(node, fragments, 1); } } else { //TODO: Move the XML encoding to the right place (CSharpPrinter) node.addTextFragment(cref.replace("{", "<").replace("}", ">")); } return node; } private ASTNode documentedNodeAttachedTo(TagElement element) { ASTNode attachedToNode = element; while (attachedToNode instanceof TagElement || attachedToNode instanceof Javadoc) { attachedToNode = attachedToNode.getParent(); } return attachedToNode; } private CSDocNode invalidTagWithCRef(final ASTNode linkTarget, String tagName, TagElement element) { warning(linkTarget, "Tag '" + element.getTagName() + "' demands a valid cref target."); CSDocNode newTag = createTagNode(tagName, element); return newTag; } private CSDocTagNode newTagWithCRef(String tagName, String cref) { CSDocTagNode node = new CSDocTagNode(tagName); node.addAttribute("cref", cref); return node; } private boolean isLinkWithSimpleLabel(List<ASTNode> fragments, final ASTNode linkTarget) { if (fragments.size() != 2) return false; if (!JavadocUtility.isTextFragment(fragments, 1)) return false; final String link = linkTarget.toString(); final String label = JavadocUtility.textFragment(fragments, 1); return label.equals(link) || label.equals(unqualifiedName(link)); } private String mapCRefTarget(final ASTNode crefTarget) { return new CRefBuilder(crefTarget).build(); } private CSDocNode mapTagParam(TagElement element) { List fragments = element.fragments(); if (!(fragments.get(0) instanceof SimpleName)) return new CSDocTagNode("?"); SimpleName name = (SimpleName) fragments.get(0); if (null == name.resolveBinding()) { warning(name, "Parameter '" + name + "' not found."); } CSDocTagNode param = isPropertyNode(documentedNodeAttachedTo(element)) ? new CSDocTagNode("value") : newCSDocTag(fixIdentifierNameFor(identifier(name), element)); collectFragments(param, fragments, 1); return param; } private CSDocTagNode newCSDocTag(final String paramName) { CSDocTagNode param; param = new CSDocTagNode("param"); param.addAttribute("name", paramName); return param; } private boolean isPropertyNode(ASTNode node) { if (node.getNodeType() != ASTNode.METHOD_DECLARATION) { return false; } return isProperty((MethodDeclaration) node); } private String fixIdentifierNameFor(String identifier, TagElement element) { return removeAtSign(identifier); } private String removeAtSign(String identifier) { return identifier.startsWith("@") ? identifier.substring(1) : identifier; } private void collectFragments(CSDocTagNode node, List fragments, int index) { for (int i = index; i < fragments.size(); ++i) { node.addFragment(mapTagElementFragment((ASTNode) fragments.get(i))); } } private CSDocNode mapTextElement(TextElement element) { final String text = element.getText(); if (HTML_ANCHOR_PATTERN.matcher(text).find()) { warning(element, "Caution: HTML anchors can result in broken links. Consider using @link instead."); } return new CSDocTextNode(text); } private CSDocNode createTagNode(String tagName, TagElement element) { CSDocTagNode summary = new CSDocTagNode(tagName); for (Object f : element.fragments()) { summary.addFragment(mapTagElementFragment((ASTNode) f)); } return summary; } private CSDocNode mapTagElementFragment(ASTNode node) { switch (node.getNodeType()) { case ASTNode.TAG_ELEMENT: return mapTagElement((TagElement) node); case ASTNode.TEXT_ELEMENT: return mapTextElement((TextElement) node); } warning(node, "Documentation node not supported: " + node.getClass() + ": " + node); return new CSDocTextNode(node.toString()); } public boolean visit(FieldDeclaration node) { if (SharpenAnnotations.hasIgnoreAnnotation(node)) { return false; } ITypeBinding fieldType = node.getType().resolveBinding(); CSTypeReferenceExpression typeName = mappedTypeReference(fieldType); CSVisibility visibility = mapVisibility(node); if (((VariableDeclarationFragment) node.fragments().get(0)).resolveBinding().getDeclaringClass() .isInterface()) visibility = CSVisibility.Public; for (Object item : node.fragments()) { VariableDeclarationFragment fragment = (VariableDeclarationFragment) item; ITypeBinding thisFieldType = fieldType; CSTypeReferenceExpression thisTypeName = typeName; if (fragment.getExtraDimensions() > 0) { thisFieldType = fieldType.createArrayType(fragment.getExtraDimensions()); thisTypeName = mappedTypeReference(thisFieldType); } ITypeBinding saved = pushExpectedType(thisFieldType); CSField field = mapFieldDeclarationFragment(node, fragment, thisTypeName, visibility); popExpectedType(saved); adjustVisibility(thisFieldType, field); _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(), fragment.resolveBinding().getDeclaringClass().isInterface()); } 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) { String name = mappedFieldName(fragment.getName()); return name != null ? name : identifier(fragment.getName()); } protected CSExpression mapFieldInitializer(VariableDeclarationFragment fragment) { return mapExpression(fragment.getInitializer()); } private boolean isConstField(FieldDeclaration node, VariableDeclarationFragment fragment) { boolean isStaticFinal = Modifier.isFinal(node.getModifiers()) && Modifier.isStatic(node.getModifiers()); if (!isStaticFinal && !fragment.resolveBinding().getDeclaringClass().isInterface()) { return false; } Type type = node.getType(); boolean isPrimitiveOrString = type.isPrimitiveType(); if (!type.isPrimitiveType() && (type.isSimpleType() || type.isQualifiedType())) { ITypeBinding binding = type.resolveBinding(); if (binding != null) { String name = binding.getQualifiedName(); isPrimitiveOrString = name.equals("java.lang.String"); } } return isPrimitiveOrString && hasConstValue(fragment); } private boolean hasConstValue(VariableDeclarationFragment fragment) { Object constValue = fragment.resolveBinding().getConstantValue(); if (null != constValue) { return !("".equals(constValue) && my(Configuration.class).mapEmptyStringLiteral()); } return false; } private void processFieldModifiers(CSField field, int modifiers, boolean inInterface) { if (Modifier.isStatic(modifiers) || inInterface) { field.addModifier(CSFieldModifier.Static); } if (Modifier.isFinal(modifiers) || inInterface) { 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(type, name, 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); if (_configuration.junitConversion() && isLegacyTestFixture(node.resolveBinding().getDeclaringClass())) { if (method.name().startsWith("Test") && method.visibility() == CSVisibility.Public) method.addAttribute(new CSAttribute("NUnit.Framework.Test")); if (isLegacyTestFixtureClass(node.resolveBinding().getDeclaringClass().getSuperclass())) { if (method.name().equals("SetUp")) { method.addAttribute(new CSAttribute("NUnit.Framework.SetUp")); method.modifier(CSMethodModifier.Virtual); cleanBaseSetupCalls(method); } else if (method.name().equals("TearDown")) { method.addAttribute(new CSAttribute("NUnit.Framework.TearDown")); method.modifier(CSMethodModifier.Virtual); cleanBaseSetupCalls(method); } } } } private void cleanBaseSetupCalls(CSMethod method) { ArrayList<CSStatement> toDelete = new ArrayList<CSStatement>(); for (CSStatement st : method.body().statements()) { if (st instanceof CSExpressionStatement) { CSExpressionStatement es = (CSExpressionStatement) st; if (es.expression() instanceof CSMethodInvocationExpression) { CSMethodInvocationExpression mie = (CSMethodInvocationExpression) es.expression(); if (mie.expression() instanceof CSMemberReferenceExpression) { CSMemberReferenceExpression mr = (CSMemberReferenceExpression) mie.expression(); if ((mr.expression() instanceof CSBaseExpression) && (mr.name().equals("SetUp") || mr.name().equals("TearDown"))) toDelete.add(st); } } } } for (CSStatement st : toDelete) method.body().removeStatement(st); } private void mapMethodParts(MethodDeclaration node, CSMethodBase method) { _currentType.addMember(method); method.startPosition(node.getStartPosition()); method.isVarArgs(node.isVarargs()); mapParameters(node, method); mapAnnotations(node, method); mapDocumentation(node, method); visitBodyDeclarationBlock(node, node.getBody(), method); IMethodBinding overriden = getOverridedMethod(node); if (overriden != null) { CSVisibility vis = mapVisibility(overriden.getModifiers()); if (vis == CSVisibility.ProtectedInternal && !overriden.getDeclaringClass().isFromSource()) vis = CSVisibility.Protected; method.visibility(vis); } else if (node.resolveBinding().getDeclaringClass().isInterface()) method.visibility(CSVisibility.Public); else mapVisibility(node, method); } private String mappedMethodDeclarationName(MethodDeclaration node) { final String mappedName = mappedMethodName(node); if (null == mappedName || 0 == mappedName.length()) { return methodName(node.getName().toString()); } else if (mappedName.contains(".")) { int lastDot = mappedName.lastIndexOf('.'); return mappedName.substring(lastDot + 1); } 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) { if (method instanceof CSMethod) { IVariableBinding vb = parameter.resolveBinding(); ITypeBinding[] ta = vb.getType().getTypeArguments(); if (ta.length > 0 && ta[0].getName().startsWith("?")) { ITypeBinding extended = mapTypeParameterExtendedType(ta[0]); CSMethod met = (CSMethod) method; String genericArg = "_T" + met.typeParameters().size(); CSTypeParameter tp = new CSTypeParameter(genericArg); if (extended != null) tp.superClass(mappedTypeReference(extended)); met.addTypeParameter(tp); CSTypeReference tr = new CSTypeReference(mappedTypeName(vb.getType())); tr.addTypeArgument(new CSTypeReference(genericArg)); method.addParameter(new CSVariableDeclaration(identifier(vb.getName()), tr)); return; } } method.addParameter(createParameter(parameter)); } ITypeBinding mapTypeParameterExtendedType(ITypeBinding tb) { ITypeBinding superc = tb.getSuperclass(); if (superc != null && !superc.getQualifiedName().equals("java.lang.Object") && !superc.getQualifiedName().equals("java.lang.Enum<?>")) { return superc; } ITypeBinding[] ints = tb.getInterfaces(); if (ints.length > 0) return ints[0]; return null; } private void mapMethodParameters(MethodDeclaration node, CSMethod method) { 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) { IMethodBinding overriden = getOverridedMethod(node); if (overriden != null) return mappedTypeReference(overriden.getReturnType()); return mappedTypeReference(node.getReturnType2()); } private void processEventDeclaration(MethodDeclaration node) { CSTypeReference eventHandlerType = new CSTypeReference(getEventHandlerTypeName(node)); CSEvent event = createEventFromMethod(node, eventHandlerType); mapMetaMemberAttributes(node, event); if (_currentType.isInterface()) return; VariableDeclarationFragment field = getEventBackingField(node); CSField backingField = (CSField) _currentType.getMember(field.getName().toString()); backingField.type(eventHandlerType); // clean field backingField.initializer(null); backingField.removeModifier(CSFieldModifier.Readonly); final CSBlock addBlock = createEventBlock(backingField, "System.Delegate.Combine"); String onAddMethod = getEventOnAddMethod(node); if (onAddMethod != null) { addBlock.addStatement(new CSMethodInvocationExpression(new CSReferenceExpression(onAddMethod))); } event.setAddBlock(addBlock); event.setRemoveBlock(createEventBlock(backingField, "System.Delegate.Remove")); } private String getEventOnAddMethod(MethodDeclaration node) { final TagElement onAddTag = javadocTagFor(node, SharpenAnnotations.SHARPEN_EVENT_ON_ADD); if (null == onAddTag) return null; return methodName(JavadocUtility.singleTextFragmentFrom(onAddTag)); } private String getEventHandlerTypeName(MethodDeclaration node) { final String eventArgsType = getEventArgsType(node); return buildEventHandlerTypeName(node, eventArgsType); } private void mapMetaMemberAttributes(MethodDeclaration node, CSMetaMember metaMember) { 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(); } @Override public boolean visit(AssertStatement node) { Expression expression = node.getExpression(); _compilationUnit.addUsing(new CSUsing("System.Diagnostics")); CSTypeReferenceExpression debugClassRef = new CSTypeReference("Debug"); CSMemberReferenceExpression debugAssertMethodRef = new CSMemberReferenceExpression(debugClassRef, "Assert"); CSMethodInvocationExpression debugAssertMethodInvocation = new CSMethodInvocationExpression( debugAssertMethodRef, mapExpression(expression)); if (node.getMessage() != null) debugAssertMethodInvocation.addArgument(mapExpression(node.getMessage())); _currentBlock.addStatement(debugAssertMethodInvocation); return false; } 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) { CSTypeReferenceExpression mappedTypeReference = mappedTypeReference(node.getType()); int extraDimensions = Integer.MAX_VALUE; for (Object f : node.fragments()) { VariableDeclarationFragment variable = (VariableDeclarationFragment) f; int ved = variable.getExtraDimensions(); if (ved < extraDimensions) extraDimensions = ved; } if (extraDimensions > 0 && extraDimensions < Integer.MAX_VALUE) { if (mappedTypeReference instanceof CSArrayTypeReference) { CSArrayTypeReference arrayTypeReference = (CSArrayTypeReference) mappedTypeReference; mappedTypeReference = new CSArrayTypeReference(arrayTypeReference.elementType(), arrayTypeReference.dimensions() + extraDimensions); } else { mappedTypeReference = new CSArrayTypeReference(mappedTypeReference, extraDimensions); } } CSDeclarationExpression expr = new CSDeclarationExpression(mappedTypeReference); for (Object f : node.fragments()) { VariableDeclarationFragment variable = (VariableDeclarationFragment) f; if (variable.getExtraDimensions() != extraDimensions) warning(variable, "Variable has unsupported extra dimensions"); expr.addFragment(formatVariableName(variable.resolveBinding()), mapExpression(variable.getInitializer())); } pushExpression(expr); 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(); ITypeBinding saved = pushExpectedType(binding.getType()); CSExpression initializer = mapExpression(variable.getInitializer()); popExpectedType(saved); return createVariableDeclaration(binding, initializer); } private CSVariableDeclaration createVariableDeclaration(IVariableBinding binding, CSExpression initializer) { return new CSVariableDeclaration(formatVariableName(binding), mappedTypeReference(binding.getType()), initializer); } private String formatVariableName(IVariableBinding binding) { String name = binding.getName(); if (_blockVariables.size() > 0) { if (_blockVariables.peek().contains(name)) { int count = 1; while (_blockVariables.peek().contains(name + "_" + count)) { count++; } _renamedVariables.peek().put(name, name + "_" + count); name = name + "_" + count; } _localBlockVariables.peek().add(name); for (Set<String> s : _blockVariables) s.add(name); } return identifier(name); } public boolean visit(ExpressionStatement node) { if (isRemovedMethodInvocation(node.getExpression())) { return false; } addStatement(new CSExpressionStatement(node.getStartPosition(), mapExpression(node.getExpression()))); return false; } private boolean isRemovedMethodInvocation(Expression expression) { if (!(expression instanceof MethodInvocation)) { return false; } MethodInvocation invocation = (MethodInvocation) expression; return isTaggedMethodInvocation(invocation, SharpenAnnotations.SHARPEN_REMOVE) || isRemoved(invocation.resolveMethodBinding()); } public boolean isEnumOrdinalMethodInvocation(MethodInvocation node) { return node.getName().getIdentifier().equals("ordinal") && node.getExpression() != null && node.getExpression().resolveTypeBinding().isEnum(); } public boolean isEnumNameMethodInvocation(MethodInvocation node) { return node.getName().getIdentifier().equals("name") && node.getExpression() != null && node.getExpression().resolveTypeBinding().isEnum(); } public boolean 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); // The exception variable is declared in a new scope pushScope(); 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; popScope(); } } 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(NumberLiteral node) { String token = node.getToken(); if (token.endsWith(".")) token = token + "0"; CSExpression literal = new CSNumberLiteralExpression(token); if (expectingType("byte") && token.startsWith("-")) { literal = uncheckedCast("byte", literal); } else if (token.startsWith("0x")) { if (token.endsWith("l") || token.endsWith("L")) { literal = uncheckedCast("long", literal); } else { literal = uncheckedCast("int", literal); } } else if (token.startsWith("0") && token.indexOf('.') == -1 && Character.isDigit(token.charAt(token.length() - 1))) { try { int n = Integer.parseInt(token, 8); if (n != 0) literal = new CSNumberLiteralExpression("0x" + Integer.toHexString(n)); } catch (NumberFormatException ex) { } } 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) { if (my(Configuration.class).mapEmptyStringLiteral()) pushExpression(new CSReferenceExpression("string.Empty")); else pushExpression(new CSStringLiteralExpression("\"\"")); } else { pushExpression(new CSStringLiteralExpression(convertStringLiteral(value))); } return false; } private static String convertStringLiteral(String stringLiteral) { StringBuilder sb = new StringBuilder(); sb.append('"'); if (needsEscaping(stringLiteral)) { for (int i = 0; i < stringLiteral.length(); i++) { String s = convertStringChar(stringLiteral.charAt(i)); if (s == null) sb.append(stringLiteral.charAt(i)); else sb.append(s); } } else { sb.append(stringLiteral); } sb.append('"'); return sb.toString(); } private static boolean needsEscaping(String s) { for (int i = 0; i < s.length(); i++) if (!isSafeChar(s.charAt(i))) return true; return false; } private static boolean isSafeChar(char c) { if (c >= 32 && c < 256 && isPrintableChar(c)) { switch (c) { case '\\': case '"': return false; default: return true; } } return false; } private static String convertStringChar(char c) { switch (c) { case 0: return "\\0"; case '\t': return "\\t"; case '\n': return "\\n"; case '\r': return "\\r"; case '\b': return "\\b"; case '\f': return "\\f"; case '\\': return "\\\\"; case '"': return "\\\""; default: if (c >= 32 && c < 256 && isPrintableChar(c)) return null; else return "\\u" + String.format("%04x", (int) c); } } public boolean visit(CharacterLiteral node) { CSExpression expr = new CSCharLiteralExpression(convertCharLiteral(node.charValue())); if (expectingType("byte")) { expr = new CSCastExpression(new CSTypeReference("byte"), new CSParenthesizedExpression(expr)); } pushExpression(expr); return false; } private static String convertCharLiteral(char charLiteral) { switch (charLiteral) { case 0: return "\'\\0\'"; case '\t': return "\'\\t\'"; case '\n': return "\'\\n\'"; case '\r': return "\'\\r\'"; case '\b': return "\'\\b\'"; case '\f': return "\'\\f\'"; case '\\': return "\'\\\\\'"; case '\'': return "\'\\'\'"; default: String escapedValue; if (charLiteral >= 32 && charLiteral < 256 && isPrintableChar(charLiteral)) escapedValue = String.valueOf(charLiteral); else escapedValue = "\\x" + String.format("%x", (int) charLiteral); return "\'" + escapedValue + "\'"; } } public static boolean isPrintableChar(char c) { Character.UnicodeBlock block = Character.UnicodeBlock.of(c); return (!Character.isISOControl(c)) && c != java.awt.event.KeyEvent.CHAR_UNDEFINED && block != null && block != Character.UnicodeBlock.SPECIALS; } private boolean expectingType(String name) { return (_currentExpectedType != null && _currentExpectedType.getName().equals(name)); } 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) { ITypeBinding saved = pushExpectedType(node.getType().getElementType().resolveBinding()); if (node.dimensions().size() > 1) { if (null != node.getInitializer()) { notImplemented(node); } pushExpression(unfoldMultiArrayCreation(node)); } else { pushExpression(mapSingleArrayCreation(node)); } popExpectedType(saved); 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 CSExpression unfoldMultiArrayCreation(ArrayCreation node) { boolean allDimsConst = true; for (int i = 0; i < node.dimensions().size() - 1; ++i) { if (getConstantDimensionExpression(node.dimensions().get(i)) == null) { allDimsConst = false; break; } } if (allDimsConst) { return unfoldMultiArray((ArrayType) node.getType().getComponentType(), node.dimensions(), 0); } else { return createMultiArray(node); } } 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 CSExpression createMultiArray(ArrayCreation node) { Type type = node.getType().getElementType(); String statement = String.format("Arrays.Create<%s>(%s", mappedTypeReference(type), node.dimensions().get(0)); for (int i = 1; i < node.dimensions().size(); ++i) { statement = String.format("%s, %s", statement, node.dimensions().get(i)); } statement = statement.concat(")"); return new CSStringLiteralExpression(statement); } private Object getConstantDimensionExpression(Object expression) { return ((Expression) expression).resolveConstantExpressionValue(); } private int resolveIntValue(Object expression) { return ((Number) getConstantDimensionExpression(expression)).intValue(); } private int lastIndex(List<?> dimensions) { return dimensions.size() - 1; } 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())); ITypeBinding saved = pushExpectedType(node.resolveTypeBinding().getElementType()); ace.initializer(mapArrayInitializer(node)); popExpectedType(saved); 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() { ArrayList<CSExpression> initializers = new ArrayList<CSExpression>(); for (Object i : node.initializers()) { initializers.add(mapExpression((Expression) i)); } CSForStatement stmt = new CSForStatement(node.getStartPosition(), mapExpression(node.getExpression())); for (CSExpression i : initializers) { stmt.addInitializer(i); } for (Object u : node.updaters()) { stmt.addUpdater(mapExpression((Expression) u)); } visitBlock(stmt.body(), node.getBody()); 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; ITypeBinding switchType = node.getExpression().resolveTypeBinding(); CSSwitchStatement mappedNode = new CSSwitchStatement(node.getStartPosition(), mapExpression(node.getExpression())); addStatement(mappedNode); CSCaseClause defaultClause = null; CSCaseClause current = null; CSBlock openCaseBlock = null; _currentBlock = null; for (ASTNode element : Types.<Iterable<ASTNode>>cast(node.statements())) { if (ASTNode.SWITCH_CASE == element.getNodeType()) { if (null == current) { if (_currentBlock != null) { List<CSStatement> stats = _currentBlock.statements(); CSStatement lastStmt = stats.size() > 0 ? stats.get(stats.size() - 1) : null; if (!(lastStmt instanceof CSThrowStatement) && !(lastStmt instanceof CSReturnStatement) && !(lastStmt instanceof CSBreakStatement) && !(lastStmt instanceof CSGotoStatement)) openCaseBlock = _currentBlock; } current = new CSCaseClause(); mappedNode.addCase(current); _currentBlock = current.body(); } SwitchCase sc = (SwitchCase) element; if (sc.isDefault()) { defaultClause = current; current.isDefault(true); if (openCaseBlock != null) openCaseBlock.addStatement(new CSGotoStatement(Integer.MIN_VALUE, "default")); } else { ITypeBinding stype = pushExpectedType(switchType); CSExpression caseExpression = mapExpression(sc.getExpression()); current.addExpression(caseExpression); popExpectedType(stype); if (openCaseBlock != null) openCaseBlock.addStatement(new CSGotoStatement(Integer.MIN_VALUE, caseExpression)); } openCaseBlock = null; } else { element.accept(this); current = null; } } if (openCaseBlock != null) openCaseBlock.addStatement(new CSBreakStatement(Integer.MIN_VALUE)); 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()))); // Make all byte casts unchecked if (node.getType().resolveBinding().getName().equals("byte")) pushExpression(new CSUncheckedExpression(popExpression())); return false; } public boolean visit(PrefixExpression node) { CSExpression expr; expr = new CSPrefixExpression(node.getOperator().toString(), mapExpression(node.getOperand())); if (expectingType("byte") && node.getOperator() == PrefixExpression.Operator.MINUS) { expr = uncheckedCast("byte", expr); } pushExpression(expr); return false; } 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()); String type = node.getLeftOperand().resolveTypeBinding().getQualifiedName(); if (node.getOperator() == InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED) { if (type.equals("byte")) { pushExpression(new CSInfixExpression(">>", left, right)); } else { CSExpression cast = new CSCastExpression(new CSTypeReference("u" + type), left); cast = new CSParenthesizedExpression(cast); CSExpression shiftResult = new CSInfixExpression(">>", cast, right); shiftResult = new CSParenthesizedExpression(shiftResult); pushExpression(new CSCastExpression(new CSTypeReference(type), shiftResult)); } return false; } if (type.equals("byte") && (node.getOperator() == InfixExpression.Operator.LESS || node.getOperator() == InfixExpression.Operator.LESS_EQUALS)) { left = new CSCastExpression(new CSTypeReference("sbyte"), left); left = new CSParenthesizedExpression(left); } String operator = node.getOperator().toString(); pushExpression(new CSInfixExpression(operator, left, right)); pushExtendedOperands(operator, node); return false; } private void pushExtendedOperands(String operator, InfixExpression node) { for (Object x : node.extendedOperands()) { pushExpression(new CSInfixExpression(operator, popExpression(), mapExpression((Expression) x))); } } 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(); Expression rhs = node.getRightHandSide(); ITypeBinding lhsType = lhs.resolveTypeBinding(); ITypeBinding saved = pushExpectedType(lhsType); if (node.getOperator() == Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN) { String type = lhsType.getQualifiedName(); if (type == "byte") { pushExpression(new CSInfixExpression(">>", mapExpression(lhs), mapExpression(lhs.resolveTypeBinding(), rhs))); } else { CSExpression mappedLhs = mapExpression(lhs); CSExpression cast = new CSCastExpression(new CSTypeReference("u" + type), mappedLhs); cast = new CSParenthesizedExpression(cast); CSExpression shiftResult = new CSInfixExpression(">>", cast, mapExpression(rhs)); shiftResult = new CSParenthesizedExpression(shiftResult); shiftResult = new CSCastExpression(new CSTypeReference(type), shiftResult); pushExpression(new CSInfixExpression("=", mappedLhs, shiftResult)); } } else { pushExpression(new CSInfixExpression(node.getOperator().toString(), mapExpression(lhs), mapExpression(lhs.resolveTypeBinding(), rhs))); } popExpectedType(saved); return false; } private CSExpression mapExpression(ITypeBinding expectedType, Expression expression) { if (expectedType != null) return castIfNeeded(expectedType, expression.resolveTypeBinding(), mapExpression(expression)); else return mapExpression(expression); } private CSExpression castIfNeeded(ITypeBinding expectedType, ITypeBinding actualType, CSExpression expression) { if (!_configuration.mapIteratorToEnumerator() && expectedType.getName().startsWith("Iterable<") && isGenericCollection(actualType)) { return new CSMethodInvocationExpression(new CSMemberReferenceExpression(expression, "AsIterable")); } if (expectedType != actualType && isSubclassOf(expectedType, actualType)) return new CSCastExpression(mappedTypeReference(expectedType), expression); ITypeBinding charType = resolveWellKnownType("char"); if (expectedType != charType) return expression; if (actualType == expectedType) return expression; return new CSCastExpression(mappedTypeReference(expectedType), expression); } private boolean isGenericCollection(ITypeBinding t) { return t.getName().startsWith("List<") || t.getName().startsWith("Set<"); } private boolean isSubclassOf(ITypeBinding t, ITypeBinding tbase) { while (t != null) { if (t.isEqualTo(tbase)) return true; t = t.getSuperclass(); } return false; } 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; } if (isEnumOrdinalMethodInvocation(node)) { processEnumOrdinalMethodInvocation(node); return; } if (isEnumNameMethodInvocation(node)) { processEnumNameMethodInvocation(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) { IMethodBinding method = node.resolveMethodBinding(); CSExpression targetExpression = mapMethodTargetExpression(node); if ((method.getModifiers() & Modifier.STATIC) != 0 && !(targetExpression instanceof CSTypeReferenceExpression) && node.getExpression() != null) targetExpression = mappedTypeReference(node.getExpression().resolveTypeBinding()); String name = resolveTargetMethodName(targetExpression, node); CSExpression target = null == targetExpression ? new CSReferenceExpression(name) : new CSMemberReferenceExpression(targetExpression, name); CSMethodInvocationExpression mie = new CSMethodInvocationExpression(target); mapMethodInvocationArguments(mie, node); mapTypeArguments(mie, node); IMethodBinding base = getOverridedMethod(method); if (base != null && base.getReturnType() != method.getReturnType() && !(node.getParent() instanceof ExpressionStatement)) pushExpression(new CSParenthesizedExpression( new CSCastExpression(mappedTypeReference(method.getReturnType()), mie))); else pushExpression(mie); } private String resolveTargetMethodName(CSExpression targetExpression, MethodInvocation node) { final IMethodBinding method = staticImportMethodBinding(node.getName(), _ast.imports()); if (method != null && targetExpression == null) { return mappedTypeName(method.getDeclaringClass()) + "." + mappedMethodName(node.resolveMethodBinding()); } return mappedMethodName(node.resolveMethodBinding()); } private void mapTypeArguments(CSMethodInvocationExpression mie, MethodInvocation node) { for (Object o : node.typeArguments()) { mie.addTypeArgument(mappedTypeReference((Type) o)); } } private void processMappedEventSubscription(MethodInvocation node) { final MethodInvocation event = (MethodInvocation) node.getExpression(); final String eventArgsType = _configuration.mappedEvent(qualifiedName(event)); final String eventHandlerType = buildEventHandlerTypeName(node, eventArgsType); mapEventSubscription(node, eventArgsType, eventHandlerType); } private void processRemovedInvocation(MethodInvocation node) { TagElement element = javadocTagFor(declaringNode(node.resolveMethodBinding()), SharpenAnnotations.SHARPEN_REMOVE); String exchangeValue = JavadocUtility.singleTextFragmentFrom(element); pushExpression(new CSReferenceExpression(exchangeValue)); } private void processEnumOrdinalMethodInvocation(MethodInvocation node) { CSExpression exp = mapExpression(node.getExpression()); pushExpression(new CSCastExpression(new CSTypeReference("int"), new CSParenthesizedExpression(exp))); } private void processEnumNameMethodInvocation(MethodInvocation node) { CSExpression exp = mapExpression(node.getExpression()); pushExpression(new CSMethodInvocationExpression(new CSMemberReferenceExpression(exp, "ToString"))); } private void 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, i < actualTypes.length ? actualTypes[i] : null); } } adjustJUnitArguments(mie, node); } private void adjustJUnitArguments(CSMethodInvocationExpression mie, MethodInvocation node) { if (!_configuration.junitConversion()) return; ITypeBinding t = node.resolveMethodBinding().getDeclaringClass(); if (t.getQualifiedName().equals("junit.framework.Assert") || t.getQualifiedName().equals("org.junit.Assert")) { String method = node.getName().getIdentifier(); int np = -1; if (method.equals("assertTrue") || method.equals("assertFalse") || method.equals("assertNull") || method.equals("assertNotNull")) np = 1; else if (method.equals("fail")) np = 0; else if (method.startsWith("assert")) np = 2; if (np == -1) return; if (mie.arguments().size() == np + 1) { // Move the comment argument to the end mie.addArgument(mie.arguments().get(0)); mie.removeArgument(0); } if (method.equals("assertSame")) { boolean useEquals = false; final List arguments = node.arguments(); for (int i = 0; i < arguments.size(); ++i) { final Expression arg = (Expression) arguments.get(i); ITypeBinding b = arg.resolveTypeBinding(); if (b.isEnum()) { useEquals = true; break; } } if (useEquals) { CSReferenceExpression mref = (CSReferenceExpression) mie.expression(); mref.name("NUnit.Framework.Assert.AreEqual"); } } } } private boolean isClassLiteral(Expression arg) { return arg.getNodeType() == ASTNode.TYPE_LITERAL; } private void processEventSubscription(MethodInvocation node) { final MethodDeclaration addListener = declaringNode(node.resolveMethodBinding()); assertValidEventAddListener(node, addListener); final MethodInvocation eventInvocation = (MethodInvocation) node.getExpression(); final MethodDeclaration eventDeclaration = declaringNode(eventInvocation.resolveMethodBinding()); mapEventSubscription(node, getEventArgsType(eventDeclaration), getEventHandlerTypeName(eventDeclaration)); } private void mapEventSubscription(MethodInvocation node, final String eventArgsType, final String eventHandlerType) { final CSAnonymousClassBuilder listenerBuilder = mapAnonymousEventListener(node); final CSMemberReferenceExpression handlerMethodRef = new CSMemberReferenceExpression( listenerBuilder.createConstructorInvocation(), eventListenerMethodName(listenerBuilder)); final CSReferenceExpression delegateType = new CSReferenceExpression(eventHandlerType); patchEventListener(listenerBuilder, eventArgsType); CSConstructorInvocationExpression delegateConstruction = new CSConstructorInvocationExpression( delegateType); delegateConstruction.addArgument(handlerMethodRef); pushExpression(new CSInfixExpression("+=", mapMethodTargetExpression(node), delegateConstruction)); } private CSAnonymousClassBuilder mapAnonymousEventListener(MethodInvocation node) { ClassInstanceCreation creation = (ClassInstanceCreation) node.arguments().get(0); return mapAnonymousClass(creation.getAnonymousClassDeclaration()); } private String eventListenerMethodName(final CSAnonymousClassBuilder listenerBuilder) { return mappedMethodName(getFirstMethod(listenerBuilder.anonymousBaseType())); } private void patchEventListener(CSAnonymousClassBuilder listenerBuilder, String eventArgsType) { final CSClass type = listenerBuilder.type(); type.clearBaseTypes(); final CSMethod handlerMethod = (CSMethod) type.getMember(eventListenerMethodName(listenerBuilder)); handlerMethod.parameters().get(0).type(OBJECT_TYPE_REFERENCE); handlerMethod.parameters().get(0).name("sender"); handlerMethod.parameters().get(1).type(new CSTypeReference(eventArgsType)); } private IMethodBinding getFirstMethod(ITypeBinding listenerType) { return listenerType.getDeclaredMethods()[0]; } private void assertValidEventAddListener(ASTNode source, MethodDeclaration addListener) { if (isValidEventAddListener(addListener)) return; unsupportedConstruct(source, SharpenAnnotations.SHARPEN_EVENT_ADD + " must take lone single method interface argument"); } private boolean isValidEventAddListener(MethodDeclaration addListener) { if (1 != addListener.parameters().size()) return false; final ITypeBinding type = getFirstParameterType(addListener); if (!type.isInterface()) return false; return type.getDeclaredMethods().length == 1; } private ITypeBinding getFirstParameterType(MethodDeclaration addListener) { return parameter(addListener, 0).getType().resolveBinding(); } private SingleVariableDeclaration parameter(MethodDeclaration method, final int index) { return (SingleVariableDeclaration) method.parameters().get(index); } private boolean isEventSubscription(MethodInvocation node) { return isTaggedMethodInvocation(node, SharpenAnnotations.SHARPEN_EVENT_ADD); } private boolean isMappedEventSubscription(MethodInvocation node) { return _configuration.isMappedEventAdd(qualifiedName(node)); } private String qualifiedName(MethodInvocation node) { return qualifiedName(node.resolveMethodBinding()); } private boolean isTaggedMethodInvocation(MethodInvocation node, final String tag) { return isTaggedMethodInvocation(node.resolveMethodBinding(), tag); } private boolean isTaggedMethodInvocation(final IMethodBinding binding, final String tag) { final MethodDeclaration method = declaringNode(originalMethodBinding(binding)); if (null == method) { return false; } return containsJavadoc(method, tag); } @SuppressWarnings("unchecked") private void processMappedMethodInvocation(MethodInvocation node, IMethodBinding binding, Configuration.MemberMapping mapping) { if (mapping.kind == MemberKind.Indexer) { processIndexerInvocation(node, binding, 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) { IMethodBinding originalBinding = node.resolveMethodBinding(); if (binding != originalBinding && originalBinding.getReturnType() != binding.getReturnType() && !(node.getParent() instanceof ExpressionStatement)) target = new CSParenthesizedExpression( new CSCastExpression(mappedTypeReference(originalBinding.getReturnType()), target)); switch (arguments.size()) { case 0: pushExpression(target); break; case 1: pushExpression(new CSInfixExpression("=", target, mapExpression(arguments.get(0)))); break; default: unsupportedConstruct(node, "Method invocation with more than 1 argument mapped to property"); break; } return; } CSMethodInvocationExpression mie = new CSMethodInvocationExpression(target); if (isMappingToStaticMethod && isInstanceMethod(binding)) { if (null == expression) { mie.addArgument(new CSThisExpression()); } else { mie.addArgument(expression); } } mapArguments(mie, arguments); adjustJUnitArguments(mie, node); 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, null); } } private void addArgument(CSMethodInvocationExpression mie, Expression arg, ITypeBinding expectedType) { mie.addArgument(mapExpression(expectedType, 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; } String mapVariableName(String name) { if (_renamedVariables.size() > 0) { String vname = name; if (vname.startsWith("@")) vname = vname.substring(1); String newName = _renamedVariables.peek().get(vname); if (newName != null) return newName; } return name; } 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 if (_currentExpression == null) { String ident = mappedFieldName(node); if (ident == null) ident = mapVariableName(identifier(node)); IBinding b = node.resolveBinding(); IVariableBinding vb = b instanceof IVariableBinding ? (IVariableBinding) b : null; if (vb != null) { ITypeBinding cls = vb.getDeclaringClass(); if (cls != null) { if (isStaticImport(vb, _ast.imports())) { if (cls != null) { pushExpression(new CSMemberReferenceExpression(mappedTypeReference(cls), ident)); return false; } } else if (cls.isEnum() && ident.indexOf('.') == -1) { pushExpression(new CSMemberReferenceExpression(mappedTypeReference(cls), ident)); return false; } else if (cls.isInterface()) { CSTypeReferenceExpression interfaceReference = mappedTypeReference(cls, true); pushExpression(new CSMemberReferenceExpression(interfaceReference, ident)); return false; } } } pushExpression(new CSReferenceExpression(ident)); } return false; } private void addStatement(CSStatement statement) { _currentBlock.addStatement(statement); } private void pushTypeReference(ITypeBinding typeBinding) { pushExpression(mappedTypeReference(typeBinding)); } protected CSReferenceExpression createTypeReference(ITypeBinding typeBinding) { return new CSReferenceExpression(mappedTypeName(typeBinding)); } private boolean isTypeReference(Name node) { final IBinding binding = node.resolveBinding(); if (null == binding) { unresolvedTypeBinding(node); return false; } return IBinding.TYPE == binding.getKind(); } 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) { CSExpression expr = mapExpression(qualifier); if (expr instanceof CSTypeReference) { String typeName = ((CSTypeReference) expr).typeName(); String fixedTypeName = interfaceStaticsClassName(typeName, true, qualifier.resolveTypeBinding()); if (!fixedTypeName.equals(typeName)) { expr = new CSTypeReference(fixedTypeName); } } pushExpression(new CSMemberReferenceExpression(expr, 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); } private ITypeBinding pushExpectedType(ITypeBinding type) { ITypeBinding old = _currentExpectedType; _currentExpectedType = type; return old; } private void popExpectedType(ITypeBinding saved) { _currentExpectedType = saved; } 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() || method.resolveBinding().getDeclaringClass().isInterface()) { return CSMethodModifier.Abstract; } int modifiers = method.getModifiers(); if (Modifier.isStatic(modifiers)) { return CSMethodModifier.Static; } if (Modifier.isPrivate(modifiers)) { return CSMethodModifier.None; } boolean override = isOverride(method); if (Modifier.isAbstract(modifiers)) { return override ? CSMethodModifier.AbstractOverride : CSMethodModifier.Abstract; } boolean isFinal = Modifier.isFinal(modifiers); if (override) { return isFinal ? CSMethodModifier.Sealed : modifierIfNewAnnotationNotApplied(method, CSMethodModifier.Override); } return isFinal || _currentType.isSealed() ? CSMethodModifier.None : CSMethodModifier.Virtual; } private CSMethodModifier modifierIfNewAnnotationNotApplied(MethodDeclaration method, CSMethodModifier modifier) { return containsJavadoc(method, SharpenAnnotations.SHARPEN_NEW) ? CSMethodModifier.None : modifier; } private boolean isExtractedNestedType(ITypeBinding type) { return _configuration.typeHasMapping(BindingUtils.typeMappingKey(type)); } private boolean isOverride(MethodDeclaration method) { return null != getOverridedMethod(method); } private IMethodBinding getOverridedMethod(MethodDeclaration method) { return getOverridedMethod(method.resolveBinding()); } private IMethodBinding getOverridedMethod(IMethodBinding methodBinding) { ITypeBinding superclass = _ignoreExtends.value() ? resolveWellKnownType("java.lang.Object") : methodBinding.getDeclaringClass().getSuperclass(); if (null != superclass) { IMethodBinding result = BindingUtils.findOverriddenMethodInHierarchy(superclass, methodBinding); if (null != result) return result; } ITypeBinding[] baseInterfaces = methodBinding.getDeclaringClass().getInterfaces(); if (baseInterfaces.length == 1 && !isValidCSInterface(baseInterfaces[0])) { // Base interface generated as a class return BindingUtils.findOverriddenMethodInType(baseInterfaces[0], methodBinding); } return null; } private boolean isValidCSInterface(ITypeBinding type) { if (type.getTypeDeclaration().getQualifiedName().equals("java.util.Iterator") || type.getTypeDeclaration().getQualifiedName().equals("java.lang.Iterable")) return false; return true; } CSClassModifier mapClassModifier(int modifiers) { if (Modifier.isAbstract(modifiers)) { return CSClassModifier.Abstract; } if (Modifier.isFinal(modifiers)) { return CSClassModifier.Sealed; } return CSClassModifier.None; } void adjustVisibility(ITypeBinding memberType, CSMember member) { if (memberType == null) return; CSVisibility typeVisibility = mapVisibility(memberType.getModifiers()); if (typeVisibility == CSVisibility.Protected && member.visibility() == CSVisibility.Internal) member.visibility(CSVisibility.Protected); } 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) { return mappedTypeReference(type, false); } protected CSTypeReferenceExpression mappedTypeReference(ITypeBinding type, boolean mapInterfaceStaticsClass) { 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); } String mappedName = mappedTypeName(type); mappedName = interfaceStaticsClassName(mappedName, mapInterfaceStaticsClass, type); final CSTypeReference typeRef = new CSTypeReference(mappedName); if (isJavaLangClass(type)) { return typeRef; } for (ITypeBinding arg : type.getTypeArguments()) { typeRef.addTypeArgument(mappedTypeReference(arg)); } return typeRef; } private String interfaceStaticsClassName(String typeName, boolean modifyLastPart, ITypeBinding type) { if (!modifyLastPart && typeName.indexOf('.') < 0) { return typeName; } else { String[] parts = typeName.split("\\."); int start = parts.length - 1; if (!modifyLastPart) { start--; type = type.getDeclaringClass(); } for (int i = start; i >= 0; i--) { if (type != null && type.isInterface()) { type = type.getDeclaringClass(); parts[i] = interfaceStaticsClassNameGen(parts[i]); } } StringBuilder sb = new StringBuilder(); boolean addDot = false; for (String name : parts) { if (addDot) sb.append('.'); else addDot = true; sb.append(name); } return sb.toString(); } } private String interfaceStaticsClassNameGen(String name) { if (name.length() > 2 && name.charAt(0) == 'I' && Character.isUpperCase(name.charAt(1))) name = name.substring(1); String suffix = "Constants"; if (!name.endsWith(suffix)) return name + suffix; else return name; } 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) { if (isBlockInsideBlock(node)) { CSBlock parent = _currentBlock; _currentBlock = new CSBlock(); _currentBlock.parent(parent); parent.addStatement(_currentBlock); } _currentContinueLabel = null; pushScope(); return super.visit(node); } @Override public void endVisit(Block node) { if (isBlockInsideBlock(node)) { _currentBlock = (CSBlock) _currentBlock.parent(); } popScope(); super.endVisit(node); } boolean isBlockInsideBlock(Block node) { return node.getParent() instanceof Block; } void pushScope() { HashSet<String> newLocalVars = new HashSet<String>(); if (_localBlockVariables.size() > 0) newLocalVars.addAll(_localBlockVariables.peek()); _localBlockVariables.push(newLocalVars); HashSet<String> newBlockVars = new HashSet<String>(); newBlockVars.addAll(newLocalVars); _blockVariables.push(newBlockVars); HashMap<String, String> newRenamed = new HashMap<String, String>(); if (_renamedVariables.size() > 0) newRenamed.putAll(_renamedVariables.peek()); _renamedVariables.push(newRenamed); } void popScope() { _blockVariables.pop(); _localBlockVariables.pop(); _renamedVariables.pop(); } }