/*******************************************************************************
 * Copyright (c) 2000, 2022 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jesper S Møller - Bug 529432 - Allow JDT UI to target Java 10
 *******************************************************************************/
package org.eclipse.jdt.internal.ui.preferences;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import com.ibm.icu.text.MessageFormat;

import org.osgi.service.prefs.Preferences;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.BundleDefaultsScope;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;

import org.eclipse.jface.dialogs.ControlEnableState;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.resource.JFaceResources;

import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;

import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;

import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Messages;

import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstall2;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;

import org.eclipse.jdt.ui.JavaUI;

import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.jdt.internal.ui.wizards.IStatusChangeListener;
import org.eclipse.jdt.internal.ui.wizards.buildpaths.BuildPathSupport;
import org.eclipse.jdt.internal.ui.wizards.buildpaths.LibrariesWorkbookPage;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.LayoutUtil;


/**
 * Configuration block for the 'Java Compiler' page.
 */
public class ComplianceConfigurationBlock extends OptionsConfigurationBlock {

	/**
	 * Key for the "Compiler compliance follows EE" setting.
	 * <br>Only applicable if <code>fProject != null</code>.
	 * <p>Values are { {@link #DEFAULT_CONF}, {@link #USER_CONF}, or {@link #DISABLED} }.
	 */
	private static final Key INTR_COMPLIANCE_FOLLOWS_EE= getLocalKey("internal.compliance.follows.ee"); //$NON-NLS-1$

	/**
	 * Key for the "Use default compliance" setting.
	 * <p>Values are { {@link #DEFAULT_CONF}, {@link #USER_CONF} }.
	 */
	private static final Key INTR_DEFAULT_COMPLIANCE= getLocalKey("internal.default.compliance"); //$NON-NLS-1$

	// Preference store keys, see JavaCore.getOptions
	private static final Key PREF_PB_ASSERT_AS_IDENTIFIER= getJDTCoreKey(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER);
	private static final Key PREF_PB_ENUM_AS_IDENTIFIER= getJDTCoreKey(JavaCore.COMPILER_PB_ENUM_IDENTIFIER);
	private static final Key PREF_SOURCE_COMPATIBILITY= getJDTCoreKey(JavaCore.COMPILER_SOURCE);
	private static final Key PREF_CODEGEN_TARGET_PLATFORM= getJDTCoreKey(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM);
	private static final Key PREF_COMPLIANCE= getJDTCoreKey(JavaCore.COMPILER_COMPLIANCE);
	private static final Key PREF_RELEASE= getJDTCoreKey(JavaCore.COMPILER_RELEASE);
	private static final Key PREF_ENABLE_PREVIEW= getJDTCoreKey(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES);
	private static final Key PREF_PB_REPORT_PREVIEW= getJDTCoreKey(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES);

	/* see also BuildPathSupport#PREFS_COMPLIANCE */
	private static final Key[] PREFS_COMPLIANCE= new Key[] { PREF_COMPLIANCE,
		PREF_PB_ASSERT_AS_IDENTIFIER, PREF_PB_ENUM_AS_IDENTIFIER,
		PREF_SOURCE_COMPATIBILITY, PREF_CODEGEN_TARGET_PLATFORM};
	private static final Key[] PREFS_COMPLIANCE_11_OR_HIGHER= new Key[] { PREF_COMPLIANCE,
		PREF_PB_ASSERT_AS_IDENTIFIER, PREF_PB_ENUM_AS_IDENTIFIER,
		PREF_SOURCE_COMPATIBILITY, PREF_CODEGEN_TARGET_PLATFORM,
		PREF_ENABLE_PREVIEW, PREF_PB_REPORT_PREVIEW};

	private static final Key PREF_CODEGEN_INLINE_JSR_BYTECODE= getJDTCoreKey(JavaCore.COMPILER_CODEGEN_INLINE_JSR_BYTECODE);

	private static final Key PREF_LOCAL_VARIABLE_ATTR=  getJDTCoreKey(JavaCore.COMPILER_LOCAL_VARIABLE_ATTR);
	private static final Key PREF_LINE_NUMBER_ATTR= getJDTCoreKey(JavaCore.COMPILER_LINE_NUMBER_ATTR);
	private static final Key PREF_SOURCE_FILE_ATTR= getJDTCoreKey(JavaCore.COMPILER_SOURCE_FILE_ATTR);
	private static final Key PREF_CODEGEN_UNUSED_LOCAL= getJDTCoreKey(JavaCore.COMPILER_CODEGEN_UNUSED_LOCAL);
	private static final Key PREF_CODEGEN_METHOD_PARAMETERS_ATTR= getJDTCoreKey(JavaCore.COMPILER_CODEGEN_METHOD_PARAMETERS_ATTR);

	// values
	private static final String GENERATE= JavaCore.GENERATE;
	private static final String DO_NOT_GENERATE= JavaCore.DO_NOT_GENERATE;

	private static final String PRESERVE= JavaCore.PRESERVE;
	private static final String OPTIMIZE_OUT= JavaCore.OPTIMIZE_OUT;

	private static final String VERSION_CLDC_1_1= JavaCore.VERSION_CLDC_1_1;
	private static final String VERSION_1_1= JavaCore.VERSION_1_1;
	private static final String VERSION_1_2= JavaCore.VERSION_1_2;
	private static final String VERSION_1_3= JavaCore.VERSION_1_3;

	private static final String VERSION_1_4= JavaCore.VERSION_1_4;
	private static final String VERSION_1_5= JavaCore.VERSION_1_5;
	private static final String VERSION_1_6= JavaCore.VERSION_1_6;
	private static final String VERSION_1_7= JavaCore.VERSION_1_7;
	private static final String VERSION_1_8= JavaCore.VERSION_1_8;
	private static final String VERSION_9= JavaCore.VERSION_9;
	private static final String VERSION_10= JavaCore.VERSION_10;
	private static final String VERSION_11= JavaCore.VERSION_11;
	private static final String VERSION_12 = JavaCore.VERSION_12;
	private static final String VERSION_13 = JavaCore.VERSION_13;
	private static final String VERSION_14 = JavaCore.VERSION_14;
	private static final String VERSION_15 = JavaCore.VERSION_15;
	private static final String VERSION_16 = JavaCore.VERSION_16;
	private static final String VERSION_17 = JavaCore.VERSION_17;
	private static final String VERSION_18 = JavaCore.VERSION_18;
	private static final String VERSION_19 = JavaCore.VERSION_19;
	private static final String VERSION_LATEST = JavaCore.latestSupportedJavaVersion();
	private static final String VERSION_JSR14= "jsr14"; //$NON-NLS-1$

	private static final String ERROR= JavaCore.ERROR;
	private static final String WARNING= JavaCore.WARNING;
	private static final String INFO= JavaCore.INFO;
	private static final String IGNORE= JavaCore.IGNORE;

	private static final String ENABLED= JavaCore.ENABLED;
	private static final String DISABLED= JavaCore.DISABLED;

	private static final String DEFAULT_CONF= "default"; //$NON-NLS-1$
	private static final String USER_CONF= "user";	 //$NON-NLS-1$

	private ArrayList<Control> fComplianceFollowsEEControls;
	private ArrayList<Control> fComplianceControls;
	private ArrayList<Control> fComplianceChildControls;
	private PixelConverter fPixelConverter;

	/**
	 * Remembered user compliance (stored when {@link #INTR_DEFAULT_COMPLIANCE} is switched to
	 * {@link #DEFAULT_CONF}). Elements are identified by <code>IDX_*</code> constants.
	 *
	 * @see #IDX_ASSERT_AS_IDENTIFIER
	 * @see #IDX_ENUM_AS_IDENTIFIER
	 * @see #IDX_SOURCE_COMPATIBILITY
	 * @see #IDX_CODEGEN_TARGET_PLATFORM
	 * @see #IDX_COMPLIANCE
	 * @see #IDX_INLINE_JSR_BYTECODE
	 * @see #IDX_METHOD_PARAMETERS_ATTR
	 * @see #IDX_RELEASE
	 * @see #IDX_ENABLE_PREVIEW
	 * @see #IDX_REPORT_PREVIEW
	 */
	private String[] fRememberedUserCompliance;

	/**
	 * Stored compliance settings that were active when the page was first shown. May be
	 * <code>null</code>. Elements are identified by <code>IDX_*</code> constants.
	 *
	 * @see #IDX_ASSERT_AS_IDENTIFIER
	 * @see #IDX_ENUM_AS_IDENTIFIER
	 * @see #IDX_SOURCE_COMPATIBILITY
	 * @see #IDX_CODEGEN_TARGET_PLATFORM
	 * @see #IDX_COMPLIANCE
	 * @see #IDX_INLINE_JSR_BYTECODE
	 * @see #IDX_METHOD_PARAMETERS_ATTR
	 * @see #IDX_RELEASE
	 * @see #IDX_ENABLE_PREVIEW
	 * @see #IDX_REPORT_PREVIEW
	 */
	private String[] fOriginalStoredCompliance;

