/******************************************************************************* * Copyright (c) 2000, 2015 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 org.eclipse.ui.texteditor; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import org.osgi.framework.Bundle; import org.eclipse.swt.widgets.Shell; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.operations.IOperationHistory; import org.eclipse.core.commands.operations.IUndoableOperation; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.resources.IResource; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.window.Window; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.undo.CreateMarkersOperation; /** * Action for creating a marker of a specified type for the editor's * input element based on the editor's selection. If required, the * action asks the user to provide a marker label. The action is initially * associated with a text editor via the constructor, but that can be * subsequently changed using setEditor. *

* The following keys, prepended by the given option prefix, * are used for retrieving resources from the given bundle: *

* This class may be instantiated but is not intended to be subclassed. *

* * @noextend This class is not intended to be subclassed by clients. */ public class AddMarkerAction extends TextEditorAction { /** The maximum length of an proposed label. */ private static final int MAX_LABEL_LENGTH= 80; /** The type for newly created markers. */ private String fMarkerType; /** Should the user be asked for a label? */ private boolean fAskForLabel; /** The action's resource bundle. */ private ResourceBundle fBundle; /** The prefix used for resource bundle lookup. */ private String fPrefix; /** * Creates a new 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 ResourceAction constructor), or * null if none * @param textEditor the text editor * @param markerType the type of marker to add * @param askForLabel true if the user should be asked for * a label for the new marker * @see TextEditorAction#TextEditorAction(ResourceBundle, String, ITextEditor) */ public AddMarkerAction(ResourceBundle bundle, String prefix, ITextEditor textEditor, String markerType, boolean askForLabel) { super(bundle, prefix, textEditor); fBundle= bundle; fPrefix= prefix; fMarkerType= markerType; fAskForLabel= askForLabel; } /** * Returns this action's resource bundle. * * @return this action's resource bundle */ protected ResourceBundle getResourceBundle() { return fBundle; } /** * Returns this action's resource key prefix. * * @return this action's resource key prefix */ protected String getResourceKeyPrefix() { return fPrefix; } @Override public void run() { IResource resource= getResource(); if (resource == null) return; Map attributes= getInitialAttributes(); if (fAskForLabel) { if (!askForLabel(attributes)) return; } String name= getToolTipText(); name= name == null ? TextEditorMessages.AddMarkerAction_addMarker : name; final Shell shell= getTextEditor().getSite().getShell(); IAdaptable context= new IAdaptable() { @SuppressWarnings("unchecked") @Override public T getAdapter(Class adapter) { if (adapter == Shell.class) return (T) shell; return null; } }; IUndoableOperation operation= new CreateMarkersOperation(fMarkerType, attributes, resource, name); IOperationHistory operationHistory= PlatformUI.getWorkbench().getOperationSupport().getOperationHistory(); try { operationHistory.execute(operation, null, context); } catch (ExecutionException x) { Bundle bundle= Platform.getBundle(PlatformUI.PLUGIN_ID); ILog log= Platform.getLog(bundle); String msg= getString(fBundle, fPrefix + "error.dialog.message", fPrefix + "error.dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$ log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK, msg, x)); } } @Override public void update() { setEnabled(getResource() != null); } /** * Asks the user for a marker label. Returns true if a label * is entered, false if the user cancels the input dialog. * The value for the attribute message is modified in the given * attribute map. * * @param attributes the attributes map * @return true if a label has been entered */ protected boolean askForLabel(Map attributes) { Object o= attributes.get("message"); //$NON-NLS-1$ String proposal= (o instanceof String) ? (String) o : ""; //$NON-NLS-1$ String title= getString(fBundle, fPrefix + "dialog.title", fPrefix + "dialog.title"); //$NON-NLS-2$ //$NON-NLS-1$ String message= getString(fBundle, fPrefix + "dialog.message", fPrefix + "dialog.message"); //$NON-NLS-2$ //$NON-NLS-1$ IInputValidator inputValidator= new IInputValidator() { @Override public String isValid(String newText) { return (newText == null || newText.trim().length() == 0) ? " " : null; //$NON-NLS-1$ } }; InputDialog dialog= new InputDialog(getTextEditor().getSite().getShell(), title, message, proposal, inputValidator); String label= null; if (dialog.open() != Window.CANCEL) label= dialog.getValue(); if (label == null) return false; label= label.trim(); if (label.length() == 0) return false; attributes.put("message", label); //$NON-NLS-1$ return true; } /** * Returns the attributes the new marker will be initialized with. *

* Subclasses may extend or replace this method.

* * @return the attributes the new marker will be initialized with */ protected Map getInitialAttributes() { Map attributes= new HashMap<>(11); ITextSelection selection= (ITextSelection) getTextEditor().getSelectionProvider().getSelection(); if (!selection.isEmpty()) { int start= selection.getOffset(); int length= selection.getLength(); if (length < 0) { length= -length; start -= length; } MarkerUtilities.setCharStart(attributes, start); MarkerUtilities.setCharEnd(attributes, start + length); // marker line numbers are 1-based int line= selection.getStartLine(); MarkerUtilities.setLineNumber(attributes, line == -1 ? -1 : line + 1); IDocument document= getTextEditor().getDocumentProvider().getDocument(getTextEditor().getEditorInput()); MarkerUtilities.setMessage(attributes, getLabelProposal(document, start, length)); } return attributes; } /** * Returns the initial label for the marker. * * @param document the document from which to extract a label proposal * @param offset the document offset of the range from which to extract the label proposal * @param length the length of the range from which to extract the label proposal * @return the label proposal */ protected String getLabelProposal(IDocument document, int offset, int length) { try { if (length > 0) { // find first white char but skip leading white chars int i= 0; boolean skip= true; while (i < length) { boolean isWhitespace= Character.isWhitespace(document.getChar(offset + i)); if (!skip && isWhitespace) break; if (skip && !isWhitespace) skip= false; i++; } String label= document.get(offset, i); return label.trim(); } char ch; // Get the first white char before the selection. int left= offset; int line= document.getLineOfOffset(offset); int limit= document.getLineOffset(line); while (left > limit) { ch= document.getChar(left); if (Character.isWhitespace(ch)) break; --left; } limit += document.getLineLength(line); // Now get the first letter. while (left <= limit) { ch= document.getChar(left); if (!Character.isWhitespace(ch)) break; ++left; } if (left > limit) return null; limit= Math.min(limit, left + MAX_LABEL_LENGTH); // Get the next white char. int right= (offset + length > limit ? limit : offset + length); while (right < limit) { ch= document.getChar(right); if (Character.isWhitespace(ch)) break; ++right; } // Trim the string and return it. if (left != right) { String label= document.get(left, right - left); return label.trim(); } } catch (BadLocationException x) { // don't proposal label then } return null; } /** * Returns the resource on which to create the marker, * or null if there is no applicable resource. This * queries the editor's input using getAdapter(IResource.class). * Subclasses may override this method. * * @return the resource to which to attach the newly created marker */ protected IResource getResource() { ITextEditor editor= getTextEditor(); if (editor != null) { IEditorInput input= editor.getEditorInput(); return ((IAdaptable) input).getAdapter(IResource.class); } return null; } }