| /******************************************************************************* |
| * Copyright (c) 2000, 2009 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.dltk.javascript.internal.corext.codemanipulation; |
| |
| import static org.eclipse.dltk.javascript.ast.MultiLineComment.JSDOC_PREFIX; |
| |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.filebuffers.ITextFileBufferManager; |
| import org.eclipse.core.filebuffers.LocationKind; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.ISchedulingRule; |
| import org.eclipse.dltk.compiler.CharOperation; |
| import org.eclipse.dltk.compiler.InvalidInputException; |
| import org.eclipse.dltk.core.IField; |
| import org.eclipse.dltk.core.IMember; |
| import org.eclipse.dltk.core.IMethod; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ISourceRange; |
| import org.eclipse.dltk.core.IType; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.internal.ui.DLTKUIStatus; |
| import org.eclipse.dltk.javascript.scriptdoc.PublicScanner; |
| import org.eclipse.dltk.javascript.scriptdoc.ScriptdocContentAccess; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.text.edits.InsertEdit; |
| import org.eclipse.text.edits.MultiTextEdit; |
| import org.eclipse.text.edits.ReplaceEdit; |
| |
| /** |
| * Add javadoc stubs to members. All members must belong to the same compilation |
| * unit. If the parent type is open in an editor, be sure to pass over its |
| * working copy. |
| */ |
| public class AddJavaDocStubOperation implements IWorkspaceRunnable { |
| |
| private IMember[] fMembers; |
| |
| public AddJavaDocStubOperation(IMember[] members) { |
| Assert.isLegal(members.length > 0); |
| fMembers = members; |
| } |
| |
| private String createTypeComment(IType type, String lineDelimiter) |
| throws CoreException { |
| String[] typeParameterNames = CharOperation.NO_STRINGS; |
| // StubUtility.getTypeParameterNames(type.getTypeParameters()); |
| return JSCodeGeneration.getTypeComment(type.getSourceModule(), |
| type.getTypeQualifiedName("."), typeParameterNames, |
| lineDelimiter); |
| } |
| |
| private String createMethodComment(IMethod meth, String lineDelimiter) |
| throws CoreException { |
| // IType declaringType = meth.getDeclaringType(); |
| |
| IMethod overridden = null; |
| if (!meth.isConstructor()) { |
| // ITypeHierarchy hierarchy = SuperTypeHierarchyCache |
| // .getTypeHierarchy(declaringType); |
| // MethodOverrideTester tester = new MethodOverrideTester( |
| // declaringType, hierarchy); |
| // overridden = tester.findOverriddenMethod(meth, true); |
| } |
| return JSCodeGeneration.getMethodComment(meth, overridden, |
| lineDelimiter); |
| } |
| |
| private String createFieldComment(IField field, String lineDelimiter) |
| throws ModelException, CoreException { |
| String existingComment = null; |
| try { |
| ISourceRange range = ScriptdocContentAccess.getJavadocRange(field); |
| if (range != null) { |
| ISourceModule compilationUnit = field.getSourceModule(); |
| existingComment = compilationUnit.getBuffer().getText( |
| range.getOffset(), range.getLength()); |
| |
| } |
| } catch (Exception e) { |
| // ignore |
| } |
| |
| String typeName = Signature.toString(field.getType()); |
| if (existingComment != null |
| && (typeName == null || existingComment.indexOf("@type " |
| + typeName) != -1)) |
| return existingComment; |
| String fieldName = field.getElementName(); |
| return JSCodeGeneration.getFieldComment(field.getSourceModule(), |
| typeName, fieldName, lineDelimiter); |
| } |
| |
| /** |
| * @return Returns the scheduling rule for this operation |
| */ |
| public ISchedulingRule getScheduleRule() { |
| return fMembers[0].getResource(); |
| } |
| |
| /* |
| * @see |
| * org.eclipse.core.resources.IWorkspaceRunnable#run(org.eclipse.core.runtime |
| * .IProgressMonitor) |
| */ |
| public void run(IProgressMonitor monitor) throws CoreException, |
| OperationCanceledException { |
| if (monitor == null) |
| monitor = new NullProgressMonitor(); |
| |
| try { |
| monitor.beginTask("Create Javadoc stub...", fMembers.length + 2); |
| |
| addJavadocComments(monitor); |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| private void addJavadocComments(IProgressMonitor monitor) |
| throws CoreException { |
| ISourceModule cu = fMembers[0].getSourceModule(); |
| |
| ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager(); |
| IPath path = cu.getPath(); |
| |
| manager.connect(path, LocationKind.IFILE, new SubProgressMonitor( |
| monitor, 1)); |
| try { |
| IDocument document = manager.getTextFileBuffer(path, |
| LocationKind.IFILE).getDocument(); |
| |
| String lineDelim = TextUtilities.getDefaultLineDelimiter(document); |
| MultiTextEdit edit = new MultiTextEdit(); |
| |
| for (int i = 0; i < fMembers.length; i++) { |
| IMember curr = fMembers[i]; |
| int memberStartOffset = getMemberStartOffset(curr, document); |
| |
| String comment = null; |
| switch (curr.getElementType()) { |
| case IModelElement.TYPE: |
| comment = createTypeComment((IType) curr, lineDelim); |
| break; |
| case IModelElement.FIELD: |
| comment = createFieldComment((IField) curr, lineDelim); |
| break; |
| case IModelElement.METHOD: |
| comment = createMethodComment((IMethod) curr, lineDelim); |
| break; |
| } |
| if (comment == null) { |
| StringBuffer buf = new StringBuffer(); |
| buf.append(JSDOC_PREFIX).append(lineDelim); |
| buf.append(" *").append(lineDelim); //$NON-NLS-1$ |
| buf.append(" */").append(lineDelim); //$NON-NLS-1$ |
| comment = buf.toString(); |
| } else { |
| if (!comment.endsWith(lineDelim)) { |
| comment = comment + lineDelim; |
| } |
| } |
| |
| final IScriptProject project = cu.getScriptProject(); |
| IRegion region = document |
| .getLineInformationOfOffset(memberStartOffset); |
| |
| String line = document.get(region.getOffset(), |
| region.getLength()); |
| String indentString = JSCodeGeneration.getIndentString(line, |
| project); |
| |
| String indentedComment = JSCodeGeneration.changeIndent(comment, |
| 0, project, indentString, lineDelim); |
| |
| ISourceRange range = ScriptdocContentAccess |
| .getJavadocRange(curr); |
| if (range != null) { |
| if (curr.getElementType() == IModelElement.TYPE) { |
| // don't override type comment |
| continue; |
| } |
| final int begin = range.getOffset(); |
| int end = begin + range.getLength(); |
| while (end < document.getLength() |
| && Character.isWhitespace(document.getChar(end))) { |
| ++end; |
| } |
| if (!indentedComment.equals(document |
| .get(begin, end - begin))) { |
| edit.addChild(new ReplaceEdit(begin, end - begin, |
| indentedComment)); |
| } |
| } else { |
| edit.addChild(new InsertEdit(memberStartOffset, |
| indentedComment)); |
| } |
| |
| monitor.worked(1); |
| } |
| edit.apply(document); // apply all edits |
| } catch (BadLocationException e) { |
| throw new CoreException(DLTKUIStatus.createError(IStatus.ERROR, e)); |
| } finally { |
| manager.disconnect(path, LocationKind.IFILE, |
| new SubProgressMonitor(monitor, 1)); |
| } |
| } |
| |
| private int getMemberStartOffset(IMember curr, IDocument document) |
| throws ModelException { |
| String docStr = document.get(); |
| int offset = curr.getSourceRange().getOffset(); |
| String substring = docStr.substring(0, offset); |
| if (substring.endsWith("var ")) { |
| offset -= 4; |
| } else if (substring.endsWith("this.")) { |
| offset -= 5; |
| } |
| |
| final PublicScanner scanner = new PublicScanner(); |
| scanner.setSource(docStr.toCharArray()); |
| |
| try { |
| // read to the first real non comment token |
| scanner.resetTo(offset, scanner.getSource().length - 1); |
| scanner.getNextToken(); |
| return scanner.getCurrentTokenStartPosition(); |
| } catch (InvalidInputException e) { |
| // ignore |
| } |
| return offset; |
| } |
| |
| } |