	private static final int IDX_ASSERT_AS_IDENTIFIER= 0;
	private static final int IDX_ENUM_AS_IDENTIFIER= 1;
	private static final int IDX_SOURCE_COMPATIBILITY= 2;
	private static final int IDX_CODEGEN_TARGET_PLATFORM= 3;
	private static final int IDX_COMPLIANCE= 4;
	private static final int IDX_INLINE_JSR_BYTECODE= 5;
	private static final int IDX_METHOD_PARAMETERS_ATTR= 6;
	private static final int IDX_RELEASE= 7;
	private static final int IDX_ENABLE_PREVIEW= 8;
	private static final int IDX_REPORT_PREVIEW= 9;

	private IStatus fComplianceStatus;

	private Link fJRE50InfoText;
	private Label fJRE50InfoImage;
	private Composite fControlsComposite;
	private ControlEnableState fBlockEnableState;
	private Button fCompilerReleaseCheck;
	private Button fEnablePreviewCheck;
	private Combo fReportPreviewCombo;

	public ComplianceConfigurationBlock(IStatusChangeListener context, IProject project, IWorkbenchPreferenceContainer container) {
		super(context, project, getKeys(project != null), container);
		setDefaultCompilerComplianceValues();

		fBlockEnableState= null;
		fComplianceFollowsEEControls= new ArrayList<>();
		fComplianceControls= new ArrayList<>();
		fComplianceChildControls= new ArrayList<>();

		fComplianceStatus= new StatusInfo();

		fRememberedUserCompliance= new String[] { // caution: order depends on IDX_* constants
			getValue(PREF_PB_ASSERT_AS_IDENTIFIER),
			getValue(PREF_PB_ENUM_AS_IDENTIFIER),
			getValue(PREF_SOURCE_COMPATIBILITY),
			getValue(PREF_CODEGEN_TARGET_PLATFORM),
			getValue(PREF_COMPLIANCE),
			getValue(PREF_CODEGEN_INLINE_JSR_BYTECODE),
			getValue(PREF_CODEGEN_METHOD_PARAMETERS_ATTR),
			getValue(PREF_RELEASE),
			getValue(PREF_ENABLE_PREVIEW),
			getValue(PREF_PB_REPORT_PREVIEW)
		};
	}

	public static Key[] getKeys(boolean projectSpecific) {
		Key[] keys= new Key[] {
				PREF_LOCAL_VARIABLE_ATTR, PREF_LINE_NUMBER_ATTR, PREF_SOURCE_FILE_ATTR, PREF_CODEGEN_UNUSED_LOCAL, PREF_CODEGEN_INLINE_JSR_BYTECODE, INTR_DEFAULT_COMPLIANCE,
				PREF_COMPLIANCE, PREF_SOURCE_COMPATIBILITY,
				PREF_CODEGEN_TARGET_PLATFORM, PREF_PB_ASSERT_AS_IDENTIFIER, PREF_PB_ENUM_AS_IDENTIFIER, PREF_CODEGEN_METHOD_PARAMETERS_ATTR, PREF_RELEASE,
				PREF_ENABLE_PREVIEW, PREF_PB_REPORT_PREVIEW
			};

		if (projectSpecific) {
			Key[] allKeys = new Key[keys.length + 1];
			System.arraycopy(keys, 0, allKeys, 0, keys.length);
			allKeys[keys.length]= INTR_COMPLIANCE_FOLLOWS_EE;
			return allKeys;
		}

		return keys;
	}

	@Override
	protected void settingsUpdated() {
		setValue(INTR_DEFAULT_COMPLIANCE, getCurrentCompliance());
		updateComplianceFollowsEE();
		super.settingsUpdated();
	}

	/*
	 * @see org.eclipse.jface.preference.PreferencePage#createContents(Composite)
	 */
	@Override
	protected Control createContents(Composite parent) {
		fPixelConverter= new PixelConverter(parent);
		setShell(parent.getShell());

		Composite complianceComposite= createComplianceTabContent(parent);

		validateSettings(null, null, null);

		return complianceComposite;
	}

	public void enablePreferenceContent(boolean enable) {
		if (fControlsComposite != null && !fControlsComposite.isDisposed()) {
			if (enable) {
				if (fBlockEnableState != null) {
					fBlockEnableState.restore();
					fBlockEnableState= null;
				}
			} else {
				if (fBlockEnableState == null) {
					fBlockEnableState= ControlEnableState.disable(fControlsComposite);
				}
			}
		}
	}

