Utils.java

Index Score
org.gudy.azureus2.ui.swt
Azureus

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
CYCLOMATICCyclomatic complexity
COMPARISONSNumber of comparison operators
BLOCKSNumber of blocks
OPERATORSNumber of operators
PROGRAM_LENGTHHalstead program length
OPERANDSNumber of operands
PARAMSNumber of formal parameter declarations
ELOCEffective lines of code
LOCLines of code
LOGICAL_LINESNumber of statements
INTERFACE_COMPLEXITYInterface complexity
EXITSProcedure exits
SIZESize of the file in bytes
LINESNumber of lines in the source file
UNIQUE_OPERANDSNumber of unique operands
PROGRAM_VOCABHalstead program vocabulary
JAVA0034JAVA0034 Missing braces in if statement
EXEC_COMMENTSComments in executable code
LOOPSNumber of loops
RETURNSNumber of return points from functions
JAVA0145JAVA0145 Tab character used in source file
FUNCTIONSNumber of function declarations
LINE_COMMENTNumber of line comments
COMMENTSComment lines
WHITESPACENumber of whitespace lines
DOC_COMMENTNumber of javadoc comment lines
DECL_COMMENTSComments in declarations
JAVA0049JAVA0049 Nested block at depth N (maximum: M)
JAVA0117JAVA0117 Missing javadoc: method 'method'
JAVA0108JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0170JAVA0170 Caught exception not derived from java.lang.Exception
JAVA0270JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0166JAVA0166 Generic exception caught
JAVA0285JAVA0285 Dereference of potentially null variable
JAVA0177JAVA0177 Variable declaration missing initializer
JAVA0076JAVA0076 Use of magic number
JAVA0029JAVA0029 Private method not used
JAVA0081JAVA0081 Boolean literal in comparison
UNIQUE_OPERATORSNumber of unique operators
JAVA0287JAVA0287 Unnecessary null check
JAVA0119JAVA0119 Control variable changed within body of for loop
JAVA0267JAVA0267 Use of System.err
JAVA0110JAVA0110 Incorrect javadoc: no @return tag
NEST_DEPTHMaximum nesting depth
JAVA0144JAVA0144 Line exceeds maximum M characters
JAVA0138JAVA0138 N parameters defined for method (maximum: M)
JAVA0159JAVA0159 Inefficient conversion of string to integer
JAVA0111JAVA0111 Incorrect javadoc: @return tag for void method
JAVA0128JAVA0128 Public constructor in non-public class
PROGRAM_VOLUMEHalstead program volume
JAVA0136JAVA0136 N methods defined in class (maximum: M)
JAVA0116JAVA0116 Missing javadoc: field 'field'
JAVA0126JAVA0126 Method declares unchecked exception in throws
JAVA0109JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA0067JAVA0067 Array descriptor on identifier name
JAVA0130JAVA0130 Non-static method does not use instance fields
/* * File : Utils.java * Created : 25 sept. 2003 16:15:07 * By : Olivier * * Azureus - a Java Bittorrent client * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.gudy.azureus2.ui.swt; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.StringTokenizer; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.DropTargetListener; import org.eclipse.swt.dnd.FileTransfer; import org.eclipse.swt.dnd.HTMLTransfer; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Region; import org.eclipse.swt.graphics.Resource; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Monitor; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Widget; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.ui.swt.mainwindow.Colors; import org.gudy.azureus2.ui.swt.mainwindow.SWTThread; import org.gudy.azureus2.ui.swt.mainwindow.TorrentOpener; import org.gudy.azureus2.ui.swt.shells.MessageBoxShell; import org.gudy.azureus2.ui.swt.views.table.TableViewSWT; import org.gudy.azureus2.ui.swt.views.utils.VerticalAligner; import com.aelitis.azureus.core.impl.AzureusCoreImpl; import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT; import com.aelitis.azureus.ui.swt.UIFunctionsSWT; /** * @author Olivier * */ public class Utils { private static final String GOOD_STRING = "(/|,jI~`gy"; public static final boolean isGTK = SWT.getPlatform().equals("gtk"); /** Some platforms expand the last column to fit the remaining width of * the table. */ public static final boolean LAST_TABLECOLUMN_EXPANDS = isGTK; /** GTK already handles alternating background for tables */ public static final boolean TABLE_GRIDLINE_IS_ALTERNATING_COLOR = isGTK; private static final boolean DIRECT_SETCHECKED = !Constants.isOSX || SWT.getVersion() >= 3212; public static final boolean SWT32_TABLEPAINT = false; //SWT.getVersion() >= 3200; /** * Debug/Diagnose SWT exec calls. Provides usefull information like how * many we are queuing up, and how long each call takes. Good to turn on * occassionally to see if we coded something stupid. */ private static final boolean DEBUG_SWTEXEC = System.getProperty( "debug.swtexec", "0").equals("1"); private static ArrayList queue; private static AEDiagnosticsLogger diag_logger; static { if (DEBUG_SWTEXEC) { queue = new ArrayList(); diag_logger = AEDiagnostics.getLogger("swt"); diag_logger.log("\n\nSWT Logging Starts"); } else { queue = null; diag_logger = null; } } public static boolean isAZ2UI() { String ui_type = COConfigurationManager.getStringParameter("ui"); return (ui_type.equals("az2")); } public static void disposeComposite(Composite composite, boolean disposeSelf) { if (composite == null || composite.isDisposed()) return; Control[] controls = composite.getChildren(); for (int i = 0; i < controls.length; i++) { Control control = controls[i]; if (control != null && !control.isDisposed()) { if (control instanceof Composite) { disposeComposite((Composite) control, true); } try { control.dispose(); } catch (SWTException e) { Debug.printStackTrace(e); } } } // It's possible that the composite was destroyed by the child if (!composite.isDisposed() && disposeSelf) try { composite.dispose(); } catch (SWTException e) { Debug.printStackTrace(e); } } public static void disposeComposite(Composite composite) { disposeComposite(composite, true); } /** * Dispose of a list of SWT objects * * @param disposeList */ public static void disposeSWTObjects(List disposeList) { disposeSWTObjects(disposeList.toArray()); disposeList.clear(); } public static void disposeSWTObjects(Object[] disposeList) { if (disposeList == null) { return; } boolean bResourceObjectExists = SWT.getVersion() >= 3129; for (int i = 0; i < disposeList.length; i++) { Object o = disposeList[i]; if (o instanceof Widget && !((Widget) o).isDisposed()) ((Widget) o).dispose(); else if (bResourceObjectExists && (o instanceof Resource) && !((Resource) o).isDisposed()) ((Resource) o).dispose(); else { try { // For Pre-SWT 3.1 if ((o instanceof Cursor) && !((Cursor) o).isDisposed()) { ((Cursor) o).dispose(); } else if ((o instanceof Font) && !((Font) o).isDisposed()) { ((Font) o).dispose(); } else if ((o instanceof GC) && !((GC) o).isDisposed()) { ((GC) o).dispose(); } else if ((o instanceof Image) && !((Image) o).isDisposed()) { ((Image) o).dispose(); } else if ((o instanceof Region) && !((Region) o).isDisposed()) { ((Region) o).dispose(); // 3.0 } else if ((o instanceof TextLayout) && !((TextLayout) o).isDisposed()) { ((TextLayout) o).dispose(); // 3.0 } } catch (NoClassDefFoundError e) { // ignore } // Path, Pattern, Transform are all 3.1, which will be instances of // Resource } } } /** * Initializes the URL dialog with http:// * If a valid link is found in the clipboard, it will be inserted * and the size (and location) of the dialog is adjusted. * @param shell to set the dialog location if needed * @param url the URL text control * @param accept_magnets * * @author Rene Leonhardt */ public static void setTextLinkFromClipboard(final Shell shell, final Text url, boolean accept_magnets) { String link = getLinkFromClipboard(shell.getDisplay(), accept_magnets); if (link != null) url.setText(link); } /** * <p>Gets an URL from the clipboard if a valid URL for downloading has been copied.</p> * <p>The supported protocols currently are http, https, and magnet.</p> * @param display * @param accept_magnets * @return first valid link from clipboard, else "http://" */ public static String getLinkFromClipboard(Display display, boolean accept_magnets) { final Clipboard cb = new Clipboard(display); final TextTransfer transfer = TextTransfer.getInstance(); String data = (String) cb.getContents(transfer); String text = UrlUtils.parseTextForURL(data, accept_magnets); if (text == null) { return "http://"; } return text; } public static void centreWindow(Shell shell) { Rectangle displayArea; // area to center in try { displayArea = shell.getMonitor().getClientArea(); } catch (NoSuchMethodError e) { displayArea = shell.getDisplay().getClientArea(); } Rectangle shellRect = shell.getBounds(); if (shellRect.height > displayArea.height) { shellRect.height = displayArea.height; } if (shellRect.width > displayArea.width - 50) { shellRect.width = displayArea.width; } shellRect.x = displayArea.x + (displayArea.width - shellRect.width) / 2; shellRect.y = displayArea.y + (displayArea.height - shellRect.height) / 2; shell.setBounds(shellRect); } /** * Centers a window relative to a control. That is to say, the window will be located at the center of the control. * @param window * @param control */ public static void centerWindowRelativeTo(final Shell window, final Control control) { final Rectangle bounds = control.getBounds(); final Point shellSize = window.getSize(); window.setLocation(bounds.x + (bounds.width / 2) - shellSize.x / 2, bounds.y + (bounds.height / 2) - shellSize.y / 2); } public static void createTorrentDropTarget(Composite composite, boolean bAllowShareAdd) { try { createDropTarget(composite, bAllowShareAdd, null); } catch (Exception e) { Debug.out(e); } } /** * @param control the control (usually a Shell) to add the DropTarget * @param url the Text control where to set the link text * * @author Rene Leonhardt */ public static void createURLDropTarget(Composite composite, Text url) { try { createDropTarget(composite, false, url); } catch (Exception e) { Debug.out(e); } } private static void createDropTarget(Composite composite, final boolean bAllowShareAdd, final Text url, DropTargetListener dropTargetListener) { Transfer[] transferList; if (SWT.getVersion() >= 3107) { transferList = new Transfer[] { HTMLTransfer.getInstance(), URLTransfer.getInstance(), FileTransfer.getInstance(), TextTransfer.getInstance() }; } else { transferList = new Transfer[] { URLTransfer.getInstance(), FileTransfer.getInstance(), TextTransfer.getInstance() }; } final DropTarget dropTarget = new DropTarget(composite, DND.DROP_DEFAULT | DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_TARGET_MOVE); dropTarget.setTransfer(transferList); dropTarget.addDropListener(dropTargetListener); // Note: DropTarget will dipose when the parent it's on diposes // On Windows, dropping on children moves up to parent // On OSX, each child needs it's own drop. if (Constants.isWindows) return; Control[] children = composite.getChildren(); for (int i = 0; i < children.length; i++) { Control control = children[i]; if (!control.isDisposed()) { if (control instanceof Composite) { createDropTarget((Composite) control, bAllowShareAdd, url, dropTargetListener); } else { final DropTarget dropTarget2 = new DropTarget(control, DND.DROP_DEFAULT | DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_TARGET_MOVE); dropTarget2.setTransfer(transferList); dropTarget2.addDropListener(dropTargetListener); } } } } private static void createDropTarget(Composite composite, boolean bAllowShareAdd, Text url) { URLDropTarget target = new URLDropTarget(url, bAllowShareAdd); createDropTarget(composite, bAllowShareAdd, url, target); } private static class URLDropTarget extends DropTargetAdapter { private final Text url; private final boolean bAllowShareAdd; public URLDropTarget(Text url, boolean bAllowShareAdd) { this.url = url; this.bAllowShareAdd = bAllowShareAdd; } public void dropAccept(DropTargetEvent event) { event.currentDataType = URLTransfer.pickBestType(event.dataTypes, event.currentDataType); } public void dragOver(DropTargetEvent event) { // skip setting detail if user is forcing a drop type (ex. via the // ctrl key), providing that the operation is valid if (event.detail != DND.DROP_DEFAULT && ((event.operations & event.detail) > 0)) return; if ((event.operations & DND.DROP_LINK) > 0) event.detail = DND.DROP_LINK; else if ((event.operations & DND.DROP_DEFAULT) > 0) event.detail = DND.DROP_DEFAULT; else if ((event.operations & DND.DROP_COPY) > 0) event.detail = DND.DROP_COPY; } public void drop(DropTargetEvent event) { if (url == null || url.isDisposed()) { TorrentOpener.openDroppedTorrents(AzureusCoreImpl.getSingleton(), event, bAllowShareAdd); } else { if (event.data instanceof URLTransfer.URLType) { if (((URLTransfer.URLType) event.data).linkURL != null) url.setText(((URLTransfer.URLType) event.data).linkURL); } else if (event.data instanceof String) { String sURL = UrlUtils.parseTextForURL((String) event.data, true); if (sURL != null) { url.setText(sURL); } } } } } /** * Force label to use more vertical space if wrapped and in a GridLayout * Place this listener on the _parent_ of the label * See Eclipse SWT Bug #9866 (GridLayout does not handle wrapped Label properly) * This workaround only works for labels who: * - horizontally span their whole parent * (ie. the parent has 3 columns, the label must span 3 columns) * - GridData style has GridData.FILL_HORIZONTAL * - Label style has SWT.WRAP * * @author TuxPaper * @note Bug 9866 fixed in 3105 and later */ public static class LabelWrapControlListener extends ControlAdapter { public void controlResized(ControlEvent e) { if (SWT.getVersion() >= 3105) return; Composite parent = (Composite) e.widget; Control children[] = parent.getChildren(); if (children.length > 0) { GridLayout parentLayout = (GridLayout) parent.getLayout(); if (parentLayout != null) { Point size; int marginWidth = parentLayout.marginWidth; Composite grandParent = parent.getParent(); if (grandParent instanceof ScrolledComposite) { Composite greatGP = grandParent.getParent(); if (greatGP != null) { size = greatGP.getSize(); if (greatGP.getLayout() instanceof GridLayout) { marginWidth += ((GridLayout) greatGP.getLayout()).marginWidth; } } else { // not tested size = grandParent.getSize(); } if (grandParent.getLayout() instanceof GridLayout) { marginWidth += ((GridLayout) grandParent.getLayout()).marginWidth; } ScrollBar sb = grandParent.getVerticalBar(); if (sb != null) { // I don't know why, but we have to remove one size.x -= sb.getSize().x + 1; } } else size = parent.getSize(); boolean oneChanged = false; for (int i = 0; i < children.length; i++) { if ((children[i] instanceof Label) && (children[i].getStyle() & SWT.WRAP) == SWT.WRAP) { GridData gd = (GridData) children[i].getLayoutData(); if (gd != null && gd.horizontalAlignment == GridData.FILL) { if (gd.horizontalSpan == parentLayout.numColumns) { gd.widthHint = size.x - 2 * marginWidth; oneChanged = true; } else { Point pt = children[i].getLocation(); gd.widthHint = size.x - pt.x - (2 * marginWidth); oneChanged = true; } } } } if (oneChanged) { parent.layout(true); if (grandParent instanceof ScrolledComposite) { ((ScrolledComposite) grandParent).setMinSize(parent.computeSize( SWT.DEFAULT, SWT.DEFAULT, true)); } } } } // size } // controlResized } // class public static void alternateRowBackground(TableItem item) { if (Utils.TABLE_GRIDLINE_IS_ALTERNATING_COLOR) { if (!item.getParent().getLinesVisible()) item.getParent().setLinesVisible(true); return; } if (item == null || item.isDisposed()) return; Color[] colors = { item.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND), Colors.colorAltRow }; Color newColor = colors[item.getParent().indexOf(item) % colors.length]; if (!item.getBackground().equals(newColor)) { item.setBackground(newColor); } } // Yes, this is actually used by the RSSFeed plugin... // so don't make private until this is fixed public static void alternateTableBackground(Table table) { if (table == null || table.isDisposed()) return; if (Utils.TABLE_GRIDLINE_IS_ALTERNATING_COLOR) { if (!table.getLinesVisible()) table.setLinesVisible(true); return; } int iTopIndex = table.getTopIndex(); int iBottomIndex = getTableBottomIndex(table, iTopIndex); Color[] colors = { table.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND), Colors.colorAltRow }; int iFixedIndex = iTopIndex; for (int i = iTopIndex; i <= iBottomIndex; i++) { TableItem row = table.getItem(i); // Rows can be disposed! if (!row.isDisposed()) { Color newColor = colors[iFixedIndex % colors.length]; iFixedIndex++; if (!row.getBackground().equals(newColor)) { // System.out.println("setting "+rows[i].getBackground() +" to " + newColor); row.setBackground(newColor); } } } } /** * <p> * Set a MenuItem's image with the given ImageRepository key. In compliance with platform * human interface guidelines, the images are not set under Mac OS X. * </p> * @param item SWT MenuItem * @param repoKey ImageRepository image key * @see <a href="http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGMenus/chapter_7_section_3.html#//apple_ref/doc/uid/TP30000356/TPXREF116">Apple HIG</a> */ public static void setMenuItemImage(final MenuItem item, final String repoKey) { if (!Constants.isOSX) item.setImage(ImageRepository.getImage(repoKey)); } public static void setMenuItemImage(final MenuItem item, final Image image) { if (!Constants.isOSX) item.setImage(image); } /** * Sets the shell's Icon(s) to the default Azureus icon. OSX doesn't require * an icon, so they are skipped * * @param shell */ public static void setShellIcon(Shell shell) { final String[] sImageNames = { "azureus", "azureus32", "azureus64", "azureus128" }; if (Constants.isOSX) return; try { ArrayList list = new ArrayList(sImageNames.length); for (int i = 0; i < sImageNames.length; i++) { Image image = ImageRepository.getImage(sImageNames[i]); if (image != null) list.add(image); } if (list.size() == 0) return; shell.setImages((Image[]) list.toArray(new Image[list.size()])); } catch (NoSuchMethodError e) { // SWT < 3.0 Image image = ImageRepository.getImage(sImageNames[0]); if (image != null) shell.setImage(image); } } public static Display getDisplay() { SWTThread swt = SWTThread.getInstance(); Display display; if (swt == null) { display = Display.getDefault(); if (display == null) { System.err.println("SWT Thread not started yet!"); return null; } } else { if (swt.isTerminated()) { return null; } display = swt.getDisplay(); } if (display == null || display.isDisposed()) { return null; } return display; } /** * Execute code in the Runnable object using SWT's thread. If current * thread it already SWT's thread, the code will run immediately. If the * current thread is not SWT's, code will be run either synchronously or * asynchronously on SWT's thread at the next reasonable opportunity. * * This method does not catch any exceptions. * * @param code code to run * @param async true if SWT asyncExec, false if SWT syncExec * @return success */ public static boolean execSWTThread(final Runnable code, boolean async) { return execSWTThread(code, async ? -1 : -2); } /** * Schedule execution of the code in the Runnable object using SWT's thread. * Even if the current thread is the SWT Thread, the code will be scheduled. * <p> * Much like Display.asyncExec, except getting the display is handled for you, * and provides the ability to diagnose and monitor scheduled code run. * * @param msLater time to wait before running code on SWT thread. 0 does not * mean immediate, but as soon as possible. * @param code Code to run * @return sucess * * @since 3.0.4.3 */ public static boolean execSWTThreadLater(int msLater, final Runnable code) { return execSWTThread(code, msLater); } /** * * @param code * @param msLater -2: sync<BR> * -1: sync if on SWT thread, async otherwise<BR> * 0: async<BR> * >0: timerExec * @return * * @since 3.0.4.3 */ private static boolean execSWTThread(final Runnable code, final int msLater) { final Display display = getDisplay(); if (display == null || code == null) { return false; } boolean isSWTThread = display.getThread() == Thread.currentThread(); if (msLater < 0 && isSWTThread) { if (queue == null) { code.run(); } else { long lStartTimeRun = SystemTime.getCurrentTime(); code.run(); long wait = SystemTime.getCurrentTime() - lStartTimeRun; if (wait > 700) { diag_logger.log(SystemTime.getCurrentTime() + "] took " + wait + "ms to run " + Debug.getCompressedStackTrace()); } } } else if (msLater >= -1) { try { if (queue == null) { if (msLater <= 0) { display.asyncExec(code); } else { if(isSWTThread) { display.timerExec(msLater, code); } else { SimpleTimer.addEvent("execSWTThreadLater", SystemTime.getOffsetTime(msLater), new TimerEventPerformer() { public void perform(TimerEvent event) { if (!display.isDisposed()) { display.asyncExec(code); } } }); } } } else { queue.add(code); diag_logger.log(SystemTime.getCurrentTime() + "] + QUEUE. size= " + queue.size() + "; add " + code + " via " + Debug.getCompressedStackTrace()); final long lStart = SystemTime.getCurrentTime(); final Display fDisplay = display; AERunnable runnableWrapper = new AERunnable() { public void runSupport() { long wait = SystemTime.getCurrentTime() - lStart - msLater; if (wait > 700) { diag_logger.log(SystemTime.getCurrentTime() + "] took " + wait + "ms before SWT ran async code " + code); } long lStartTimeRun = SystemTime.getCurrentTime(); try { if (fDisplay.isDisposed()) { Debug.out("Display disposed while trying to execSWTThread " + code); // run anayway, except trap SWT error try { code.run(); } catch (SWTException e) { Debug.out("Error while execSWTThread w/disposed Display", e); } } else { code.run(); } } finally { wait = SystemTime.getCurrentTime() - lStartTimeRun; if (wait > 500) { diag_logger.log(SystemTime.getCurrentTime() + "] took " + wait + "ms to run " + code); } diag_logger.log(SystemTime.getCurrentTime() + "] - QUEUE. size=" + queue.size()); queue.remove(code); } } }; if (msLater <= 0) { display.asyncExec(runnableWrapper); } else { if(isSWTThread) { display.timerExec(msLater, code); } else { SimpleTimer.addEvent("execSWTThreadLater", SystemTime.getOffsetTime(msLater), new TimerEventPerformer() { public void perform(TimerEvent event) { if (!display.isDisposed()) { display.asyncExec(code); } } }); } } } } catch (NullPointerException e) { // If the display is being disposed of, asyncExec may give a null // pointer error return false; } } else { display.syncExec(code); } return true; } /** * Execute code in the Runnable object using SWT's thread. If current * thread it already SWT's thread, the code will run immediately. If the * current thread is not SWT's, code will be run asynchronously on SWT's * thread at the next reasonable opportunity. * * This method does not catch any exceptions. * * @param code code to run * @return success */ public static boolean execSWTThread(Runnable code) { return execSWTThread(code, -1); } public static boolean isThisThreadSWT() { SWTThread swt = SWTThread.getInstance(); if (swt == null) { System.err.println("WARNING: SWT Thread not started yet"); } Display display = (swt == null) ? Display.getCurrent() : swt.getDisplay(); if (display == null) { return false; } // This will throw if we are disposed or on the wrong thread // Much better that display.getThread() as that one locks Device.class // and may end up causing sync lock when disposing try { display.getWarnings(); } catch (SWTException e) { return false; } return (display.getThread() == Thread.currentThread()); } /** Open a messagebox using resource keys for title/text * * @param parent Parent shell for messagebox * @param style SWT styles for messagebox * @param keyPrefix message bundle key prefix used to get title and text. * Title will be keyPrefix + ".title", and text will be set to * keyPrefix + ".text" * @param textParams any parameters for text * * @return what the messagebox returns */ public static int openMessageBox(Shell parent, int style, String keyPrefix, String[] textParams) { if ((style & (0x7f << 5)) == 0) { // need at least one button style |= SWT.OK; } Object[] buttonInfo = swtButtonStylesToText(style); MessageBoxShell mb = new MessageBoxShell(parent, MessageText.getString(keyPrefix + ".title"), MessageText.getString( keyPrefix + ".text", textParams), (String[]) buttonInfo[0], 0); mb.setLeftImage(style & 0x1f); int ret = mb.open(); Integer[] buttonVals = (Integer[]) buttonInfo[1]; if (ret < 0 || ret > buttonVals.length) { return SWT.CANCEL; } return buttonVals[ret].intValue(); } /** Open a messagebox with actual title and text * * @param parent * @param style * @param title * @param text * @return */ public static int openMessageBox(Shell parent, int style, String title, String text) { if (parent == null) { parent = findAnyShell(); } if ((style & (0x7f << 5)) == 0) { // need at least one button style |= SWT.OK; } Object[] buttonInfo = swtButtonStylesToText(style); MessageBoxShell mb = new MessageBoxShell(parent, title, text, (String[]) buttonInfo[0], 0); mb.setLeftImage(style & 0x1f); int ret = mb.open(); Integer[] buttonVals = (Integer[]) buttonInfo[1]; if (ret < 0 || ret > buttonVals.length) { return SWT.CANCEL; } return buttonVals[ret].intValue(); } public static int openMessageBox(Shell parent, int style, int default_style, String title, String text) { if (parent == null) { parent = findAnyShell(); } if ((style & (0x7f << 5)) == 0) { // need at least one button style |= SWT.OK; } Object[] buttonInfo = swtButtonStylesToText(style); Object[] defaultButtonInfo = swtButtonStylesToText(default_style); int defaultIndex = 0; if (defaultButtonInfo.length > 0) { String name = ((String[]) defaultButtonInfo[0])[0]; String[] names = (String[]) buttonInfo[0]; for (int i = 0; i < names.length; i++) { if (names[i].equals(name)) { defaultIndex = i; break; } } } MessageBoxShell mb = new MessageBoxShell(parent, title, text, (String[]) buttonInfo[0], defaultIndex); mb.setLeftImage(style & 0x1f); int ret = mb.open(); Integer[] buttonVals = (Integer[]) buttonInfo[1]; if (ret < 0 || ret > buttonVals.length) { return SWT.CANCEL; } return buttonVals[ret].intValue(); } private static Object[] swtButtonStylesToText(int style) { List buttons = new ArrayList(2); List buttonVal = new ArrayList(2); int buttonCount = 0; if ((style & SWT.OK) > 0) { buttons.add(MessageText.getString("Button.ok")); buttonVal.add(new Integer(SWT.OK)); buttonCount++; } if ((style & SWT.YES) > 0) { buttons.add(MessageText.getString("Button.yes")); buttonVal.add(new Integer(SWT.YES)); buttonCount++; } if ((style & SWT.NO) > 0) { buttons.add(MessageText.getString("Button.no")); buttonVal.add(new Integer(SWT.NO)); buttonCount++; } if ((style & SWT.CANCEL) > 0) { buttons.add(MessageText.getString("Button.cancel")); buttonVal.add(new Integer(SWT.CANCEL)); buttonCount++; } if ((style & SWT.ABORT) > 0) { buttons.add(MessageText.getString("Button.abort")); buttonVal.add(new Integer(SWT.ABORT)); buttonCount++; } if ((style & SWT.RETRY) > 0) { buttons.add(MessageText.getString("Button.retry")); buttonVal.add(new Integer(SWT.RETRY)); buttonCount++; } if ((style & SWT.IGNORE) > 0) { buttons.add(MessageText.getString("Button.ignore")); buttonVal.add(new Integer(SWT.IGNORE)); buttonCount++; } return new Object[] { (String[]) buttons.toArray(new String[buttonCount]), (Integer[]) buttonVal.toArray(new Integer[buttonCount]) }; } /** * Bottom Index may be negative */ public static int getTableBottomIndex(Table table, int iTopIndex) { // on Linux, getItemHeight is slow AND WRONG. so is getItem(x).getBounds().y // getItem(Point) is slow on OSX int itemCount = table.getItemCount(); if (!table.isVisible() || iTopIndex >= itemCount) return -1; if (Constants.isOSX) { try { TableItem item = table.getItem(iTopIndex); Rectangle bounds = item.getBounds(); Rectangle clientArea = table.getClientArea(); int itemHeight = table.getItemHeight(); int iBottomIndex = Math.min(iTopIndex + (clientArea.height + clientArea.y - bounds.y - 1) / itemHeight, itemCount - 1); // System.out.println(bounds + ";" + clientArea + ";" + itemHeight + ";bi=" // + iBottomIndex + ";ti=" + iTopIndex + ";" // + (clientArea.height + clientArea.y - bounds.y - 1)); return iBottomIndex; } catch (NoSuchMethodError e) { // item.getBounds is 3.2 return Math.min( iTopIndex + ((table.getClientArea().height - table.getHeaderHeight() - 1) / table.getItemHeight()) + 1, table.getItemCount() - 1); } } // getItem will return null if clientArea's height is smaller than // header height. int areaHeight = table.getClientArea().height; if (areaHeight <= table.getHeaderHeight()) return -1; // 2 offset to be on the safe side TableItem bottomItem = table.getItem(new Point(2, table.getClientArea().height - 1)); int iBottomIndex = (bottomItem != null) ? table.indexOf(bottomItem) : itemCount - 1; return iBottomIndex; } public static void launch(String sFile) { if (sFile == null) { return; } if (SWT.getVersion() >= 3315 || SWT.getVersion() < 3300 || UrlUtils.isURL(sFile) || sFile.startsWith("mailto:")) { boolean launched = Program.launch(sFile); if (!launched && Constants.isUnix && (UrlUtils.isURL(sFile) || sFile.startsWith("mailto:"))) { if (!Program.launch("xdg-open " + sFile)) { Program.launch("htmlview " + sFile); } } } else { if (Constants.isOSX) { Program.launch("file://" + sFile.replaceAll(" ", "%20")); } else { Program.launch(sFile); } } } /** * Sets the checkbox in a Virtual Table while inside a SWT.SetData listener * trigger. SWT 3.1 has an OSX bug that needs working around. * * @param item * @param checked */ public static void setCheckedInSetData(final TableItem item, final boolean checked) { if (DIRECT_SETCHECKED) { item.setChecked(checked); } else { item.setChecked(!checked); item.getDisplay().asyncExec(new AERunnable() { public void runSupport() { item.setChecked(checked); } }); } if (Constants.isWindowsXP || isGTK) { Rectangle r = item.getBounds(0); Table table = item.getParent(); Rectangle rTable = table.getClientArea(); r.y += VerticalAligner.getTableAdjustVerticalBy(table); table.redraw(0, r.y, rTable.width, r.height, true); } } public static boolean linkShellMetricsToConfig(final Shell shell, final String sConfigPrefix) { String windowRectangle = COConfigurationManager.getStringParameter( sConfigPrefix + ".rectangle", null); boolean bDidResize = false; if (null != windowRectangle) { int i = 0; int[] values = new int[4]; StringTokenizer st = new StringTokenizer(windowRectangle, ","); try { while (st.hasMoreTokens() && i < 4) { values[i++] = Integer.valueOf(st.nextToken()).intValue(); } if (i == 4) { Rectangle shellBounds = new Rectangle(values[0], values[1], values[2], values[3]); shell.setBounds(shellBounds); verifyShellRect(shell, true); bDidResize = true; } } catch (Exception e) { } } boolean isMaximized = COConfigurationManager.getBooleanParameter(sConfigPrefix + ".maximized"); if (Constants.isOSX && windowRectangle != null) { isMaximized = false; } shell.setMaximized(isMaximized); new ShellMetricsResizeListener(shell, sConfigPrefix); return bDidResize; } private static class ShellMetricsResizeListener implements Listener { private int state = -1; private String sConfigPrefix; private Rectangle bounds = null; ShellMetricsResizeListener(Shell shell, String sConfigPrefix) { this.sConfigPrefix = sConfigPrefix; state = calcState(shell); if (state == SWT.NONE) bounds = shell.getBounds(); shell.addListener(SWT.Resize, this); shell.addListener(SWT.Move, this); shell.addListener(SWT.Dispose, this); } private int calcState(Shell shell) { return shell.getMinimized() ? SWT.MIN : shell.getMaximized() && !Constants.isOSX ? SWT.MAX : SWT.NONE; } private void saveMetrics() { COConfigurationManager.setParameter(sConfigPrefix + ".maximized", state == SWT.MAX); if (bounds == null) return; COConfigurationManager.setParameter(sConfigPrefix + ".rectangle", bounds.x + "," + bounds.y + "," + bounds.width + "," + bounds.height); COConfigurationManager.save(); } public void handleEvent(Event event) { Shell shell = (Shell) event.widget; state = calcState(shell); if (event.type != SWT.Dispose && state == SWT.NONE) bounds = shell.getBounds(); if (event.type == SWT.Dispose) saveMetrics(); } } public static GridData setGridData(Composite composite, int gridStyle, Control ctrlBestSize, int maxHeight) { GridData gridData = new GridData(gridStyle); gridData.heightHint = ctrlBestSize.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; if (gridData.heightHint > maxHeight && maxHeight > 0) gridData.heightHint = maxHeight; composite.setLayoutData(gridData); return gridData; } public static FormData getFilledFormData() { FormData formData = new FormData(); formData.top = new FormAttachment(0, 0); formData.left = new FormAttachment(0, 0); formData.right = new FormAttachment(100, 0); formData.bottom = new FormAttachment(100, 0); return formData; } public static int pixelsToPoint(int pixels, int dpi) { int ret = (int) Math.round((pixels * 72.0) / dpi); return ret; } private static int pixelsToPoint(double pixels, int dpi) { int ret = (int) Math.round((pixels * 72.0) / dpi); return ret; } private static boolean drawImage(GC gc, Image image, Rectangle dstRect, Rectangle clipping, int hOffset, int vOffset, boolean clearArea) { return drawImage(gc, image, new Point(0, 0), dstRect, clipping, hOffset, vOffset, clearArea); } private static boolean drawImage(GC gc, Image image, Rectangle dstRect, Rectangle clipping, int hOffset, int vOffset) { return drawImage(gc, image, new Point(0, 0), dstRect, clipping, hOffset, vOffset, false); } public static boolean drawImage(GC gc, Image image, Point srcStart, Rectangle dstRect, Rectangle clipping, int hOffset, int vOffset, boolean clearArea) { Rectangle srcRect; Point dstAdj; if (clipping == null) { dstAdj = new Point(0, 0); srcRect = new Rectangle(srcStart.x, srcStart.y, dstRect.width, dstRect.height); } else { if (!dstRect.intersects(clipping)) { return false; } dstAdj = new Point(Math.max(0, clipping.x - dstRect.x), Math.max(0, clipping.y - dstRect.y)); srcRect = new Rectangle(0, 0, 0, 0); srcRect.x = srcStart.x + dstAdj.x; srcRect.y = srcStart.y + dstAdj.y; srcRect.width = Math.min(dstRect.width - dstAdj.x, clipping.x + clipping.width - dstRect.x); srcRect.height = Math.min(dstRect.height - dstAdj.y, clipping.y + clipping.height - dstRect.y); } if (!srcRect.isEmpty()) { try { if (clearArea) { gc.fillRectangle(dstRect.x + dstAdj.x + hOffset, dstRect.y + dstAdj.y + vOffset, srcRect.width, srcRect.height); } gc.drawImage(image, srcRect.x, srcRect.y, srcRect.width, srcRect.height, dstRect.x + dstAdj.x + hOffset, dstRect.y + dstAdj.y + vOffset, srcRect.width, srcRect.height); } catch (Exception e) { System.out.println("drawImage: " + e.getMessage() + ": " + image + ", " + srcRect + ", " + (dstRect.x + dstAdj.y + hOffset) + "," + (dstRect.y + dstAdj.y + vOffset) + "," + srcRect.width + "," + srcRect.height + "; imageBounds = " + image.getBounds()); } } return true; } /** * @param area * @param event id * @param listener */ public static void addListenerAndChildren(Composite area, int event, Listener listener) { area.addListener(event, listener); Control[] children = area.getChildren(); for (int i = 0; i < children.length; i++) { Control child = children[i]; if (child instanceof Composite) { addListenerAndChildren((Composite) child, event, listener); } else { child.addListener(event, listener); } } } public static Shell findAnyShell() { // Pick the main shell if we can UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT(); if (uiFunctions != null) { Shell shell = uiFunctions.getMainShell(); if (shell != null && !shell.isDisposed()) { return shell; } } // Get active shell from current display if we can Display current = Display.getCurrent(); if (current == null) { return null; } Shell shell = current.getActiveShell(); if (shell != null && !shell.isDisposed()) { return shell; } // Get first shell of current display if we can Shell[] shells = current.getShells(); if (shells.length == 0) { return null; } if (shells[0] != null && !shells[0].isDisposed()) { return shells[0]; } return null; } /** * @param listener */ public static boolean verifyShellRect(Shell shell, boolean bAdjustIfInvalid) { boolean bMetricsOk; try { bMetricsOk = false; Point ptTopLeft = shell.getLocation(); Monitor[] monitors = shell.getDisplay().getMonitors(); for (int j = 0; j < monitors.length && !bMetricsOk; j++) { Rectangle bounds = monitors[j].getBounds(); bMetricsOk = bounds.contains(ptTopLeft); } } catch (NoSuchMethodError e) { Rectangle bounds = shell.getDisplay().getBounds(); bMetricsOk = shell.getBounds().intersects(bounds); } if (!bMetricsOk && bAdjustIfInvalid) { centreWindow(shell); } return bMetricsOk; } /** * Relayout all composites up from control until there's enough room for the * control to fit * * @param control Control that had it's sized changed and needs more room */ public static void relayout(Control control) { relayout(control, false); } /** * Relayout all composites up from control until there's enough room for the * control to fit * * @param control Control that had it's sized changed and needs more room */ public static void relayout(Control control, boolean expandOnly) { if (control == null || control.isDisposed()) { return; } Composite parent = control.getParent(); Point targetSize = control.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); Point size = control.getSize(); if (size.y == targetSize.y && size.x == targetSize.x) { return; } Object layoutData = control.getLayoutData(); if (layoutData instanceof FormData) { FormData fd = (FormData) layoutData; if (fd.width != SWT.DEFAULT && fd.height != SWT.DEFAULT) { parent.layout(); return; } } if (expandOnly && size.y >= targetSize.y && size.x >= targetSize.x) { parent.layout(); return; } while (parent != null) { parent.layout(true, true); parent = parent.getParent(); Point newSize = control.getSize(); //System.out.println("new=" + newSize + ";target=" + targetSize); if (newSize.y >= targetSize.y && newSize.x >= targetSize.x) { break; } } if (parent != null) { parent.layout(); } } /** * */ public static void beep() { execSWTThread(new AERunnable() { public void runSupport() { Display display = Display.getDefault(); if (display != null) { display.beep(); } } }); } /** * * @param baseFont * @param gc Can be null * @param heightInPixels * @return * * @since 3.0.0.7 */ public static int getFontHeightFromPX(Font baseFont, GC gc, int heightInPixels) { Font font = null; Device device = baseFont.getDevice(); // hack.. heightInPixels++; // This isn't accurate, but gets us close int size = Utils.pixelsToPoint(heightInPixels, device.getDPI().y) + 1; if (size <= 0) { return 0; } boolean bOurGC = gc == null || gc.isDisposed(); try { if (bOurGC) { gc = new GC(device); } FontData[] fontData = baseFont.getFontData(); do { if (font != null) { size--; font.dispose(); } fontData[0].setHeight(size); font = new Font(device, fontData); gc.setFont(font); } while (gc.textExtent(GOOD_STRING).y > heightInPixels && size > 1); } finally { if (bOurGC) { gc.dispose(); } if (font != null && !font.isDisposed()) { font.dispose(); } } return size; } public static int getFontHeightFromPX(Device device, FontData[] fontData, GC gc, int heightInPixels) { Font font = null; // hack.. heightInPixels++; // This isn't accurate, but gets us close int size = Utils.pixelsToPoint(heightInPixels, device.getDPI().y) + 1; if (size <= 0) { return 0; } boolean bOurGC = gc == null || gc.isDisposed(); try { if (bOurGC) { gc = new GC(device); } do { if (font != null) { size--; font.dispose(); } fontData[0].setHeight(size); font = new Font(device, fontData); gc.setFont(font); } while (font != null && gc.textExtent(GOOD_STRING).y > heightInPixels && size > 1); } finally { if (bOurGC) { gc.dispose(); } if (font != null && !font.isDisposed()) { font.dispose(); } } return size; } public static Font getFontWithHeight(Font baseFont, GC gc, int heightInPixels) { Font font = null; Device device = baseFont.getDevice(); // hack.. heightInPixels++; // This isn't accurate, but gets us close int size = Utils.pixelsToPoint(heightInPixels, device.getDPI().y) + 1; if (size <= 0) { size = 2; } boolean bOurGC = gc == null || gc.isDisposed(); try { if (bOurGC) { gc = new GC(device); } FontData[] fontData = baseFont.getFontData(); do { if (font != null) { size--; font.dispose(); } fontData[0].setHeight(size); font = new Font(device, fontData); gc.setFont(font); } while (font != null && gc.textExtent(GOOD_STRING).y > heightInPixels && size > 1); } finally { if (bOurGC) { gc.dispose(); } } return font; } /** * @deprecated Use {@link #execSWTThread(AERunnableWithCallback)} to avoid * thread locking issues */ public static boolean execSWTThreadWithBool(String ID, AERunnableBoolean code) { return execSWTThreadWithBool(ID, code, 0); } /** * Runs code within the SWT thread, waits for code to complete executing, * (using a semaphore), and then returns a value. * * @note USE WITH CAUTION. If the calling function synchronizes, and the * runnable code ends up synchronizing on the same object, an indefinite * thread lock or an unexpected timeout may occur (if one of the threads * is the SWT thread).<p> * ex - Thread1 calls c.foo(), which synchronized(this). * - Thread2 is the SWT Thread. Thread2 calls c.foo(), which waits on * Thread1 to complete. * - c.foo() from Thread1 calls execSWTThreadWithBoolean(.., swtcode, ..), * which waits for the SWT Thread to return run the swtcode. * - Deadlock, or Timoeout which returns a false (and no code ran) * * @param ID id for debug * @param code code to run * @param millis ms to timeout in * @return */ public static boolean execSWTThreadWithBool(String ID, AERunnableBoolean code, long millis) { if (code == null) { return false; } boolean[] returnValueObject = { false }; Display display = getDisplay(); AESemaphore sem = null; if (display == null || display.getThread() != Thread.currentThread()) { sem = new AESemaphore(ID); } try { code.setupReturn(ID, returnValueObject, sem); if (!execSWTThread(code)) { // code never got run // XXX: throw instead? return false; } } catch (Throwable e) { if (sem != null) { sem.release(); } Debug.out(ID, e); } if (sem != null) { sem.reserve(millis); } return returnValueObject[0]; } /** * @deprecated Use {@link #execSWTThread(AERunnableWithCallback)} to avoid * thread locking issues */ public static Object execSWTThreadWithObject(String ID, AERunnableObject code) { return execSWTThreadWithObject(ID, code, 0); } /** * Runs code within the SWT thread, waits for code to complete executing, * (using a semaphore), and then returns a value. * * @note USE WITH CAUTION. If the calling function synchronizes, and the * runnable code ends up synchronizing on the same object, an indefinite * thread lock or an unexpected timeout may occur (if one of the threads * is the SWT thread).<p> * ex - Thread1 calls c.foo(), which synchronized(this). * - Thread2 is the SWT Thread. Thread2 calls c.foo(), which waits on * Thread1 to complete. * - c.foo() from Thread1 calls execSWTThreadWithObject(.., swtcode, ..), * which waits for the SWT Thread to return run the swtcode. * - Deadlock, or Timoeout which returns a null (and no code ran) * * @param ID id for debug * @param code code to run * @param millis ms to timeout in * @return */ public static Object execSWTThreadWithObject(String ID, AERunnableObject code, long millis) { if (code == null) { return null; } Object[] returnValueObject = { null }; Display display = getDisplay(); AESemaphore sem = null; if (display == null || display.getThread() != Thread.currentThread()) { sem = new AESemaphore(ID); } try { code.setupReturn(ID, returnValueObject, sem); if (!execSWTThread(code)) { // XXX: throw instead? return null; } } catch (Throwable e) { if (sem != null) { sem.release(); } Debug.out(ID, e); } if (sem != null) { sem.reserve(millis); } return returnValueObject[0]; } /** * Waits until modal dialogs are disposed. Assumes we are on SWT thread * * @since 3.0.1.3 */ public static void waitForModals() { SWTThread swt = SWTThread.getInstance(); Display display; if (swt == null) { display = Display.getDefault(); if (display == null) { System.err.println("SWT Thread not started yet!"); return; } } else { if (swt.isTerminated()) { return; } display = swt.getDisplay(); } if (display == null || display.isDisposed()) { return; } Shell[] shells = display.getShells(); Shell modalShell = null; for (int i = 0; i < shells.length; i++) { Shell shell = shells[i]; if ((shell.getStyle() & SWT.APPLICATION_MODAL) > 0) { modalShell = shell; break; } } if (modalShell != null) { while (!modalShell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } } public static GridData getWrappableLabelGridData(int hspan, int styles) { GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | styles); gridData.horizontalSpan = hspan; gridData.widthHint = 0; return gridData; } private static Image createAlphaImage(Device device, int width, int height) { return createAlphaImage(device, width, height, (byte) 0); } public static Image createAlphaImage(Device device, int width, int height, byte defaultAlpha) { byte[] alphaData = new byte[width * height]; Arrays.fill(alphaData, 0, alphaData.length, (byte) defaultAlpha); ImageData imageData = new ImageData(width, height, 24, new PaletteData( 0xFF, 0xFF00, 0xFF0000)); Arrays.fill(imageData.data, 0, imageData.data.length, (byte) 0); imageData.alphaData = alphaData; Image image = new Image(device, imageData); return image; } private static Image blitImage(Device device, Image srcImage, Rectangle srcArea, Image dstImage, Point dstPos) { if (srcArea == null) { srcArea = srcImage.getBounds(); } Rectangle dstBounds = dstImage.getBounds(); if (dstPos == null) { dstPos = new Point(dstBounds.x, dstBounds.y); } else { dstBounds.x = dstPos.x; dstBounds.y = dstPos.y; } srcArea.intersect(dstBounds); // draw the image with no mask! :( GC gc = new GC(dstImage); try { gc.drawImage(srcImage, srcArea.x, srcArea.y, srcArea.width, srcArea.height, dstPos.x, dstPos.y, srcArea.width, srcArea.height); } finally { gc.dispose(); } ImageData dstImageData = dstImage.getImageData(); ImageData srcImageData = srcImage.getImageData(); int yPos = dstPos.y; for (int y = 0; y < srcArea.height; y++) { int xPos = dstPos.x; for (int x = 0; x < srcArea.width; x++) { dstImageData.setAlpha(xPos, yPos, srcImageData.getAlpha(x + srcArea.x, y + srcArea.y)); xPos++; } yPos++; } return new Image(device, dstImageData); } /** * Draws diagonal stripes onto the specified area of a GC * @param lineDist spacing between the individual lines * @param leftshift moves the stripes to the left, useful to shift with the background * @param fallingLines true for top left to bottom-right lines, false otherwise */ public static void drawStriped(GC gcImg, int x, int y, int width, int height, int lineDist, int leftshift, boolean fallingLines) { lineDist += 2; final int xm = x + width; final int ym = y + height; for (int i = x; i < xm; i++) { for (int j = y; j < ym; j++) { if ((i + leftshift + (fallingLines ? -j : j)) % lineDist == 0) gcImg.drawPoint(i, j); } } } /** * * @param display * @param background * @param foreground * @param foregroundOffsetOnBg * @param modifyForegroundAlpha 0 (fully transparent) to 255 (retain current alpha) * @return */ public static Image renderTransparency(Display display, Image background, Image foreground, Point foregroundOffsetOnBg, int modifyForegroundAlpha) { //Checks if (display == null || display.isDisposed() || background == null || background.isDisposed() || foreground == null || foreground.isDisposed()) return null; Rectangle backgroundArea = background.getBounds(); Rectangle foregroundDrawArea = foreground.getBounds(); foregroundDrawArea.x += foregroundOffsetOnBg.x; foregroundDrawArea.y += foregroundOffsetOnBg.y; foregroundDrawArea.intersect(backgroundArea); if (foregroundDrawArea.isEmpty()) return null; Image image = new Image(display, backgroundArea); ImageData backData = background.getImageData(); ImageData foreData = foreground.getImageData(); ImageData imgData = image.getImageData(); PaletteData backPalette = backData.palette; ImageData backMask = backData.getTransparencyType() != SWT.TRANSPARENCY_ALPHA ? backData.getTransparencyMask() : null; PaletteData forePalette = foreData.palette; ImageData foreMask = foreData.getTransparencyType() != SWT.TRANSPARENCY_ALPHA ? foreData.getTransparencyMask() : null; PaletteData imgPalette = imgData.palette; image.dispose(); for (int x = 0; x < backgroundArea.width; x++) { for (int y = 0; y < backgroundArea.height; y++) { RGB cBack = backPalette.getRGB(backData.getPixel(x, y)); int aBack = backData.getAlpha(x, y); if (backMask != null && backMask.getPixel(x, y) == 0) aBack = 0; // special treatment for icons with transparency masks int aFore = 0; if (foregroundDrawArea.contains(x, y)) { final int fx = x - foregroundDrawArea.x; final int fy = y - foregroundDrawArea.y; RGB cFore = forePalette.getRGB(foreData.getPixel(fx, fy)); aFore = foreData.getAlpha(fx, fy); if (foreMask != null && foreMask.getPixel(fx, fy) == 0) aFore = 0; // special treatment for icons with transparency masks aFore = aFore * modifyForegroundAlpha / 255; cBack.red *= aBack * (255 - aFore); cBack.red /= 255; cBack.red += aFore * cFore.red; cBack.red /= 255; cBack.green *= aBack * (255 - aFore); cBack.green /= 255; cBack.green += aFore * cFore.green; cBack.green /= 255; cBack.blue *= aBack * (255 - aFore); cBack.blue /= 255; cBack.blue += aFore * cFore.blue; cBack.blue /= 255; } imgData.setAlpha(x, y, aFore + aBack * (255 - aFore) / 255); imgData.setPixel(x, y, imgPalette.getPixel(cBack)); } } return new Image(display, imgData); } public static Control findBackgroundImageControl(Control control) { Image image = control.getBackgroundImage(); if (image == null) { return control; } Composite parent = control.getParent(); Composite lastParent = parent; while (parent != null) { Image parentImage = parent.getBackgroundImage(); if (!image.equals(parentImage)) { return lastParent; } lastParent = parent; parent = parent.getParent(); } return control; } /** * @return * * @since 3.0.3.5 */ public static boolean anyShellHaveStyle(int styles) { Display display = Display.getCurrent(); if (display != null) { Shell[] shells = display.getShells(); for (int i = 0; i < shells.length; i++) { Shell shell = shells[i]; int style = shell.getStyle(); if ((style & styles) == styles) { return true; } } } return false; } public static int[] colorToIntArray(Color color) { if (color == null || color.isDisposed()) { return null; } return new int[] { color.getRed(), color.getGreen(), color.getBlue() }; } /** * Centers the target <code>Rectangle</code> relative to the reference Rectangle * @param target * @param reference */ public static void centerRelativeTo(Rectangle target, Rectangle reference) { target.x = reference.x + (reference.width / 2) - target.width / 2; target.y = reference.y + (reference.height / 2) - target.height / 2; } /** * Ensure that the given <code>Rectangle</code> is fully visible on the monitor that the cursor * is currently in. This method does not resize the given Rectangle; it merely reposition it * if appropriate. If the given Rectangle is taller or wider than the current monitor then * it may not fit 'fully' in the monitor. * <P> * We use a best-effort approach with an emphasis to have at least the top-left of the Rectangle * be visible. If the given Rectangle does not fit entirely in the monitor then portion * of the right and/or left may be off-screen. * * <P> * This method does honor global screen elements when possible. Screen elements include the TaskBar on Windows * and the Application menu on OSX, and possibly others. The re-positioned Rectangle returned will fit on the * screen without overlapping (or sliding under) these screen elements. * @param rect * @return */ public static void makeVisibleOnCursor(Rectangle rect) { if (null == rect) { return; } Display display = Display.getCurrent(); if (null == display) { Debug.out("No current display detected. This method [Utils.makeVisibleOnCursor()] must be called from a display thread."); return; } try { /* * Get cursor location */ Point cursorLocation = display.getCursorLocation(); /* * Make visible on the monitor that the mouse cursor resides in */ makeVisibleOnMonitor(rect, getMonitor(cursorLocation)); } catch (Throwable t) { //Do nothing } } /** * Ensure that the given <code>Rectangle</code> is fully visible on the given <code>Monitor</code>. * This method does not resize the given Rectangle; it merely reposition it if appropriate. * If the given Rectangle is taller or wider than the current monitor then it may not fit 'fully' in the monitor. * <P> * We use a best-effort approach with an emphasis to have at least the top-left of the Rectangle * be visible. If the given Rectangle does not fit entirely in the monitor then portion * of the right and/or left may be off-screen. * * <P> * This method does honor global screen elements when possible. Screen elements include the TaskBar on Windows * and the Application menu on OSX, and possibly others. The re-positioned Rectangle returned will fit on the * screen without overlapping (or sliding under) these screen elements. * @param rect * @param monitor */ public static void makeVisibleOnMonitor(Rectangle rect, Monitor monitor) { if (null == rect || null == monitor) { return; } try { Rectangle monitorBounds = monitor.getClientArea(); /* * Make sure the bottom is fully visible on the monitor */ int bottomDiff = (monitorBounds.y + monitorBounds.height) - (rect.y + rect.height); if (bottomDiff < 0) { rect.y += bottomDiff; } /* * Make sure the right is fully visible on the monitor */ int rightDiff = (monitorBounds.x + monitorBounds.width) - (rect.x + rect.width); if (rightDiff < 0) { rect.x += rightDiff; } /* * Make sure the left is fully visible on the monitor */ if (rect.x < monitorBounds.x) { rect.x = monitorBounds.x; } /* * Make sure the top is fully visible on the monitor */ if (rect.y < monitorBounds.y) { rect.y = monitorBounds.y; } } catch (Throwable t) { //Do nothing } } /** * Returns the <code>Monitor</code> that the given x,y coordinates resides in * @param x * @param y * @return the monitor if found; otherwise returns <code>null</code> */ private static Monitor getMonitor(int x, int y) { return getMonitor(new Point(x, y)); } /** * Returns the <code>Monitor</code> that the given <code>Point</code> resides in * @param location * @return the monitor if found; otherwise returns <code>null</code> */ public static Monitor getMonitor(Point location) { Display display = Display.getCurrent(); if (null == display) { Debug.out("No current display detected. This method [Utils.makeVisibleOnCursor()] must be called from a display thread."); return null; } try { /* * Find the monitor that this location resides in */ Monitor[] monitors = display.getMonitors(); Rectangle monitorBounds = null; for (int i = 0; i < monitors.length; i++) { monitorBounds = monitors[i].getClientArea(); if (true == monitorBounds.contains(location)) { return monitors[i]; } } } catch (Throwable t) { //Do nothing } return null; } private static boolean gotBrowserStyle = false; private static int browserStyle = SWT.NONE; /** * Consistently applies the browser style obtained during the first invocation * @param style the style you wish to apply * @return the style, possibly ORed with <code>SWT.MOZILLA</code> */ public static int getInitialBrowserStyle(int style) { if (!gotBrowserStyle) { browserStyle = COConfigurationManager.getBooleanParameter("swt.forceMozilla") ? SWT.MOZILLA : SWT.NONE; gotBrowserStyle = true; } return style | browserStyle; } /** * Change the height of the installed <code>Font</code> and takes care of disposing * the new font when the control is disposed * @param control * @param height * @param style one or both of SWT.BOLD, SWT.ITALIC, or SWT.NORMAL */ public static void setFontHeight(Control control, int height, int style) { FontData[] fDatas = control.getFont().getFontData(); for (int i = 0; i < fDatas.length; i++) { fDatas[i].height = height; fDatas[i].setStyle(style); } final Font newFont = new Font(control.getDisplay(), fDatas); control.setFont(newFont); control.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { if (null != newFont && false == newFont.isDisposed()) { newFont.dispose(); } } }); } public static final long IMMEDIATE_ADDREMOVE_DELAY = 150; private static final long IMMEDIATE_ADDREMOVE_MAXDELAY = 2000; private static Timer timerProcessDataSources = new Timer("Process Data Sources"); private static TimerEvent timerEventProcessDS; private static List processDataSourcesOutstanding = new ArrayList(); public static boolean addDataSourceAggregated( addDataSourceCallback callback ) { if ( callback == null ){ return( true ); } boolean processQueueImmediately = false; List to_do_now = null; synchronized( timerProcessDataSources ){ if ( timerEventProcessDS != null && !timerEventProcessDS.hasRun()){ // Push timer forward, unless we've pushed it forward for over x seconds long now = SystemTime.getCurrentTime(); if (now - timerEventProcessDS.getCreatedTime() < IMMEDIATE_ADDREMOVE_MAXDELAY) { long lNextTime = now + IMMEDIATE_ADDREMOVE_DELAY; timerProcessDataSources.adjustAllBy( lNextTime - timerEventProcessDS.getWhen()); if ( !processDataSourcesOutstanding.contains( callback )){ processDataSourcesOutstanding.add( callback ); } }else{ timerEventProcessDS.cancel(); timerEventProcessDS = null; processQueueImmediately = true; to_do_now = processDataSourcesOutstanding; processDataSourcesOutstanding = new ArrayList(); } }else{ if ( !processDataSourcesOutstanding.contains( callback )){ processDataSourcesOutstanding.add( callback ); } timerEventProcessDS = timerProcessDataSources.addEvent( SystemTime.getCurrentTime() + IMMEDIATE_ADDREMOVE_DELAY, new TimerEventPerformer() { public void perform( TimerEvent event ) { List to_do; synchronized( timerProcessDataSources ){ timerEventProcessDS = null; to_do = processDataSourcesOutstanding; processDataSourcesOutstanding = new ArrayList(); } for (int i=0;i<to_do.size();i++){ try{ addDataSourceCallback this_callback = (addDataSourceCallback)to_do.get(i); if (TableViewSWT.DEBUGADDREMOVE ) { this_callback.debug("processDataSourceQueue after " + (SystemTime.getCurrentTime() - event.getCreatedTime()) + "ms"); } this_callback.process(); }catch( Throwable e ){ Debug.printStackTrace(e); } } } }); } if ( to_do_now != null ){ // process outside the synchronized block, otherwise we'll end up with deadlocks to_do_now.remove( callback ); for (int i=0;i<to_do_now.size();i++){ try{ addDataSourceCallback this_callback = (addDataSourceCallback)to_do_now.get(i); if ( TableViewSWT.DEBUGADDREMOVE ){ this_callback.debug("Over immediate delay limit, processing queue now"); } this_callback.process(); }catch( Throwable e ){ Debug.printStackTrace(e); } } } } return( processQueueImmediately ); } public interface addDataSourceCallback { public void process(); public void debug( String str ); } }

The table below shows all metrics for Utils.java.

MetricValueDescription
BLOCKS358.00Number of blocks
BLOCK_COMMENT41.00Number of block comment lines
COMMENTS413.00Comment lines
COMMENT_DENSITY 0.33Comment density
COMPARISONS292.00Number of comparison operators
CYCLOMATIC412.00Cyclomatic complexity
DECL_COMMENTS43.00Comments in declarations
DOC_COMMENT317.00Number of javadoc comment lines
ELOC1256.00Effective lines of code
EXEC_COMMENTS49.00Comments in executable code
EXITS222.00Procedure exits
FUNCTIONS88.00Number of function declarations
HALSTEAD_DIFFICULTY135.00Halstead difficulty
HALSTEAD_EFFORT 0.00Halstead effort
INTERFACE_COMPLEXITY320.00Interface complexity
JAVA0001 1.00JAVA0001 Package name does not contain only lower case letters
JAVA0002 0.00JAVA0002 Package name does not begin with a top level domain name or country code
JAVA0003 0.00JAVA0003 Minimize use of on-demand (.*) imports
JAVA0004 0.00JAVA0004 Unnecessary import from java.lang
JAVA0005 0.00JAVA0005 Imports not in specified order
JAVA0006 0.00JAVA0006 Empty finally block
JAVA0007 0.00JAVA0007 Should not declare public field
JAVA0008 1.00JAVA0008 Empty catch block
JAVA0009 0.00JAVA0009 Protected member in final class
JAVA0010 0.00JAVA0010 Non-instantiable class does not contain a non-private static member
JAVA0011 0.00JAVA0011 Abstract class does not contain an abstract method
JAVA0012 0.00JAVA0012 Non-constructor method with same name as declaring class
JAVA0013 0.00JAVA0013 Non-blank final field is not static
JAVA0014 0.00JAVA0014 Class with only static members has non-private constructor
JAVA0015 0.00JAVA0015 Package class contains public nested type
JAVA0016 0.00JAVA0016 Abstract class contains public constructor
JAVA0017 0.00JAVA0017 Class name does not have required form
JAVA0018 0.00JAVA0018 Method name does not have required form
JAVA0019 0.00JAVA0019 Interface name does not have required form
JAVA0020 0.00JAVA0020 Field name does not have required form
JAVA0021 0.00JAVA0021 Interface method name does not have required form
JAVA0022 0.00JAVA0022 Static final field name does not have required form
JAVA0023 0.00JAVA0023 Empty finalize method
JAVA0024 0.00JAVA0024 Empty class
JAVA0025 0.00JAVA0025 Method override is empty
JAVA0026 0.00JAVA0026 Finalize method with parameters
JAVA0029 6.00JAVA0029 Private method not used
JAVA0030 0.00JAVA0030 Private field not used
JAVA0031 0.00JAVA0031 Case statement not properly closed
JAVA0032 0.00JAVA0032 Switch statement missing default
JAVA0033 0.00JAVA0033 default: not last case in switch statement
JAVA003435.00JAVA0034 Missing braces in if statement
JAVA0035 0.00JAVA0035 Missing braces in for statement
JAVA0036 0.00JAVA0036 Missing braces in while statement
JAVA0038 0.00JAVA0038 Non-case label in switch statement
JAVA0039 0.00JAVA0039 Break statement with label
JAVA0040 0.00JAVA0040 Switch statement contains N cases (maximum: M)
JAVA0041 0.00JAVA0041 Nested synchronized block
JAVA0042 0.00JAVA0042 Empty synchronized statement
JAVA0043 0.00JAVA0043 Inner class does not use outer class
JAVA0044 0.00JAVA0044 Serializable class with no instance variables
JAVA0045 0.00JAVA0045 Serializable class with only transient fields
JAVA0046 0.00JAVA0046 Name of class not derived from Exception ends with 'Exception'
JAVA0047 0.00JAVA0047 Serializable class derives from invalid base class
JAVA0048 0.00JAVA0048 Name of class derived from Exception does not end with 'Exception'
JAVA004912.00JAVA0049 Nested block at depth N (maximum: M)
JAVA0050 0.00JAVA0050 Class derives from java.lang.Error
JAVA0051 0.00JAVA0051 Class derives from java.lang.RuntimeException
JAVA0052 0.00JAVA0052 Class derives from java.lang.Throwable
JAVA0053 0.00JAVA0053 Unused label
JAVA0054 0.00JAVA0054 Inheritance depth N exceeds maximum M
JAVA0055 0.00JAVA0055 Class should be interface
JAVA0056 0.00JAVA0056 Unnecessary abstract modifier for interface or annotation
JAVA0057 0.00JAVA0057 Unnecessary default constructor
JAVA0058 0.00JAVA0058 Constructor calls super()
JAVA0059 0.00JAVA0059 Method override only calls super()
JAVA0061 0.00JAVA0061 Inaccessible member in anonymous class
JAVA0062 0.00JAVA0062 Public class missing public member or protected constructor
JAVA0063 0.00JAVA0063 Identifier name should not contain '$'
JAVA0064 0.00JAVA0064 N variations of identifier name (maximum: M)
JAVA0065 0.00JAVA0065 Unnecessary final modifier for method in final class
JAVA0066 0.00JAVA0066 Unnecessary modifier for interface nested type
JAVA0067 1.00JAVA0067 Array descriptor on identifier name
JAVA0068 0.00JAVA0068 Modifiers not declared in recommended order
JAVA0071 0.00JAVA0071 Strings compared with ==
JAVA0073 0.00JAVA0073 Integer division in floating-point context
JAVA0074 0.00JAVA0074 Use of Object.notify()
JAVA0075 0.00JAVA0075 Method parameter hides field
JAVA007624.00JAVA0076 Use of magic number
JAVA0077 0.00JAVA0077 Private field not used in declaring class
JAVA0078 0.00JAVA0078 Floating point values compared with ==
JAVA0079 0.00JAVA0079 Use of instance to reference static member
JAVA0080 0.00JAVA0080 Import declaration not used
JAVA0081 2.00JAVA0081 Boolean literal in comparison
JAVA0082 1.00JAVA0082 Unnecessary widening cast
JAVA0083 0.00JAVA0083 Unnecessary instanceof test
JAVA0084 0.00JAVA0084 Should use compound assignment operator
JAVA0085 0.00JAVA0085 Use of sun.* class
JAVA0087 0.00JAVA0087 Use of Thread.sleep()
JAVA0089 0.00JAVA0089 Use of restricted package
JAVA0092 0.00JAVA0092 Use of restricted type
JAVA0093 0.00JAVA0093 Redundant assignment
JAVA0094 0.00JAVA0094 Field hides a superclass field
JAVA0095 0.00JAVA0095 Uninitialized private field
JAVA0096 0.00JAVA0096 Field in nested class hides outer field
JAVA0098 0.00JAVA0098 Minimize use of implicit field initializers
JAVA0100 0.00JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0101 0.00JAVA0101 Unnecessary modifier for field in interface
JAVA0102 0.00JAVA0102 Last statement in finalize() not super.finalize()
JAVA0103 0.00JAVA0103 Explicit call to finalize()
JAVA0104 0.00JAVA0104 finalize() only calls super.finalize()
JAVA0105 0.00JAVA0105 Duplicate import declaration
JAVA0106 0.00JAVA0106 Unnecessary import from current package
JAVA010816.00JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0109 2.00JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA0110 4.00JAVA0110 Incorrect javadoc: no @return tag
JAVA0111 1.00JAVA0111 Incorrect javadoc: @return tag for void method
JAVA0112 0.00JAVA0112 Incorrect javadoc: no exception 'exception' in throws
JAVA0113 0.00JAVA0113 Incorrect javadoc: no @author tag
JAVA0114 1.00JAVA0114 Incorrect javadoc: no @version tag
JAVA0115 0.00JAVA0115 Incorrect javadoc: no @throws or @exception tag for 'exception'
JAVA0116 3.00JAVA0116 Missing javadoc: field 'field'
JAVA011729.00JAVA0117 Missing javadoc: method 'method'
JAVA0118 1.00JAVA0118 Missing javadoc: type 'type'
JAVA0119 2.00JAVA0119 Control variable changed within body of for loop
JAVA0123 0.00JAVA0123 Use all three components of for loop
JAVA0125 0.00JAVA0125 Continue statement with label
JAVA0126 0.00JAVA0126 Method declares unchecked exception in throws
JAVA0128 1.00JAVA0128 Public constructor in non-public class
JAVA0130 1.00JAVA0130 Non-static method does not use instance fields
JAVA0131 0.00JAVA0131 Compatible method does not override base
JAVA0132 0.00JAVA0132 Method overload with compatible signature
JAVA0133 0.00JAVA0133 Non-synchronized method overrides synchronized method
JAVA0135 0.00JAVA0135 Only one of Object.equals and Object.hashCode defined: missing 'method'
JAVA0136 1.00JAVA0136 N methods defined in class (maximum: M)
JAVA0137 0.00JAVA0137 Non-abstract class missing constructor
JAVA0138 4.00JAVA0138 N parameters defined for method (maximum: M)
JAVA0139 0.00JAVA0139 Definition of main other than public static void main(java.lang.String[])
JAVA0141 2.00JAVA0141 Unnecessary modifier for method in interface
JAVA0143 0.00JAVA0143 Synchronized method
JAVA0144 3.00JAVA0144 Line exceeds maximum M characters
JAVA01455589.00JAVA0145 Tab character used in source file
JAVA0150 0.00JAVA0150 java.lang.Error (or subclass) thrown
JAVA0153 0.00JAVA0153 Inefficient conversion of integer to string
JAVA0159 1.00JAVA0159 Inefficient conversion of string to integer
JAVA0160 0.00JAVA0160 Method does not throw specified exception
JAVA0161 0.00JAVA0161 Conditional wait() not in loop
JAVA0163 0.00JAVA0163 Empty statement
JAVA0165 0.00JAVA0165 Conflicting return statement in finally block
JAVA016611.00JAVA0166 Generic exception caught
JAVA0167 0.00JAVA0167 ThreadDeath not rethrown
JAVA0169 0.00JAVA0169 Unnecessary catch block: exception 'exception'
JAVA017012.00JAVA0170 Caught exception not derived from java.lang.Exception
JAVA0171 0.00JAVA0171 Unused local variable
JAVA0173 0.00JAVA0173 Unused method parameter
JAVA0174 0.00JAVA0174 Assigned local variable never used
JAVA0175 0.00JAVA0175 Successive assignment to variable
JAVA0176 0.00JAVA0176 Local variable name does not have required form
JAVA0177 9.00JAVA0177 Variable declaration missing initializer
JAVA0179 0.00JAVA0179 Local variable hides visible field
JAVA0233 0.00JAVA0233 Definition of serialVersionUID other than 'private static final long serialVersionUID'
JAVA0234 0.00JAVA0234 Class is Serializable but does not define serialVersionUID
JAVA0235 0.00JAVA0235 Class defines serialVersionUID but does not implement Serializable
JAVA0236 0.00JAVA0236 Attempt to clone an object which does not implement Cloneable
JAVA0237 0.00JAVA0237 Class implements Cloneable but does not have public clone method
JAVA0238 0.00JAVA0238 Clone method does not call super.clone()
JAVA0239 0.00JAVA0239 Class declares 'readObject' or 'writeObject' but does not implement Serializable
JAVA0240 0.00JAVA0240 Serializable class which declares readObject or writeObject but not both
JAVA0241 0.00JAVA0241 'readObject' or 'writeObject' should be declared private in Serializable class