/******************************************************************************* * Copyright (c) 2010, 2011 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.jdt.internal.ui.propertiesfileeditor; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.custom.VerifyKeyListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.resources.IFile; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.text.source.ISourceViewerExtension3; import org.eclipse.ui.progress.WorkbenchJob; /** * Auto edit strategy that escapes a character if it cannot be encoded in the .properties file's * encoding. * * <p> * A quick assist to escape backslashes is offered iff the pasted text is not perfectly correct for * the .properties file, i.e. if the text contains * <ul> * <li>an invalid escape sequence as defined by * {@link PropertiesFileEscapes#containsInvalidEscapeSequence(String)}</li> * <li>a character which requires Unicode escapes</li> * </ul> * </p> * * @since 3.7 */ public class PropertiesFileAutoEditStrategy implements IAutoEditStrategy { private final IFile fFile; private String fCharsetName; private CharsetEncoder fCharsetEncoder; private final ISourceViewer fSourceViewer; public PropertiesFileAutoEditStrategy(IFile file, ISourceViewer sourceViewer) { fFile= file; fSourceViewer= sourceViewer; } /* * (non-Javadoc) * @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand) */ public void customizeDocumentCommand(IDocument document, DocumentCommand command) { showProposal(escape(command), document); } private ICompletionProposal escape(DocumentCommand command) { try { String charsetName= fFile.getCharset(); if (!charsetName.equals(fCharsetName)) { fCharsetName= charsetName; fCharsetEncoder= Charset.forName(fCharsetName).newEncoder(); } } catch (CoreException e) { return null; } String text= command.text; boolean escapeUnicodeChars= !fCharsetEncoder.canEncode(text); boolean escapeBackslash= (text.length() > 1) && ((escapeUnicodeChars && PropertiesFileEscapes.containsUnescapedBackslash(text)) || PropertiesFileEscapes.containsInvalidEscapeSequence(text)); if (!escapeUnicodeChars && !escapeBackslash) return null; command.text= PropertiesFileEscapes.escape(text, false, false, escapeUnicodeChars); if (escapeBackslash) { String proposalText= PropertiesFileEscapes.escape(text, false, true, escapeUnicodeChars); return new EscapeBackslashCompletionProposal(proposalText, command.offset, command.text.length(), true); } return null; } private void showProposal(final ICompletionProposal proposal, final IDocument document) { if (proposal != null && fSourceViewer instanceof ISourceViewerExtension3) { final WorkbenchJob job= new WorkbenchJob(PropertiesFileEditorMessages.PropertiesFileAutoEditStrategy_showQuickAssist) { @Override public IStatus runInUIThread(IProgressMonitor monitor) { IQuickAssistAssistant assistant= ((ISourceViewerExtension3)fSourceViewer).getQuickAssistAssistant(); IQuickAssistProcessor processor= assistant.getQuickAssistProcessor(); if (processor instanceof PropertiesCorrectionProcessor) { ((PropertiesCorrectionProcessor)processor).setProposals(new ICompletionProposal[] { proposal }); assistant.showPossibleQuickAssists(); } return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(500); final StyledText textWidget= fSourceViewer.getTextWidget(); textWidget.addVerifyKeyListener(new VerifyKeyListener() { public void verifyKey(VerifyEvent event) { job.cancel(); textWidget.removeVerifyKeyListener(this); } }); final IDocumentListener documentListener= new IDocumentListener() { private boolean pasteComplete= false; public void documentAboutToBeChanged(DocumentEvent event) { } public void documentChanged(DocumentEvent event) { if (pasteComplete) { job.cancel(); document.removeDocumentListener(this); } pasteComplete= true; } }; document.addDocumentListener(documentListener); } } }