/* * 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.generatecode; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jface.text.IDocument; import org.eclipse.text.edits.TextEdit; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.texteditor.AbstractTextEditor; import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.generatemenucode.model.codegenerators.CodeGeneratorDataBasedOnMenu; import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout; /** * Manager responsible to modify activity / fragment. */ public abstract class JavaCodeModifier { protected List<AbstractCodeGenerator> codeGenerators = new ArrayList<AbstractCodeGenerator>(); protected AbstractCodeGeneratorData codeGeneratorData; public static final List<String> IMPORT_LIST = new ArrayList<String>(); protected TypeDeclaration typeDeclaration; /** * Insert code into the class (activity / fragment) and adds imports if necessary. * @throws JavaModelException Thrown if there were problems parsing the java file. */ public void insertCode(IProgressMonitor monitor, IEditorPart editor) throws JavaModelException { final SubMonitor theMonitor = SubMonitor.convert(monitor); IResource resource = codeGeneratorData.getResource(); if (resource instanceof IFile) { IFile java = (IFile) resource; StudioLogger .info("Trying to insert code for class: " + java.getFullPath() + " based on resource " + getDataResource()); //$NON-NLS-1$ IDocument document = null; try { document = ((AbstractTextEditor) editor).getDocumentProvider().getDocument( editor.getEditorInput()); final ICompilationUnit compUnit = getCodeGeneratorData().getICompilationUnit(); CompilationUnit cpU = getCodeGeneratorData().getCompilationUnit(); try { cpU.recordModifications(); initVariables(); codeGenerators.clear(); codeGenerators = populateListOfCodeGenerators(getCodeGeneratorData()); theMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_InsertingCode, 1000 * getNumberOfTasks()); callCodeGenerators(theMonitor, java); addImportsIfRequired(theMonitor, cpU); Map<?, ?> mapOptions = JavaCore.create(java.getProject()).getOptions(true); final TextEdit edit = cpU.rewrite(document, mapOptions); PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { public void run() { try { compUnit.applyTextEdit(edit, theMonitor); } catch (JavaModelException e) { StudioLogger.error(this.getClass(), "Error applying changes: " + e.getMessage(), e); //$NON-NLS-1$ } } }); } catch (CoreException e) { StudioLogger.error(this.getClass(), "Error changing AST activity/fragment: " + e.getMessage()); //$NON-NLS-1$ throw new JavaModelException(e); } catch (RuntimeException rte) { StudioLogger.error(this.getClass(), "Error changing AST activity/fragment: " + rte.getMessage()); //$NON-NLS-1$ throw new JavaModelException(rte, IJavaModelStatusConstants.CORE_EXCEPTION); } } catch (CoreException e) { StudioLogger .error(this.getClass(), "Error creating IDocument from java file: " + java + " message: " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ throw new JavaModelException(e, IJavaModelStatusConstants.CORE_EXCEPTION); } finally { theMonitor.done(); } } } /** * Init variables required by the code modifier (default behaviour init only typeDeclaration variable, override if necessary to add other variables) */ protected void initVariables() { typeDeclaration = getCodeGeneratorData().getAbstractCodeVisitor().getTypeDeclaration(); } /** * @return file representing the path to data resource (e.g.: layout or menu) */ protected abstract File getDataResource(); /** * Calls code generators (override it if you have a special condition flag to generate code). * <br> * It iterates over {@link JavaCodeModifier#codeGenerators} list and calls {@link AbstractCodeGenerator#generateCode(IProgressMonitor)} * @param theMonitor * @param java file being modified * @throws JavaModelException */ protected void callCodeGenerators(final SubMonitor theMonitor, IFile java) throws JavaModelException { for (AbstractCodeGenerator codeGenerator : codeGenerators) { codeGenerator.generateCode(theMonitor); } } /** * Adds imports if they were not added yet * @param theMonitor * @param compUnit * @throws JavaModelException */ public void addImportsIfRequired(SubMonitor theMonitor, CompilationUnit compUnit) throws JavaModelException { for (String importString : IMPORT_LIST) { String importName = ""; boolean onDemand = false; if (importString.endsWith(".*")) { importName = importString.substring(0, importString.length() - 2); onDemand = true; } else { importName = importString; } boolean exists = false; for (Object importDecl : compUnit.imports()) { ImportDeclaration declaration = (ImportDeclaration) importDecl; String name = declaration.getName().getFullyQualifiedName(); if (importName.equals(name)) { exists = true; break; } } if (!exists) { createImport(importName, compUnit, onDemand); } } } @SuppressWarnings("unchecked") private void createImport(String name, CompilationUnit compUnit, boolean onDemand) { AST ast = compUnit.getAST(); ImportDeclaration importDeclaration = ast.newImportDeclaration(); importDeclaration.setName(ast.newName(name)); importDeclaration.setOnDemand(onDemand); compUnit.imports().add(importDeclaration); } /** * Creates the necessary imports listed in {@link JavaCodeModifier#IMPORT_LIST}. * @throws JavaModelException Thrown if there were problems during the insertion of imports in the java file. */ protected void createImports(IProgressMonitor monitor, ICompilationUnit compilationUnit) throws JavaModelException { SubMonitor subMonitor = SubMonitor.convert(monitor); //need to look at each GUI item and them create 1 method subMonitor.beginTask(CodeUtilsNLS.JavaViewBasedOnLayoutModifier_CreatingImports, IMPORT_LIST.size()); if (IMPORT_LIST != null) { for (String importItem : IMPORT_LIST) { compilationUnit.createImport(importItem, null, subMonitor); } } subMonitor.worked(IMPORT_LIST.size()); } /** * Sets the code generator input data (for example: {@link CodeGeneratorDataBasedOnLayout} or {@link CodeGeneratorDataBasedOnMenu}) to be used for the java code modifier * @param codeGeneratorData the codeGeneratorData to set. */ public void setCodeGeneratorData(AbstractCodeGeneratorData codeGeneratorData) { this.codeGeneratorData = codeGeneratorData; } /** * @return the codeGeneratorData */ public AbstractCodeGeneratorData getCodeGeneratorData() { return codeGeneratorData; } /** * Populates the list of code generators that the modifier will use to change the code. * @param codeGeneratorDataBasedOnLayout the data source to use into the modification * @return list of code generators. */ public abstract List<AbstractCodeGenerator> populateListOfCodeGenerators( AbstractCodeGeneratorData abstractCodeGeneratorData); /** * @return The number of tasks based on the number of code generators. */ protected int getNumberOfTasks() { return codeGenerators.size(); } }