com.motorola.studio.android.model.java.JavaClass.java Source code

Java tutorial

Introduction

Here is the source code for com.motorola.studio.android.model.java.JavaClass.java

Source

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.motorola.studio.android.model.java;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodRef;
import org.eclipse.jdt.core.dom.MethodRefParameter;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.osgi.util.NLS;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;

import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
import com.motorola.studio.android.common.exception.AndroidException;
import com.motorola.studio.android.common.log.StudioLogger;

/**
 * Abstract class that contains the basic structure to create java classes programatically
 */
public abstract class JavaClass {
    protected static Annotation OVERRIDE_ANNOTATION;

    static {
        AST tempAST = AST.newAST(AST.JLS3);

        OVERRIDE_ANNOTATION = tempAST.newMarkerAnnotation();
        OVERRIDE_ANNOTATION.setTypeName(tempAST.newSimpleName("Override"));

        tempAST = null;
    }

    protected CompilationUnit compUnit;

    protected AST ast;

    protected TypeDeclaration classDecl = null;

    protected IDocument document;

    protected String className;

    protected String[] packageName;

    protected String[] superClass;

    /**
     * Class constructor. Creates the basic elements for the class:
     * package name and class declaration based on a super class.
     * 
     * @param className The simple class name
     * @param packageName The full-qualified class package name
     * @param superClass The full-qualified super class name
     */
    @SuppressWarnings("unchecked")
    protected JavaClass(String className, String packageName, String superClass) {
        // It is expected that the parameters have been validated
        // by the UI
        Assert.isNotNull(className);
        Assert.isNotNull(packageName);
        Assert.isNotNull(superClass);

        this.className = className;
        this.packageName = getFQNAsArray(packageName);
        this.superClass = getFQNAsArray(superClass);

        // The package name must have two identifiers at least, according to
        // the Android specifications
        Assert.isLegal(packageName.length() > 1);
        // So, the superclass must have at least two identifiers plus the name
        Assert.isLegal(superClass.length() > 2);

        ast = AST.newAST(AST.JLS3);
        compUnit = ast.newCompilationUnit();

        Type superClassType = null;

        // Sets the package name to the class
        PackageDeclaration pd = ast.newPackageDeclaration();
        QualifiedName qPackageName = ast.newQualifiedName(ast.newName(getQualifier(this.packageName)),
                ast.newSimpleName(getName(this.packageName)));
        pd.setName(qPackageName);
        compUnit.setPackage(pd);

        // Imports the super class
        ImportDeclaration id = ast.newImportDeclaration();
        id.setName(ast.newName(superClass));
        compUnit.imports().add(id);
        superClassType = ast.newSimpleType(ast.newName(getName(this.superClass)));

        // Creates the class
        classDecl = ast.newTypeDeclaration();
        classDecl.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
        if (superClassType != null) {
            classDecl.setSuperclassType(superClassType);
        }

        classDecl.setName(ast.newSimpleName(className));
        compUnit.types().add(classDecl);

        document = new Document(compUnit.toString());
    }

    /**
     * Gets the class content
     * 
     * @return an IDocument object containing the class content
     */
    public IDocument getClassContent() throws AndroidException {
        String content = compUnit.toString();
        document = new Document(content);

        // Formats the code using the Eclipse settings
        CodeFormatter codeFormatter = ToolFactory
                .createCodeFormatter(DefaultCodeFormatterConstants.getEclipseDefaultSettings());

        TextEdit textEdit = codeFormatter.format(
                CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS, content, 0, content.length(),
                0, null);

        try {
            textEdit.apply(document);
        } catch (MalformedTreeException e) {
            String errMsg = NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorFormattingSourceCode, className);

            StudioLogger.error(JavaClass.class, errMsg, e);
            throw new AndroidException(errMsg);
        } catch (BadLocationException e) {
            String errMsg = NLS.bind(CodeUtilsNLS.EXC_JavaClass_ErrorFormattingSourceCode, className);

            StudioLogger.error(JavaClass.class, errMsg, e);
            throw new AndroidException(errMsg);
        }

        addComments();

