Omaha #4959: GFE formatter launcher word wrap calls to python now happen off UI thread.

Change-Id: I9cc2c1c88a9dd554aa4a557f7658f5012671b357

Former-commit-id: 3f703cfef74d24af9f5b75500f28fae532130812
This commit is contained in:
David Gillingham 2015-10-14 18:04:25 -05:00
parent c0aa08888e
commit 5d75e31d00
3 changed files with 162 additions and 73 deletions

View file

@ -174,6 +174,7 @@ import com.raytheon.viz.ui.simulatedtime.SimulatedTimeOperations;
* 08/10/2015 4721 randerso Changed getNNNid() to use the productID field (not textdbPil)
* Aug 31, 2015 4749 njensen Changed setCloseCallback to addCloseCallback
* 09/15/2015 4858 dgilling Disable store/transmit in DRT mode.
* 10/14/2015 4959 dgilling Support new function signature for wordWrap.
*
* </pre>
*
@ -2961,7 +2962,7 @@ public class ProductEditorComp extends Composite implements
while ((curLine <= lastLine) && (curLine < styledText.getLineCount())) {
int lineOff = styledText.getOffsetAtLine(curLine);
// word wrap a block, and find out how the text length changed.
indices = textComp.wordWrap(styledText, lineOff, wrapColumn);
indices = textComp.wordWrap(lineOff, wrapColumn);
int firstIdx = indices[0];
int lastIdx = indices[1];
int newLen = indices[2];

View file

@ -56,8 +56,6 @@ import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import com.raytheon.uf.common.dataplugin.gfe.python.GfePyIncludeUtil;
import com.raytheon.uf.common.python.PythonScript;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.ProductEditorLogger;
import com.raytheon.uf.common.status.UFStatus;
@ -70,7 +68,7 @@ import com.raytheon.viz.gfe.textformatter.TextFmtParserUtil;
/**
* Composite containing the product editor.
*
*
* <pre>
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
@ -91,12 +89,14 @@ import com.raytheon.viz.gfe.textformatter.TextFmtParserUtil;
* editing of framing codes.
* 07/02/2015 13753 lshi Update times for products in Product Editor
* 08/06/2015 13753 lshi use isSystemTextChange instead of isUpdateTime
*
* 14 OCT 2015 4959 dgilling Use WordWrapperPythonExecutor to get
* python calls off UI thread.
*
* </pre>
*
*
* @author lvenable
* @version 1.0
*
*
*/
public class StyledTextComp extends Composite {
@ -129,11 +129,28 @@ public class StyledTextComp extends Composite {
private static final String PRODUCT_PARSE_ERROR = "An unhandled exception was encountered trying to parse your text product. Please cancel and run your formatter again.";
private static final String NORM_SEP = "^\\s*$";
private static final String FUNNY_SEP = "^(\\s*)\\*(\\s*)";
private static final String NWS_SEP = "^\\..*\\.{3}";
private static final String PARA_SEP_STRING = "(" + NORM_SEP + ")|("
+ FUNNY_SEP + ")|(" + NWS_SEP + ")";
private static final Pattern PARA_SEP_PATTERN = Pattern
.compile(PARA_SEP_STRING);
private static final Pattern FUNNY_SEP_PATTERN = Pattern.compile(FUNNY_SEP,
Pattern.MULTILINE);
/**
* Parent composite.
*/
private final ProductEditorComp parent;
private final WordWrapPythonExecutor wrapper;
/**
* Styled text editor.
*/
@ -166,8 +183,6 @@ public class StyledTextComp extends Composite {
private boolean dirty = false;
private PythonScript python = null;
protected Color bgColor;
protected Color fgColor;
@ -182,15 +197,6 @@ public class StyledTextComp extends Composite {
private final ProductEditorLogger peLog;
private static final String NORM_SEP = "^\\s*$";
private static final String FUNNY_SEP = "^(\\s*)\\*(\\s*)";
private static final String NWS_SEP = "^\\..*\\.{3}";
private static final String PARA_SEP_STRING = "(" + NORM_SEP + ")|("
+ FUNNY_SEP + ")|(" + NWS_SEP + ")";
public boolean isAutoWrapMode() {
return autoWrapMode;
}
@ -201,7 +207,7 @@ public class StyledTextComp extends Composite {
/**
* Constructor.
*
*
* @param parent
* Parent composite.
* @param wrapMode
@ -214,6 +220,7 @@ public class StyledTextComp extends Composite {
this.parent = parent;
this.wrapColumn = wrapColumn;
this.autoWrapMode = wrapMode;
this.wrapper = new WordWrapPythonExecutor();
this.peLog = new ProductEditorLogger(parent.getProductName());
@ -250,30 +257,15 @@ public class StyledTextComp extends Composite {
this.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent arg0) {
wrapper.dispose();
textFont.dispose();
bgColor.dispose();
fgColor.dispose();
frameColor.dispose();
insertColor.dispose();
lockColor.dispose();
if (python != null) {
python.dispose();
python = null;
}
}
});
List<String> preEvals = new ArrayList<String>();
preEvals.add("import textwrap");
try {
python = new PythonScript(
GfePyIncludeUtil.getCommonGfeIncludePath(), this.getClass()
.getClassLoader(), preEvals);
} catch (JepException je) {
if (python != null) {
python.dispose();
}
}
}
/**
@ -323,7 +315,7 @@ public class StyledTextComp extends Composite {
/**
* Get the StyledText editor.
*
*
* @return The StyledText editor.
*/
public StyledText getTextEditorST() {
@ -332,7 +324,7 @@ public class StyledTextComp extends Composite {
/**
* Set the product text.
*
*
* @param text
* The product text.
*/
@ -502,7 +494,7 @@ public class StyledTextComp extends Composite {
/**
* Parse the product text string.
*
*
* @param productText
* Complete product text.
* @throws JepException
@ -598,7 +590,7 @@ public class StyledTextComp extends Composite {
/**
* Replacement of the text in the given range with new text.
*
*
* @param tip
* the range of text to be replaced
* @param text
@ -707,7 +699,7 @@ public class StyledTextComp extends Composite {
* A verify event occurs after the user has done something to modify the
* text (typically typed a key), but before the text is modified. The doit
* field in the verify event indicates whether or not to modify the text.
*
*
* @param event
* Verify event that was fired.
*/
@ -797,7 +789,7 @@ public class StyledTextComp extends Composite {
/**
* Check if there is selected text and if there is locked text in the
* selected text.
*
*
* @return True if there is selected text that contains locked text.
*/
private boolean selectionHasLockedText() {
@ -811,12 +803,12 @@ public class StyledTextComp extends Composite {
/**
* Check if there is locked text in the specified range of text.
*
*
* @param offset
* The starting point of the locked text search.
* @param length
* The length of the search.
*
*
* @return Whether or not there is text in the range that contains locked
* text.
*/
@ -834,7 +826,7 @@ public class StyledTextComp extends Composite {
/**
* Check if the key being pressed is a "non-edit" key.
*
*
* @param event
* Verify event.
* @return True if the key is an arrow or "non-edit" key.
@ -868,7 +860,7 @@ public class StyledTextComp extends Composite {
/**
* Handle the mouse down event.
*
*
* @param e
* Event fired.
*/
@ -983,7 +975,7 @@ public class StyledTextComp extends Composite {
/**
* Checks if the system is editing, e.g. updating the issue time every
* minute, vs a user typing text in the text area
*
*
* @return
*/
private boolean isSystemTextChange() {
@ -1103,7 +1095,7 @@ public class StyledTextComp extends Composite {
eventCursor = event.start + event.length;
}
wordWrap(textEditorST, eventCursor, wrapColumn);
wordWrap(eventCursor, wrapColumn);
if (cursorOffset != eventCursor) {
// restore cursor position for programmatic changes
@ -1139,7 +1131,7 @@ public class StyledTextComp extends Composite {
/**
* Getter for the column at which wrap and auto-wrap will wrap the text.
*
*
* @return the column number
*/
public int getWrapColumn() {
@ -1148,7 +1140,7 @@ public class StyledTextComp extends Composite {
/**
* Getter for the column at which wrap and auto-wrap will wrap the text.
*
*
* @param wrapColumn
* the column number
*/
@ -1187,7 +1179,7 @@ public class StyledTextComp extends Composite {
/**
* Query the prefs for setting. If it does not exist, use colorDft as its
* value. Create an SWT Color for display from the value and return it.
*
*
* @param prefs
* A preference store which might have config values.
* @param display
@ -1210,7 +1202,7 @@ public class StyledTextComp extends Composite {
/**
* Send a PROBLEM message if color1 is exactly equal to color2.
*
*
* @param color1
* the first color
* @param color2
@ -1237,7 +1229,7 @@ public class StyledTextComp extends Composite {
* <p>
* The getter name is different to avoid confusion with the getFgColor()
* method of Control.
*
*
* @return the foreground Color
*/
public Color getFgndColor() {
@ -1248,7 +1240,7 @@ public class StyledTextComp extends Composite {
* Get the framed text color of the StyledTextComp. This is the actual
* color, not a copy. It will be disposed when the StyledTextComp is, and
* should not be disposed before then.
*
*
* @return the frameColor
*/
public Color getFrameColor() {
@ -1259,7 +1251,7 @@ public class StyledTextComp extends Composite {
* Get the insert color of the StyledTextComp. This is the actual color, not
* a copy. It will be disposed when the StyledTextComp is, and should not be
* disposed before then.
*
*
* @return the insertColor
*/
public Color getInsertColor() {
@ -1270,7 +1262,7 @@ public class StyledTextComp extends Composite {
* Get the locked text color of the StyledTextComp. This is the actual
* color, not a copy. It will be disposed when the StyledTextComp is, and
* should not be disposed before then.
*
*
* @return the lockColor
*/
public Color getLockColor() {
@ -1280,9 +1272,7 @@ public class StyledTextComp extends Composite {
/**
* Word wrap the text in the block around cursorIndex. Adjust the cursor
* position to account for inserted or deleted whitespace.
*
* @param st
* The StyledText in which word wrap is to be performed
*
* @param cursorIndex
* The cursor index
* @param width
@ -1294,15 +1284,11 @@ public class StyledTextComp extends Composite {
* <li>The length of the replacement text</li>
* </ol>
*/
public int[] wordWrap(StyledText st, int cursorIndex, int width) {
public int[] wordWrap(int cursorIndex, int width) {
StyledText st = getTextEditorST();
final Pattern PARA_SEP_PATTERN = Pattern.compile(PARA_SEP_STRING);
final Matcher PARA_MATCHER = PARA_SEP_PATTERN.matcher("");
final Pattern FUNNY_SEP_PATTERN = Pattern.compile(FUNNY_SEP,
Pattern.MULTILINE);
final Matcher FUNNY_SEP_MATCHER = FUNNY_SEP_PATTERN.matcher("");
final String NL = st.getLineDelimiter();
String line;
@ -1428,11 +1414,8 @@ public class StyledTextComp extends Composite {
preArgs.put("text", pre);
try {
python.instantiatePythonClass("_wrapper", "textwrap.TextWrapper",
args);
pre = (String) python.execute("fill", "_wrapper", preArgs);
} catch (JepException e) {
pre = wrapper.callWordWrapPython(args, preArgs);
} catch (Exception e) {
statusHandler.error("Python error wrapping text preceding cursor:",
e);
}
@ -1455,10 +1438,8 @@ public class StyledTextComp extends Composite {
postArgs.put("text", post);
try {
python.instantiatePythonClass("_wrapper", "textwrap.TextWrapper",
args);
post = (String) python.execute("fill", "_wrapper", postArgs);
} catch (JepException e) {
post = wrapper.callWordWrapPython(args, postArgs);
} catch (Exception e) {
statusHandler.error("Python error wrapping text after cursor:", e);
}

View file

@ -0,0 +1,107 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.gfe.dialogs.formatterlauncher;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import com.raytheon.uf.common.dataplugin.gfe.python.GfePyIncludeUtil;
import com.raytheon.uf.common.python.PythonScript;
/**
* Executor service for running GFE's python-based word wrapping code.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 14, 2015 #4959 dgilling Initial creation
*
* </pre>
*
* @author dgilling
* @version 1.0
*/
public final class WordWrapPythonExecutor {
private final ExecutorService execService;
private PythonScript python;
public WordWrapPythonExecutor() {
this.execService = Executors.newSingleThreadExecutor();
}
public synchronized String callWordWrapPython(
final Map<String, Object> classArgs,
final Map<String, Object> methodArgs) throws InterruptedException,
ExecutionException {
Callable<String> wordWrapTask = new Callable<String>() {
@Override
public String call() throws Exception {
if (python == null) {
List<String> preEvals = Arrays.asList("import textwrap");
python = new PythonScript(
GfePyIncludeUtil.getCommonGfeIncludePath(),
getClass().getClassLoader(), preEvals);
}
python.instantiatePythonClass("_wrapper",
"textwrap.TextWrapper", classArgs);
return (String) python.execute("fill", "_wrapper", methodArgs);
}
};
Future<String> result = execService.submit(wordWrapTask);
return result.get();
}
public void dispose() {
/*
* Have to create a Runnable to dispose the PythonScript instance to
* avoid thread access issues.
*/
Runnable disposeTask = new Runnable() {
@Override
public void run() {
if (python != null) {
python.close();
python = null;
}
}
};
execService.submit(disposeTask);
if (execService != null) {
execService.shutdown();
}
}
}