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