	private Composite createComplianceTabContent(Composite folder) {

		final String[] complianceVersions= new String[] { VERSION_1_3, VERSION_1_4,
				VERSION_1_5, VERSION_1_6, VERSION_1_7, VERSION_1_8, VERSION_9, VERSION_10, VERSION_11, VERSION_12, VERSION_13, VERSION_14, VERSION_15, VERSION_16, VERSION_17, VERSION_18, VERSION_19 };
		final String[] complianceLabels= new String[] {
			PreferencesMessages.ComplianceConfigurationBlock_version13,
			PreferencesMessages.ComplianceConfigurationBlock_version14,
			PreferencesMessages.ComplianceConfigurationBlock_version15,
			PreferencesMessages.ComplianceConfigurationBlock_version16,
			PreferencesMessages.ComplianceConfigurationBlock_version17,
			PreferencesMessages.ComplianceConfigurationBlock_version18,
			PreferencesMessages.ComplianceConfigurationBlock_version9,
			PreferencesMessages.ComplianceConfigurationBlock_version10,
			PreferencesMessages.ComplianceConfigurationBlock_version_11,
			PreferencesMessages.ComplianceConfigurationBlock_version_12,
			PreferencesMessages.ComplianceConfigurationBlock_version_13,
			PreferencesMessages.ComplianceConfigurationBlock_version_14,
			PreferencesMessages.ComplianceConfigurationBlock_version_15,
			PreferencesMessages.ComplianceConfigurationBlock_version_16,
			PreferencesMessages.ComplianceConfigurationBlock_version_17,
			PreferencesMessages.ComplianceConfigurationBlock_version_18,
			PreferencesMessages.ComplianceConfigurationBlock_version_19
		};

		String[] targetVersions= new String[] { VERSION_CLDC_1_1, VERSION_1_1, VERSION_1_2, VERSION_1_3, VERSION_1_4,
				VERSION_1_5, VERSION_1_6, VERSION_1_7, VERSION_1_8, VERSION_9, VERSION_10, VERSION_11, VERSION_12, VERSION_13, VERSION_14, VERSION_15, VERSION_16, VERSION_17, VERSION_18, VERSION_19 };
		String[] targetLabels= new String[] {
				PreferencesMessages.ComplianceConfigurationBlock_versionCLDC11,
				PreferencesMessages.ComplianceConfigurationBlock_version11,
				PreferencesMessages.ComplianceConfigurationBlock_version12,
				PreferencesMessages.ComplianceConfigurationBlock_version13,
				PreferencesMessages.ComplianceConfigurationBlock_version14,
				PreferencesMessages.ComplianceConfigurationBlock_version15,
				PreferencesMessages.ComplianceConfigurationBlock_version16,
				PreferencesMessages.ComplianceConfigurationBlock_version17,
				PreferencesMessages.ComplianceConfigurationBlock_version18,
				PreferencesMessages.ComplianceConfigurationBlock_version9,
				PreferencesMessages.ComplianceConfigurationBlock_version10,
				PreferencesMessages.ComplianceConfigurationBlock_version_11,
				PreferencesMessages.ComplianceConfigurationBlock_version_12,
				PreferencesMessages.ComplianceConfigurationBlock_version_13,
				PreferencesMessages.ComplianceConfigurationBlock_version_14,
				PreferencesMessages.ComplianceConfigurationBlock_version_15,
				PreferencesMessages.ComplianceConfigurationBlock_version_16,
				PreferencesMessages.ComplianceConfigurationBlock_version_17,
				PreferencesMessages.ComplianceConfigurationBlock_version_18,
				PreferencesMessages.ComplianceConfigurationBlock_version_19
		};
		if (ComplianceConfigurationBlock.VERSION_JSR14.equals(getValue(PREF_CODEGEN_TARGET_PLATFORM))) {
			targetVersions= append(targetVersions, ComplianceConfigurationBlock.VERSION_JSR14);
			targetLabels= append(targetLabels, ComplianceConfigurationBlock.VERSION_JSR14);
		}

		String[] sourceVersions= new String[] { VERSION_1_3, VERSION_1_4,
				VERSION_1_5, VERSION_1_6, VERSION_1_7, VERSION_1_8, VERSION_9, VERSION_10, VERSION_11, VERSION_12, VERSION_13, VERSION_14, VERSION_15, VERSION_16, VERSION_17, VERSION_18, VERSION_19 };
		String[] sourceLabels= new String[] {
				PreferencesMessages.ComplianceConfigurationBlock_version13,
				PreferencesMessages.ComplianceConfigurationBlock_version14,
				PreferencesMessages.ComplianceConfigurationBlock_version15,
				PreferencesMessages.ComplianceConfigurationBlock_version16,
				PreferencesMessages.ComplianceConfigurationBlock_version17,
				PreferencesMessages.ComplianceConfigurationBlock_version18,
				PreferencesMessages.ComplianceConfigurationBlock_version9,
				PreferencesMessages.ComplianceConfigurationBlock_version10,
				PreferencesMessages.ComplianceConfigurationBlock_version_11,
				PreferencesMessages.ComplianceConfigurationBlock_version_12,
				PreferencesMessages.ComplianceConfigurationBlock_version_13,
				PreferencesMessages.ComplianceConfigurationBlock_version_14,
				PreferencesMessages.ComplianceConfigurationBlock_version_15,
				PreferencesMessages.ComplianceConfigurationBlock_version_16,
				PreferencesMessages.ComplianceConfigurationBlock_version_17,
				PreferencesMessages.ComplianceConfigurationBlock_version_18,
				PreferencesMessages.ComplianceConfigurationBlock_version_19
		};

		final ScrolledPageContent sc1 = new ScrolledPageContent(folder);
		Composite composite= sc1.getBody();
		GridLayout layout= new GridLayout();
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		composite.setLayout(layout);

		fControlsComposite= new Composite(composite, SWT.NONE);
		fControlsComposite.setFont(composite.getFont());
		fControlsComposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));

		layout= new GridLayout();
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		layout.numColumns= 1;
		fControlsComposite.setLayout(layout);

		int nColumns= 3;

		layout= new GridLayout();
		layout.numColumns= nColumns;

		Group group= new Group(fControlsComposite, SWT.NONE);
		group.setFont(fControlsComposite.getFont());
		group.setText(PreferencesMessages.ComplianceConfigurationBlock_compliance_group_label);
		group.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
		group.setLayout(layout);

		String[] defaultUserValues= new String[] { DEFAULT_CONF, USER_CONF };

		Control[] otherChildren= group.getChildren();
		if (fProject != null) {
			String label= PreferencesMessages.ComplianceConfigurationBlock_compliance_follows_EE_label;
			int widthHint= fPixelConverter.convertWidthInCharsToPixels(40);
			addCheckBoxWithLink(group, label, INTR_COMPLIANCE_FOLLOWS_EE, defaultUserValues, 0, widthHint, new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					openBuildPathPropertyPage();
				}
			});
		}

		Control[] allChildren= group.getChildren();
		fComplianceFollowsEEControls.addAll(Arrays.asList(allChildren));
		fComplianceFollowsEEControls.removeAll(Arrays.asList(otherChildren));
		otherChildren= allChildren;


		String label= PreferencesMessages.ComplianceConfigurationBlock_compiler_compliance_label;
		addComboBox(group, label, PREF_COMPLIANCE, complianceVersions, complianceLabels, 0);

		label= PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_backwardcompatibility_label;
		fCompilerReleaseCheck= addCheckBox(group, label, PREF_RELEASE, new String[] { DISABLED, ENABLED }, 0, false);

		label= PreferencesMessages.ComplianceConfigurationBlock_default_settings_label;
		addCheckBox(group, label, INTR_DEFAULT_COMPLIANCE, defaultUserValues, 0);

		allChildren= group.getChildren();
		fComplianceControls.addAll(Arrays.asList(allChildren));
		fComplianceControls.removeAll(Arrays.asList(otherChildren));
		otherChildren= allChildren;


		int indent= LayoutUtil.getIndent();

		String[] warningInfoIgnore= new String[] { WARNING, INFO, IGNORE };
		String[] warningInfoIgnoreLabels= new String[] {
				PreferencesMessages.ComplianceConfigurationBlock_warning,
				PreferencesMessages.ComplianceConfigurationBlock_info,
				PreferencesMessages.ComplianceConfigurationBlock_ignore
		};

		label= Messages.format(PreferencesMessages.ComplianceConfigurationBlock_enable_preview_label, new String[] { getVersionLabel(VERSION_LATEST) });
		fEnablePreviewCheck= addCheckBox(group, label, PREF_ENABLE_PREVIEW, new String[] { ENABLED, DISABLED }, indent);
		label= PreferencesMessages.ComplianceConfigurationBlock_enable_preview_severity_label;
		fReportPreviewCombo= addComboBox(group, label, PREF_PB_REPORT_PREVIEW, warningInfoIgnore, warningInfoIgnoreLabels, indent * 2);
		fReportPreviewCombo.setEnabled(fEnablePreviewCheck.isEnabled() && fEnablePreviewCheck.getSelection());

		label= PreferencesMessages.ComplianceConfigurationBlock_codegen_targetplatform_label;
		addComboBox(group, label, PREF_CODEGEN_TARGET_PLATFORM, targetVersions, targetLabels, indent);

		label= PreferencesMessages.ComplianceConfigurationBlock_source_compatibility_label;
		addComboBox(group, label, PREF_SOURCE_COMPATIBILITY, sourceVersions, sourceLabels, indent);

		String[] errorWarningInfoIgnore= new String[] { ERROR, WARNING, INFO, IGNORE };

		String[] errorWarningInfoIgnoreLabels= new String[] {
			PreferencesMessages.ComplianceConfigurationBlock_error,
			PreferencesMessages.ComplianceConfigurationBlock_warning,
			PreferencesMessages.ComplianceConfigurationBlock_info,
			PreferencesMessages.ComplianceConfigurationBlock_ignore
		};

		label= PreferencesMessages.ComplianceConfigurationBlock_pb_assert_as_identifier_label;
		addComboBox(group, label, PREF_PB_ASSERT_AS_IDENTIFIER, errorWarningInfoIgnore, errorWarningInfoIgnoreLabels, indent);

		label= PreferencesMessages.ComplianceConfigurationBlock_pb_enum_as_identifier_label;
		addComboBox(group, label, PREF_PB_ENUM_AS_IDENTIFIER, errorWarningInfoIgnore, errorWarningInfoIgnoreLabels, indent);

		allChildren= group.getChildren();
		fComplianceChildControls.addAll(Arrays.asList(allChildren));
		fComplianceChildControls.removeAll(Arrays.asList(otherChildren));


		layout= new GridLayout();
		layout.numColumns= nColumns;

		group= new Group(fControlsComposite, SWT.NONE);
		group.setFont(fControlsComposite.getFont());
		group.setText(PreferencesMessages.ComplianceConfigurationBlock_classfiles_group_label);
		group.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
		group.setLayout(layout);

		String[] generateValues= new String[] { GENERATE, DO_NOT_GENERATE };
		String[] enableDisableValues= new String[] { ENABLED, DISABLED };

		label= PreferencesMessages.ComplianceConfigurationBlock_variable_attr_label;
		addCheckBox(group, label, PREF_LOCAL_VARIABLE_ATTR, generateValues, 0);

		label= PreferencesMessages.ComplianceConfigurationBlock_line_number_attr_label;
		addCheckBox(group, label, PREF_LINE_NUMBER_ATTR, generateValues, 0);

		label= PreferencesMessages.ComplianceConfigurationBlock_source_file_attr_label;
		addCheckBox(group, label, PREF_SOURCE_FILE_ATTR, generateValues, 0);

		label= PreferencesMessages.ComplianceConfigurationBlock_codegen_unused_local_label;
		addCheckBox(group, label, PREF_CODEGEN_UNUSED_LOCAL, new String[] { PRESERVE, OPTIMIZE_OUT }, 0);

		label= PreferencesMessages.ComplianceConfigurationBlock_codegen_inline_jsr_bytecode_label;
		addCheckBox(group, label, PREF_CODEGEN_INLINE_JSR_BYTECODE, enableDisableValues, 0);

		label= PreferencesMessages.ComplianceConfigurationBlock_codegen_method_parameters_attr;
		addCheckBox(group, label, PREF_CODEGEN_METHOD_PARAMETERS_ATTR, generateValues, 0);

		Composite infoComposite= new Composite(fControlsComposite, SWT.NONE);
		infoComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		infoComposite.setLayout(new GridLayout(2, false));

		fJRE50InfoImage= new Label(infoComposite, SWT.NONE);
		fJRE50InfoImage.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING));
		GridData gd= new GridData(GridData.BEGINNING, GridData.BEGINNING, false, false);
		fJRE50InfoImage.setLayoutData(gd);

		fJRE50InfoText= new Link(infoComposite, SWT.WRAP);
		fJRE50InfoText.setFont(composite.getFont());
		// set a text: not the real one, just for layouting
		fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_info_project, new String[] { getVersionLabel(VERSION_1_3), getVersionLabel(VERSION_1_3) }));
		fJRE50InfoText.setVisible(false);
		fJRE50InfoText.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				if ("1".equals(e.text)) { //$NON-NLS-1$
					openJREInstallPreferencePage(false);
				} else if ("2".equals(e.text)) { //$NON-NLS-1$
					openJREInstallPreferencePage(true);
				} else {
					openBuildPathPropertyPage();
				}
			}
			@Override
			public void widgetSelected(SelectionEvent e) {
				widgetDefaultSelected(e);
			}
		});
		gd= new GridData(GridData.FILL, GridData.FILL, true, true);
		gd.widthHint= fPixelConverter.convertWidthInCharsToPixels(50);
		fJRE50InfoText.setLayoutData(gd);
		initializeReleaseCheckBox(false);
		updateReleaseOptionStatus();
		validateComplianceStatus();
		return sc1;
	}

	private void initializeReleaseCheckBox(boolean useProjectSpecificSettings) {
		if (fProject != null && !useProjectSpecificSettings) {
			String value= PREF_RELEASE.getStoredValue(new IScopeContext[] { new ProjectScope(fProject) }, false, null);
			if (value != null) {
				setValue(PREF_RELEASE, value);
				fCompilerReleaseCheck.setSelection(DISABLED.equals(value) ? false : true);
			} else {
				setValue(PREF_RELEASE, DISABLED);
				fCompilerReleaseCheck.setSelection(false);
			}
		} else {
			String value= PREF_RELEASE.getStoredValue(new IScopeContext[] { InstanceScope.INSTANCE }, false, null);
			if (value != null) {
				setValue(PREF_RELEASE, value);
				fCompilerReleaseCheck.setSelection(DISABLED.equals(value) ? false : true);
			} else {
				setValue(PREF_RELEASE, DISABLED);
				fCompilerReleaseCheck.setSelection(false);
			}
		}
	}

	private static String[] append(String[] versions, String version) {
		String[] result= new String[versions.length + 1];
		System.arraycopy(versions, 0, result, 0, versions.length);
		result[versions.length]= version;
		return result;
	}

	protected final void openBuildPathPropertyPage() {
		if (getPreferenceContainer() != null) {
			Map<Object, IClasspathEntry> data= new HashMap<>();
			data.put(BuildPathsPropertyPage.DATA_REVEAL_ENTRY, JavaRuntime.getDefaultJREContainerEntry());
			getPreferenceContainer().openPage(BuildPathsPropertyPage.PROP_ID, data);
		}
		updateReleaseOptionStatus();
		validateComplianceStatus();
	}

	protected final void openJREInstallPreferencePage(boolean openEE) {
		String jreID= BuildPathSupport.JRE_PREF_PAGE_ID;
		String eeID= BuildPathSupport.EE_PREF_PAGE_ID;
		String pageId= openEE ? eeID : jreID;
		if (fProject == null && getPreferenceContainer() != null) {
			getPreferenceContainer().openPage(pageId, null);
		} else {
			PreferencesUtil.createPreferenceDialogOn(getShell(), pageId, new String[] { jreID, eeID }, null).open();
		}
		updateReleaseOptionStatus();
		validateComplianceStatus();
	}

	@Override
	protected void validateSettings(Key changedKey, String oldValue, String newValue) {
		if (!areSettingsEnabled()) {
			return;
		}
		if (changedKey != null) {
			if (INTR_DEFAULT_COMPLIANCE.equals(changedKey)) {
				updateComplianceEnableState();
				updateComplianceDefaultSettings(true, null);
				fComplianceStatus= validateCompliance();
			} else if (INTR_COMPLIANCE_FOLLOWS_EE.equals(changedKey)) {
				setValue(INTR_DEFAULT_COMPLIANCE, DEFAULT_CONF);
				updateReleaseOptionStatus();
				updateComplianceEnableState();
				updateComplianceDefaultSettings(true, null);
				updateControls();
				fComplianceStatus= validateCompliance();
				validateComplianceStatus();
			} else if (PREF_COMPLIANCE.equals(changedKey)) {
				updateReleaseOptionStatus();
			    // set compliance settings to default
			    Object oldDefault= getValue(INTR_DEFAULT_COMPLIANCE);
				boolean rememberOld= USER_CONF.equals(oldDefault);
				updateComplianceDefaultSettings(rememberOld, oldValue);
				fComplianceStatus= validateCompliance();
				validateComplianceStatus();
			} else if (PREF_RELEASE.equals(changedKey)) {
				setValue(PREF_RELEASE, DISABLED.equals(newValue) ? ENABLED : DISABLED);
				updateReleaseOptionStatus();
				updateComplianceDefaultSettings(true, null);
				fComplianceStatus= validateCompliance();
				validateComplianceStatus();
				if (fComplianceStatus.isOK() && ENABLED.equals(getValue(PREF_RELEASE))) {
					String exportOptionValue= addsExportToSystemModule();
					if (exportOptionValue != null) {
						int slash = exportOptionValue.indexOf('/');
						if (slash > -1) {
							fComplianceStatus = new Status(IStatus.ERROR, JavaUI.ID_PLUGIN,
									MessageFormat.format(PreferencesMessages.ComplianceConfigurationBlock_release_notWith_addExports_error, exportOptionValue.substring(0, slash)));
						}
					}
				}
			} else if (PREF_SOURCE_COMPATIBILITY.equals(changedKey)) {
				updatePreviewFeaturesState();
				updateAssertEnumAsIdentifierEnableState();
				fComplianceStatus= validateCompliance();
			} else if (PREF_CODEGEN_TARGET_PLATFORM.equals(changedKey)) {
				if (VERSION_CLDC_1_1.equals(newValue) && !oldValue.equals(newValue)) {
					String compliance= getValue(PREF_COMPLIANCE);
					String source= getValue(PREF_SOURCE_COMPATIBILITY);
					if (!JavaModelUtil.isVersionLessThan(compliance, VERSION_1_5)) {
						setValue(PREF_COMPLIANCE, VERSION_1_4);
					}
					if (!VERSION_1_3.equals(source)) {
						setValue(PREF_SOURCE_COMPATIBILITY, VERSION_1_3);
					}
				}
				updateControls();
				updateInlineJSREnableState();
				updateStoreMethodParamNamesEnableState();
				updatePreviewFeaturesState();
				updateAssertEnumAsIdentifierEnableState();
				fComplianceStatus= validateCompliance();
			} else if (PREF_PB_ENUM_AS_IDENTIFIER.equals(changedKey) ||
					PREF_PB_ASSERT_AS_IDENTIFIER.equals(changedKey)) {
				fComplianceStatus= validateCompliance();
			} else if (PREF_ENABLE_PREVIEW.equals(changedKey)) {
				fComplianceStatus= validateCompliance();
				updatePreviewFeaturesState();
			} else if (PREF_PB_REPORT_PREVIEW.equals(changedKey)) {
				fComplianceStatus= validateCompliance();
			} else {
				return;
			}
		} else {
			updateComplianceFollowsEE();
			updateControls();
			updateComplianceEnableState();
			updatePreviewFeaturesState();
			updatePreviewControls();
			updateAssertEnumAsIdentifierEnableState();
			updateInlineJSREnableState();
			updateStoreMethodParamNamesEnableState();
			fComplianceStatus= validateCompliance();
			updateComplianceReleaseSettings();
			updateReleaseOptionStatus();
			validateComplianceStatus();
		}
		fContext.statusChanged(fComplianceStatus);
	}

	public void refreshComplianceSettings() {
		if (fOriginalStoredCompliance == null) {
			fOriginalStoredCompliance= new String[] { // caution: order depends on IDX_* constants
					getOriginalStoredValue(PREF_PB_ASSERT_AS_IDENTIFIER),
					getOriginalStoredValue(PREF_PB_ENUM_AS_IDENTIFIER),
					getOriginalStoredValue(PREF_SOURCE_COMPATIBILITY),
					getOriginalStoredValue(PREF_CODEGEN_TARGET_PLATFORM),
					getOriginalStoredValue(PREF_COMPLIANCE),
					getOriginalStoredValue(PREF_CODEGEN_INLINE_JSR_BYTECODE),
					getOriginalStoredValue(PREF_CODEGEN_METHOD_PARAMETERS_ATTR),
					getOriginalStoredValue(PREF_RELEASE),
					getOriginalStoredValue(PREF_ENABLE_PREVIEW),
					getOriginalStoredValue(PREF_PB_REPORT_PREVIEW)
				};

		} else {
			String[] storedCompliance= new String[] {
					getOriginalStoredValue(PREF_PB_ASSERT_AS_IDENTIFIER),
					getOriginalStoredValue(PREF_PB_ENUM_AS_IDENTIFIER),
					getOriginalStoredValue(PREF_SOURCE_COMPATIBILITY),
					getOriginalStoredValue(PREF_CODEGEN_TARGET_PLATFORM),
					getOriginalStoredValue(PREF_COMPLIANCE),
					getOriginalStoredValue(PREF_CODEGEN_INLINE_JSR_BYTECODE),
					getOriginalStoredValue(PREF_CODEGEN_METHOD_PARAMETERS_ATTR),
					getOriginalStoredValue(PREF_RELEASE),
					getOriginalStoredValue(PREF_ENABLE_PREVIEW),
					getOriginalStoredValue(PREF_PB_REPORT_PREVIEW)
				};
			if (!Arrays.equals(fOriginalStoredCompliance, storedCompliance)) {
				// compliance changed on disk -> override user modifications

				fOriginalStoredCompliance= storedCompliance;

				setValue(PREF_PB_ASSERT_AS_IDENTIFIER, storedCompliance[IDX_ASSERT_AS_IDENTIFIER]);
				setValue(PREF_PB_ENUM_AS_IDENTIFIER, storedCompliance[IDX_ENUM_AS_IDENTIFIER]);
				setValue(PREF_SOURCE_COMPATIBILITY, storedCompliance[IDX_SOURCE_COMPATIBILITY]);
				setValue(PREF_CODEGEN_TARGET_PLATFORM, storedCompliance[IDX_CODEGEN_TARGET_PLATFORM]);
				setValue(PREF_COMPLIANCE, storedCompliance[IDX_COMPLIANCE]);
				setValue(PREF_CODEGEN_INLINE_JSR_BYTECODE, storedCompliance[IDX_INLINE_JSR_BYTECODE]);
				setValue(PREF_CODEGEN_METHOD_PARAMETERS_ATTR, storedCompliance[IDX_METHOD_PARAMETERS_ATTR]);
				setValue(PREF_RELEASE, storedCompliance[IDX_RELEASE]);
				setValue(PREF_ENABLE_PREVIEW, storedCompliance[IDX_ENABLE_PREVIEW]);
				setValue(PREF_PB_REPORT_PREVIEW, storedCompliance[IDX_REPORT_PREVIEW]);
			}

			updateComplianceFollowsEE();
			updateControls();
			updateComplianceEnableState();
			validateComplianceStatus();
			updateInlineJSREnableState();
			updateAssertEnumAsIdentifierEnableState();
			updatePreviewFeaturesState();
			updateStoreMethodParamNamesEnableState();
		}
	}

	@Override
	public void performDefaults() {
		super.performDefaults();
		performReleasePreferenceDefault();
	}

	private void performReleasePreferenceDefault() {
		String defValue= getDefaultValue(PREF_RELEASE);
		if (defValue == null) {
			defValue= DISABLED;
		}
		fCompilerReleaseCheck.setSelection(DISABLED.equals(defValue) ? false : true);
		setValue(PREF_RELEASE, defValue);
		updateReleaseOptionStatus();
	}

	private void validateComplianceStatus() {
		if (fJRE50InfoText != null && !fJRE50InfoText.isDisposed()) {
			boolean isVisible= false;
			Image image= JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_WARNING);
			String compliance= getStoredValue(PREF_COMPLIANCE); // get actual value
			IVMInstall install= null;
			if (fProject != null) { // project specific settings: only test if a 50 JRE is installed
				try {
					install= JavaRuntime.getVMInstall(JavaCore.create(fProject));
				} catch (CoreException e) {
					JavaPlugin.log(e);
				}
			} else {
				install= JavaRuntime.getDefaultVMInstall();
			}
			boolean isJREUnsupportedAndGreater= false;
			if (install instanceof IVMInstall2) {
				String version= ((IVMInstall2) install).getJavaVersion();
				String compilerCompliance= JavaModelUtil.getCompilerCompliance((IVMInstall2) install, compliance);
				isJREUnsupportedAndGreater= isJREVersionUnsupportedAndGreater(version, compilerCompliance);
				if (isJREUnsupportedAndGreater) {
					version= getJREVersionString(version);
				}
				if (!compilerCompliance.equals(compliance)) { // Discourage using compiler with version other than compliance
					String[] args= { getVersionLabel(compliance), getVersionLabel(compilerCompliance) };
					if (isJREUnsupportedAndGreater) {
						args[1]= getVersionLabel(version);
					}
					if (JavaModelUtil.is9OrHigher(compilerCompliance)) {
						if (!JavaModelUtil.is1d6OrHigher(compliance) || !fCompilerReleaseCheck.getSelection()) {
							if (fProject == null) {
								fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_info, args));
							} else {
								fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_info_project, args));
							}
						} else {
							if (JavaModelUtil.is10OrHigher(compilerCompliance) && !JavaModelUtil.is12OrHigher(compilerCompliance)) {
								fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_backwardcompatibility_warning, args));
							}
							else {
								fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_backwardcompatibility_info, args));
								image= JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_INFO);							}
						}
					} else {
						if (fProject == null) {
							fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_info, args));
						} else {
							fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_info_project, args));
						}
					}
					isVisible= true;
				} else {
					if (isJREUnsupportedAndGreater) {
						String[] args= { getVersionLabel(compilerCompliance), getVersionLabel(version) };
						if (fCompilerReleaseCheck.getSelection()) {
							fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_backwardcompatibility_info, args));
							image= JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_INFO);
						} else {
							if (fProject == null) {
								fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_info, args));
							} else {
								fJRE50InfoText.setText(Messages.format(PreferencesMessages.ComplianceConfigurationBlock_jrecompliance_info_project, args));
							}
						}
						isVisible= true;
					}
				}
			}

