Bug 479109 - [formatter] Add option to group aligned fields with blank lines

Change-Id: Ia54e06234084e996a9c6b1c6fd561569fafa4fcf
Signed-off-by: Mateusz Matela <mateusz.matela@gmail.com>
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/FormatterMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/FormatterMessages.java
index 8110720..c82a552 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/FormatterMessages.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/FormatterMessages.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -408,6 +408,8 @@
 	public static String IndentationTabPage_general_group_option_indent_size;
 	public static String IndentationTabPage_field_alignment_group_title;
 	public static String IndentationTabPage_field_alignment_group_align_fields_in_columns;
+	public static String IndentationTabPage_field_alignment_group_blank_lines_separating_independent_groups;
+	public static String IndentationTabPage_field_alignment_group_blank_lines_to_preserve_info;
 	public static String IndentationTabPage_indent_group_title;
 	public static String IndentationTabPage_class_group_option_indent_declarations_within_class_body;
 	public static String IndentationTabPage_class_group_option_indent_declarations_within_enum_const;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/FormatterMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/FormatterMessages.properties
index 1ca2f7c..6d88f73 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/FormatterMessages.properties
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/FormatterMessages.properties
@@ -1,5 +1,5 @@
 ###############################################################################
-# Copyright (c) 2000, 2014 IBM Corporation and others.
+# Copyright (c) 2000, 2016 IBM Corporation and others.
 # All rights reserved. This program and the accompanying materials
 # are made available under the terms of the Eclipse Public License v1.0
 # which accompanies this distribution, and is available at
@@ -455,6 +455,8 @@
 
 IndentationTabPage_field_alignment_group_title=Alignment of fields in class declarations
 IndentationTabPage_field_alignment_group_align_fields_in_columns=Align &fields in columns
+IndentationTabPage_field_alignment_group_blank_lines_separating_independent_groups=Blank lines separating &independent groups: 
+IndentationTabPage_field_alignment_group_blank_lines_to_preserve_info='Blank lines to preserve' is set to {0}. You'll need extra comments to separate field groups.
 
 IndentationTabPage_indent_group_title=Indent
 
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/IndentationTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/IndentationTabPage.java
index aea59a2..05fa804 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/IndentationTabPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/IndentationTabPage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -12,18 +12,28 @@
  *******************************************************************************/
 package org.eclipse.jdt.internal.ui.preferences.formatter;
 
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Observable;
 import java.util.Observer;
 
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
 
 import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
 
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
 
+import org.eclipse.jdt.internal.corext.util.Messages;
+
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+
 
 public class IndentationTabPage extends FormatterTabPage {
 
@@ -32,6 +42,7 @@
 	"class Example {" +	//$NON-NLS-1$
 	"  int [] myArray= {1,2,3,4,5,6};" + //$NON-NLS-1$
 	"  int theInt= 1;" + //$NON-NLS-1$
+	"\n\n" + //$NON-NLS-1$
 	"  String someString= \"Hello\";" + //$NON-NLS-1$
 	"  double aDouble= 3.0;" + //$NON-NLS-1$
 	"  void foo(int a, int b, int c, int d, int e, int f) {" + //$NON-NLS-1$
@@ -62,6 +73,8 @@
 	private CompilationUnitPreview fPreview;
 	private String fOldTabChar= null;
 
+	private IStatus fCurrentStatus;
+
 	public IndentationTabPage(ModifyDialog modifyDialog, Map<String, String> workingValues) {
 		super(modifyDialog, workingValues);
 	}
@@ -97,8 +110,7 @@
 			}
 		});
 
