Bug 574833: Fix not disposed GC in ShortedLabel

Change-Id: Ibdcff18944b5aa6223add0cff86e2d58a60ca1ff
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/components/ShortedLabel.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/components/ShortedLabel.java
index 5d79876..e463072 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/components/ShortedLabel.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/components/ShortedLabel.java
@@ -14,6 +14,8 @@
 
 package org.eclipse.statet.ecommons.ui.components;
 
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
 import java.util.regex.Pattern;
 
 import org.eclipse.swt.SWT;
@@ -24,6 +26,8 @@
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Listener;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
 
 /**
  * Replacement for CLabel.
@@ -33,20 +37,22 @@
  *  - Better algorithm (faster and prettier).
  *  - Hover text.
  */
+@NonNullByDefault
 public class ShortedLabel {
 	
-	private static final Pattern fLineBreakPattern = Pattern.compile("\\r[\\n]?|\\n"); //$NON-NLS-1$
+	private static final Pattern LINEBREAK_PATTERN= Pattern.compile("\\r[\\n]?|\\n"); //$NON-NLS-1$
 	
 	
-	private String fText;
-	private String fCheckedText;
-	private final Label fLabel;
-	private String fLineBreakReplacement = " "; //$NON-NLS-1$
+	private String text;
+	private String checkedText;
+	
+	private final Label label;
+	private String lineBreakReplacement= " "; //$NON-NLS-1$
 	
 	
 	public ShortedLabel(final Composite parent, final int style) {
-		fLabel = new Label(parent, style);
-		fLabel.addListener(SWT.Resize, new Listener() {
+		this.label= new Label(parent, style);
+		this.label.addListener(SWT.Resize, new Listener() {
 			@Override
 			public void handleEvent(final Event event) {
 				updateShortening();
@@ -55,7 +61,7 @@
 	}
 	
 	public Label getControl() {
-		return fLabel;
+		return this.label;
 	}
 	
 	public void setText(final String label) {
@@ -63,89 +69,102 @@
 			throw new NullPointerException();
 		}
 		
-		if (label.equals(fText)) {
+		if (label.equals(this.text)) {
 			return;
 		}
-		fText = label;
+		this.text= label;
 		updateChecking();
 		updateShortening();
 	}
 	
 	public void setLineBreakReplacement(final String s) {
-		assert (s != null);
-		
-		fLineBreakReplacement = s;
-		if (fText != null) {
+		this.lineBreakReplacement= nonNullAssert(s);
+		if (this.text != null) {
 			updateChecking();
 		}
 	}
 	
 	private void updateChecking() {
-		fCheckedText = fLineBreakPattern.matcher(fText).replaceAll(fLineBreakReplacement);
+		this.checkedText= LINEBREAK_PATTERN.matcher(this.text).replaceAll(this.lineBreakReplacement);
 	}
 	
 	private void updateShortening() {
-		final String text = new Shorter(fLabel).shorten(fCheckedText);
-		fLabel.setText(text);
-		fLabel.setToolTipText((text == fCheckedText) ? null : fText);
+		final Shorter shorter= new Shorter(this.label);
+		try {
+			final String text= shorter.shorten(this.checkedText);
+			this.label.setText(text);
+			this.label.setToolTipText((text == this.checkedText) ? null : this.text);
+		}
+		finally {
+			shorter.dispose();
+		}
 	}
 	
 	
 	private static class Shorter {
 		
-		private static final String ELLIPSIS = " ... "; //$NON-NLS-1$
-		private static final int DRAW_FLAGS = SWT.DRAW_TAB;
+		private static final String ELLIPSIS= " ... "; //$NON-NLS-1$
+		private static final int DRAW_FLAGS= SWT.DRAW_TAB;
 		
-		Control fControl;
-		GC fGC;
-		int fMaxWidth;
+		private final Control control;
+		private GC gc;
+		private int maxWidth;
 		
-		String fText;
+		private String text;
 		
 		
 		public Shorter(final Control control) {
-			fControl = control;
+			this.control= control;
 		}
 		
+		public void dispose() {
+			final var gc= this.gc;
+			if (gc != null) {
+				this.gc= null;
+				gc.dispose();
+			}
+		}
+		
+		
 		public String shorten(final String text) {
 			if (text == null || text.isEmpty()) {
 				return text;
 			}
 			
-			if (fGC == null) {
-				fGC = new GC(fControl);
-				fMaxWidth = fControl.getBounds().width;
+			if (this.gc == null) {
+				this.gc= new GC(this.control);
+				this.maxWidth= this.control.getBounds().width;
 			}
-			if (fGC.textExtent(text, DRAW_FLAGS).x <= fMaxWidth) {
+			if (this.gc.textExtent(text, DRAW_FLAGS).x <= this.maxWidth) {
 				return text;
 			}
 			
-			fText = text;
-			final String shortedText = doShorten();
-			fText = null;
+			this.text= text;
+			final String shortedText= doShorten();
+			this.text= null;
 			return shortedText;
 		}
 		
 		private String doShorten() {
-			final double avgCharWidth = fGC.getFontMetrics().getAverageCharacterWidth();
-			final int textLength = fText.length();
+			final double avgCharWidth= this.gc.getFontMetrics().getAverageCharacterWidth();
+			final int textLength= this.text.length();
 			
-			final int ellipsisWidth = fGC.textExtent(ELLIPSIS, DRAW_FLAGS).x;
+			final int ellipsisWidth= this.gc.textExtent(ELLIPSIS, DRAW_FLAGS).x;
 			
-			int max2 = (fMaxWidth-ellipsisWidth) * 42 / 100;
+			int max2= (this.maxWidth-ellipsisWidth) * 42 / 100;
 			if (max2 < avgCharWidth * 3) {
-				max2 = 0;
+				max2= 0;
 			}
-			int e = Math.max(textLength - (int)(max2 / avgCharWidth), 0);
-			int w2 = measurePart2(e);
+			int e= Math.max(textLength - (int)(max2 / avgCharWidth), 0);
+			int w2= measurePart2(e);
 			while (w2 > max2 && e < textLength) {
-				w2 = measurePart2(e++);
+				w2= measurePart2(e++);
 			}
 			while (e > 0) {
-				final int test = measurePart2(e-1);
+				final int test= measurePart2(e-1);
 				if (test <= max2) {
 					e--;
-					w2 = test;
+					w2= test;
 					continue;
 				}
 				else {
@@ -153,17 +172,17 @@
 				}
 			}
 			
-			final int max1 = fMaxWidth-ellipsisWidth-w2;
-			int s = Math.min((int)(max2 / avgCharWidth), textLength);
-			int w1 = measurePart1(s);
+			final int max1= this.maxWidth-ellipsisWidth-w2;
+			int s= Math.min((int)(max2 / avgCharWidth), textLength);
+			int w1= measurePart1(s);
 			while (w1 > max1 && s > 3) {
-				w1 = measurePart1(s--);
+				w1= measurePart1(s--);
 			}
 			while (s < textLength) {
-				final int test = measurePart1(s+1);
+				final int test= measurePart1(s + 1);
 				if (test <= max1) {
 					s++;
-					w1 = test;
+					w1= test;
 					continue;
 				}
 				else {
@@ -171,15 +190,15 @@
 				}
 			}
 			
-			return fText.substring(0, s)+ELLIPSIS+fText.substring(e, textLength);
+			return this.text.substring(0, s) + ELLIPSIS + this.text.substring(e, textLength);
 		}
 		
 		private int measurePart1(final int end) {
-			return fGC.textExtent(fText.substring(0, end), DRAW_FLAGS).x; 
+			return this.gc.textExtent(this.text.substring(0, end), DRAW_FLAGS).x; 
 		}
 		
 		private int measurePart2(final int start) {
-			return fGC.textExtent(fText.substring(start), DRAW_FLAGS).x;
+			return this.gc.textExtent(this.text.substring(start), DRAW_FLAGS).x;
 		}
 	}