org.juniversal.translator.core.ASTUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.juniversal.translator.core.ASTUtil.java

Source

/*
 * Copyright (c) 2012-2015, Microsoft Mobile
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package org.juniversal.translator.core;

import org.eclipse.jdt.core.dom.*;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class ASTUtil {

    public static void parseJava(List<File> javaProjectDirectories) {

        /*
          ArrayList<File> files = new ArrayList<File>();
        for (File javaProjectDirectory : javaProjectDirectories)
           Util.getFilesRecursive(javaProjectDirectory, ".java", files);
            
        ArrayList<String> filePathsList = new ArrayList<String>();
        for (File file : files) {
           filePathsList.add(file.getPath());
        }
            
        String[] filePaths = filePathsList.toArray(new String[filePathsList.size()]);
            
        ASTParser parser = ASTParser.newParser(AST.JLS3);
        parser.setKind(ASTParser.K_COMPILATION_UNIT);
            
        parser.createASTs(filePaths, encodings, bindingKeys, requestor, monitor)
            
        String source = readFile(args[0]);
        parser.setSource(source.toCharArray());
            
        CompilationUnit compilationUnit = (CompilationUnit) parser.createAST(null /xx* IProgressMonitor *xx/);
            
        TypeDeclaration typeDeclaration = ASTUtil.getFirstTypeDeclaration(compilationUnit);
            
        StringWriter writer = new StringWriter();
        CPPProfile profile = new CPPProfile();
        // profile.setTabStop(4);
            
        CPPWriter cppWriter = new CPPWriter(writer, profile);
            
        Context context = new Context((CompilationUnit) compilationUnit.getRoot(), source, 8, profile, cppWriter,
        OutputType.SOURCE);
            
        context.setPosition(typeDeclaration.getStartPosition());
            
        ASTWriters astWriters = new ASTWriters();
            
        try {
           context.setPosition(typeDeclaration.getStartPosition());
           skipSpaceAndComments();
            
           astWriters.writeNode(typeDeclaration, context);
        } catch (UserViewableException e) {
           System.err.println(e.getMessage());
           System.exit(1);
        } catch (RuntimeException e) {
           if (e instanceof ContextPositionMismatchException)
        throw e;
           else
        throw new JUniversalException(e.getMessage() + "\nError occurred with context at position\n"
              + context.getPositionDescription(context.getPosition()), e);
        }
            
        String cppOutput = writer.getBuffer().toString();
            
        System.out.println("Output:");
        System.out.println(cppOutput);
        */

    }

    public static TypeDeclaration getFirstTypeDeclaration(CompilationUnit compilationUnit) {
        return (TypeDeclaration) compilationUnit.types().get(0);
    }

    public static Block getFirstMethodBlock(CompilationUnit compilationUnit) {
        return getFirstTypeDeclaration(compilationUnit).getMethods()[0].getBody();
    }

    /**
     * Return true is the specified modifier is an @Nullable annotation.   Currently we don't care about, nor even
     * support, fully qualified names for the @Nullable annotation.   Any non-qualified name that's @Nullable counts
     * here, but fully names currently never do currently.   The intention is that the programmer can pick between
     * several different @Nullable annotations, though JSimple code currently uses the IntelliJ one.
     *
     * @param extendedModifier modifier in question
     * @return true if modifier is an annotation for @Nullable, false otherwise
     */
    public static boolean isNullable(IExtendedModifier extendedModifier) {
        if (!(extendedModifier instanceof MarkerAnnotation))
            return false;

        Name typeName = ((MarkerAnnotation) extendedModifier).getTypeName();
        return typeName.isSimpleName() && ((SimpleName) typeName).getIdentifier().equals("Nullable");
    }

    /**
     * Return true is the specified modifier is an @Nullable annotation.   Currently we don't care about, nor even
     * support, fully qualified names for the @Nullable annotation.   Any non-qualified name that's @Nullable counts
     * here, but fully qualified names currently never do currently.   The intention is that the programmer can pick
     * between several different @Nullable annotations, though JSimple code currently uses the IntelliJ one.
     *
     * @param extendedModifier modifier in question
     * @return true if modifier is an annotation for @Nullable, false otherwise
     */
    public static boolean isFunctionalInterface(IExtendedModifier extendedModifier) {
        if (!(extendedModifier instanceof MarkerAnnotation))
            return false;

        Name typeName = ((MarkerAnnotation) extendedModifier).getTypeName();
        return typeName.isSimpleName() && ((SimpleName) typeName).getIdentifier().equals("FunctionalInterface");
    }

    /**
     * Return true is the specified modifier is "final".
     *
     * @param extendedModifier modifier in question
     * @return true if modifier is "final"
     */
    public static boolean isFinal(IExtendedModifier extendedModifier) {
        return extendedModifier instanceof Modifier && ((Modifier) extendedModifier).isFinal();
    }

    /**
     * Return true is the specified modifier is "final".
     *
     * @param extendedModifier modifier in question
     * @return true if modifier is "final"
     */
    public static boolean isAnnotation(IExtendedModifier extendedModifier) {
        return extendedModifier instanceof Annotation;
    }

    /**
     * Returns true if the list of extended modifiers (modifiers & annotations) includes "final".
     *
     * @param extendedModifiers extended modifiers to check
     * @return true if and only if list contains "final"
     */
    public static boolean containsFinal(List<?> extendedModifiers) {
        for (Object extendedModifierObject : extendedModifiers) {
            if (isFinal((IExtendedModifier) extendedModifierObject))
                return true;
        }
        return false;
    }

    /**
     * Returns true if the list of extended modifiers (modifiers & annotations) includes "static".
     *
     * @param extendedModifiers extended modifiers to check
     * @return true if and only if list contains "static"
     */
    public static boolean containsStatic(List extendedModifiers) {
        for (Object extendedModifierObject : extendedModifiers) {
            IExtendedModifier extendedModifier = (IExtendedModifier) extendedModifierObject;
            if (extendedModifier.isModifier() && ((Modifier) extendedModifier).isStatic())
                return true;
        }
        return false;
    }

    /**
     * Returns true if the list of extended modifiers (modifiers & annotations) includes "abstract".
     *
     * @param extendedModifiers extended modifiers to check
     * @return true if and only if list contains "static"
     */
    public static boolean containsAbstract(List<?> extendedModifiers) {
        for (Object extendedModifierObject : extendedModifiers) {
            IExtendedModifier extendedModifier = (IExtendedModifier) extendedModifierObject;
            if (extendedModifier.isModifier() && ((Modifier) extendedModifier).isAbstract())
                return true;
        }
        return false;
    }

    /**
     * Determines the access modifier specified in list of modifiers. If no access modifier is specified, the default
     * access of Package is returned.
     *
     * @param extendedModifiers extended modifiers to check
     * @return AccessModifier specified or Package by default
     */
    public static AccessLevel getAccessModifier(List<?> extendedModifiers) {
        for (Object extendedModifierObject : extendedModifiers) {
            IExtendedModifier extendedModifier = (IExtendedModifier) extendedModifierObject;
            if (extendedModifier.isModifier()) {
                Modifier modifier = (Modifier) extendedModifier;
                if (modifier.isPublic())
                    return AccessLevel.PUBLIC;
                else if (modifier.isProtected())
                    return AccessLevel.PROTECTED;
                else if (modifier.isPrivate())
                    return AccessLevel.PRIVATE;
            }
        }

        return AccessLevel.PACKAGE;
    }

    public static boolean isFinal(BodyDeclaration bodyDeclaration) {
        return containsFinal(bodyDeclaration.modifiers());
    }

    public static boolean isAbstract(BodyDeclaration bodyDeclaration) {
        return containsAbstract(bodyDeclaration.modifiers());
    }

    public static boolean isStatic(BodyDeclaration bodyDeclaration) {
        return containsStatic(bodyDeclaration.modifiers());
    }

    public static boolean isPrivate(BodyDeclaration bodyDeclaration) {
        return getAccessModifier(bodyDeclaration.modifiers()) == AccessLevel.PRIVATE;
    }

    public static boolean isFinal(AbstractTypeDeclaration typeDeclaration) {
        // Enums are implicitly final
        return typeDeclaration instanceof EnumDeclaration || containsFinal(typeDeclaration.modifiers());
    }

    public static boolean isInterface(AbstractTypeDeclaration typeDeclaration) {
        return typeDeclaration instanceof TypeDeclaration && ((TypeDeclaration) typeDeclaration).isInterface();
    }

    public static boolean isAbstract(TypeDeclaration typeDeclaration) {
        return containsAbstract(typeDeclaration.modifiers());
    }

    public static boolean isConstructor(MethodDeclaration methodDeclaration) {
        return methodDeclaration.isConstructor();
    }

    public static boolean isStatic(IMethodBinding methodBinding) {
        return (methodBinding.getModifiers() & Modifier.STATIC) != 0;
    }

    public static boolean isType(Type type, String qualifiedTypeName) {
        return isType(type.resolveBinding(), qualifiedTypeName);
    }

    public static boolean isType(@Nullable ITypeBinding typeBinding, String qualifiedTypeName) {
        return typeBinding != null && typeBinding.getQualifiedName().equals(qualifiedTypeName);
    }

    public static boolean isThisMethod(MethodDeclaration methodDeclaration, String methodName,
            String... parameterTypes) {
        // If the method names don't match, it's not the specified method
        if (!methodDeclaration.getName().getIdentifier().equals(methodName))
            return false;

        // If the method parameter counts don't match, it's not the specified method
        if (methodDeclaration.parameters().size() != parameterTypes.length)
            return false;

        // Ensure each parameter has the expected type
        int index = 0;
        for (Object parameterObject : methodDeclaration.parameters()) {
            SingleVariableDeclaration parameter = (SingleVariableDeclaration) parameterObject;

            if (!isType(parameter.getType(), parameterTypes[index]))
                return false;

            ++index;
        }

        return true;
    }

    public static boolean isThisName(Name name, String nameString) {
        // Check first, in the case of a qualified name, whether the name string has a suffix that's the simple name
        // portion of the qualified name.   This check is just an optimization, so in the typical case where there is
        // no match it won't allocate a new object & build the full name string to compare
        if (name instanceof QualifiedName) {
            if (!nameString.endsWith(((QualifiedName) name).getName().getIdentifier()))
                return false;
        }

        return name.getFullyQualifiedName().equals(nameString);
    }

    public static boolean isArrayLengthField(FieldAccess fieldAccess) {
        if (!fieldAccess.getName().getIdentifier().equals("length"))
            return false;
        @Nullable
        IVariableBinding variableBinding = fieldAccess.resolveFieldBinding();
        return variableBinding != null && variableBinding.getType().isArray();
    }

    public static boolean isArrayLengthField(QualifiedName qualifiedName) {
        if (!qualifiedName.getName().getIdentifier().equals("length"))
            return false;
        @Nullable
        ITypeBinding binding = qualifiedName.getQualifier().resolveTypeBinding();
        return binding != null;
    }

    /**
     * Returns the position following the last character in the node; just a shortcut for adding the length to the start
     * position. Note that the end position may be (one) past the end of the source.
     *
     * @param node ASTNode in question
     * @return last position in the node + 1
     */
    public static int getEndPosition(ASTNode node) {
        return node.getStartPosition() + node.getLength();
    }

    /**
     * Returns the position following the last character in the last node in the list. Note that the end position may be
     * (one) past the end of the source.
     *
     * @param nodes list of nodes; each item in the list should actually be of a type that's a subclass of ASTNode
     * @return last position of the last node in the list + 1
     */
    public static int getEndPosition(List<?> nodes) {
        Object lastNodeObject = null;
        for (Object node : nodes)
            lastNodeObject = node;

        return getEndPosition((ASTNode) lastNodeObject);
    }

    public static @Nullable String qualifierFromQualifiedName(String qualifiedName) {
        int lastPeriodIndex = qualifiedName.lastIndexOf('.');
        if (lastPeriodIndex == -1)
            return null;
        else
            return qualifiedName.substring(0, lastPeriodIndex);
    }

    public static String simpleNameFromQualifiedName(String qualifiedName) {
        int lastPeriodIndex = qualifiedName.lastIndexOf('.');
        if (lastPeriodIndex == -1)
            return qualifiedName;
        else
            return qualifiedName.substring(lastPeriodIndex + 1);
    }

    public static boolean directlyImplementsInterface(ITypeBinding typeBinding,
            String desiredInterfaceQualifiedName) {
        for (ITypeBinding interfaceTypeBinding : typeBinding.getInterfaces()) {
            if (interfaceTypeBinding.getQualifiedName().equals(desiredInterfaceQualifiedName))
                return true;
        }

        return false;
    }

    public static boolean implementsInterface(ITypeBinding typeBinding, String interfaceQualifiedName) {
        while (typeBinding != null) {
            if (directlyImplementsInterface(typeBinding, interfaceQualifiedName))
                return true;
            typeBinding = typeBinding.getSuperclass();
        }

        return false;
    }

    public static boolean isFunctionalInterface(TypeDeclaration typeDeclaration) {
        return anyMatch(typeDeclaration.modifiers(),
                (IExtendedModifier extendedModifier) -> isFunctionalInterface(extendedModifier));
    }

    /*
        public static boolean isFunctionalInterface(ITypeBinding typeBinding) {
    typeBinding.
        
        
    boolean hasAnnotation = false;
    for (Object extendedModifierObject : typeDeclaration.modifiers()) {
        IExtendedModifier extendedModifier = (IExtendedModifier) extendedModifierObject;
        if (isFunctionalInterface(extendedModifier)) {
            hasAnnotation = true;
            break;
        }
    }
        
    return hasAnnotation;
        }
    */

    public static boolean isNumericPrimitiveType(Type type) {
        if (!(type instanceof PrimitiveType))
            return false;

        PrimitiveType.Code code = ((PrimitiveType) type).getPrimitiveTypeCode();
        return code == PrimitiveType.BYTE || code == PrimitiveType.SHORT || code == PrimitiveType.INT
                || code == PrimitiveType.LONG || code == PrimitiveType.FLOAT || code == PrimitiveType.DOUBLE;
    }

    public static boolean isGenericImport(ImportDeclaration importDeclaration) {
        @Nullable
        IBinding binding = importDeclaration.resolveBinding();
        return binding != null && binding instanceof ITypeBinding && ((ITypeBinding) binding).isGenericType();
    }

    public static boolean isFunctionalInterface(ITypeBinding typeBinding) {
        if (!typeBinding.isInterface())
            return false;

        int methodCount = typeBinding.getDeclaredMethods().length;
        if (methodCount != 1)
            return false;

        // See if the type has the FunctionalInterface annotation
        return anyMatch(typeBinding.getAnnotations(), (IAnnotationBinding annotationBinding) -> annotationBinding
                .getAnnotationType().getQualifiedName().equals("java.lang.FunctionalInterface"));

        // TODO: Ensure no default implementation (I think) nor constants defined for the interface
    }

    public static boolean isFunctionalInterfaceImplementation(SourceFileWriter sourceFileWriter, Type type) {
        return isFunctionalInterface(sourceFileWriter.resolveTypeBinding(type));
    }

    /**
     * Add all the wildcards referenced from a given type, searching recursively in the type definition to add all of
     * them.   That is, for a type like this:
     * <p/>
     * "Foo< Bar<? extends Fizz>, <Blip <? super Pop>>, ? >"
     * <p/>
     * 3 wildcard types would be added (<? extends Fizz>, <? super Pop>, and ?) since essentially the ? appears 3 times
     * the in the type definition above.
     *
     * @param type
     * @param wildcardTypes
     */
    public static void addWildcardTypes(Type type, ArrayList<WildcardType> wildcardTypes) {
        if (type.isParameterizedType()) {
            ParameterizedType parameterizedType = (ParameterizedType) type;

            forEach(parameterizedType.typeArguments(), (Type typeArgument) -> {
                addWildcardTypes(typeArgument, wildcardTypes);
            });
        } else if (type.isWildcardType()) {
            WildcardType wildcardType = (WildcardType) type;

            wildcardTypes.add(wildcardType);
            @Nullable
            Type bound = wildcardType.getBound();

            if (bound != null)
                addWildcardTypes(bound, wildcardTypes);
        }
    }

    /**
     * Checks if the cast expression is creating an array (which in Java can never be generic) and then turns around and
     * casts it to an array of a generic type.   In Java, which doesn't allow directly creating a generic array,
     * creating a non-generic array and casting is the the only way to create an array of generic type.   However, in
     * other languages, like C#, that don't do type erasure for generics you can create a generic array directly--and in
     * fact you have to if that's what you want to end up with.   This method helps us map from the Java way (cast) to
     * target language (create directly, with no cast).
     *
     * @param castExpression cast expression in question
     * @return true if the expression creates an array then casts it to a generic array
     */
    public static boolean isGenericArrayCreation(CastExpression castExpression) {
        if (!(castExpression.getExpression() instanceof ArrayCreation))
            return false;

        Type type = castExpression.getType();
        return type instanceof ArrayType && isGenericType(((ArrayType) type).getElementType());
    }

    /**
     * Returns true if the specified type is generic in any way (that is, it has generic parameters, like
     * ArrayList&lt;E&gt;, is a type variable itself, like E, or is an array of a generic type, like E[]).
     *
     * @param type type in question
     * @return true if it's a generic type
     */
    public static boolean isGenericType(Type type) {
        if (type instanceof ParameterizedType)
            return true;

        if (type instanceof ArrayType)
            return isGenericType(((ArrayType) type).getElementType());

        if (type instanceof SimpleType) {
            ITypeBinding typeBinding = type.resolveBinding();
            if (typeBinding != null && typeBinding.isTypeVariable())
                return true;
        }

        return false;
    }

    @FunctionalInterface
    public interface ConsumerWithFirst<T> {
        public void accept(T elmt, boolean first);
    }

    public static <T> boolean forEach(List list, Consumer<T> consumer) {
        boolean hasItem = false;
        for (Object elmtObject : list) {
            T elmt = (T) elmtObject;
            consumer.accept(elmt);
            hasItem = true;
        }
        return hasItem;
    }

    public static <T> boolean forEach(List list, ConsumerWithFirst<T> consumerWithFirst) {
        boolean first = true;
        for (Object elmtObject : list) {
            T elmt = (T) elmtObject;
            consumerWithFirst.accept(elmt, first);
            first = false;
        }
        return !first;
    }

    public static <T> boolean anyMatch(List list, Predicate<? super T> predicate) {
        for (Object elmtObject : list) {
            T elmt = (T) elmtObject;
            if (predicate.test(elmt))
                return true;
        }
        return false;
    }

    public static <T> boolean anyMatch(T[] array, Predicate<? super T> predicate) {
        for (T elmt : array) {
            if (predicate.test(elmt))
                return true;
        }
        return false;
    }

    public static boolean anyTypeOrAncestorMatch(ITypeBinding typeBinding,
            Predicate<? super ITypeBinding> predicate) {
        if (predicate.test(typeBinding))
            return true;

        return anyAncestorMatch(typeBinding, predicate);
    }

    public static boolean anyAncestorMatch(ITypeBinding typeBinding, Predicate<? super ITypeBinding> predicate) {
        ITypeBinding superClassTypeBinding = typeBinding.getSuperclass();
        if (superClassTypeBinding != null && anyTypeOrAncestorMatch(superClassTypeBinding, predicate))
            return true;

        for (ITypeBinding interfaceTypeBinding : typeBinding.getInterfaces()) {
            if (anyTypeOrAncestorMatch(interfaceTypeBinding, predicate))
                return true;
        }

        return false;
    }

    /**
     * Check all the superclasses (all the way up the tree) for the given type, seeing if any of them match the
     * specified predicate.   Note that the type itself isn't checked, just its superclasses.   Interfaces aren't
     * checked either; use anyAncestorInterfaceMatch for that.
     *
     * @param typeBinding type (normally a class)
     * @param predicate   predicate to test against
     * @return true if any superclass matches the specified test
     */
    public static boolean anySuperclassMatch(ITypeBinding typeBinding, Predicate<? super ITypeBinding> predicate) {
        // See if this method doesn't have the @Override annotation but actually is an override, by iterating through
        // all ancestor classes/interfaces and checking methods on each to see if this method overrides
        ITypeBinding superclass = typeBinding.getSuperclass();

        while (superclass != null) {
            if (predicate.test(superclass))
                return true;
            superclass = superclass.getSuperclass();
        }

        return false;
    }

    /**
     * Check all the ancestor interfaces of the given type, to see if any of them match the specified predicate.
     * Ancestor interfaces include super interfaces for a type & their super interfaces, all the way up the tree. Super
     * interfaces implemented by are are also included.   Note that superclasses themselves aren't tested (use
     * anySuperclassMatch for that), but all interfaces implemented by superclasses are tested.
     *
     * @param typeBinding type (normally a class) in question
     * @param predicate   predicate to test against
     * @return true if any superinterface matches the specified test
     */
    public static boolean anyAncestorInterfaceMatch(ITypeBinding typeBinding,
            Predicate<? super ITypeBinding> predicate) {
        // See if this method doesn't have the @Override annotation but actually is an override, by iterating through
        // all ancestor classes/interfaces and checking methods on each to see if this method overrides
        for (ITypeBinding interfaceBinding : typeBinding.getInterfaces()) {
            if (predicate.test(interfaceBinding))
                return true;

            if (anyAncestorMatch(interfaceBinding, predicate))
                return true;
        }

        return anySuperclassMatch(typeBinding, superclass -> anyAncestorInterfaceMatch(superclass, predicate));
    }

    public static BigInteger getIntegerLiteralValue(NumberLiteral numberLiteral) {
        // Get the literal token string, normalizing by removing any _ delimiter characters and converting to upper case
        String token = numberLiteral.getToken().replace("_", "").toUpperCase();

        // Strip off any integer type suffix at the end
        if (token.endsWith("L"))
            token = token.substring(0, token.length() - 1);

        if (token.startsWith("0X"))
            return new BigInteger(token.substring(2), 16);
        else if (token.startsWith("0B"))
            return new BigInteger(token.substring(2), 2);
        else if (token.startsWith("0") && !token.equals("0"))
            return new BigInteger(token.substring(1), 8);
        else
            return new BigInteger(token, 10);
    }
}