-		final Group typeMemberGroup= createGroup(numColumns, composite, FormatterMessages.IndentationTabPage_field_alignment_group_title);
-		createCheckboxPref(typeMemberGroup, numColumns, FormatterMessages.IndentationTabPage_field_alignment_group_align_fields_in_columns, DefaultCodeFormatterConstants.FORMATTER_ALIGN_TYPE_MEMBERS_ON_COLUMNS, FALSE_TRUE);
+		createAlignFieldsGroup(composite, numColumns);
 
 		final Group classGroup = createGroup(numColumns, composite, FormatterMessages.IndentationTabPage_indent_group_title);
 		createCheckboxPref(classGroup, numColumns, FormatterMessages.IndentationTabPage_class_group_option_indent_declarations_within_class_body, DefaultCodeFormatterConstants.FORMATTER_INDENT_BODY_DECLARATIONS_COMPARE_TO_TYPE_HEADER, FALSE_TRUE);
@@ -120,6 +132,72 @@
         createCheckboxPref(classGroup, numColumns, FormatterMessages.IndentationTabPage_indent_empty_lines, DefaultCodeFormatterConstants.FORMATTER_INDENT_EMPTY_LINES, FALSE_TRUE);
 	}
 
+	private void createAlignFieldsGroup(Composite parent, int numColumns) {
+		final Map<String, String> fieldGroupingValuesDummy= new HashMap<>();
+		final String GROUPING_KEY= "grouping"; //$NON-NLS-1$
+		final String GROUPING_LINES_KEY= "grouping.lines"; //$NON-NLS-1$
+		int groupingBlankLines;
+		try {
+			groupingBlankLines= Integer.parseInt(fWorkingValues.get(DefaultCodeFormatterConstants.FORMATTER_ALIGN_FIELDS_GROUPING_BLANK_LINES));
+		} catch (NumberFormatException e) {
+			groupingBlankLines= Integer.MAX_VALUE;
+		}
+		fieldGroupingValuesDummy.put(GROUPING_KEY, groupingBlankLines == Integer.MAX_VALUE ? DefaultCodeFormatterConstants.FALSE : DefaultCodeFormatterConstants.TRUE);
+		fieldGroupingValuesDummy.put(GROUPING_LINES_KEY, Integer.toString(groupingBlankLines == Integer.MAX_VALUE ? 1 : groupingBlankLines));
+
+		final Group alignFieldsGroup= createGroup(numColumns, parent, FormatterMessages.IndentationTabPage_field_alignment_group_title);
+		final CheckboxPreference alignFieldsPref= createCheckboxPref(alignFieldsGroup, numColumns, FormatterMessages.IndentationTabPage_field_alignment_group_align_fields_in_columns,
+				DefaultCodeFormatterConstants.FORMATTER_ALIGN_TYPE_MEMBERS_ON_COLUMNS, FALSE_TRUE);
+
+		final Label indent= new Label(alignFieldsGroup, SWT.NONE);
+		GridData gd= new GridData();
+		gd.widthHint= fPixelConverter.convertWidthInCharsToPixels(4);
+		indent.setLayoutData(gd);
+
+		final CheckboxPreference fieldGroupingPref= new CheckboxPreference(alignFieldsGroup, numColumns - 2, fieldGroupingValuesDummy, GROUPING_KEY, FALSE_TRUE,
+				FormatterMessages.IndentationTabPage_field_alignment_group_blank_lines_separating_independent_groups);
+		fieldGroupingPref.setEnabled(alignFieldsPref.getChecked());
+		fDefaultFocusManager.add(fieldGroupingPref);
+		final NumberPreference fieldGroupingBlankLinesPref= new NumberPreference(alignFieldsGroup, 1, fieldGroupingValuesDummy, GROUPING_LINES_KEY, 1, 99, null);
+		fieldGroupingBlankLinesPref.setEnabled(alignFieldsPref.getChecked() && fieldGroupingPref.getChecked());
+		fDefaultFocusManager.add(fieldGroupingBlankLinesPref);
+
+		fieldGroupingBlankLinesPref.addObserver(new Observer() {
+			@Override
+			public void update(Observable o, Object arg) {
+				if (fCurrentStatus == null) {
+					try {
+						final int blankLinesToPreserve= Integer.parseInt(fWorkingValues.get(DefaultCodeFormatterConstants.FORMATTER_NUMBER_OF_EMPTY_LINES_TO_PRESERVE));
+						final int groupingLines= Integer.parseInt(fieldGroupingValuesDummy.get(GROUPING_LINES_KEY));
+						if (groupingLines > blankLinesToPreserve) {
+							updateStatus(new Status(IStatus.INFO, JavaPlugin.getPluginId(), 0,
+									Messages.format(FormatterMessages.IndentationTabPage_field_alignment_group_blank_lines_to_preserve_info, Integer.valueOf(blankLinesToPreserve)), null));
+						}
+					} catch (NumberFormatException e) {
+						// settings corrupted, don't add info
+					}
+				}
+			}
+		});
+
+		final Observer alignGroupingObserver= new Observer() {
+			@Override
+			public void update(Observable o, Object arg) {
+				fieldGroupingPref.setEnabled(alignFieldsPref.getChecked());
+				fieldGroupingBlankLinesPref.setEnabled(alignFieldsPref.getChecked() && fieldGroupingPref.getChecked());
+
+				fWorkingValues.put(DefaultCodeFormatterConstants.FORMATTER_ALIGN_FIELDS_GROUPING_BLANK_LINES,
+						fieldGroupingPref.getChecked() ? fieldGroupingValuesDummy.get(GROUPING_LINES_KEY) : Integer.toString(Integer.MAX_VALUE));
+				doUpdatePreview();
+				notifyValuesModified();
+			}
+		};
+		alignFieldsPref.deleteObserver(fUpdater);
+		alignFieldsPref.addObserver(alignGroupingObserver);
+		fieldGroupingPref.addObserver(alignGroupingObserver);
+		fieldGroupingBlankLinesPref.addObserver(alignGroupingObserver);
+	}
+
 	@Override
 	public void initializePage() {
 	    fPreview.setPreviewText(PREVIEW);
@@ -137,6 +215,12 @@
         fPreview.update();
     }
 
