| 1 | /** |
| 2 | * |
| 3 | */ |
| 4 | package de.uka.ipd.sdq.pcm.dialogs.stoex; |
| 5 | |
| 6 | import org.antlr.runtime.Lexer; |
| 7 | import org.antlr.runtime.RecognitionException; |
| 8 | import org.eclipse.emf.ecore.EObject; |
| 9 | import org.eclipse.jface.dialogs.IDialogConstants; |
| 10 | import org.eclipse.jface.dialogs.IMessageProvider; |
| 11 | import org.eclipse.jface.dialogs.TitleAreaDialog; |
| 12 | import org.eclipse.jface.text.Document; |
| 13 | import org.eclipse.jface.text.ITextListener; |
| 14 | import org.eclipse.jface.text.Position; |
| 15 | import org.eclipse.jface.text.TextEvent; |
| 16 | import org.eclipse.jface.text.source.Annotation; |
| 17 | import org.eclipse.jface.text.source.AnnotationModel; |
| 18 | import org.eclipse.jface.text.source.AnnotationPainter; |
| 19 | import org.eclipse.jface.text.source.AnnotationRulerColumn; |
| 20 | import org.eclipse.jface.text.source.CompositeRuler; |
| 21 | import org.eclipse.jface.text.source.IAnnotationAccess; |
| 22 | import org.eclipse.jface.text.source.IAnnotationAccessExtension; |
| 23 | import org.eclipse.jface.text.source.ImageUtilities; |
| 24 | import org.eclipse.jface.text.source.SourceViewer; |
| 25 | import org.eclipse.swt.SWT; |
| 26 | import org.eclipse.swt.custom.StyledText; |
| 27 | import org.eclipse.swt.graphics.Color; |
| 28 | import org.eclipse.swt.graphics.GC; |
| 29 | import org.eclipse.swt.graphics.RGB; |
| 30 | import org.eclipse.swt.graphics.Rectangle; |
| 31 | import org.eclipse.swt.layout.FillLayout; |
| 32 | import org.eclipse.swt.layout.GridData; |
| 33 | import org.eclipse.swt.widgets.Canvas; |
| 34 | import org.eclipse.swt.widgets.Composite; |
| 35 | import org.eclipse.swt.widgets.Control; |
| 36 | import org.eclipse.swt.widgets.Display; |
| 37 | import org.eclipse.swt.widgets.Event; |
| 38 | import org.eclipse.swt.widgets.Group; |
| 39 | import org.eclipse.swt.widgets.Listener; |
| 40 | import org.eclipse.swt.widgets.Shell; |
| 41 | import org.eclipse.ui.PlatformUI; |
| 42 | |
| 43 | import de.uka.ipd.sdq.errorhandling.IIssue; |
| 44 | import de.uka.ipd.sdq.pcm.dialogs.DialogsImages; |
| 45 | import de.uka.ipd.sdq.pcm.dialogs.SWTResourceManager; |
| 46 | import de.uka.ipd.sdq.pcm.repository.Parameter; |
| 47 | import de.uka.ipd.sdq.pcm.stochasticexpressions.parser.ErrorEntry; |
| 48 | |
| 49 | /** |
| 50 | * @author Snowball |
| 51 | */ |
| 52 | public abstract class AbstractGrammerBasedEditDialog extends TitleAreaDialog { |
| 53 | |
| 54 | private String DIALOG_TITLE = "Edit a stochastic expression"; |
| 55 | |
| 56 | public static final String ERROR_TYPE = "ERROR"; |
| 57 | public static final String WARNING_TYPE = "WARNING"; |
| 58 | |
| 59 | // private Text editText; |
| 60 | private SourceViewer textViewer; |
| 61 | |
| 62 | protected String newText = null; |
| 63 | |
| 64 | private AnnotationModel fAnnotationModel; |
| 65 | |
| 66 | private Object result = null; |
| 67 | private String resultText = null; |
| 68 | |
| 69 | protected Parameter[] context = null; |
| 70 | |
| 71 | /** |
| 72 | * Constructor. |
| 73 | * |
| 74 | * @param parent |
| 75 | * shell |
| 76 | */ |
| 77 | public AbstractGrammerBasedEditDialog(Shell parent) { |
| 78 | super(parent); |
| 79 | newText = getInitialText(); |
| 80 | this.context = new Parameter[]{}; |
| 81 | // make the possible change dialogue size. |
| 82 | this.setShellStyle(SWT.RESIZE|SWT.MAX); |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * Constructor. |
| 87 | * |
| 88 | * @param parent |
| 89 | * shell |
| 90 | * @param context - |
| 91 | * A list of parameters used in code completion |
| 92 | */ |
| 93 | public AbstractGrammerBasedEditDialog(Shell parent, Parameter[] context) { |
| 94 | super(parent); |
| 95 | newText = getInitialText(); |
| 96 | this.context = context; |
| 97 | // make the possible change dialogue size. |
| 98 | setShellStyle(SWT.RESIZE|SWT.MAX); |
| 99 | // activate help |
| 100 | this.setHelpAvailable(true); |
| 101 | } |
| 102 | |
| 103 | protected abstract String getInitialText(); |
| 104 | |
| 105 | public void setDisplayTitle(String newTitle) { |
| 106 | this.DIALOG_TITLE = newTitle; |
| 107 | } |
| 108 | |
| 109 | @Override |
| 110 | protected void cancelPressed() { |
| 111 | super.cancelPressed(); |
| 112 | result = null; |
| 113 | resultText = ""; |
| 114 | } |
| 115 | |
| 116 | @Override |
| 117 | protected Control createDialogArea(Composite parent) { |
| 118 | Composite area = (Composite) super.createDialogArea(parent); |
| 119 | Composite container = new Composite(area, SWT.NONE); |
| 120 | container.setLayout(new FillLayout()); |
| 121 | container.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| 122 | |
| 123 | this.setTitle(DIALOG_TITLE); |
| 124 | PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "de.uka.ipd.sdq.pcmbench.help.stoexdialog"); |
| 125 | |
| 126 | IAnnotationAccess fAnnotationAccess = new AnnotationMarkerAccess(); |
| 127 | |
| 128 | final Group editStochasticExpressionGroup = new Group(container, SWT.NONE); |
| 129 | editStochasticExpressionGroup.setText(""); |
| 130 | editStochasticExpressionGroup.setLayout(new FillLayout()); |
| 131 | |
| 132 | fAnnotationModel = new AnnotationModel(); |
| 133 | CompositeRuler fCompositeRuler = new CompositeRuler(); |
| 134 | AnnotationRulerColumn annotationRuler = new AnnotationRulerColumn( |
| 135 | fAnnotationModel, 16, fAnnotationAccess); |
| 136 | fCompositeRuler.setModel(fAnnotationModel); |
| 137 | // annotation ruler is decorating our composite ruler |
| 138 | fCompositeRuler.addDecorator(0, annotationRuler); |
| 139 | |
| 140 | // add what types are show on the different rulers |
| 141 | annotationRuler.addAnnotationType(ERROR_TYPE); |
| 142 | annotationRuler.addAnnotationType(WARNING_TYPE); |
| 143 | textViewer = new SourceViewer(editStochasticExpressionGroup, |
| 144 | fCompositeRuler, SWT.V_SCROLL | SWT.MULTI | SWT.H_SCROLL | SWT.RESIZE); |
| 145 | final StyledText styledText = textViewer.getTextWidget(); |
| 146 | styledText.setWordWrap(true); |
| 147 | final AbstractGrammarBasedViewerConfiguration config = new AbstractGrammarBasedViewerConfiguration(fAnnotationModel,context,getLexerClass(),getTokenMapper()); |
| 148 | styledText.setFont(SWTResourceManager.getFont("Courier New", 12, |
| 149 | SWT.NONE)); |
| 150 | styledText.addListener(SWT.KeyDown, new Listener(){ |
| 151 | |
| 152 | public void handleEvent(Event event) { |
| 153 | if (event.character == ' ' && (event.stateMask & SWT.CTRL) == SWT.CTRL){ |
| 154 | config.getContentAssistant(textViewer).showPossibleCompletions(); |
| 155 | } |
| 156 | |
| 157 | } |
| 158 | |
| 159 | }); |
| 160 | |
| 161 | // to paint the annotations |
| 162 | AnnotationPainter ap = new AnnotationPainter(textViewer, |
| 163 | fAnnotationAccess); |
| 164 | ap.addAnnotationType(ERROR_TYPE); |
| 165 | ap.setAnnotationTypeColor(ERROR_TYPE, new Color(Display.getDefault(), |
| 166 | new RGB(255, 0, 0))); |
| 167 | ap.addAnnotationType(WARNING_TYPE); |
| 168 | ap.setAnnotationTypeColor(WARNING_TYPE, new Color(Display.getDefault(), |
| 169 | new RGB(255, 255, 0))); |
| 170 | |
| 171 | // this will draw the squigglies under the text |
| 172 | textViewer.addPainter(ap); |
| 173 | |
| 174 | textViewer.configure(config); |
| 175 | GridData layoutData = new GridData(GridData.FILL_BOTH);//new GridData(GridData.FILL_BOTH |
| 176 | //| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL); |
| 177 | //layoutData.heightHint = 300; |
| 178 | //layoutData.widthHint = 450; |
| 179 | |
| 180 | //textViewer.getControl().setLayoutData(layoutData); |
| 181 | // editText.setText(newText); |
| 182 | textViewer.setDocument(new Document(newText), fAnnotationModel); |
| 183 | textViewer.addTextListener(new ITextListener(){ |
| 184 | |
| 185 | public void textChanged(TextEvent event) { |
| 186 | if (event.getDocumentEvent() != null) |
| 187 | { |
| 188 | parseInputAndRefreshAnnotations(); |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | }); |
| 193 | return textViewer.getControl(); |
| 194 | } |
| 195 | |
| 196 | @Override |
| 197 | protected Control createContents(Composite parent) { |
| 198 | Control result = super.createContents(parent); |
| 199 | parseInputAndRefreshAnnotations(); |
| 200 | return result; |
| 201 | } |
| 202 | |
| 203 | protected abstract ITokenMapper getTokenMapper(); |
| 204 | protected abstract Class<?> getLexerClass(); |
| 205 | |
| 206 | protected abstract String getTitle(); |
| 207 | |
| 208 | protected void parseInputAndRefreshAnnotations() { |
| 209 | EObject value = null; |
| 210 | fAnnotationModel.removeAllAnnotations(); |
| 211 | Lexer lexer = null; |
| 212 | boolean hasErrors = false, hasWarnings = false; |
| 213 | try { |
| 214 | String text = this.textViewer.getDocument().get(); |
| 215 | lexer = getLexer(text); |
| 216 | value = parse(lexer); |
| 217 | } catch (RecognitionException e) { |
| 218 | showInputInvalidInfo(e); |
| 219 | return; |
| 220 | } catch (StoExParserException e) { |
| 221 | for (IIssue ex : e.getIssuesList()) { |
| 222 | if (ex instanceof ErrorEntry) { |
| 223 | showInputInvalidInfo((ErrorEntry)ex); |
| 224 | hasErrors = true; |
| 225 | } |
| 226 | else { |
| 227 | hasWarnings = true; |
| 228 | showInputWarning(ex); |
| 229 | } |
| 230 | } |
| 231 | if (hasErrors) |
| 232 | return; |
| 233 | else if (hasWarnings) |
| 234 | this.setMessage(e.getIssuesList().get(0).getMessage(), IMessageProvider.WARNING); |
| 235 | } catch (Exception e) { |
| 236 | showInputInvalidInfo(e); |
| 237 | return; |
| 238 | } |
| 239 | this.getButton(IDialogConstants.OK_ID).setEnabled(true); |
| 240 | if (!hasErrors) |
| 241 | this.setErrorMessage(null); |
| 242 | if (!hasWarnings) |
| 243 | this.setMessage(null); |
| 244 | result = value; |
| 245 | resultText = this.textViewer.getDocument().get(); |
| 246 | } |
| 247 | |
| 248 | private void showInputWarning(IIssue ex) { |
| 249 | fAnnotationModel.addAnnotation( |
| 250 | new Annotation(WARNING_TYPE, false, |
| 251 | ex.getMessage()), |
| 252 | new Position(0, textViewer.getDocument().getLength())); |
| 253 | } |
| 254 | |
| 255 | protected abstract Lexer getLexer(String text); |
| 256 | protected abstract EObject parse(Lexer lexer) throws RecognitionException, StoExParserException; |
| 257 | |
| 258 | // private void showInputInvalidInfo(TokenStreamException e,CharScanner scanner) { |
| 259 | // result = null; |
| 260 | // fAnnotationModel.addAnnotation( |
| 261 | // new Annotation(ERROR_TYPE, false, |
| 262 | // e.getMessage()), |
| 263 | // new Position(ParserHelper.positionToOffset(textViewer.getDocument(), scanner.getLine(), scanner.getColumn()),1)); |
| 264 | // this.getButton(IDialogConstants.OK_ID).setEnabled(false); |
| 265 | // this.setErrorMessage("Entered stochastic expression is invalid. Cause given: "+e.getMessage()); |
| 266 | // } |
| 267 | |
| 268 | /** |
| 269 | * @param e |
| 270 | */ |
| 271 | private void showInputInvalidInfo(Exception e) { |
| 272 | result = null; |
| 273 | fAnnotationModel.addAnnotation( |
| 274 | new Annotation(ERROR_TYPE, false, |
| 275 | e.getMessage() == null ? e.getClass().getName() : e.getMessage()), |
| 276 | guessPosition(e)); |
| 277 | this.getButton(IDialogConstants.OK_ID).setEnabled(false); |
| 278 | this.setErrorMessage("Entered stochastic expression is invalid. Cause given: "+e.getLocalizedMessage()); |
| 279 | } |
| 280 | |
| 281 | private void showInputInvalidInfo(ErrorEntry e) { |
| 282 | result = null; |
| 283 | fAnnotationModel.addAnnotation( |
| 284 | new Annotation(ERROR_TYPE, false, |
| 285 | e.getMessage()), |
| 286 | guessPosition(e.getEx())); |
| 287 | this.getButton(IDialogConstants.OK_ID).setEnabled(false); |
| 288 | this.setErrorMessage("Entered stochastic expression is invalid. Cause given: "+e.getMessage()); |
| 289 | } |
| 290 | |
| 291 | /** |
| 292 | * @param e |
| 293 | * @return |
| 294 | */ |
| 295 | private Position guessPosition(Exception e) { |
| 296 | if (e instanceof RecognitionException) |
| 297 | { |
| 298 | RecognitionException recException = (RecognitionException) e; |
| 299 | int col = recException.charPositionInLine; |
| 300 | int line = recException.line; |
| 301 | int offset = ParserHelper.positionToOffset(textViewer.getDocument(),line,col); |
| 302 | offset = offset < 0 ? 0 : offset; |
| 303 | return new Position(offset, textViewer.getDocument().getLength()-offset); |
| 304 | } |
| 305 | return new Position(0, textViewer.getDocument().getLength()); |
| 306 | } |
| 307 | |
| 308 | protected Object getResult() |
| 309 | { |
| 310 | return result; |
| 311 | } |
| 312 | |
| 313 | @Override |
| 314 | protected void configureShell(Shell newShell) { |
| 315 | super.configureShell(newShell); |
| 316 | newShell.setText(getTitle()); |
| 317 | } |
| 318 | |
| 319 | public String getResultText() { |
| 320 | return resultText; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | class AnnotationMarkerAccess implements IAnnotationAccess, |
| 325 | IAnnotationAccessExtension { |
| 326 | |
| 327 | public String getType(Annotation annotation) { |
| 328 | return annotation.getType(); |
| 329 | } |
| 330 | |
| 331 | public boolean isMultiLine(Annotation annotation) { |
| 332 | return true; |
| 333 | } |
| 334 | |
| 335 | public boolean isTemporary(Annotation annotation) { |
| 336 | return !annotation.isPersistent(); |
| 337 | } |
| 338 | |
| 339 | public String getTypeLabel(Annotation annotation) { |
| 340 | if (getType(annotation).equals(AbstractGrammerBasedEditDialog.ERROR_TYPE)) |
| 341 | return "Errors"; |
| 342 | if (getType(annotation).equals(AbstractGrammerBasedEditDialog.WARNING_TYPE)) |
| 343 | return "Warnings"; |
| 344 | return null; |
| 345 | } |
| 346 | |
| 347 | public int getLayer(Annotation annotation) { |
| 348 | return 0; |
| 349 | } |
| 350 | |
| 351 | public void paint(Annotation annotation, GC gc, Canvas canvas, |
| 352 | Rectangle bounds) { |
| 353 | if (getType(annotation).equals(AbstractGrammerBasedEditDialog.ERROR_TYPE)) |
| 354 | ImageUtilities.drawImage( |
| 355 | DialogsImages.imageRegistry.get(DialogsImages.ERROR),gc,canvas,bounds,SWT.CENTER); |
| 356 | else if (getType(annotation).equals(AbstractGrammerBasedEditDialog.WARNING_TYPE)) |
| 357 | ImageUtilities.drawImage( |
| 358 | DialogsImages.imageRegistry.get(DialogsImages.WARNING),gc,canvas,bounds,SWT.CENTER); |
| 359 | } |
| 360 | |
| 361 | public boolean isPaintable(Annotation annotation) { |
| 362 | return true; |
| 363 | } |
| 364 | |
| 365 | public boolean isSubtype(Object annotationType, Object potentialSupertype) { |
| 366 | if (annotationType.equals(potentialSupertype)) |
| 367 | return true; |
| 368 | |
| 369 | return false; |
| 370 | } |
| 371 | |
| 372 | public Object[] getSupertypes(Object annotationType) { |
| 373 | return new Object[0]; |
| 374 | } |
| 375 | } |