blob: 523a38e6dec716aa229a5897898e3c08932887be [file] [log] [blame]
/*
* 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;
}
}