+	@Override
+	protected void updateStatus(IStatus status) {
+		super.updateStatus(status);
+		this.fCurrentStatus= status;
+	}
+
 	private void updateTabPreferences(String tabPolicy, NumberPreference tabPreference, NumberPreference indentPreference, CheckboxPreference onlyForLeading) {
 		/*
 		 * If the tab-char is SPACE (or TAB), INDENTATION_SIZE
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java
index 5b5c785..0b69ba6 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/preferences/formatter/ModifyDialogTabPage.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -341,14 +341,14 @@
 		 * @param key The key to store the values.
 		 * @param minValue The minimum value which is valid input.
 		 * @param maxValue The maximum value which is valid input.
-		 * @param text The label text for this Preference.
+		 * @param text The label text for this Preference or {@code null} if none.
 		 */
 		public NumberPreference(Composite composite, int numColumns,
 							   Map<String, String> preferences, String key,
 							   int minValue, int maxValue, String text) {
 		    super(preferences, key);
 
-			fNumberLabel= createLabel(numColumns - 1, composite, text, GridData.FILL_HORIZONTAL);
+			fNumberLabel= text == null ? null : createLabel(numColumns - 1, composite, text, GridData.FILL_HORIZONTAL);
 			fNumberText= new Text(composite, SWT.SINGLE | SWT.BORDER | SWT.RIGHT);
 			fNumberText.setFont(composite.getFont());
 
@@ -442,7 +442,9 @@
 		protected void updateWidget() {
 		    final boolean hasKey= getKey() != null;
 
-		    fNumberLabel.setEnabled(hasKey && getEnabled());
+			if (fNumberLabel != null)
+				fNumberLabel.setEnabled(hasKey && getEnabled());
+
 			fNumberText.setEnabled(hasKey && getEnabled());
 
 			if (hasKey) {