| /* |
| * 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; |
| } |
| |
| } |