        return document;
    }

    /**
     * Adds comments to the code. As we cannot add comments when
     * we are making the AST, comments need to be insert after the
     * class creation
     */
    protected abstract void addComments() throws AndroidException;

    /**
     * Adds a parameter to a method
     *  
     * @param method The method object
     * @param parameterName The parameter name
     * @param parameterType The parameter type (only the single name, not the qualified)
     */
    @SuppressWarnings("unchecked")
    protected void addMethodParameter(MethodDeclaration method, String parameterName, Type parameterType) {
        SingleVariableDeclaration vd = ast.newSingleVariableDeclaration();
        vd.setName(ast.newSimpleName(parameterName));
        vd.setType(parameterType);
        method.parameters().add(vd);
    }

    /**
     * Adds a comment to a BodyDeclaration object.
     * For now, this method does nothing.
     * 
     * @param element The element to add the comment
     * @param comment The comment
     */
    //@SuppressWarnings("unchecked")
    protected void addComment(BodyDeclaration element, String comment) {
        // TODO These comments will be reviewed for the Phase B
        /*Javadoc javadoc = element.getJavadoc();
        TextElement textElement = ast.newTextElement();
        TagElement tagElement = ast.newTagElement();
            
        if (javadoc == null)
        {
        javadoc = ast.newJavadoc();
        element.setJavadoc(javadoc);
        }
            
        textElement.setText(comment);
        tagElement.fragments().add(textElement);
        javadoc.tags().add(tagElement);*/
    }

    /**
     * Adds documentation reference to a method (the see tag to the javadoc)
     * 
     * @param element The method declaration object
     * @param qualifiedClassName The full qualified class name to refer
     * @param methodName The method to refer
     * @param parameters The method parameters
     */
    @SuppressWarnings("unchecked")
    protected void addMethodReference(MethodDeclaration element, String qualifiedClassName, String methodName,
            Type[] parameters) {
        String[] fqnArray = getFQNAsArray(qualifiedClassName);

        MethodRef methodRef = ast.newMethodRef();
        methodRef.setQualifier(
                ast.newQualifiedName(ast.newName(getQualifier(fqnArray)), ast.newSimpleName(getName(fqnArray))));

        methodRef.setName(ast.newSimpleName(methodName));

        if ((parameters != null) && (parameters.length > 0)) {
            for (Type param : parameters) {
                MethodRefParameter methodParam = ast.newMethodRefParameter();
                methodParam.setType(param);
                methodRef.parameters().add(methodParam);
            }
        }

        Javadoc javadoc = element.getJavadoc();
        TagElement tagElement = ast.newTagElement();
        tagElement.setTagName(TagElement.TAG_SEE);

        if (javadoc == null) {
            javadoc = ast.newJavadoc();
            element.setJavadoc(javadoc);
        }

        tagElement.fragments().add(methodRef);
        javadoc.tags().add(tagElement);
    }

    /**
     * Adds an empty block to a method declaration
     * 
     * @param method The method declaration
     * @param returnType The method return type. If the method does not have one, use null
     */
    @SuppressWarnings("unchecked")
    protected void addEmptyBlock(MethodDeclaration method) {
        Expression expression = null;
        Block emptyBlock = ast.newBlock();
        ReturnStatement returnStatement = ast.newReturnStatement();
        Type returnType = method.getReturnType2();

        if (returnType instanceof PrimitiveType) {
            PrimitiveType pType = (PrimitiveType) returnType;
            if (pType.getPrimitiveTypeCode() == PrimitiveType.BOOLEAN) {
                expression = ast.newBooleanLiteral(false);
            } else if (pType.getPrimitiveTypeCode() != PrimitiveType.VOID) {
                expression = ast.newNumberLiteral("0");
            }
        } else {
            expression = ast.newNullLiteral();
        }

        if (expression != null) {
            returnStatement.setExpression(expression);
            emptyBlock.statements().add(returnStatement);
        }

        method.setBody(emptyBlock);
    }

    /**
     * Creates a new string Type object
     * 
     * @return a new string Type object
     */
    protected Type stringType() {
        return ast.newSimpleType(ast.newSimpleName(String.class.getSimpleName()));
    }

    /**
     * Creates a new string array Type object
     * 
     * @return a new string array Type object
     */
    protected Type stringArrayType() {
        return ast.newArrayType(ast.newSimpleType(ast.newName(String.class.getSimpleName())));
    }

    /**
     * Creates a new int array Type object
     * 
     * @return a new int array Type object
     */
    protected Type intArrayType() {
        return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.INT));
    }

    /**
     * Returns a full qualified class name as a array
     *    
     * @param fqn The full qualified class name
     * @return the full qualified class name as a array
     */
    protected static String[] getFQNAsArray(String fqn) {
        String[] parts;

        if (fqn.contains(".")) {
            parts = fqn.split("\\.");
        } else {
            parts = new String[] { fqn };
        }

        return parts;
    }

    /**
     * Retrieves the qualifier for a full qualified name.
     * Example:
     *      com.motorola.studio.android.MyClass
     * 
     * The qualifier for the class is com.motorola.studio.android
     *  
     * @param qualifiedName The full qualified name
     * @return The qualifier
     */
    protected static String[] getQualifier(String[] qualifiedName) {
        String[] qualifier;

        if (qualifiedName.length > 1) {
            qualifier = new String[qualifiedName.length - 1];

            System.arraycopy(qualifiedName, 0, qualifier, 0, qualifiedName.length - 1);
        } else {
            qualifier = qualifiedName;
        }

        return qualifier;
    }

    /** 
     * Gets the name part from a full qualified name
     * 
     * @param qualifiedName The full qualified name
     * 
     * @return The name part from a full qualified name
     */
    protected static String getName(String[] qualifiedName) {
        String name = null;

        if ((qualifiedName != null) && (qualifiedName.length > 0)) {
            name = qualifiedName[qualifiedName.length - 1];
        }

        return name;
    }

}