/** * <copyright> * </copyright> * * */ package org.dresdenocl.language.ocl.resource.ocl.ui; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.IDocument; /** * The OclAutoEditStrategy extends the default auto edit strategy such that an * additional tab is added if a line break is entered after opening brackets which * are configured as <code>closeAfterEnter</code>. Also, closing brackets are * automatically inserted right away when opening brackets are added where * <code>closeAfterEnter</code> is set to <code>false</code>. */ public class OclAutoEditStrategy extends DefaultIndentLineAutoEditStrategy { private org.dresdenocl.language.ocl.resource.ocl.ui.OclBracketSet bracketSet; public OclAutoEditStrategy() { super(); org.dresdenocl.language.ocl.resource.ocl.ui.OclUIPlugin plugin = org.dresdenocl.language.ocl.resource.ocl.ui.OclUIPlugin.getDefault(); if (plugin != null) { IPreferenceStore preferenceStore = plugin.getPreferenceStore(); bracketSet = new org.dresdenocl.language.ocl.resource.ocl.ui.OclBracketSet(); bracketSet.resetBrackets(preferenceStore); } } /** * This method is only used for injecting a bracket set during tests. */ @Deprecated public void setBracketSet(org.dresdenocl.language.ocl.resource.ocl.ui.OclBracketSet bracketSet) { this.bracketSet = bracketSet; } @Override public void customizeDocumentCommand(IDocument document, DocumentCommand command) { String text = command.text; String textBefore = command.text; super.customizeDocumentCommand(document, command); String textAfter = command.text; if (textAfter.length() < textBefore.length()) { return; } consumeAddedClosingBracket(document, command); addClosingBracketAfterEnterIfRequired(document, command, text, textBefore, textAfter); addClosingBracket(document, command); } protected void consumeAddedClosingBracket(IDocument document, DocumentCommand command) { // When typing the closing bracket manually and the next character is an auto // generated closing bracket, then do not insert the new closing bracket (i.e., // make it feel like it was overridden). String insertedText = command.text; if (!bracketSet.isClosingBracket(insertedText) || insertedText.length() != 1) { return; } try { char insertedBracket = insertedText.charAt(0); int offset = command.offset; char nextCharacter = document.getChar(offset); // NOTE: To be entirely accurate, one would have to check whether the next // character truly _functions_ as a closing bracket (e.g., is the second of a pair // of quotes, not the first). boolean nextCharacterIsClosingBracket = bracketSet.isClosingBracket(Character.toString(nextCharacter)); boolean nextCharacterWasAddedAutomatically = true; if (insertedBracket == nextCharacter && nextCharacterIsClosingBracket && nextCharacterWasAddedAutomatically) { // Do not add the character again but forward the caret. Effectively gives the // illusion of overriding the previously automatically added closing bracket. command.text = ""; command.caretOffset = offset + 1; command.shiftsCaret = true; } } catch(BadLocationException e) { } } protected void addClosingBracket(IDocument document, DocumentCommand command) { String openingBracket = command.text; if (!bracketSet.isOpeningBracket(openingBracket) || !bracketSet.isCloseInstantly(openingBracket)) { return; } String closingBracket = bracketSet.getCounterpart(openingBracket); command.text = command.text + closingBracket; command.shiftsCaret = false; command.caretOffset = command.offset + 1; } protected void addClosingBracketAfterEnterIfRequired(IDocument document, DocumentCommand command, String text, String textBefore, String textAfter) { boolean isLineBreak = isLineBreak(text); if (!isLineBreak) { return; } String documentText = document.get(); String openingBracketBefore = getCloseAfterBracketBefore(documentText, command.offset); if (openingBracketBefore == null) { return; } // add additional indentation (because a line break was entered after an opening // bracket) command.text = command.text + "\t"; String closingBracket = bracketSet.getCounterpart(openingBracketBefore); boolean closingBracketIsMissing = count(documentText, openingBracketBefore) != count(documentText, closingBracket); // add closing bracket (if required) if (closingBracketIsMissing) { command.text = command.text + textAfter; command.text = command.text + closingBracket; } command.shiftsCaret = false; command.caretOffset = command.offset + textAfter.length() + 1; } /** * Returns the number of occurrences of 'searchString' in 'text'. */ protected int count(String text, String searchString) { int index = text.indexOf(searchString); int count = 0; while (index >= 0) { count++; index = text.indexOf(searchString, index + 1); } return count; } /** * Searches for a bracket that must be closed when line breaks are entered and * which is located right before the cursor (ignoring whitespace). */ protected String getCloseAfterBracketBefore(String text, int offset) { for (int i = offset - 1; i >= 0; i--) { char charAtI = text.charAt(i); String stringAtI = Character.toString(charAtI); if (bracketSet.isCloseAfterEnter(stringAtI)) { return stringAtI; } if (charAtI == ' ' || charAtI == '\t') { continue; } break; } return null; } protected boolean isLineBreak(String text) { return "\n".equals(text) || "\r".equals(text) || "\r\n".equals(text); } }