Java tutorial
/****************************************************************************** * Copyright (c) 2005, 2006 IBM Corporation and others. All rights reserved. This program and the accompanying materials * are 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: IBM Corporation - initial API and implementation ****************************************************************************/ package de.tud.cs.st.vespucci.vespucci_model.diagram.sheet; import java.util.ArrayList; import java.util.ResourceBundle; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.emf.ecore.EObject; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.common.core.util.StringStatics; import org.eclipse.gmf.runtime.common.ui.util.StatusLineUtil; import org.eclipse.gmf.runtime.diagram.ui.properties.sections.AbstractModelerPropertySection; import org.eclipse.gmf.runtime.diagram.ui.properties.views.TextChangeHelper; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jdt.ui.PreferenceConstants; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.editors.text.EditorsUI; import org.eclipse.ui.texteditor.AbstractTextEditor; import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; import org.osgi.framework.FrameworkUtil; import de.tud.cs.st.vespucci.io.KeywordReader; /** * <p>A Changed Copy of AbstractBasicTextPropertySection * (org.eclipse.gmf.runtime.diagram * .ui.properties.sections.AbstractBasicTextPropertySection)</p> * * <p><i>Reviewd by Thomas Schulz, Alexander Weitzmann (Aug. 16, 2011)</i><br/> * <i>Reviewed by Dominic Scheurer (Sept. 18, 2011)</i></p> * * Original version by: * * @author Natalia Balaba * * Changed by: * @author Malte Viering * @author Benjamin Lck * @author Dominic Scheurer * @author Alexander Weitzmann * @author Thomas Schulz */ public abstract class ChangedAbstractBasicTextPropertySection extends AbstractModelerPropertySection { private static final ResourceBundle PLUGIN_RES_BUNDLE = ResourceBundle.getBundle("plugin"); private static final int QUERY_TAB_HEIGHT_SHIFT = 25; private static final int QUERY_TAB_WIDTH_SHIFT = 32; private static final int START_HEIGHT = 15; /** styled text widget to display and set value of the property */ private MarkableStyledText styledTextWidget; /** parent parent ... parent composite for the size of the text field */ private Composite scrolledParent; private Composite sectionComposite; /** * Preference-store of the java source viewer. Used for highlighting and * text-settings for query. */ private static final IPreferenceStore SRC_VIEWER_PREFS = PreferenceConstants.getPreferenceStore(); Listener resizeLinstener = new Listener() { @Override public void handleEvent(final Event e) { updateHeight(); scrolledParent.getVerticalBar().setVisible(false); scrolledParent.getHorizontalBar().setVisible(false); } }; /** * A helper to listen for events that indicate that a text field has been * changed. */ private final TextChangeHelper textChangeListener = new TextChangeHelper() { private boolean textModified = false; /** * Keywords to be marked */ private final String[] keywords = KeywordReader.readAndParseResourceFile( FrameworkUtil.getBundle(getClass()).getSymbolicName(), PLUGIN_RES_BUNDLE.getString("queryKeywordsFile")); /** * Pattern to be used to match strings in query including the single quotes */ private static final String STRING_PATTERN = "'.+?'"; /** * Performs text highlighting of the query properties tab. * * @author Dominic Scheurer * @author Alexander Weitzmann */ private void doSyntaxHighlighting() { startNonUserChange(); // first, set everything to black and normal resetStyle(); // highlight bracket, depending on caret highlightBrackets(); // highlight keywords highlightKeywords(); // highlight strings highlightStrings(); finishNonUserChange(); } /** * Returns a StyleRange that surrounds the character at the given * position with a rectangle. * * @param position * The position of the bracket to be highlighted. * @return A StyleRange that highlights bracket at given position */ private StyleRange getBracketStyle(final int position) { final StyleRange bracketStyle = styledTextWidget.getStyleRangeAtOffset(position); // surround with rectangle bracketStyle.borderStyle = SWT.BORDER_SOLID; return bracketStyle; } /** * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event) */ @Override public void handleEvent(final Event event) { switch (event.type) { case SWT.KeyDown: doSyntaxHighlighting(); textModified = true; if (event.character == SWT.CR) { getPropertyValueString(); } break; case SWT.FocusOut: textChanged((Control) event.widget); break; case SWT.FocusIn: doSyntaxHighlighting(); break; case SWT.MouseDown: doSyntaxHighlighting(); break; default: break; } } /** * Highlights the corresponding bracket relative to caret position */ private void highlightBrackets() { final int offset = styledTextWidget.getCaretOffset(); // Check if caret is at first position // => no bracket highlighting if (offset == 0) { return; } final int size = styledTextWidget.getCharCount(); final char currentChar = styledTextWidget.getText().charAt(offset - 1); // mark closing or opening bracket markCorrespondingBrackets(offset, size, currentChar); } /* * Brackets in strings will be ignored (purpose of: textWidget.isPositionMarked(i)) */ private void markCorrespondingBrackets(final int offset, final int size, final char currentChar) { if (currentChar == '(' && !styledTextWidget.isPositionMarked(offset - 1)) { markCorrespondingClosingBracket(offset, size); } else if (currentChar == ')' && offset > 1 && !styledTextWidget.isPositionMarked(offset - 1)) { markCorrespondingOpeningBracket(offset); } } private void markCorrespondingClosingBracket(final int offset, final int size) { int furtherOpeningBrackets = 0; for (int i = offset; i < size; i++) { if (styledTextWidget.getText().charAt(i) == '(' && !styledTextWidget.isPositionMarked(i)) { furtherOpeningBrackets++; } if (styledTextWidget.getText().charAt(i) == ')' && furtherOpeningBrackets == 0 && !styledTextWidget.isPositionMarked(i)) { // highlight corresponding bracket styledTextWidget.setStyleRange(getBracketStyle(i)); return; } if (styledTextWidget.getText().charAt(i) == ')' && !styledTextWidget.isPositionMarked(i)) { furtherOpeningBrackets--; } } } private void markCorrespondingOpeningBracket(final int offset) { int furtherClosingBrackets = 0; for (int i = offset - 2; i >= 0; i--) { if (styledTextWidget.getText().charAt(i) == ')' && !styledTextWidget.isPositionMarked(i)) { furtherClosingBrackets++; } if (styledTextWidget.getText().charAt(i) == '(' && furtherClosingBrackets == 0 && !styledTextWidget.isPositionMarked(i)) { // highlight corresponding bracket styledTextWidget.setStyleRange(getBracketStyle(i)); return; } if (styledTextWidget.getText().charAt(i) == '(' && !styledTextWidget.isPositionMarked(i)) { furtherClosingBrackets--; } } } /** * Highlights all keywords and strings. */ private void highlightKeywords() { final String targetText = styledTextWidget.getText(); Pattern keywordPattern = null; Matcher keywordMatcher = null; final StyleRange styleRange = prepareKeywordsStyleRange(); // highlight keywords for (final String str : keywords) { keywordPattern = Pattern.compile(String.format("\\b%s\\b", str)); keywordMatcher = keywordPattern.matcher(targetText); while (keywordMatcher.find()) { styleRange.start = keywordMatcher.start(); styleRange.length = keywordMatcher.end() - keywordMatcher.start(); styledTextWidget.setStyleRange(styleRange); } } } private StyleRange prepareKeywordsStyleRange() { final boolean bold = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_KEYWORD_BOLD); final boolean italic = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_KEYWORD_ITALIC); final StyleRange styleRange = new StyleRange(); styleRange.fontStyle = SWT.NORMAL; if (bold) { styleRange.fontStyle |= SWT.BOLD; } if (italic) { styleRange.fontStyle |= SWT.ITALIC; } styleRange.strikeout = SRC_VIEWER_PREFS .getBoolean(PreferenceConstants.EDITOR_JAVA_KEYWORD_STRIKETHROUGH); styleRange.underline = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_KEYWORD_UNDERLINE); styleRange.foreground = JavaUI.getColorManager() .getColor(PreferenceConstants.EDITOR_JAVA_KEYWORD_COLOR); return styleRange; } /** * Highlights all strings */ private void highlightStrings() { final Matcher stringMatcher = Pattern.compile(STRING_PATTERN, Pattern.DOTALL) .matcher(styledTextWidget.getText()); final StyleRange styleRange = prepareStringsStyleRange(); // remove all string-markings for {@link #highlightBrackets} styledTextWidget.unmarkAllPositions(); // highlight strings while (stringMatcher.find()) { styleRange.start = stringMatcher.start() + 1; styleRange.length = stringMatcher.end() - styleRange.start - 1; styledTextWidget.setStyleRange(styleRange); // mark string-locations for {@link #highlightBrackets} for (int i = stringMatcher.start(); i < stringMatcher.end(); ++i) { styledTextWidget.markPosition(i); } } } private StyleRange prepareStringsStyleRange() { final boolean bold = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_STRING_BOLD); final boolean italic = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_STRING_ITALIC); final StyleRange styleRange = new StyleRange(); styleRange.fontStyle = SWT.NORMAL; if (bold) { styleRange.fontStyle |= SWT.BOLD; } if (italic) { styleRange.fontStyle |= SWT.ITALIC; } styleRange.strikeout = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_STRING_STRIKETHROUGH); styleRange.underline = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_STRING_UNDERLINE); styleRange.foreground = JavaUI.getColorManager().getColor(PreferenceConstants.EDITOR_STRING_COLOR); return styleRange; } /** * Resets the whole text style in the StyledText component to its "normal" state. */ private void resetStyle() { final Color foreground = JavaUI.getColorManager() .getColor(PreferenceConstants.EDITOR_JAVA_DEFAULT_COLOR); final Color background = EditorsUI.getSharedTextColors().getColor(PreferenceConverter .getColor(EditorsUI.getPreferenceStore(), AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND)); final boolean bold = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_DEFAULT_BOLD); final boolean italic = SRC_VIEWER_PREFS.getBoolean(PreferenceConstants.EDITOR_JAVA_DEFAULT_ITALIC); int fontstyle = SWT.NORMAL; if (bold) { fontstyle |= SWT.BOLD; } if (italic) { fontstyle |= SWT.ITALIC; } final StyleRange normalStyle = new StyleRange(0, styledTextWidget.getCharCount(), foreground, background, fontstyle); styledTextWidget.setStyleRange(normalStyle); } @Override public void startListeningTo(final Control control) { control.addListener(SWT.FocusOut, this); control.addListener(SWT.Modify, this); control.addListener(SWT.FocusIn, this); control.addListener(SWT.MouseDown, this); } @Override public void textChanged(final Control control) { if (textModified) { // clear error message final IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage() .getActivePart(); StatusLineUtil.outputErrorMessage(part, StringStatics.BLANK); setPropertyValue(control); textModified = false; } } }; /** * @return - the default implementation returns contents of the text widget * as a new value for the property. Subclasses can could be * override. */ protected final Object computeNewPropertyValue() { return getTextWidget().getText(); } /* * (non-Javadoc) * @see * org.eclipse.ui.views.properties.tabbed.ISection#createControls(org.eclipse * .swt.widgets.Composite, * org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage) */ @Override public final void createControls(final Composite parent, final TabbedPropertySheetPage aTabbedPropertySheetPage) { doCreateControls(parent, aTabbedPropertySheetPage); } /** * Instantiate a text widget * * @param parent * - parent composite * @return - a text widget to display and edit the property */ protected final MarkableStyledText createTextWidget(final Composite parent) { getSectionComposite().getSize(); final MarkableStyledText st = new MarkableStyledText(parent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); final Font userFont = JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT); st.setFont(userFont); final FormData data = new FormData(); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.top = new FormAttachment(0, 0); data.height = START_HEIGHT; st.setLayoutData(data); st.setBackground(EditorsUI.getSharedTextColors().getColor(PreferenceConverter .getColor(EditorsUI.getPreferenceStore(), AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND))); if (isReadOnly()) { st.setEditable(false); } return st; } /** * @see org.eclipse.ui.views.properties.tabbed.ISection#dispose() */ @Override public final void dispose() { stopTextWidgetEventListener(); super.dispose(); } /** * Creates the GUI <code>Control</code> for this text property section * * @param parent * parent <code>Composite</code> * @param aTabbedPropertySheetPage * <code>TabbedPropertySheetPage</code> * @see org.eclipse.gmf.runtime.common.ui.properties.ISection#createControls(org.eclipse.swt.widgets.Composite, * org.eclipse.gmf.runtime.common.ui.properties.TabbedPropertySheetPage) */ public final void doCreateControls(final Composite parent, final TabbedPropertySheetPage aTabbedPropertySheetPage) { super.createControls(parent, aTabbedPropertySheetPage); sectionComposite = getWidgetFactory().createFlatFormComposite(parent); styledTextWidget = createTextWidget(sectionComposite); scrolledParent = parent; while (true) { // TODO: There must be a nicer way for redraw the text widget then // to say the first scrolledcomposite .layout() // the scrolledParent should be "main" composite for the query tab if (scrolledParent instanceof ScrolledComposite) { break; } if (scrolledParent.getParent() == null) { break; } scrolledParent = scrolledParent.getParent(); } scrolledParent.addListener(SWT.Resize, resizeLinstener); updateHeight(); startTextWidgetEventListener(); } private int getHeight() { return scrolledParent.getSize().y - QUERY_TAB_HEIGHT_SHIFT; } /** * @return Returns the listener. */ protected final TextChangeHelper getListener() { return textChangeListener; } /** * @return - title of the command which will be executed to set the property */ protected abstract String getPropertyChangeCommandName(); /** * @return - name of the property to place in the label widget */ protected abstract String getPropertyNameLabel(); /** * returns as an array the property name * * @return - array of strings where each describes a property name one per * property. The strings will be used to calculate common indent * from the left */ protected final String[] getPropertyNameStringsArray() { return new String[] { getPropertyNameLabel() }; } /** * @return - string representation of the property value */ protected abstract String getPropertyValueString(); /** * @return Returns the sectionComposite. */ public final Composite getSectionComposite() { return sectionComposite; } /** * @return Returns the textWidget. */ protected final StyledText getTextWidget() { return styledTextWidget; } private int getWidth() { return scrolledParent.getSize().x - QUERY_TAB_WIDTH_SHIFT; } /** * @see org.eclipse.ui.views.properties.tabbed.ISection#refresh() */ @Override public final void refresh() { getListener().startNonUserChange(); try { executeAsReadAction(new Runnable() { @Override public void run() { refreshUI(); } }); } finally { getListener().finishNonUserChange(); } } /** * Refresh UI body - refresh will surround this with read action block */ protected final void refreshUI() { if (styledTextWidget != null) { styledTextWidget.setText(getPropertyValueString()); } } /** * User pressed Enter key after editing text field - update the model * * @param control * <code>Control</code> */ protected final synchronized void setPropertyValue(final Control control) { final Object value = computeNewPropertyValue(); final ArrayList<ICommand> commands = new ArrayList<ICommand>(); for (Object o : getEObjectList()) { final EObject next = (EObject) o; commands.add(createCommand(getPropertyChangeCommandName(), next, new Runnable() { @Override public void run() { setPropertyValue(next, value); } })); } executeAsCompositeCommand(getPropertyChangeCommandName(), commands); refresh(); } /** * Set property value for the given object * * @param object * - owner of the property * @param value * - new value */ protected abstract void setPropertyValue(EObject object, Object value); public final void setScrolledParent(final Composite scrolledParent) { this.scrolledParent = scrolledParent; } /** * Start listening to the text widget events */ protected final void startTextWidgetEventListener() { if (!isReadOnly()) { getListener().startListeningTo(getTextWidget()); getListener().startListeningForEnter(getTextWidget()); } } /** * Stop listening to text widget events */ protected final void stopTextWidgetEventListener() { if (!isReadOnly()) { getListener().stopListeningTo(getTextWidget()); } } /** * calculates the new size of the widget and updates it */ private void updateHeight() { if (getTextWidget() != null && !getTextWidget().isDisposed()) { getTextWidget().setVisible(false); scrolledParent.setVisible(false); final FormData data = new FormData(); data.left = new FormAttachment(0, 0); data.right = new FormAttachment(100, 0); data.top = new FormAttachment(0, 0); data.height = getHeight(); data.width = getWidth(); getTextWidget().setLayoutData(data); final int HEIGHTS_SCROLLLINE = 30; Composite com = getSectionComposite(); // TODO: there must be a nice way to update the heights and widths // of the textwidget and its parents while (true) { if (com instanceof ScrolledComposite) { break; } if (com.getParent() == null) { break; } com.setSize(getWidth(), getHeight() + HEIGHTS_SCROLLLINE); com = com.getParent(); } com.layout(); getTextWidget().setVisible(true); scrolledParent.setVisible(true); } } }