/*******************************************************************************
* Copyright (c) 2007 Business Objects Software Limited and others.
* All rights reserved.
* This file is 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
*
* Contributors:
* Business Objects Software Limited - initial API and implementation
*******************************************************************************/
/*
* GenerateElementCommentAction.java
* Creation date: Oct 10, 2007
* By: Greg McClement
*/
package org.openquark.cal.eclipse.ui.caleditor;
import java.util.ResourceBundle;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentRewriteSessionType;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ResourceAction;
import org.eclipse.ui.texteditor.TextEditorAction;
import org.openquark.cal.caldoc.CALDocToJavaDocUtilities;
import org.openquark.cal.compiler.CompilerMessageLogger;
import org.openquark.cal.compiler.DataConstructor;
import org.openquark.cal.compiler.FunctionalAgent;
import org.openquark.cal.compiler.MessageLogger;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.Refactorer;
import org.openquark.cal.compiler.SourceMetricsManager;
import org.openquark.cal.compiler.SourceModel;
import org.openquark.cal.compiler.SourcePosition;
import org.openquark.cal.compiler.SourceRange;
import org.openquark.cal.compiler.TypeClass;
import org.openquark.cal.compiler.TypeConstructor;
import org.openquark.cal.compiler.SourceModel.SourceElement;
import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn;
import org.openquark.cal.eclipse.core.CALModelManager;
import org.openquark.cal.eclipse.core.CALModelManager.SourceManagerFactory;
import org.openquark.cal.eclipse.ui.CALEclipseUIPlugin;
import org.openquark.cal.eclipse.ui.actions.ActionMessages;
import org.openquark.cal.eclipse.ui.actions.ActionUtilities;
import org.openquark.cal.eclipse.ui.actions.IndentAction;
import org.openquark.cal.eclipse.ui.templates.CALTemplateVariableResolver;
import org.openquark.cal.eclipse.ui.util.CoreUtility;
import org.openquark.util.Pair;
/**
* Generates a CAL Doc comment initialized with argument names if possible.
*
* @author Greg McClement
*/
public class GenerateElementCommentAction extends TextEditorAction {
final String errorTitle = ActionMessages.GenerateElementComment_error_title;
private final SourceMetricsManager sourceMetrics;
/**
* Creates and initializes the action for the given text editor. The action
* configures its visual representation from the given resource bundle.
*
* @param bundle the resource bundle
* @param prefix a prefix to be prepended to the various resource keys
* (described in <code>ResourceAction</code> constructor), or
* <code>null</code> if none
* @param editor the text editor
* @see ResourceAction#ResourceAction(ResourceBundle, String, int)
*/
public GenerateElementCommentAction(ResourceBundle bundle, String prefix, ITextEditor editor) {
super(bundle, prefix, editor);
CALModelManager modelManager = CALModelManager.getCALModelManager();
sourceMetrics = modelManager.getSourceMetrics();
}
/**
* Implementation of the <code>IAction</code> prototype. Checks if the selected
* lines are all commented or not and uncomments/comments them respectively.
*/
@Override
public void run() {
// update has been called by the framework
if (!isEnabled()) {
return;
}
if (!CoreUtility.builderEnabledCheck(ActionMessages.OpenDeclarationAction_error_title)){
return;
}
final CALEditor textEditor = (CALEditor) getTextEditor();
final ITextSelection selection = ActionUtilities.getSelection(textEditor);
final PartiallySynchronizedDocument currentDocument = (PartiallySynchronizedDocument) ActionUtilities.getDocument(textEditor);
if (currentDocument != null) {
final int currentOffset = selection.getOffset();
try {
CoreUtility.initializeCALBuilder(null, 100, 100);
final IDocument originalDocument = currentDocument.getOriginalDocument();
final int originalOffset = currentDocument.getOriginalOffset(currentOffset);
final int originalFirstLine = originalDocument.getLineOfOffset(originalOffset);
final int originalColumn = CoreUtility.getColumn(originalFirstLine, originalOffset, originalDocument);
CALModelManager cmm = CALModelManager.getCALModelManager();
FileEditorInput ei = (FileEditorInput) textEditor.getEditorInput();
final IFile memberFile = ei.getFile();
ModuleName moduleName = cmm.getModuleName(memberFile);
final ModuleTypeInfo mti = cmm.getModuleTypeInfo(moduleName);
if (mti == null){
// show message
CoreUtility.showMessage(errorTitle, ActionMessages.error_messageBadSelection_noTypeInformation_CAL, IStatus.ERROR);
return; // bail
}
CompilerMessageLogger messageLogger = new MessageLogger();
Pair<SourceElement, SourceRange> searchResult = sourceMetrics.findContainingSourceElement(moduleName, originalFirstLine+1, originalColumn+1, messageLogger);
if (searchResult == null){
// show message
CoreUtility.showMessage(errorTitle, ActionMessages.GenerateElementComment_error_noSelectedElement, IStatus.ERROR);
return; // bail
}
SourceElement element = searchResult.fst();
// Find the argument names
String[] argumentNames = null;
if (element instanceof SourceModel.FunctionDefn){
SourceModel.FunctionDefn functionDefn = (SourceModel.FunctionDefn) element;
FunctionalAgent scopedEntity = mti.getFunctionOrClassMethod(functionDefn.getName());
if (scopedEntity != null){
argumentNames = CALDocToJavaDocUtilities.getArgumentNamesFromCALDocComment(scopedEntity.getCALDocComment(), scopedEntity);
}
}
else if (element instanceof SourceModel.LocalDefn.Function.Definition){
// SourceModel.LocalDefn.Function.Definition localFunctionDefn = (SourceModel.LocalDefn.Function.Definition) element;
// FunctionalAgent scopedEntity = mti.getFunctionOrClassMethod(functionDefn.getName());
// localFunctionDefn.
// final LocalFunctionIdentifier localFunctionIdentifier = localFunctionDefn.getLocalFunctionIdentifier();
// Function topLevelFunction = mti.getFunction(localFunctionIdentifier.getToplevelFunctionName().getUnqualifiedName());
// if (topLevelFunction != null){
// Function localFunction = topLevelFunction.getLocalFunction(localFunctionIdentifier);
// if (localFunction != null){
// argumentNames = CALDocToJavaDocUtilities.getArgumentNamesFromCALDocComment(scopedEntity.getCALDocComment(), scopedEntity);
// }
// }
}
else if (element instanceof SourceModel.LocalDefn.Function.TypeDeclaration){
}
else if (element instanceof SourceModel.FunctionTypeDeclaration){
SourceModel.FunctionTypeDeclaration functionTypeDeclaration = (SourceModel.FunctionTypeDeclaration) element;
FunctionalAgent scopedEntity = mti.getFunctionOrClassMethod(functionTypeDeclaration.getFunctionName());
if (scopedEntity != null){
argumentNames = CALDocToJavaDocUtilities.getArgumentNamesFromCALDocComment(scopedEntity.getCALDocComment(), scopedEntity);
}
}
else if (element instanceof SourceModel.TypeConstructorDefn){
SourceModel.TypeConstructorDefn tcd = (TypeConstructorDefn) element;
TypeConstructor scopedEntity = mti.getTypeConstructor(tcd.getTypeConsName());
if (scopedEntity != null){
argumentNames = CALDocToJavaDocUtilities.getArgumentNamesFromCALDocComment(scopedEntity.getCALDocComment());
}
}
else if (element instanceof SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn){
SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn dataConsDefn = (SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn) element;
DataConstructor scopedEntity = mti.getDataConstructor(dataConsDefn.getDataConsName());
if (scopedEntity != null){
argumentNames = CALTemplateVariableResolver.getValues(scopedEntity);
}
// argumentNames = CALDocToJavaDocUtilities.getArgumentNamesFromCALDocComment(scopedEntity.getCALDocComment(), scopedEntity);
}
else if (element instanceof SourceModel.TypeClassDefn){
SourceModel.TypeClassDefn tcd = (SourceModel.TypeClassDefn) element;
TypeClass scopedEntity = mti.getTypeClass(tcd.getTypeClassName());
if (scopedEntity != null){
argumentNames = CALDocToJavaDocUtilities.getArgumentNamesFromCALDocComment(scopedEntity.getCALDocComment());
}
}
else if (element instanceof SourceModel.TypeClassDefn.ClassMethodDefn){
SourceModel.TypeClassDefn.ClassMethodDefn tcd = (SourceModel.TypeClassDefn.ClassMethodDefn) element;
FunctionalAgent scopedEntity = mti.getFunctionOrClassMethod(tcd.getMethodName());
if (scopedEntity != null){
argumentNames = CALDocToJavaDocUtilities.getArgumentNamesFromCALDocComment(scopedEntity.getCALDocComment(), scopedEntity);
}
}
else if (element instanceof SourceModel.ModuleDefn){
argumentNames = new String[0];
}
else{
// show message
CoreUtility.showMessage(errorTitle, ActionMessages.GenerateElementComment_error_noSelectedElement, IStatus.ERROR);
return; // bail
}
// build the cal doc
StringBuilder calDocText = new StringBuilder();
final SourceRange originalRange = searchResult.snd();
if (originalRange == null){
// show message
CoreUtility.showMessage(errorTitle, ActionMessages.GenerateElementComment_error_noSelectedElement, IStatus.ERROR);
return; // bail
}
if (argumentNames == null){
// doing this is safer and eliminates a bunch of if's
argumentNames = new String[0];
}
calDocText.append("/**\n");
for(String argumentName : argumentNames){
calDocText.append(" * @arg ");
calDocText.append(argumentName);
calDocText.append("\n");
}
if (argumentNames.length == 0){
calDocText.append(" *\n");
}
calDocText.append(" */\n");
final int nLines = 3 + argumentNames.length;
SourceManagerFactory smf = textEditor.getSourceManagerFactory(true);
final int originalRangeStartOffset = CoreUtility.toOffset(originalRange.getStartSourcePosition(), originalDocument);
final int currentRangeStartOffset = currentDocument.fromOriginalOffset(originalRangeStartOffset);
// line number staring at one like CAL likes
final int currentRangeStartLine = currentDocument.getLineOfOffset(currentRangeStartOffset) + 1;
currentDocument.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED);
try{
Refactorer.ReplaceText refactorer = new Refactorer.ReplaceText(
CALModelManager.getCALModelManager().getModuleContainer(smf),
moduleName,
"",
calDocText.toString(),
currentRangeStartLine,
1);
final int currentFirstLine = originalDocument.getLineOfOffset(currentOffset);
final int currentColumn = CoreUtility.getColumn(currentFirstLine, currentOffset, currentDocument);
refactorer.setCursorPosition(currentFirstLine+1, currentColumn+1);
refactorer.calculateModifications(messageLogger);
if (messageLogger.getNErrors() == 0){
refactorer.apply(messageLogger);
}
IndentAction.indentLine(currentDocument, currentRangeStartLine - 1, nLines, originalOffset);
SourcePosition newSourcePosition = refactorer.getNewCursorPosition();
final int currentStart = CoreUtility.toOffset(newSourcePosition, currentDocument);
textEditor.selectAndReveal(currentStart, 0);
}
finally{
currentDocument.stopRewriteSession(currentDocument.getActiveRewriteSession());
}
} catch (BadLocationException e) {
// will only happen on concurrent modification
CALEclipseUIPlugin.log(new Status(IStatus.ERROR, CALEclipseUIPlugin.PLUGIN_ID, IStatus.OK, "", e)); //$NON-NLS-1$
return;
}
}
}
}