Commit 6f42ebb4 authored by Christoph Döbber's avatar Christoph Döbber
Browse files

YELLOW + cleanup

parent faf250f9
/*--------------------------------------------------------------------------+
$Id: codetemplates.xml 1 2011-01-01 00:00:01Z hoelzl $
| |
| Copyright 2012 ForTISS GmbH |
| |
| 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 org.fortiss.af3.component.ui.editor;
import static org.fortiss.af3.component.ui.utils.ComponentFieldAssistUtils.getInputPortNames;
import static org.fortiss.af3.component.ui.utils.ComponentFieldAssistUtils.getOutputPortNames;
import static org.fortiss.af3.expression.ui.utils.ExpressionFieldAssistUtils.getAllConstructors;
import static org.fortiss.af3.expression.ui.utils.ExpressionFieldAssistUtils.getAllConstructorsWithoutBrackets;
import static org.fortiss.af3.expression.ui.utils.ExpressionFieldAssistUtils.getFunctions;
import static org.fortiss.af3.expression.ui.utils.ExpressionFieldAssistUtils.getPrimitiveTypes;
import static org.fortiss.af3.expression.ui.utils.ExpressionFieldAssistUtils.getPrimitiveValues;
import static org.fortiss.af3.expression.ui.utils.ExpressionFieldAssistUtils.getUsefulExpressions;
import static org.fortiss.tooling.kernel.ui.util.DataBindingUtils.performComplexTextBinding;
import static org.fortiss.tooling.kernel.ui.util.ObservableUtils.observeValue;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WordRule;
import org.eclipse.jface.text.source.AnnotationBarHoverManager;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.AnnotationPainter;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.OverviewRuler;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.actions.TextActionHandler;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.fortiss.af3.component.model.behavior.code.CodePackage;
import org.fortiss.af3.component.model.behavior.code.CodeSpecification;
import org.fortiss.af3.component.ui.editor.code.AnnotationHover;
import org.fortiss.af3.component.ui.editor.code.AnnotationMarkerAccess;
import org.fortiss.af3.component.ui.editor.code.CodeUtils;
import org.fortiss.af3.component.ui.editor.code.ColorCache;
import org.fortiss.af3.component.ui.editor.code.ErrorAnnotation;
import org.fortiss.af3.expression.language.Compiler;
import org.fortiss.af3.expression.language.ParseException;
import org.fortiss.af3.expression.language.TypeSystemHandler;
import org.fortiss.af3.expression.ui.databinding.convert.StatementSequenceToStringConverter;
import org.fortiss.af3.expression.ui.databinding.convert.StringToStatementSequenceConverter;
import org.fortiss.af3.expression.ui.databinding.validate.StringToStatementSequenceValidator;
import org.fortiss.af3.project.typesystem.ITypeSystemCompiler;
import org.fortiss.tooling.base.ui.fieldassist.FieldAssistImageRegistry;
import org.fortiss.tooling.kernel.ui.ToolingKernelUIActivator;
import org.fortiss.tooling.kernel.ui.extension.base.EditorBase;
import org.fortiss.tooling.kernel.ui.service.IActionService;
/**
* Editor for code specifications.
*
* @author doebber
* @author $Author: hoelzl $
* @version $Rev: 18709 $
* @ConQAT.Rating GREEN Hash: 74C67BD50B3087FF796C300A8C4B9752
*/
public class CodeSpecificationEditorBackend extends EditorBase<CodeSpecification> {
/** Stores the GUI object. */
private CodeSpecificationEditorGUI gui;
private final FormToolkit formToolkit = new FormToolkit(Display.getDefault());
private ScrolledForm scrldfrmCodeSpecification;
private SourceViewer codeViewer;
/** Stores the text action handler. */
private TextActionHandler textActionHandler;
/** {@inheritDoc} */
@Override
public void createPartControl(Composite parent) {
parent.setLayout(new FillLayout());
scrldfrmCodeSpecification = formToolkit.createScrolledForm(parent);
formToolkit.paintBordersFor(scrldfrmCodeSpecification);
scrldfrmCodeSpecification.setText("Code Specification");
scrldfrmCodeSpecification.getBody().setLayout(new FillLayout(SWT.HORIZONTAL));
formToolkit.decorateFormHeading(scrldfrmCodeSpecification.getForm());
createCodeEditor(scrldfrmCodeSpecification.getBody());
updateModel();
}
private RuleBasedScanner getScannerForSyntaxHighlighting() {
ArrayList<WordRule> rules = new ArrayList<WordRule>();
IWordDetector detector = new IWordDetector() {
/** {@inheritDoc} */
@Override
public boolean isWordStart(char c) {
return Character.isLetterOrDigit(c);
}
/** {@inheritDoc} */
@Override
public boolean isWordPart(char c) {
return Character.isLetterOrDigit(c);
}
};
WordRule keywordsRule = new WordRule(detector);
rules.add(keywordsRule);
WordRule portsRule = new WordRule(detector);
rules.add(portsRule);
WordRule dataelementsRule = new WordRule(detector);
rules.add(dataelementsRule);
final IToken keywordToken =
new Token(new TextAttribute(new Color(null, 128, 0, 0), null, SWT.BOLD));
final IToken portToken = new Token(new TextAttribute(null, null, SWT.BOLD));
final IToken dataelementToken =
new Token(new TextAttribute(new Color(null, 0, 0, 128), null, SWT.ITALIC));
ArrayList<String> ports = getInputPortNames(editedObject.getComponent());
ports.addAll(getOutputPortNames(editedObject.getComponent()));
addWordsToRule(ports, portsRule, portToken);
ArrayList<String> dataelements = getFunctions(editedObject);
dataelements.addAll(getAllConstructorsWithoutBrackets(editedObject, false));
addWordsToRule(dataelements, dataelementsRule, dataelementToken);
ArrayList<String> keywords = new ArrayList<String>();
keywords.addAll(getPrimitiveValues());
keywords.addAll(getPrimitiveTypes());
keywords.add("if");
keywords.add("else");
keywords.add("return");
keywords.add("NoVal");
addWordsToRule(keywords, keywordsRule, keywordToken);
RuleBasedScanner scanner = new RuleBasedScanner();
scanner.setRules(rules.toArray(new IRule[0]));
return scanner;
}
private void addWordsToRule(List<String> words, WordRule rule, IToken token) {
for(String word : words) {
rule.addWord(word, token);
}
}
// error identifiers, images and colors
public static String ERROR_TYPE = "error.type";
public static Image ERROR_IMAGE = ToolingKernelUIActivator
.getImageDescriptor("icons/error.gif").createImage();
public static final RGB ERROR_RGB = new RGB(255, 0, 0);
// annotation model
AnnotationModel annotationModel = new AnnotationModel();
class AnnotationConfiguration implements IInformationControlCreator {
public IInformationControl createInformationControl(Shell shell) {
return new DefaultInformationControl(shell);
}
}
private void createCodeEditor(Composite parent) {
IAnnotationAccess fAnnotationAccess = new AnnotationMarkerAccess();
// necessary magic
ColorCache cc = new ColorCache();
// rulers
CompositeRuler fCompositeRuler = new CompositeRuler();
OverviewRuler fOverviewRuler = new OverviewRuler(fAnnotationAccess, 12, cc);
AnnotationRulerColumn annotationRuler =
new AnnotationRulerColumn(annotationModel, 16, fAnnotationAccess);
fCompositeRuler.setModel(annotationModel);
fOverviewRuler.setModel(annotationModel);
// annotation ruler is decorating our composite ruler
fCompositeRuler.addDecorator(0, annotationRuler);
// add what types are show on the different rulers
annotationRuler.addAnnotationType(ERROR_TYPE);
fOverviewRuler.addAnnotationType(ERROR_TYPE);
fOverviewRuler.addHeaderAnnotationType(ERROR_TYPE);
// set what layer this type is on
fOverviewRuler.setAnnotationTypeLayer(ERROR_TYPE, 3);
// set what color is used on the overview ruler for the type
fOverviewRuler.setAnnotationTypeColor(ERROR_TYPE,
new Color(Display.getDefault(), ERROR_RGB));
// create the actual source viewer
codeViewer =
new SourceViewer(parent, fCompositeRuler, fOverviewRuler, true, SWT.MULTI |
SWT.V_SCROLL | SWT.H_SCROLL);
codeViewer.setDocument(new Document(), annotationModel);
// hover manager that shows text when hovering
AnnotationBarHoverManager fAnnotationHoverManager =
new AnnotationBarHoverManager(fCompositeRuler, codeViewer, new AnnotationHover(
annotationModel), new AnnotationConfiguration());
fAnnotationHoverManager.install(annotationRuler.getControl());
// to paint the annotations
AnnotationPainter ap = new AnnotationPainter(codeViewer, fAnnotationAccess);
ap.addAnnotationType(ERROR_TYPE);
ap.setAnnotationTypeColor(ERROR_TYPE, new Color(Display.getDefault(), ERROR_RGB));
codeViewer.addPainter(ap);
// setup the editor concerning syntax highlighting and content assist
codeViewer.configure(getSourceViewerConfig());
// set code font
codeViewer.getTextWidget().setFont(new Font(Display.getCurrent(), getOSSpecificCodeFont()));
codeViewer.addTextListener(new ITextListener() {
@Override
public void textChanged(TextEvent event) {
if(event.getDocumentEvent() != null) {
showErrors();
}
}
});
}
/**
* Creates the SourceViewerConfiguration containing the setup for both syntax highlighting and
* content assist.
*/
private SourceViewerConfiguration getSourceViewerConfig() {
return new SourceViewerConfiguration() {
/** {@inheritDoc} */
@Override
public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
PresentationReconciler reconciler = new PresentationReconciler();
DefaultDamagerRepairer dr = new DefaultDamagerRepairer(getScanner());
reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
return reconciler;
}
private ITokenScanner getScanner() {
return getScannerForSyntaxHighlighting();
}
public IAnnotationHover getAnnotationHover(ISourceViewer sourceViewer) {
return new AnnotationHover(annotationModel);
}
@Override
public org.eclipse.jface.text.contentassist.IContentAssistant getContentAssistant(
ISourceViewer sourceViewer) {
ContentAssistant assistant = new ContentAssistant();
IContentAssistProcessor proc = new IContentAssistProcessor() {
@Override
public String getErrorMessage() {
// nothing to do
return null;
}
@Override
public IContextInformationValidator getContextInformationValidator() {
// nothing to do
return null;
}
@Override
public char[] getContextInformationAutoActivationCharacters() {
// nothing to do
return null;
}
@Override
public char[] getCompletionProposalAutoActivationCharacters() {
String allLetters =
" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
return allLetters.toCharArray();
}
@Override
public IContextInformation[] computeContextInformation(ITextViewer viewer,
int offset) {
// nothing to do
return null;
}
@Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer,
int documentOffset) {
List<ICompletionProposal> proposalList =
new ArrayList<ICompletionProposal>();
IRegion currentWordRegion =
CodeUtils.findWord(viewer.getDocument(), documentOffset - 1);
if(currentWordRegion != null) {
try {
String currentWord =
viewer.getDocument().get(currentWordRegion.getOffset(),
currentWordRegion.getLength());
ArrayList<String> prop =
getInputPortNames(editedObject.getComponent());
prop.addAll(getOutputPortNames(editedObject.getComponent()));
prop.addAll(getFunctions(editedObject));
prop.addAll(getAllConstructors(editedObject, true));
prop.addAll(getPrimitiveValues());
prop.addAll(getUsefulExpressions());
for(String proposal : prop) {
if(proposal.toLowerCase().startsWith(currentWord.toLowerCase())) {
proposalList.add(new CompletionProposal(proposal,
currentWordRegion.getOffset(), currentWordRegion
.getLength(), proposal.length(),
FieldAssistImageRegistry.getIcon(proposal), null,
null, null));
}
}
return proposalList.toArray(new ICompletionProposal[0]);
} catch(BadLocationException e) {
e.printStackTrace();
}
}
return null;
}
};
assistant.setContentAssistProcessor(proc, IDocument.DEFAULT_CONTENT_TYPE);
assistant.enableAutoActivation(true);
assistant.setAutoActivationDelay(500);
return assistant;
};
};
}
private FontData getOSSpecificCodeFont() {
String os = System.getProperty("os.name");
String font = "Courier";
if(os.toLowerCase().contains("mac")) {
font = "Monaco";
} else if(os.toLowerCase().contains("windows")) {
// TODO find out appropriate font
font = "Consolas";
} else if(os.toLowerCase().contains("linux")) {
// TODO find out appropriate font
font = "Monospace";
}
return new FontData(font, 11, SWT.NORMAL);
}
final ITypeSystemCompiler compiler = TypeSystemHandler.INSTANCE.getTypeSystemCompiler();
final Pattern pattern = Pattern.compile("line (\\d*):(\\d*) (.*)");
public void showErrors() {
String code = codeViewer.getTextWidget().getText();
annotationModel.removeAllAnnotations();
try {
((Compiler)compiler).compileStatementTerm(code);
} catch(ParseException e) {
for(String msg : e.getMessages()) {
Matcher matcher = pattern.matcher(msg);
boolean matchFound = matcher.find();
if(matchFound) {
int line = Integer.parseInt(matcher.group(1));
int lineOffset = Integer.parseInt(matcher.group(2));
String errorMsg = matcher.group(3);
ErrorAnnotation errorAnnotation = new ErrorAnnotation(line, errorMsg);
codeViewer.getAnnotationModel().addAnnotation(errorAnnotation,
findErrorRange(code, line, lineOffset));
}
}
}
}
/**
* Returns the range of the error from the position in the string based on the given line number
* and the line-relative offset to the next newline or end of string.
*/
private Position findErrorRange(String string, int line, int lineOffset) {
int start = 0;
int end = 0;
for(int i = 1; i < line; i++) {
start += findNewLine(string, start);
}
start += lineOffset;
end = findNewLine(string, start);
return new Position(start, end);
}
/**
* Utility method returning the first occurrence of a newline (or the end of the string)
* relative to the given offset.
*/
private int findNewLine(String string, int offset) {
int newline = string.substring(offset + 1).indexOf('\n');
if(newline == -1) {
newline = string.substring(offset + 1).length() - 1;
}
return newline + 2;
}
/** {@inheritDoc} */
@Override
public void registerGlobalActions(IActionBars bars) {
textActionHandler = new TextActionHandler(bars);
// FIXME
// textActionHandler.addText(codeViewer.getTextWidget());
IActionService.INSTANCE.registerGlobalUndoRedoActions(bars);
}
/** {@inheritDoc} */
@Override
public void dispose() {
textActionHandler.dispose();
super.dispose();
}
/** Performs the text to model binding. */
private void updateModel() {
IObservableValue modelObservable =
observeValue(getEditedObject(), CodePackage.Literals.CODE_SPECIFICATION__BODY);
performComplexTextBinding(dbc, codeViewer.getTextWidget(), modelObservable,
new StatementSequenceToStringConverter(true),
new StringToStatementSequenceConverter(), new StringToStatementSequenceValidator(
getEditedObject()), null);
}
}
/*--------------------------------------------------------------------------+
$Id: codetemplates.xml 1 2011-01-01 00:00:01Z hoelzl $
| |
| Copyright 2012 ForTISS GmbH |
| |
| 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 org.fortiss.af3.component.ui.editor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
/**
*
* @author doebber
* @author $Author: hoelzl $
* @version $Rev: 18709 $
* @ConQAT.Rating GREEN Hash: 20546E0DB7DC6EBE032A4F69E3F6DD08
*/
public class CodeSpecificationEditorGUI extends Composite {
private final FormToolkit formToolkit = new FormToolkit(
Display.getDefault());
private Text text;
private ScrolledForm scrldfrmCodeSpecification;
/**
* Create the composite.
*
* @param parent
* @param style
*/
public CodeSpecificationEditorGUI(Composite parent, int style) {
super(parent, style);
setLayout(new FillLayout(SWT.HORIZONTAL));
scrldfrmCodeSpecification = formToolkit.createScrolledForm(this);
formToolkit.paintBordersFor(scrldfrmCodeSpecification);
scrldfrmCodeSpecification.setText("Code Specification");