| /******************************************************************************* |
| * Copyright (c) 2000, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * $Id: TypeCreator.java 23435 2010-02-04 00:14:38Z stephan $ |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Fraunhofer FIRST - extended API and implementation |
| * Technical University Berlin - extended API and implementation |
| *******************************************************************************/ |
| package org.eclipse.objectteams.otdt.internal.ui.wizards.typecreation; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IBuffer; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.ToolFactory; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.core.compiler.IScanner; |
| import org.eclipse.jdt.core.compiler.ITerminalSymbols; |
| import org.eclipse.jdt.core.compiler.InvalidInputException; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTParser; |
| import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.ImportDeclaration; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; |
| import org.eclipse.jdt.core.formatter.CodeFormatter; |
| import org.eclipse.jdt.internal.corext.codemanipulation.AddUnimplementedConstructorsOperation; |
| import org.eclipse.jdt.internal.corext.codemanipulation.AddUnimplementedMethodsOperation; |
| import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings; |
| import org.eclipse.jdt.internal.core.manipulation.StubUtility; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.TokenScanner; |
| import org.eclipse.jdt.internal.corext.refactoring.StubTypeContext; |
| import org.eclipse.jdt.internal.corext.refactoring.TypeContextChecker; |
| import org.eclipse.jdt.internal.core.manipulation.CodeTemplateContext; |
| import org.eclipse.jdt.internal.core.manipulation.CodeTemplateContextType; |
| import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.jdt.internal.corext.util.Strings; |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings; |
| import org.eclipse.jdt.internal.ui.refactoring.contentassist.JavaTypeCompletionProcessor; |
| import org.eclipse.jdt.internal.core.manipulation.ProjectTemplateStore; |
| import org.eclipse.jdt.internal.ui.wizards.NewWizardMessages; |
| import org.eclipse.jdt.core.manipulation.CodeGeneration; |
| import org.eclipse.jdt.ui.PreferenceConstants; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.templates.Template; |
| import org.eclipse.jface.text.templates.TemplateBuffer; |
| import org.eclipse.jface.text.templates.TemplateException; |
| import org.eclipse.jface.text.templates.TemplateVariable; |
| import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; |
| import org.eclipse.objectteams.otdt.internal.ui.OTDTUIPluginConstants; |
| import org.eclipse.objectteams.otdt.ui.OTDTUIPlugin; |
| import org.eclipse.text.edits.DeleteEdit; |
| import org.eclipse.text.edits.MalformedTreeException; |
| import org.eclipse.text.edits.MultiTextEdit; |
| import org.eclipse.text.edits.TextEdit; |
| |
| /** |
| * Individual method copied from {@link org.eclipse.jdt.internal.corext.codemanipulation.StubUtility#getCompilationUnitContent(ICompilationUnit, String, String, String, String)} |
| * |
| * @author kaschja |
| */ |
| public abstract class TypeCreator |
| { |
| |
| private TypeInfo _typeInfo; |
| |
| private boolean _addComments; |
| |
| private IType _createdType; |
| private String _defaultSupertypeName; |
| |
| private StubTypeContext fSuperClassStubTypeContext; |
| private StubTypeContext fSuperInterfaceStubTypeContext; // FIXME(SH): use it!?! (see writeSuperClass / writeSuperInterfaces) |
| |
| |
| |
| public TypeCreator() |
| { |
| _defaultSupertypeName = createDefaultSupertypeName(); |
| } |
| |
| |
| public void setTypeInfo(TypeInfo typeInfo) |
| { |
| _typeInfo = typeInfo; |
| } |
| |
| protected TypeInfo getTypeInfo() |
| { |
| return _typeInfo; |
| } |
| |
| public void setAddComments(boolean addComments) { |
| this._addComments = addComments; |
| } |
| |
| /** |
| * Hook method. Gets called by Constructor. |
| */ |
| protected abstract String createDefaultSupertypeName(); |
| |
| protected String getDefaultSupertypeName() |
| { |
| return _defaultSupertypeName; |
| } |
| |
| private void setCreatedType(IType createdType) |
| { |
| _createdType = createdType; |
| } |
| |
| public IType createType(IProgressMonitor monitor) throws CoreException, InterruptedException |
| { |
| if (monitor == null) { |
| monitor= new NullProgressMonitor(); |
| } |
| |
| monitor.beginTask(NewWizardMessages.NewTypeWizardPage_operationdesc, 10); |
| |
| List<ICompilationUnit> createdWorkingCopies= new ArrayList<ICompilationUnit>(); |
| |
| try |
| { |
| validateTypeCreation(); |
| |
| IPackageFragment pack = getPackageFragment(); |
| |
| monitor.worked(1); |
| |
| IType createdType; |
| ImportsManager imports; |
| int indent= 0; |
| |
| String lineDelimiter= null; |
| boolean needsSave = false; |
| |
| // first try to setup the CU of the enclosing team: |
| IType enclosingType = getEnclosingType(); |
| |
| ICompilationUnit teamCU = null; |
| CompilationUnit teamAST = null; |
| Set<String> existingImports= null; |
| |
| CompilationUnit newAST= null; |
| |
| if (enclosingType != null) { |
| // if we have an enclosing type, we may indeed not to write to it (RoFi may add imports to enclosing team) |
| teamCU = enclosingType.getCompilationUnit(); |
| needsSave= !teamCU.isWorkingCopy(); |
| teamCU.becomeWorkingCopy(new SubProgressMonitor(monitor, 1)); // cu is now for sure (primary) a working copy |
| createdWorkingCopies.add(teamCU); |
| |
| teamAST= createASTForImports(teamCU); |
| } else { |
| // sanity check (suppose we are creating a top-level team since enclosingType is null) |
| if (_typeInfo.isInlineType()) |
| throw new CoreException(new Status(IStatus.ERROR, OTDTUIPlugin.UIPLUGIN_ID, "missing enclosing type for inline type")); //$NON-NLS-1$ |
| } |
| |
| if (!_typeInfo.isInlineType()) |
| { |
| // Need one more CU: either team or RoFi: |
| |
| lineDelimiter= StubUtility.getLineDelimiterUsed(pack.getJavaProject()); |
| |
| ICompilationUnit newCU= pack.createCompilationUnit(_typeInfo.getTypeName() + ".java", "", false, new SubProgressMonitor(monitor, 2)); //$NON-NLS-1$ //$NON-NLS-2$ |
| // create a working copy with a new owner |
| needsSave= true; |
| newCU.becomeWorkingCopy(new SubProgressMonitor(monitor, 1)); // cu is now a (primary) working copy |
| createdWorkingCopies.add(newCU); |
| |
| IBuffer buffer= newCU.getBuffer(); |
| |
| String cuContent= constructCUContent(newCU, constructSimpleTypeStub(), lineDelimiter); |
| buffer.setContents(cuContent); |
| |
| newAST= createASTForImports(newCU); |
| existingImports= getExistingImports(newAST); |
| imports= (teamAST != null) ? new ImportsManager(newAST, teamAST) : new ImportsManager(newAST); |
| |
| // add an import that will be removed again. Having this import solves 14661 |
| imports.addImport(JavaModelUtil.concatenateName(pack.getElementName(), _typeInfo.getTypeName())); |
| |
| String typeContent= constructTypeStub(newCU, imports, lineDelimiter); |
| |
| AbstractTypeDeclaration typeNode= (AbstractTypeDeclaration) newAST.types().get(0); |
| int start= ((ASTNode) typeNode.modifiers().get(0)).getStartPosition(); |
| int end= typeNode.getStartPosition() + typeNode.getLength(); |
| |
| buffer.replace(start, end - start, typeContent); |
| |
| createdType= newCU.getType(_typeInfo.getTypeName()); |
| } |
| else |
| { |
| imports= new ImportsManager(teamAST); |
| existingImports= getExistingImports(teamAST); |
| |
| // add imports that will be removed again. Having the imports solves 14661 |
| IType[] topLevelTypes= teamCU.getTypes(); |
| for (int i= 0; i < topLevelTypes.length; i++) { |
| imports.addImport(topLevelTypes[i].getFullyQualifiedName('.')); |
| } |
| |
| lineDelimiter= StubUtility.getLineDelimiterUsed(enclosingType); |
| StringBuffer content= new StringBuffer(); |
| |
| if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.CODEGEN_ADD_COMMENTS)) |
| { |
| String comment= getTypeComment(teamCU, lineDelimiter); |
| if (comment != null) |
| { |
| content.append(comment); |
| content.append(lineDelimiter); |
| } |
| } |
| content.append(constructTypeStub(teamCU, imports, lineDelimiter)); |
| IJavaElement[] elems= enclosingType.getChildren(); |
| IJavaElement sibling= elems.length > 0 ? elems[0] : null; |
| |
| // try |
| // { |
| createdType = enclosingType.createType( |
| content.toString(), |
| sibling, |
| false, |
| new SubProgressMonitor(monitor, 2)); |
| // } |
| // catch (Exception ex) |
| // { |
| // OTDTUIPlugin.getExceptionHandler().logException(ex); |
| // } |
| |
| indent = StubUtility.getIndentUsed(enclosingType) + 1; |
| } |
| |
| if (monitor.isCanceled()) { |
| throw new InterruptedException(); |
| } |
| |
| |
| // add imports for superclass/interfaces, so types can be resolved correctly |
| |
| ICompilationUnit cu= createdType.getCompilationUnit(); |
| |
| imports.create(false, new SubProgressMonitor(monitor, 1)); |
| |
| JavaModelUtil.reconcile(cu); |
| // save the team of a rofi if needed: |
| if (imports.fHasAddedBaseImportsForRofi) { |
| teamCU.commitWorkingCopy(true, monitor); |
| teamCU.save(monitor, true); |
| } |
| |
| if (monitor.isCanceled()) { |
| throw new InterruptedException(); |
| } |
| |
| // ---- create methods: ----- |
| |
| // set up again |
| CompilationUnit astRoot= createASTForImports(imports.getCompilationUnit()); |
| imports= new ImportsManager(astRoot); |
| |
| createTypeMembers(createdType, imports, new SubProgressMonitor(monitor, 1)); |
| |
| // add imports |
| imports.create(false, new SubProgressMonitor(monitor, 1)); |
| |
| removeUnusedImports(cu, existingImports, false); |
| |
| JavaModelUtil.reconcile(cu); |
| |
| ISourceRange range= createdType.getSourceRange(); |
| |
| IBuffer buf= cu.getBuffer(); |
| String originalContent= buf.getText(range.getOffset(), range.getLength()); |
| |
| String formattedContent= CodeFormatterUtil.format(CodeFormatter.K_CLASS_BODY_DECLARATIONS, originalContent, indent, lineDelimiter, pack.getJavaProject()); |
| formattedContent= Strings.trimLeadingTabsAndSpaces(formattedContent); |
| buf.replace(range.getOffset(), range.getLength(), formattedContent); |
| |
| |
| // ----- file comment for role files: ----- |
| if (!_typeInfo.isInlineType()) { |
| String fileComment= getFileComment(cu); |
| if (fileComment != null && fileComment.length() > 0) { |
| buf.replace(0, 0, fileComment + lineDelimiter); |
| } |
| } |
| |
| if (needsSave) { |
| cu.commitWorkingCopy(true, new SubProgressMonitor(monitor, 1)); |
| } else { |
| monitor.worked(1); |
| } |
| |
| |
| setCreatedType(createdType); |
| } |
| finally |
| { |
| for (ICompilationUnit wc : createdWorkingCopies) |
| wc.discardWorkingCopy(); |
| monitor.done(); |
| } |
| |
| return _createdType; |
| } |
| |
| private CompilationUnit createASTForImports(ICompilationUnit cu) { |
| ASTParser parser= ASTParser.newParser(AST.JLS9); |
| parser.setSource(cu); |
| parser.setResolveBindings(false); |
| parser.setFocalPosition(0); |
| return (CompilationUnit) parser.createAST(null); |
| } |
| |
| |
| private Set<String> getExistingImports(CompilationUnit root) { |
| List imports= root.imports(); |
| Set<String> res= new HashSet<String>(imports.size()); |
| for (int i= 0; i < imports.size(); i++) { |
| res.add(ASTNodes.asString((ImportDeclaration) imports.get(i))); |
| } |
| return res; |
| } |
| |
| private boolean isValidComment(String template) { |
| IScanner scanner= ToolFactory.createScanner(true, false, false, false); |
| scanner.setSource(template.toCharArray()); |
| try { |
| int next= scanner.getNextToken(); |
| while (TokenScanner.isComment(next)) { |
| next= scanner.getNextToken(); |
| } |
| return next == ITerminalSymbols.TokenNameEOF; |
| } catch (InvalidInputException e) { |
| } |
| return false; |
| } |
| |
| /** |
| * @deprecated Instead of file templates, the new type code template |
| * specifies the stub for a compilation unit. |
| */ |
| protected String getFileComment(ICompilationUnit parentCU) { |
| return null; |
| } |
| |
| /** |
| * Hook method that gets called from <code>createType</code> to retrieve |
| * a file comment. This default implementation returns the content of the |
| * 'file comment' template or <code>null</code> if no comment should be created. |
| * |
| * @param parentCU the parent compilation unit |
| * @param lineDelimiter the line delimiter to use |
| * @return the file comment or <code>null</code> if a file comment |
| * is not desired |
| * @throws CoreException |
| * |
| * @since 3.1 |
| */ |
| protected String getFileComment(ICompilationUnit parentCU, String lineDelimiter) throws CoreException { |
| if (this._addComments) { |
| return CodeGeneration.getFileComment(parentCU, lineDelimiter); |
| } |
| return null; |
| } |
| |
| /** |
| * Hook method that gets called from <code>createType</code> to retrieve |
| * a type comment. This default implementation returns the content of the |
| * 'type comment' template. |
| * |
| * @param parentCU the parent compilation unit |
| * @param lineDelimiter the line delimiter to use |
| * @return the type comment or <code>null</code> if a type comment |
| * is not desired |
| * |
| * @since 3.0 |
| */ |
| protected String getTypeComment(ICompilationUnit parentCU, String lineDelimiter) { |
| try { |
| StringBuffer typeName= new StringBuffer(); |
| // if (isEnclosingTypeSelected()) { |
| // typeName.append(JavaModelUtil.getTypeQualifiedName(getEnclosingType(page))).append('.'); |
| // } |
| typeName.append(_typeInfo.getTypeName()); |
| String comment= CodeGeneration.getTypeComment(parentCU, typeName.toString(), lineDelimiter); |
| if (comment != null && isValidComment(comment)) { |
| return comment; |
| } |
| } catch (CoreException e) { |
| JavaPlugin.log(e); |
| } |
| return null; |
| } |
| |
| private void removeUnusedImports(ICompilationUnit cu, Set<String> existingImports, boolean needsSave) throws CoreException { |
| ASTParser parser= ASTParser.newParser(AST.JLS9); |
| parser.setSource(cu); |
| parser.setResolveBindings(true); |
| |
| CompilationUnit root= (CompilationUnit) parser.createAST(null); |
| if (root.getProblems().length == 0) { |
| return; |
| } |
| |
| List importsDecls= root.imports(); |
| if (importsDecls.isEmpty()) { |
| return; |
| } |
| ImportsManager imports= new ImportsManager(root); |
| |
| int importsEnd= ASTNodes.getExclusiveEnd((ASTNode) importsDecls.get(importsDecls.size() - 1)); |
| IProblem[] problems= root.getProblems(); |
| for (int i= 0; i < problems.length; i++) { |
| IProblem curr= problems[i]; |
| if (curr.getSourceEnd() < importsEnd) { |
| int id= curr.getID(); |
| if (id == IProblem.UnusedImport || id == IProblem.NotVisibleType) { // not visible problems hide unused -> remove both |
| int pos= curr.getSourceStart(); |
| for (int k= 0; k < importsDecls.size(); k++) { |
| ImportDeclaration decl= (ImportDeclaration) importsDecls.get(k); |
| if (decl.getStartPosition() <= pos && pos < decl.getStartPosition() + decl.getLength()) { |
| if (existingImports.isEmpty() || !existingImports.contains(ASTNodes.asString(decl))) { |
| String name= decl.getName().getFullyQualifiedName(); |
| if (decl.isOnDemand()) { |
| name += ".*"; //$NON-NLS-1$ |
| } |
| if (decl.isStatic()) { |
| imports.removeStaticImport(name); |
| } else { |
| imports.removeImport(name); |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| imports.create(needsSave, null); |
| } |
| |
| |
| private IType getEnclosingType() throws CoreException |
| { |
| IType enclosingType = null; |
| |
| if (_typeInfo.getEnclosingTypeName().trim().length() != 0) |
| { |
| try |
| { |
| enclosingType = _typeInfo.getPkgFragmentRoot().getJavaProject().findType(_typeInfo.getEnclosingTypeName()); |
| |
| if ((enclosingType == null) |
| || (enclosingType.getCompilationUnit() == null)) |
| { |
| throw new Exception("The enclosing type " //$NON-NLS-1$ |
| + _typeInfo.getEnclosingTypeName() |
| + " or its compilation unit does not exist."); //$NON-NLS-1$ |
| } |
| |
| if (!JavaModelUtil.isEditable(enclosingType.getCompilationUnit())) |
| { |
| throw new Exception("The compilation unit of the enclosing type " //$NON-NLS-1$ |
| + _typeInfo.getEnclosingTypeName() |
| + " is not editable!"); //$NON-NLS-1$ |
| } |
| } |
| catch (Exception ex) |
| { |
| throw new CoreException(new Status(IStatus.ERROR, |
| OTDTUIPluginConstants.UIPLUGIN_ID, |
| IStatus.OK, |
| ex.getMessage(), |
| null)); |
| } |
| } |
| return enclosingType; |
| } |
| |
| private IPackageFragment getPackageFragment() |
| throws JavaModelException, CoreException |
| { |
| |
| IPackageFragment pkgFragment = null; |
| IType enclosingType = getEnclosingType(); |
| |
| if (enclosingType == null) |
| { |
| pkgFragment = _typeInfo.getPkgFragment(); |
| if (pkgFragment == null) |
| { |
| pkgFragment = _typeInfo.getPkgFragmentRoot().getPackageFragment(""); //$NON-NLS-1$ |
| } |
| } |
| else |
| { |
| if (_typeInfo.isInlineType()) |
| { |
| pkgFragment = enclosingType.getPackageFragment(); |
| } |
| else //external defined role class (= role class with its own file) |
| { |
| String qualifiedEnclosingTypeName = getEnclosingType().getFullyQualifiedName('.'); |
| |
| pkgFragment = _typeInfo.getPkgFragmentRoot().getPackageFragment(qualifiedEnclosingTypeName); |
| } |
| |
| } |
| |
| if (!pkgFragment.exists()) |
| { |
| pkgFragment = _typeInfo.getPkgFragmentRoot().createPackageFragment(pkgFragment.getElementName(), true, null); |
| } |
| |
| return pkgFragment; |
| } |
| |
| |
| protected void validateTypeCreation() |
| throws CoreException |
| { |
| if (_typeInfo.isInlineType() && (_typeInfo.getEnclosingTypeName().trim().length() == 0)) |
| { |
| throw new CoreException(new Status(IStatus.ERROR, |
| OTDTUIPluginConstants.UIPLUGIN_ID, |
| IStatus.OK, |
| "The class " + _typeInfo.getTypeName() //$NON-NLS-1$ |
| + " is declared to be an inner class" //$NON-NLS-1$ |
| + " but fails to specify its enclosing type.", //$NON-NLS-1$ |
| null)); |
| } |
| } |
| |
| private String constructSimpleTypeStub() { |
| StringBuffer buf= new StringBuffer("public class "); //$NON-NLS-1$ |
| buf.append(_typeInfo.getTypeName()); |
| buf.append("{ }"); //$NON-NLS-1$ |
| return buf.toString(); |
| } |
| |
| /* |
| * Called from createType to construct the source for this type |
| */ |
| private String constructTypeStub(ICompilationUnit parentCU, ImportsManager imports, String lineDelimiter) throws CoreException |
| { |
| StringBuffer buf= new StringBuffer(); |
| |
| buf.append(Flags.toString(_typeInfo.getModifiers())); |
| |
| if (_typeInfo.getModifiers() != 0) |
| { |
| buf.append(' '); |
| } |
| |
| buf.append("class "); //$NON-NLS-1$ |
| |
| buf.append(_typeInfo.getTypeName()); |
| |
| writeInheritanceRelations(imports, buf); |
| |
| buf.append(" {").append(lineDelimiter); //$NON-NLS-1$ |
| String typeBody= CodeGeneration.getTypeBody(CodeGeneration.CLASS_BODY_TEMPLATE_ID, parentCU, _typeInfo.getTypeName(), lineDelimiter); |
| if (typeBody != null) { |
| buf.append(typeBody); |
| } else { |
| buf.append(lineDelimiter); |
| } |
| buf.append('}').append(lineDelimiter); |
| |
| return buf.toString(); |
| } |
| |
| |
| |
| |
| protected void writeInheritanceRelations(ImportsManager imports, StringBuffer buf) throws CoreException |
| { |
| writeSuperClass(buf, imports); |
| |
| writeSuperInterfaces(buf, imports); |
| } |
| |
| private void writeSuperClass(StringBuffer buf, ImportsManager imports) throws CoreException { |
| String superclass= _typeInfo.getSuperClassName(); |
| if (superclass.length() > 0 && !"java.lang.Object".equals(superclass) //$NON-NLS-1$ |
| //{ObjectTeams: also ignore default supertype (TPX-430) |
| && ! String.valueOf(IOTConstants.STR_ORG_OBJECTTEAMS_TEAM).equals(superclass) |
| // km} |
| |
| ) { |
| buf.append(" extends "); //$NON-NLS-1$ |
| |
| ITypeBinding binding= TypeContextChecker.resolveSuperClass(superclass, _typeInfo.getCurrentType(), getSuperClassStubTypeContext()); |
| if (binding != null) { |
| buf.append(imports.addImport(binding)); |
| } else { |
| buf.append(imports.addImport(superclass)); |
| } |
| } |
| } |
| |
| protected StubTypeContext getSuperClassStubTypeContext() throws CoreException { |
| if (fSuperClassStubTypeContext == null) { |
| String typeName; |
| if (_typeInfo.getCurrentType() != null) { |
| typeName= _typeInfo.getTypeName(); |
| } else { |
| typeName= JavaTypeCompletionProcessor.DUMMY_CLASS_NAME; |
| } |
| fSuperClassStubTypeContext= TypeContextChecker.createSuperClassStubTypeContext(typeName, getEnclosingType(), getPackageFragment()); |
| } |
| return fSuperClassStubTypeContext; |
| |
| |
| } |
| |
| protected StubTypeContext getBaseTypeStubTypeContext() throws CoreException { |
| if (fSuperClassStubTypeContext == null) { |
| String typeName; |
| if (_typeInfo.getCurrentType() != null) { |
| typeName= _typeInfo.getTypeName(); |
| } else { |
| typeName= JavaTypeCompletionProcessor.DUMMY_CLASS_NAME; |
| } |
| // may want to check if base type is an interface, but so far constant 'false' below seems to work OK |
| fSuperClassStubTypeContext= createBaseTypeStubTypeContext(typeName, false, getEnclosingType(), getPackageFragment()); |
| } |
| return fSuperClassStubTypeContext; |
| } |
| |
| // cf. TypeContextChecker.createSupertypeStubTypeContext |
| private StubTypeContext createBaseTypeStubTypeContext(String typeName, boolean isInterface, IType enclosingType, IPackageFragment packageFragment) { |
| if (enclosingType == null) { |
| JavaPlugin.log(new IllegalArgumentException("Missing enclosing type")); //$NON-NLS-1$ |
| return new StubTypeContext(null, null, null); |
| } else { |
| // notes: |
| // + when resolving mypack.base.MyBase we have to cope with a 'base' package fragment, |
| // that in the final source will be handled using a base import, but here we have |
| // to include this with FQN in the class declaration, which implies: |
| // - do not use a team so the scanner will see 'base' as an identifier |
| ICompilationUnit cu= enclosingType.getCompilationUnit(); |
| StringBuffer teamString = new StringBuffer(); |
| teamString.append("package ").append(packageFragment.getElementName()).append(";\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| teamString.append("public"); //$NON-NLS-1$ |
| int levels = writeClassHeader(enclosingType, teamString); |
| for (int i = 0; i < levels; i++) |
| teamString.append('}'); |
| |
| String prolog= "class " + typeName + (isInterface ? " implements " : " extends "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| String epilog= " {} "; //$NON-NLS-1$ |
| return new StubTypeContext(cu, teamString.toString() + prolog, epilog); |
| } |
| } |
| |
| private int writeClassHeader(IType type, StringBuffer buf) { |
| IJavaElement parent = type.getParent(); |
| int nestingLevels = 1; |
| if (parent instanceof IType) { |
| nestingLevels += writeClassHeader((IType) parent, buf); |
| } |
| buf.append(" class ").append(type.getElementName()).append(" {"); //$NON-NLS-1$ //$NON-NLS-2$ |
| return nestingLevels; |
| } |
| |
| private void writeSuperInterfaces(StringBuffer buf, ImportsManager imports) throws CoreException |
| { |
| List<String> interfaces= _typeInfo.getSuperInterfacesNames(); |
| int last= interfaces.size() - 1; |
| if (last >= 0) { |
| buf.append(" implements "); //$NON-NLS-1$ |
| |
| String[] intfs= interfaces.toArray(new String[interfaces.size()]); |
| ITypeBinding[] bindings; |
| IType currentType = _typeInfo.getCurrentType(); |
| if (currentType != null) { |
| bindings= TypeContextChecker.resolveSuperInterfaces(intfs, currentType, getSuperInterfacesStubTypeContext()); |
| } else { |
| bindings= new ITypeBinding[intfs.length]; |
| } |
| for (int i= 0; i <= last; i++) { |
| ITypeBinding binding= bindings[i]; |
| if (binding != null) { |
| buf.append(imports.addImport(binding)); |
| } else { |
| buf.append(imports.addImport(intfs[i])); |
| } |
| if (i < last) { |
| buf.append(','); |
| } |
| } |
| } |
| } |
| |
| |
| protected StubTypeContext getSuperInterfacesStubTypeContext() throws CoreException { |
| if (fSuperInterfaceStubTypeContext == null) { |
| String typeName; |
| if (_typeInfo != null) { |
| typeName= _typeInfo.getTypeName(); |
| } else { |
| typeName= JavaTypeCompletionProcessor.DUMMY_CLASS_NAME; |
| } |
| fSuperInterfaceStubTypeContext= TypeContextChecker.createSuperInterfaceStubTypeContext(typeName, getEnclosingType(), getPackageFragment()); |
| } |
| return fSuperInterfaceStubTypeContext; |
| } |
| |
| //{OTDTUI |
| private String createCUHeaderFromScratch(IPackageFragment pack, String lineDelimiter) |
| { |
| StringBuffer buf= new StringBuffer(); |
| // external role classes never have a normal package, they reference their enclosing team as package |
| if (_typeInfo.isRole()) |
| { |
| buf.append("team "); //$NON-NLS-1$ |
| buf.append("package ").append(pack.getElementName()).append(';'); //$NON-NLS-1$ |
| } |
| else |
| { |
| if (!pack.isDefaultPackage()) |
| { |
| buf.append("package ").append(pack.getElementName()).append(';'); //$NON-NLS-1$ |
| } |
| } |
| buf.append(lineDelimiter).append(lineDelimiter); |
| return buf.toString(); |
| } |
| //carp} |
| |
| //{OTDTUI: OT_COPY_PASTE: replacement for StubUtility.getCompilationUnitContent() |
| private String getCompilationUnitContent(ICompilationUnit cu, String fileComment, String typeComment, String typeContent, String lineDelimiter) throws CoreException { |
| //{OTDTUI: added team modifier |
| IPackageFragment pack= (IPackageFragment) cu.getParent(); |
| String packageString = _typeInfo.isRole() ? "team package " : "package "; //$NON-NLS-1$ //$NON-NLS-2$ |
| String packDecl= pack.isDefaultPackage() ? "" : packageString + pack.getElementName() + ';'; //$NON-NLS-1$ |
| //carp} |
| |
| Template template= getCodeTemplate(CodeTemplateContextType.NEWTYPE_ID, cu.getJavaProject()); |
| if (template == null) { |
| return null; |
| } |
| |
| IJavaProject project= cu.getJavaProject(); |
| CodeTemplateContext context= new CodeTemplateContext(template.getContextTypeId(), project, lineDelimiter); |
| context.setCompilationUnitVariables(cu); |
| context.setVariable(CodeTemplateContextType.PACKAGE_DECLARATION, packDecl); |
| context.setVariable(CodeTemplateContextType.TYPE_COMMENT, typeComment != null ? typeComment : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.FILE_COMMENT, fileComment != null ? fileComment : ""); //$NON-NLS-1$ |
| context.setVariable(CodeTemplateContextType.TYPE_DECLARATION, typeContent); |
| context.setVariable(CodeTemplateContextType.TYPENAME, JavaCore.removeJavaLikeExtension(cu.getElementName())); |
| |
| String[] fullLine= { CodeTemplateContextType.PACKAGE_DECLARATION, CodeTemplateContextType.FILE_COMMENT, CodeTemplateContextType.TYPE_COMMENT }; |
| return evaluateTemplate(context, template, fullLine); |
| } |
| |
| private static Template getCodeTemplate(String id, IJavaProject project) { |
| if (project == null) |
| return JavaPlugin.getDefault().getCodeTemplateStore().findTemplateById(id); |
| ProjectTemplateStore projectStore= new ProjectTemplateStore(project.getProject()); |
| try { |
| projectStore.load(); |
| } catch (IOException e) { |
| JavaPlugin.log(e); |
| } |
| return projectStore.findTemplateById(id); |
| } |
| |
| |
| private static String evaluateTemplate(CodeTemplateContext context, Template template, String[] fullLineVariables) throws CoreException { |
| TemplateBuffer buffer; |
| try { |
| buffer= context.evaluate(template); |
| if (buffer == null) |
| return null; |
| String str= fixEmptyVariables(buffer, fullLineVariables); |
| if (Strings.containsOnlyWhitespaces(str)) { |
| return null; |
| } |
| return str; |
| } catch (BadLocationException e) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } catch (TemplateException e) { |
| throw new CoreException(Status.CANCEL_STATUS); |
| } |
| } |
| |
| // remove lines for empty variables |
| private static String fixEmptyVariables(TemplateBuffer buffer, String[] variables) throws MalformedTreeException, BadLocationException { |
| IDocument doc= new Document(buffer.getString()); |
| int nLines= doc.getNumberOfLines(); |
| MultiTextEdit edit= new MultiTextEdit(); |
| HashSet<Integer> removedLines= new HashSet<Integer>(); |
| for (int i= 0; i < variables.length; i++) { |
| TemplateVariable position= findVariable(buffer, variables[i]); // look if Javadoc tags have to be added |
| if (position == null || position.getLength() > 0) { |
| continue; |
| } |
| int[] offsets= position.getOffsets(); |
| for (int k= 0; k < offsets.length; k++) { |
| int line= doc.getLineOfOffset(offsets[k]); |
| IRegion lineInfo= doc.getLineInformation(line); |
| int offset= lineInfo.getOffset(); |
| String str= doc.get(offset, lineInfo.getLength()); |
| if (Strings.containsOnlyWhitespaces(str) && nLines > line + 1 && removedLines.add(new Integer(line))) { |
| int nextStart= doc.getLineOffset(line + 1); |
| edit.addChild(new DeleteEdit(offset, nextStart - offset)); |
| } |
| } |
| } |
| edit.apply(doc, 0); |
| return doc.get(); |
| } |
| private static TemplateVariable findVariable(TemplateBuffer buffer, String variable) { |
| TemplateVariable[] positions= buffer.getVariables(); |
| for (int i= 0; i < positions.length; i++) { |
| TemplateVariable curr= positions[i]; |
| if (variable.equals(curr.getType())) { |
| return curr; |
| } |
| } |
| return null; |
| } |
| |
| |
| //end OT_COPY_PASTE |
| //carp} |
| |
| /** |
| * Uses the New Java file template from the code template page to generate a |
| * compilation unit with the given type content. |
| * @param cu The new created compilation unit |
| * @param typeContent The content of the type, including signature and type |
| * body. |
| * @param lineDelimiter The line delimiter to be used. |
| * @return String Returns the result of evaluating the new file template |
| * with the given type content. |
| * @throws CoreException |
| * @since 2.1 |
| */ |
| protected String constructCUContent(ICompilationUnit cu, String typeContent, String lineDelimiter) throws CoreException { |
| String fileComment= getFileComment(cu, lineDelimiter); |
| String typeComment= getTypeComment(cu, lineDelimiter); |
| IPackageFragment pack= (IPackageFragment) cu.getParent(); |
| // String content= CodeGeneration.getCompilationUnitContent(cu, fileComment, typeComment, typeContent, lineDelimiter); |
| //{OTDTUI: use own own code generation (team modifier!) |
| String content= getCompilationUnitContent(cu, fileComment, typeComment, typeContent, lineDelimiter); |
| //carp} |
| if (content != null) { |
| ASTParser parser= ASTParser.newParser(AST.JLS9); |
| //OTDTUI: set unit name and project for role-file handling in parser -- probably obsolete! |
| parser.setUnitName(cu.getPath().toString()); |
| //carp} |
| |
| parser.setProject(cu.getJavaProject()); |
| parser.setSource(content.toCharArray()); |
| CompilationUnit unit= (CompilationUnit) parser.createAST(null); |
| if ((pack.isDefaultPackage() || unit.getPackage() != null) && !unit.types().isEmpty()) { |
| return content; |
| } |
| } |
| |
| //{OTDTUI: adapted to add the "team" package modifier |
| System.out.println("TypeCreator: does this ever happen?"); |
| StringBuffer buf= new StringBuffer(); |
| buf.append (createCUHeaderFromScratch(pack, lineDelimiter)); |
| //carp} |
| if (typeComment != null) { |
| buf.append(typeComment).append(lineDelimiter); |
| } |
| buf.append(typeContent); |
| return buf.toString(); |
| } |
| |
| private void createTypeMembers(IType type, ImportsManager imports, IProgressMonitor monitor) |
| throws CoreException |
| { |
| createInheritedMethods(type, imports, new SubProgressMonitor(monitor, 1)); |
| |
| if(_typeInfo.isCreateMainMethod()) |
| { |
| StringBuffer buf = new StringBuffer(); |
| buf.append("public static void main("); //$NON-NLS-1$ |
| buf.append(imports.addImport("java.lang.String")); //$NON-NLS-1$ |
| buf.append("[] args) {}"); //$NON-NLS-1$ |
| type.createMethod(buf.toString(), null, false, null); |
| } |
| |
| if(monitor != null) |
| { |
| monitor.done(); |
| } |
| } |
| |
| /** |
| * Copied from NewTypeWizardPage.createInheritedMethods |
| * doXYZ Parameters replaced by is-Methods in _typeInfo |
| */ |
| protected IMethod[] createInheritedMethods(IType type, ImportsManager imports, IProgressMonitor monitor) |
| throws CoreException |
| { |
| final ICompilationUnit cu= type.getCompilationUnit(); |
| JavaModelUtil.reconcile(cu); |
| IMethod[] typeMethods= type.getMethods(); |
| Set<String> handleIds= new HashSet<String>(typeMethods.length); |
| for (int index= 0; index < typeMethods.length; index++) |
| handleIds.add(typeMethods[index].getHandleIdentifier()); |
| ArrayList<IMethod> newMethods= new ArrayList<IMethod>(); |
| CodeGenerationSettings settings= JavaPreferencesSettings.getCodeGenerationSettings(type.getJavaProject()); |
| settings.createComments= this._addComments; |
| ASTParser parser= ASTParser.newParser(AST.JLS9); |
| parser.setResolveBindings(true); |
| parser.setSource(cu); |
| CompilationUnit unit= (CompilationUnit) parser.createAST(new SubProgressMonitor(monitor, 1)); |
| final ITypeBinding binding= ASTNodes.getTypeBinding(unit, type); |
| if (binding != null) { |
| if (_typeInfo.isCreateAbstractInheritedMethods()) { |
| AddUnimplementedMethodsOperation operation= new AddUnimplementedMethodsOperation(unit, binding, null, -1, false, true, false); |
| operation.setCreateComments(this._addComments); |
| operation.run(monitor); |
| createImports(imports, operation.getCreatedImports()); |
| } |
| if (_typeInfo.isCreateConstructor()) { |
| AddUnimplementedConstructorsOperation operation= new AddUnimplementedConstructorsOperation(unit, binding, null, -1, false, true, false); |
| operation.setCreateComments(this._addComments); |
| operation.run(monitor); |
| createImports(imports, operation.getCreatedImports()); |
| } |
| } |
| JavaModelUtil.reconcile(cu); |
| typeMethods= type.getMethods(); |
| for (int index= 0; index < typeMethods.length; index++) |
| if (!handleIds.contains(typeMethods[index].getHandleIdentifier())) |
| newMethods.add(typeMethods[index]); |
| IMethod[] methods= new IMethod[newMethods.size()]; |
| newMethods.toArray(methods); |
| return methods; |
| } |
| |
| private void createImports(ImportsManager imports, String[] createdImports) { |
| for (int index= 0; index < createdImports.length; index++) |
| imports.addImport(createdImports[index]); |
| } |
| |
| |
| /** |
| * Class used in stub creation routines to add needed imports to a |
| * compilation unit. |
| */ |
| public static class ImportsManager |
| { |
| ImportRewrite fImportsRewrite; |
| ImportRewrite fTeamImportsRewrite; // a Rofi creates its base imports here into the team CU |
| boolean fHasAddedBaseImportsForRofi; // whether a base import has been added using fTeamImportsRewrite |
| |
| /* package */ ImportsManager(CompilationUnit astRoot) throws CoreException { |
| fImportsRewrite= StubUtility.createImportRewrite(astRoot, true); |
| } |
| |
| /* package */ ImportsManager(CompilationUnit astRoot, CompilationUnit teamAST) throws CoreException { |
| fImportsRewrite= StubUtility.createImportRewrite(astRoot, true); |
| fTeamImportsRewrite= StubUtility.createImportRewrite(teamAST, true); |
| } |
| |
| /* package */ ImportsManager(ICompilationUnit createdWorkingCopy) throws CoreException { |
| fImportsRewrite= StubUtility.createImportRewrite(createdWorkingCopy, true); |
| } |
| |
| /* package */ ICompilationUnit getCompilationUnit() { |
| return fImportsRewrite.getCompilationUnit(); |
| } |
| |
| /** |
| * Adds a new import declaration that is sorted in the existing imports. |
| * If an import already exists or the import would conflict with an import |
| * of an other type with the same simple name, the import is not added. |
| * |
| * @param qualifiedTypeName The fully qualified name of the type to import |
| * (dot separated). |
| * @return Returns the simple type name that can be used in the code or the |
| * fully qualified type name if an import conflict prevented the import. |
| */ |
| public String addImport(String qualifiedTypeName) { |
| return fImportsRewrite.addImport(qualifiedTypeName); |
| } |
| |
| /** |
| * Adds a new import declaration that is sorted in the existing imports. |
| * If an import already exists or the import would conflict with an import |
| * of an other type with the same simple name, the import is not added. |
| * |
| * @param typeBinding the binding of the type to import |
| * |
| * @return Returns the simple type name that can be used in the code or the |
| * fully qualified type name if an import conflict prevented the import. |
| */ |
| public String addImport(ITypeBinding typeBinding) { |
| return fImportsRewrite.addImport(typeBinding); |
| } |
| |
| /* package */ void create(boolean needsSave, IProgressMonitor monitor) throws CoreException { |
| TextEdit edit= fImportsRewrite.rewriteImports(monitor); |
| JavaModelUtil.applyEdit(fImportsRewrite.getCompilationUnit(), edit, needsSave, null); |
| |
| if (fTeamImportsRewrite != null) { |
| edit= fTeamImportsRewrite.rewriteImports(monitor); |
| fHasAddedBaseImportsForRofi = edit.hasChildren(); |
| JavaModelUtil.applyEdit(fTeamImportsRewrite.getCompilationUnit(), edit, needsSave, null); |
| } |
| } |
| |
| /* package */ void removeImport(String qualifiedName) { |
| fImportsRewrite.removeImport(qualifiedName); |
| } |
| |
| /* package */ void removeStaticImport(String qualifiedName) { |
| fImportsRewrite.removeStaticImport(qualifiedName); |
| } |
| |
| } |
| } |