//			//TODO: Comment once Java SE 19 has been shipped:
//			String selectedCompliance= getValue(PREF_COMPLIANCE);
//			if (VERSION_19.equals(selectedCompliance)) {
//				fJRE50InfoText.setText(
//						"This is an implementation of an early-draft specification developed under the Java Community Process (JCP) and is made available for testing and evaluation purposes only. The code is not compatible with any specification of the JCP."); //$NON-NLS-1$
//				isVisible= true;
//			}

			fJRE50InfoText.setVisible(isVisible);
			fJRE50InfoImage.setImage(isVisible ? image : null);
			fJRE50InfoImage.getParent().layout();
		}
	}

	private String addsExportToSystemModule() {
		try {
			for (IClasspathEntry cpe : JavaCore.create(fProject).getRawClasspath()) {
				if (cpe.getEntryKind() == IClasspathEntry.CPE_CONTAINER && LibrariesWorkbookPage.isJREContainer(cpe.getPath())) {
					for (IClasspathAttribute attribute : cpe.getExtraAttributes()) {
						if (IClasspathAttribute.ADD_EXPORTS.equals(attribute.getName())) {
							return attribute.getValue();
						}
					}
					break;
				}
			}
		} catch (JavaModelException e) {
			return null;
		}
		return null;
	}

	private void updateReleaseOptionStatus() {
		if (fCompilerReleaseCheck != null && !fCompilerReleaseCheck.isDisposed()) {
			String compliance= getStoredValue(PREF_COMPLIANCE); // get actual value
			IVMInstall install= null;
			if (fProject != null) { // project specific settings: only test if a 50 JRE is installed
				try {
					install= JavaRuntime.getVMInstall(JavaCore.create(fProject));
				} catch (CoreException e) {
					JavaPlugin.log(e);
				}
			} else {
				install= JavaRuntime.getDefaultVMInstall();
			}
			if (install instanceof IVMInstall2) {
				String compilerCompliance= JavaModelUtil.getCompilerCompliance((IVMInstall2) install, compliance);
				String version= ((IVMInstall2) install).getJavaVersion();
				boolean isJREUnsupportedAndGreater= isJREVersionUnsupportedAndGreater(version, compilerCompliance);
				if (!compilerCompliance.equals(compliance)) { // Discourage using compiler with version other than compliance
					if (JavaModelUtil.is9OrHigher(compilerCompliance)) {
						if (!JavaModelUtil.isVersionLessThan(compliance, compilerCompliance)
								|| !JavaModelUtil.is1d6OrHigher(compliance)
								|| ( JavaModelUtil.is12OrHigher(compilerCompliance))
								&& !JavaModelUtil.is1d7OrHigher(compliance)) {
							fCompilerReleaseCheck.setEnabled(false);
							fCompilerReleaseCheck.setSelection(false);
							setValue(PREF_RELEASE, DISABLED);
						} else {
							if (fProject != null) {
								fCompilerReleaseCheck.setEnabled(checkValue(INTR_COMPLIANCE_FOLLOWS_EE, USER_CONF) || checkValue(INTR_COMPLIANCE_FOLLOWS_EE, DISABLED));
							} else {
								fCompilerReleaseCheck.setEnabled(true);
							}
						}
						updateComplianceEnableSourceTargetState();
					} else {
						fCompilerReleaseCheck.setEnabled(false);
						fCompilerReleaseCheck.setSelection(false);
						setValue(PREF_RELEASE, DISABLED);
					}
				} else if (!JavaModelUtil.is9OrHigher(compilerCompliance)) {
					fCompilerReleaseCheck.setEnabled(false);
					fCompilerReleaseCheck.setSelection(false);
					setValue(PREF_RELEASE, JavaCore.DISABLED);
				} else {
					if (fProject == null
							|| (isJREUnsupportedAndGreater && JavaModelUtil.is1d6OrHigher(compliance))
							|| JavaModelUtil.is9OrHigher(compliance)) {
						fCompilerReleaseCheck.setEnabled(true);
					}
					updateComplianceEnableSourceTargetState();
				}
			}
		}
	}

	private boolean isJREVersionUnsupportedAndGreater(String version, String compilerCompliance) {
		boolean isJREUnsupportedAndGreater= false;
		String versionStr= version;
		if (!JavaCore.isSupportedJavaVersion(versionStr)) {
			try {
				versionStr= getJREVersionString(versionStr);
				int jreVersion= Integer.parseInt(versionStr);
				int supportedVersion= Integer.parseInt(compilerCompliance);
				if (jreVersion > supportedVersion) {
					isJREUnsupportedAndGreater= true;
				}
			} catch (NumberFormatException e) {
				//do nothing
			}
		}
		return isJREUnsupportedAndGreater;
	}

	private String getJREVersionString(String version) {
		String newVersion= version;
		int index= newVersion.indexOf('.');
		if (index != -1) {
			newVersion= newVersion.substring(0, index);
		} else {
			index= newVersion.indexOf('-');
			if (index != -1)
				newVersion= newVersion.substring(0, index);
		}
		return newVersion;
	}

	private String getVersionLabel(String version) {
		return BasicElementLabels.getVersionName(version);
	}


	private IStatus validateCompliance() {
		StatusInfo status= new StatusInfo();
		String compliance= getValue(PREF_COMPLIANCE);
		String source= getValue(PREF_SOURCE_COMPATIBILITY);
		String target= getValue(PREF_CODEGEN_TARGET_PLATFORM);

		if (ComplianceConfigurationBlock.VERSION_JSR14.equals(target)) {
			target= source;
		}

		// compliance must not be smaller than source or target
		if (JavaModelUtil.isVersionLessThan(compliance, source)) {
			status.setError(PreferencesMessages.ComplianceConfigurationBlock_src_greater_compliance);
			return status;
		}

		if (JavaModelUtil.isVersionLessThan(compliance, target)) {
			status.setError(PreferencesMessages.ComplianceConfigurationBlock_classfile_greater_compliance);
			return status;
		}

		if (VERSION_CLDC_1_1.equals(target)) {
			if (!VERSION_1_3.equals(source) || !JavaModelUtil.isVersionLessThan(compliance, VERSION_1_5)) {
				status.setError(PreferencesMessages.ComplianceConfigurationBlock_cldc11_requires_source13_compliance_se14);
				return status;
			}
		}

		// target must not be smaller than source
		if (!VERSION_1_3.equals(source) && JavaModelUtil.isVersionLessThan(target, source)) {
			status.setError(PreferencesMessages.ComplianceConfigurationBlock_classfile_greater_source);
			return status;
		}

		return status;
	}


	@Override
	public void useProjectSpecificSettings(boolean enable) {
		super.useProjectSpecificSettings(enable);
		if (!enable) {
			initializeReleaseCheckBox(!enable);
		}
		updateReleaseOptionStatus();
		validateComplianceStatus();
	}

	private void updateComplianceFollowsEE() {
		if (fProject != null) {
			String complianceFollowsEE= DISABLED;
			IExecutionEnvironment ee= getEE();
			String label;
			if (ee != null) {
				complianceFollowsEE= getComplianceFollowsEE(ee);
				label= Messages.format(PreferencesMessages.ComplianceConfigurationBlock_compliance_follows_EE_with_EE_label, ee.getId());
			} else {
				label= PreferencesMessages.ComplianceConfigurationBlock_compliance_follows_EE_label;
			}
			Link checkBoxLink= getCheckBoxLink(INTR_COMPLIANCE_FOLLOWS_EE);
			if (checkBoxLink != null) {
				checkBoxLink.setText(label);
			}
			setValue(INTR_COMPLIANCE_FOLLOWS_EE, complianceFollowsEE);
		}
	}

	private void updateComplianceEnableState() {
		boolean enableComplianceControls= true;
		if (fProject != null) {
			boolean hasProjectSpecificOptions= hasProjectSpecificOptions(fProject);
			String complianceFollowsEE= getValue(INTR_COMPLIANCE_FOLLOWS_EE);
			updateCheckBox(getCheckBox(INTR_COMPLIANCE_FOLLOWS_EE));
			boolean enableComplianceFollowsEE= hasProjectSpecificOptions && ! DISABLED.equals(complianceFollowsEE); // is default or user
			updateControlsEnableState(fComplianceFollowsEEControls, enableComplianceFollowsEE);

			enableComplianceControls= hasProjectSpecificOptions && ! DEFAULT_CONF.equals(complianceFollowsEE); // is disabled or user
			updateControlsEnableState(fComplianceControls, enableComplianceControls);
		}

		boolean enableComplianceChildren= enableComplianceControls && checkValue(INTR_DEFAULT_COMPLIANCE, USER_CONF);
		updateControlsEnableState(fComplianceChildControls, enableComplianceChildren);
		updateReleaseOptionStatus();
	}

	private void updateComplianceEnableSourceTargetState() {
		boolean enableComplianceControls= true;
		if (fProject != null) {
			boolean hasProjectSpecificOptions= hasProjectSpecificOptions(fProject);
			String complianceFollowsEE= getValue(INTR_COMPLIANCE_FOLLOWS_EE);
			enableComplianceControls= hasProjectSpecificOptions && !DEFAULT_CONF.equals(complianceFollowsEE); // is disabled or user
		}
		boolean enableBasedOnRelease= !fCompilerReleaseCheck.getSelection() || !JavaModelUtil.is1d6OrHigher(getValue(PREF_COMPLIANCE));
		boolean enableComplianceChildren= enableComplianceControls && checkValue(INTR_DEFAULT_COMPLIANCE, USER_CONF) && enableBasedOnRelease;
		for (int i= fComplianceChildControls.size() - 1; i >= 0; i--) {
			Control curr= fComplianceChildControls.get(i);
			ControlData data= (ControlData) curr.getData();
			if (data != null) {
				if (PREF_SOURCE_COMPATIBILITY.equals(data.getKey())
						|| PREF_CODEGEN_TARGET_PLATFORM.equals(data.getKey())) {
					Combo combo= getComboBox(data.getKey());
					combo.setEnabled(enableComplianceChildren);
				}
			}
		}
	}

	private void updateControlsEnableState(List<Control> controls, boolean enable) {
		for (int i= controls.size() - 1; i >= 0; i--) {
			Control curr= controls.get(i);
			if (curr instanceof Composite) {
				updateControlsEnableState(Arrays.asList(((Composite)curr).getChildren()), enable);
			}
			curr.setEnabled(enable);
		}
		if (controls.contains(fReportPreviewCombo)) {
			fReportPreviewCombo.setEnabled(fEnablePreviewCheck.isEnabled() && fEnablePreviewCheck.getSelection());
		}
	}

	private void updatePreviewFeaturesState() {
		if (checkValue(INTR_DEFAULT_COMPLIANCE, USER_CONF)) {
			String compatibility= getValue(PREF_SOURCE_COMPATIBILITY);

			boolean isLessThanLatest= JavaModelUtil.isVersionLessThan(compatibility, VERSION_LATEST);
			updateRememberedComplianceOption(PREF_ENABLE_PREVIEW, IDX_ENABLE_PREVIEW, !isLessThanLatest, null);
			updateRememberedComplianceOption(PREF_PB_REPORT_PREVIEW, IDX_REPORT_PREVIEW, fEnablePreviewCheck.isEnabled() && fEnablePreviewCheck.getSelection(), WARNING);
		}
	}

	private void updatePreviewControls() {
		String compliance= getValue(PREF_COMPLIANCE);
		if (JavaCore.compareJavaVersions(compliance, VERSION_LATEST) < 0) {
			fEnablePreviewCheck.setSelection(false);
			fReportPreviewCombo.select(0);
		}
	}

	private void updateAssertEnumAsIdentifierEnableState() {
		if (checkValue(INTR_DEFAULT_COMPLIANCE, USER_CONF)) {
			String compatibility= getValue(PREF_SOURCE_COMPATIBILITY);

			boolean isLessThan14= VERSION_1_3.equals(compatibility);
			updateRememberedComplianceOption(PREF_PB_ASSERT_AS_IDENTIFIER, IDX_ASSERT_AS_IDENTIFIER, isLessThan14, ERROR);

			boolean isLessThan15= isLessThan14 || VERSION_1_4.equals(compatibility);
			updateRememberedComplianceOption(PREF_PB_ENUM_AS_IDENTIFIER, IDX_ENUM_AS_IDENTIFIER, isLessThan15, ERROR);
		}
	}

	private void updateRememberedComplianceOption(Key prefKey, int idx, boolean enabled, String defaultComboValue) {
		if (prefKey.getName().equals(PREF_ENABLE_PREVIEW.getName())) {
			Button checkBox= getCheckBox(prefKey);
			boolean wasCheckBoxEnabled= checkBox.isEnabled();
			checkBox.setEnabled(enabled);

			if (enabled) {
				if (!wasCheckBoxEnabled) {
					String val= fRememberedUserCompliance[idx];
					if (ENABLED.equals(val)) {
						setValue(PREF_ENABLE_PREVIEW, val);
						updateCheckBox(checkBox);
					}
				}
			} else {
				String val= getValue(PREF_ENABLE_PREVIEW);
				if (wasCheckBoxEnabled) {
					fRememberedUserCompliance[idx]= val;
				}

				if (ENABLED.equals(val)) {
					setValue(PREF_ENABLE_PREVIEW, DISABLED);
					updateCheckBox(checkBox);
				}
			}

		} else {
			Combo combo= getComboBox(prefKey);
			combo.setEnabled(enabled);

			if (!enabled) {
				String val= getValue(prefKey);
				if (!defaultComboValue.equals(val)) {
					setValue(prefKey, defaultComboValue);
					updateCombo(combo);
					fRememberedUserCompliance[idx]= val;
				}
			} else {
				String val= fRememberedUserCompliance[idx];
				if (!defaultComboValue.equals(val)) {
					setValue(prefKey, val);
					updateCombo(combo);
				}
			}
		}

	}

	private void updateInlineJSREnableState() {
		String target= getValue(PREF_CODEGEN_TARGET_PLATFORM);

		boolean enabled= JavaModelUtil.isVersionLessThan(target, VERSION_1_5);
		Button checkBox= getCheckBox(PREF_CODEGEN_INLINE_JSR_BYTECODE);
		boolean wasCheckBoxEnabled= checkBox.isEnabled();
		checkBox.setEnabled(enabled);

		if (!enabled) {
			String val= getValue(PREF_CODEGEN_INLINE_JSR_BYTECODE);
			if (wasCheckBoxEnabled)
				fRememberedUserCompliance[IDX_INLINE_JSR_BYTECODE]= val;

			if (!ENABLED.equals(val)) {
				setValue(PREF_CODEGEN_INLINE_JSR_BYTECODE, ENABLED);
				updateCheckBox(checkBox);
			}
		} else {
			if (!wasCheckBoxEnabled) {
				String val= fRememberedUserCompliance[IDX_INLINE_JSR_BYTECODE];
				if (!ENABLED.equals(val)) {
					setValue(PREF_CODEGEN_INLINE_JSR_BYTECODE, val);
					updateCheckBox(checkBox);
				}
			}
		}
	}

	private void updateStoreMethodParamNamesEnableState() {
		String target= getValue(PREF_CODEGEN_TARGET_PLATFORM);
		boolean enabled= JavaModelUtil.is1d8OrHigher(target);
		Button checkBox= getCheckBox(PREF_CODEGEN_METHOD_PARAMETERS_ATTR);
		boolean wasCheckBoxEnabled= checkBox.isEnabled();
		checkBox.setEnabled(enabled);

		if (enabled) {
			if (!wasCheckBoxEnabled) {
				String val= fRememberedUserCompliance[IDX_METHOD_PARAMETERS_ATTR];
				if (GENERATE.equals(val)) {
					setValue(PREF_CODEGEN_METHOD_PARAMETERS_ATTR, val);
					updateCheckBox(checkBox);
				}
			}
		} else {
			String val= getValue(PREF_CODEGEN_METHOD_PARAMETERS_ATTR);
			if (wasCheckBoxEnabled)
				fRememberedUserCompliance[IDX_METHOD_PARAMETERS_ATTR]= val;

			if (GENERATE.equals(val)) {
				setValue(PREF_CODEGEN_METHOD_PARAMETERS_ATTR, DO_NOT_GENERATE);
				updateCheckBox(checkBox);
			}
		}
	}

	/**
	 * Sets the default compliance values derived from the chosen level or restores the user
	 * compliance settings.
	 *
	 * @param rememberOld if <code>true</code>, the current compliance settings are remembered as
	 *            user settings. If <code>false</code>, overwrite the current settings.
	 * @param oldComplianceLevel the previous compliance level
	 */
	private void updateComplianceDefaultSettings(boolean rememberOld, String oldComplianceLevel) {
		String enablePreview, reportPreview, assertAsId, enumAsId, source, target;
		boolean isDefault= checkValue(INTR_DEFAULT_COMPLIANCE, DEFAULT_CONF);
		boolean isFollowEE= checkValue(INTR_COMPLIANCE_FOLLOWS_EE, DEFAULT_CONF);
		String complianceLevel= getValue(PREF_COMPLIANCE);
		boolean isRelease= checkValue(PREF_RELEASE, JavaCore.ENABLED) && JavaModelUtil.is1d6OrHigher(complianceLevel) && !isDefault;

		if (isDefault || isFollowEE || isRelease) {
			if (rememberOld) {
				if (oldComplianceLevel == null) {
					oldComplianceLevel= complianceLevel;
				}

				fRememberedUserCompliance[IDX_ENABLE_PREVIEW]= getValue(PREF_ENABLE_PREVIEW);
				fRememberedUserCompliance[IDX_REPORT_PREVIEW]= getValue(PREF_PB_REPORT_PREVIEW);
				fRememberedUserCompliance[IDX_ASSERT_AS_IDENTIFIER]= getValue(PREF_PB_ASSERT_AS_IDENTIFIER);
				fRememberedUserCompliance[IDX_ENUM_AS_IDENTIFIER]= getValue(PREF_PB_ENUM_AS_IDENTIFIER);
				fRememberedUserCompliance[IDX_SOURCE_COMPATIBILITY]= getValue(PREF_SOURCE_COMPATIBILITY);
				fRememberedUserCompliance[IDX_CODEGEN_TARGET_PLATFORM]= getValue(PREF_CODEGEN_TARGET_PLATFORM);
				fRememberedUserCompliance[IDX_RELEASE]= getValue(PREF_RELEASE);
				fRememberedUserCompliance[IDX_COMPLIANCE]= oldComplianceLevel;
			}

			if (isFollowEE) {
				IExecutionEnvironment ee= getEE();
				Map<String, String> eeOptions= BuildPathSupport.getEEOptions(ee);
				if (eeOptions == null)
					return;

				enablePreview= eeOptions.get(PREF_ENABLE_PREVIEW.getName());
				reportPreview= eeOptions.get(PREF_PB_REPORT_PREVIEW.getName());
				assertAsId= eeOptions.get(PREF_PB_ASSERT_AS_IDENTIFIER.getName());
				enumAsId= eeOptions.get(PREF_PB_ENUM_AS_IDENTIFIER.getName());
				source= eeOptions.get(PREF_SOURCE_COMPATIBILITY.getName());
				target= eeOptions.get(PREF_CODEGEN_TARGET_PLATFORM.getName());

				setValue(PREF_COMPLIANCE, eeOptions.get(PREF_COMPLIANCE.getName()));
				String inlineJSR= eeOptions.get(PREF_CODEGEN_INLINE_JSR_BYTECODE.getName());
				if (inlineJSR != null) {
					setValue(PREF_CODEGEN_INLINE_JSR_BYTECODE, inlineJSR);
				}
				String release= eeOptions.get(PREF_RELEASE.getName());
				if (release == null) {
					setValue(PREF_RELEASE, DISABLED);
					fCompilerReleaseCheck.setSelection(false);
				}

			} else if (isRelease) {
				enablePreview= getValue(PREF_ENABLE_PREVIEW);
				reportPreview= getValue(PREF_PB_REPORT_PREVIEW);
				source= getValue(PREF_COMPLIANCE);
				target= getValue(PREF_COMPLIANCE);
				assertAsId= getValue(PREF_PB_ASSERT_AS_IDENTIFIER);
				enumAsId= getValue(PREF_PB_ENUM_AS_IDENTIFIER);
			} else {
				HashMap<String, String> options= new HashMap<>();
				JavaModelUtil.setComplianceOptions(options, complianceLevel);
				if (complianceLevel.equals(options.get(JavaCore.COMPILER_COMPLIANCE))) {
					enablePreview= options.get(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES);
					reportPreview= options.get(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES);
					source= options.get(JavaCore.COMPILER_SOURCE);
					target= options.get(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM);
					assertAsId= options.get(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER);
					enumAsId= options.get(JavaCore.COMPILER_PB_ENUM_IDENTIFIER);
				} else {
					enablePreview= DISABLED;
					reportPreview= WARNING;
					assertAsId= IGNORE;
					enumAsId= IGNORE;
					source= VERSION_1_3;
					target= VERSION_1_1;
				}
			}
		} else {
			if (rememberOld && complianceLevel.equals(fRememberedUserCompliance[IDX_COMPLIANCE])) {
				enablePreview= fRememberedUserCompliance[IDX_ENABLE_PREVIEW];
				reportPreview= fRememberedUserCompliance[IDX_REPORT_PREVIEW];
				assertAsId= fRememberedUserCompliance[IDX_ASSERT_AS_IDENTIFIER];
				enumAsId= fRememberedUserCompliance[IDX_ENUM_AS_IDENTIFIER];
				source= fRememberedUserCompliance[IDX_SOURCE_COMPATIBILITY];
				target= fRememberedUserCompliance[IDX_CODEGEN_TARGET_PLATFORM];
			} else {
				updateInlineJSREnableState();
				updatePreviewFeaturesState();
				updateAssertEnumAsIdentifierEnableState();
				updateStoreMethodParamNamesEnableState();
				return;
			}
		}
		if (enablePreview == null) {
			enablePreview= DISABLED;
		}
		if (reportPreview == null) {
			reportPreview= WARNING;
		}
		setValue(PREF_ENABLE_PREVIEW, enablePreview);
		setValue(PREF_PB_REPORT_PREVIEW, reportPreview);
		setValue(PREF_PB_ASSERT_AS_IDENTIFIER, assertAsId);
		setValue(PREF_PB_ENUM_AS_IDENTIFIER, enumAsId);
		setValue(PREF_SOURCE_COMPATIBILITY, source);
		setValue(PREF_CODEGEN_TARGET_PLATFORM, target);
		updateControls();
		updateInlineJSREnableState();
		updatePreviewFeaturesState();
		updateAssertEnumAsIdentifierEnableState();
		updateStoreMethodParamNamesEnableState();
	}

	private void updateComplianceReleaseSettings() {
		String compliance= getValue(PREF_COMPLIANCE);
		boolean isRelease= checkValue(PREF_RELEASE, JavaCore.ENABLED) && JavaModelUtil.is1d6OrHigher(compliance);
		if (isRelease) {
			setValue(PREF_SOURCE_COMPATIBILITY, compliance);
			setValue(PREF_CODEGEN_TARGET_PLATFORM, compliance);
		}
	}

	/**
	 * Evaluate if the current compliance setting correspond to a default setting.
	 *
	 * @return {@link #DEFAULT_CONF} or {@link #USER_CONF}
	 */
	private String getCurrentCompliance() {
		String complianceLevel= getValue(PREF_COMPLIANCE);

		HashMap<String, String> defaultOptions= new HashMap<>();
		JavaModelUtil.setComplianceOptions(defaultOptions, complianceLevel);

		boolean isDefault= complianceLevel.equals(defaultOptions.get(JavaCore.COMPILER_COMPLIANCE))
				&& getValue(PREF_SOURCE_COMPATIBILITY).equals(defaultOptions.get(JavaCore.COMPILER_SOURCE))
				&& getValue(PREF_CODEGEN_TARGET_PLATFORM).equals(defaultOptions.get(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM))
				&& getValue(PREF_PB_ASSERT_AS_IDENTIFIER).equals(defaultOptions.get(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER))
				&& getValue(PREF_PB_ENUM_AS_IDENTIFIER).equals(defaultOptions.get(JavaCore.COMPILER_PB_ENUM_IDENTIFIER));
		if (JavaCore.compareJavaVersions(complianceLevel, JavaCore.VERSION_10) > 0) {
			isDefault= isDefault
					&& getValue(PREF_ENABLE_PREVIEW).equals(defaultOptions.get(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES))
					&& getValue(PREF_PB_REPORT_PREVIEW).equals(defaultOptions.get(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES));
		}
		return isDefault ? DEFAULT_CONF : USER_CONF;
	}

	private IExecutionEnvironment getEE() {
		if (fProject == null)
			return null;

		try {
			for (IClasspathEntry entry : JavaCore.create(fProject).getRawClasspath()) {
				if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
					String eeId= JavaRuntime.getExecutionEnvironmentId(entry.getPath());
					if (eeId != null) {
						return JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(eeId);
					}
				}
			}
		} catch (CoreException e) {
			JavaPlugin.log(e);
		}
		return null;
	}

	/**
	 * Evaluate if the builds path contains an execution environment and the current compliance
	 * settings follow the EE options.
	 *
	 * @param ee the EE, or <code>null</code> if none available
	 * @return {@link #DEFAULT_CONF} if the compliance follows the EE, or {@link #USER_CONF} if the
	 *         settings differ, or {@link #DISABLED} if there's no EE at all
	 */
	private String getComplianceFollowsEE(IExecutionEnvironment ee) {
		Map<String, String> options= BuildPathSupport.getEEOptions(ee);
		if (options == null)
			return DISABLED;
		String complianceOption= options.get(JavaCore.COMPILER_COMPLIANCE);
		if (JavaCore.compareJavaVersions(complianceOption, JavaCore.VERSION_10) > 0) {
			return checkDefaults(PREFS_COMPLIANCE_11_OR_HIGHER, options);
		} else {
			return checkDefaults(PREFS_COMPLIANCE, options);
		}
	}

	private String checkDefaults(Key[] keys, Map<String, String> options) {
		for (Key key : keys) {
			Object option= options.get(key.getName());
			if (!checkValue(key, (String)option))
				return USER_CONF;
		}
		return DEFAULT_CONF;
	}

	@Override
	protected String[] getFullBuildDialogStrings(boolean workspaceSettings) {
		String title= PreferencesMessages.ComplianceConfigurationBlock_needsbuild_title;
		String message;
		if (workspaceSettings) {
			message= PreferencesMessages.ComplianceConfigurationBlock_needsfullbuild_message;
		} else {
			message= PreferencesMessages.ComplianceConfigurationBlock_needsprojectbuild_message;
		}
		return new String[] { title, message };
	}

	/**
	 * Sets the default compiler compliance options based on the current default JRE in the
	 * workspace.
	 *
	 * @since 3.5
	 */
	private void setDefaultCompilerComplianceValues() {
		IVMInstall defaultVMInstall= JavaRuntime.getDefaultVMInstall();
		if (defaultVMInstall instanceof IVMInstall2) {
			String complianceLevel= JavaModelUtil.getCompilerCompliance((IVMInstall2)defaultVMInstall, JavaCore.VERSION_1_4);
			if (isOriginalDefaultCompliance(complianceLevel)) {
				Map<String, String> complianceOptions= new HashMap<>();
				JavaModelUtil.setComplianceOptions(complianceOptions, complianceLevel);
				String releaseVal= complianceOptions.get(PREF_RELEASE.getName());
				setDefaultValue(PREF_COMPLIANCE, complianceOptions.get(PREF_COMPLIANCE.getName()));
				setDefaultValue(PREF_PB_ASSERT_AS_IDENTIFIER, complianceOptions.get(PREF_PB_ASSERT_AS_IDENTIFIER.getName()));
				setDefaultValue(PREF_PB_ENUM_AS_IDENTIFIER, complianceOptions.get(PREF_PB_ENUM_AS_IDENTIFIER.getName()));
				setDefaultValue(PREF_SOURCE_COMPATIBILITY, complianceOptions.get(PREF_SOURCE_COMPATIBILITY.getName()));
				setDefaultValue(PREF_CODEGEN_TARGET_PLATFORM, complianceOptions.get(PREF_CODEGEN_TARGET_PLATFORM.getName()));
				setDefaultValue(PREF_RELEASE, releaseVal != null ? releaseVal : DISABLED);
				if (JavaCore.compareJavaVersions(complianceLevel, JavaCore.VERSION_10) > 0) {
					setDefaultValue(PREF_ENABLE_PREVIEW, complianceOptions.get(PREF_ENABLE_PREVIEW.getName()));
					setDefaultValue(PREF_PB_REPORT_PREVIEW, complianceOptions.get(PREF_PB_REPORT_PREVIEW.getName()));
				} else {
					setDefaultValue(PREF_ENABLE_PREVIEW, DISABLED);
					setDefaultValue(PREF_PB_REPORT_PREVIEW, WARNING);
				}
			}
		}
	}

	/**
	 * Tells whether the compliance option is the same as the original default.
	 * @param complianceLevel the compliance level
	 *
	 * @return <code>true</code> if the compliance is the same as the original default
	 * @since 3.6
	 */
	private static final boolean isOriginalDefaultCompliance(String complianceLevel) {
		Hashtable<String, String> options= JavaCore.getDefaultOptions();
		Preferences bundleDefaults= BundleDefaultsScope.INSTANCE.getNode(JavaCore.PLUGIN_ID);

		boolean isDefault= equals(JavaCore.COMPILER_COMPLIANCE, bundleDefaults, options)
				&& equals(JavaCore.COMPILER_SOURCE, bundleDefaults, options)
				&& equals(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, bundleDefaults, options)
				&& equals(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER, bundleDefaults, options)
				&& equals(JavaCore.COMPILER_PB_ENUM_IDENTIFIER, bundleDefaults, options)
				&& equals(JavaCore.COMPILER_RELEASE, bundleDefaults, options);
		if (JavaCore.compareJavaVersions(complianceLevel, JavaCore.VERSION_10) > 0) {
			isDefault= isDefault
					&& equals(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, bundleDefaults, options)
					&& equals(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, bundleDefaults, options);
		}
		return isDefault;
	}

	/**
	 * Returns whether the option for the given key is the same in the map and the preferences.
	 *
	 * @param key the key of option to test
	 * @param preferences the preferences
	 * @param map the map
	 * @return <code>true</code> if the options are the same in both maps
	 * @since 3.6
	 */
	private static boolean equals(String key, Preferences preferences, Map<String, String> map) {
		String dummy= ""; //$NON-NLS-1$
		String defaultValue= preferences.get(key, dummy);
		return defaultValue != null && defaultValue != dummy
				? map.containsKey(key) && equals(defaultValue, map.get(key))
				: !map.containsKey(key);
	}

	/**
	 * Returns whether the objects are equal.
	 *
	 * @param o1 an object
	 * @param o2 an object
	 * @return <code>true</code> if the two objects are equal
	 * @since 3.6
	 */
	private static boolean equals(Object o1, Object o2) {
		return o1 == null ? o2 == null : o1.equals(o2);
	}

}
