Java tutorial
/******************************************************************************* * Copyright (c) 2007-2010 Exadel, Inc. and Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Exadel, Inc. and Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.jst.jsp.i18n; import java.util.HashSet; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.DialogPage; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; 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.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.wst.xml.core.internal.document.AttrImpl; import org.eclipse.wst.xml.core.internal.document.TextImpl; import org.jboss.tools.common.model.loaders.impl.EncodedProperties; import org.jboss.tools.common.model.ui.ModelUIImages; import org.jboss.tools.jst.jsp.JspEditorPlugin; import org.jboss.tools.jst.jsp.bundle.BundleMap; import org.jboss.tools.jst.jsp.bundle.BundleMap.BundleEntry; import org.jboss.tools.jst.jsp.messages.JstUIMessages; import org.jboss.tools.jst.jsp.util.Constants; import org.w3c.dom.Attr; public class ExternalizeStringsWizardPage extends WizardPage { public static final String PAGE_NAME = "ExternalizeStringsWizardBasicPage"; //$NON-NLS-1$ private final int DIALOG_WIDTH = 450; private final int DIALOG_HEIGHT = 650; private Text propsKey; private Text propsValue; private Button newFile; private Label propsFileLabel; private Text propsFile; private Label rbListLabel; private Combo rbCombo; private BundleMap bm; private Group propsFilesGroup; private Status propsKeyStatus; private Status propsValueStatus; private Status duplicateKeyStatus; private Status duplicateValueStatus; private Table tagsTable; private IDocument document; private ISelectionProvider selectionProvider; /** * Creates the wizard page * * @param pageName * the name of the page * @param editor * the source text editor * @param bm * bundle map, or not null */ public ExternalizeStringsWizardPage(String pageName, BundleMap bm, IDocument document, ISelectionProvider selectionProvider) { /* * Setting dialog Title, Description, Image. */ super(pageName, JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_TITLE, ModelUIImages.getImageDescriptor(ModelUIImages.WIZARD_DEFAULT)); setDescription(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_DESCRIPTION); setPageComplete(false); this.bm = bm; this.document = document; this.selectionProvider = selectionProvider; propsKeyStatus = new Status(IStatus.OK, JspEditorPlugin.PLUGIN_ID, Constants.EMPTY); propsValueStatus = new Status(IStatus.OK, JspEditorPlugin.PLUGIN_ID, Constants.EMPTY); duplicateKeyStatus = new Status(IStatus.OK, JspEditorPlugin.PLUGIN_ID, Constants.EMPTY); duplicateValueStatus = new Status(IStatus.OK, JspEditorPlugin.PLUGIN_ID, Constants.EMPTY); } public void createControl(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(1, false)); GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); gd.widthHint = DIALOG_WIDTH; gd.heightHint = DIALOG_HEIGHT; composite.setLayoutData(gd); /* * Create properties string group */ Group propsStringGroup = new Group(composite, SWT.SHADOW_ETCHED_IN); propsStringGroup.setLayout(new GridLayout(3, false)); propsStringGroup.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 1, 1)); propsStringGroup.setText(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_PROPS_STRINGS_GROUP); /* * Create Properties Key label */ Label propsKeyLabel = new Label(propsStringGroup, SWT.NONE); propsKeyLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.NONE, false, false, 1, 1)); propsKeyLabel.setText(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_PROPERTIES_KEY); /* * Create Properties Key value */ propsKey = new Text(propsStringGroup, SWT.BORDER); propsKey.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 2, 1)); propsKey.setText(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_DEFAULT_KEY); /* * Create Properties Value label */ Label propsValueLabel = new Label(propsStringGroup, SWT.NONE); propsValueLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.NONE, false, false, 1, 1)); propsValueLabel.setText(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_PROPERTIES_VALUE); /* * Create Properties Value value */ propsValue = new Text(propsStringGroup, SWT.BORDER); propsValue.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 2, 1)); propsValue.setText(Constants.EMPTY); /* * Create New File Checkbox */ newFile = new Button(composite, SWT.CHECK); newFile.setLayoutData(new GridData(SWT.LEFT, SWT.NONE, false, false, 1, 1)); newFile.setText(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_NEW_FILE); /* * Create properties string group */ propsFilesGroup = new Group(composite, SWT.SHADOW_ETCHED_IN); propsFilesGroup.setLayout(new GridLayout(3, false)); gd = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1); gd.heightHint = 300; propsFilesGroup.setLayoutData(gd); propsFilesGroup.setText(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_PROPS_FILES_GROUP); /* * Create Resource Bundles List label */ rbListLabel = new Label(propsFilesGroup, SWT.NONE); rbListLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.NONE, false, false, 1, 1)); rbListLabel.setText(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_RESOURCE_BUNDLE_LIST); /* * Create Resource Bundles combobox */ rbCombo = new Combo(propsFilesGroup, SWT.NONE); rbCombo.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 2, 1)); /* * Create Properties File label */ propsFileLabel = new Label(propsFilesGroup, SWT.NONE); propsFileLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.NONE, false, false, 1, 1)); propsFileLabel.setText(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_PROPERTIES_FILE); /* * Create Properties File path field */ propsFile = new Text(propsFilesGroup, SWT.BORDER); propsFile.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false, 2, 1)); propsFile.setText(Constants.EMPTY); propsFile.setEditable(false); /* * Create properties file table of content */ tagsTable = ExternalizeStringsUtils.createPropertiesTable(propsFilesGroup, SWT.BORDER); /* * Initialize all fields with real values. */ initializeFieldsAndAddLIsteners(); /* * Wizard Page control should be initialized. */ setControl(composite); } /** * Gets the bundle prefix. * * @return the bundle prefix */ public String getBundlePrefix() { String bundlePrefix = Constants.EMPTY; if (!isNewFile()) { for (BundleEntry be : bm.getBundles()) { if (be.uri.equalsIgnoreCase(rbCombo.getText())) { bundlePrefix = be.prefix; } } } return bundlePrefix; } /** * Gets resource bundle's file * @return the file */ public IFile getBundleFile() { return bm.getBundleFile(rbCombo.getText()); } /** * Use existed key-value pair from the properties file * without writing any data to the file. * * @return */ public boolean isDuplicatedKeyAndValue() { boolean exists = false; if (isValueDuplicated(propsValue.getText()) && isKeyDuplicated(propsKey.getText())) { exists = true; } return exists; } /** * Gets <code>key=value</code> pair * * @return a pair <code>key=value</code> */ public String getKeyValuePair() { /* * While initializing 'propsValue' field \t\r\n were replaced by \\\\t, etc. * Now we should return escaped characters, and during saving tha properties * they will treated as escape symbols as well. * Otherwise \\\\t string will be put into the properties file. */ String value = propsValue.getText(); if (value != null) { value = value.replaceAll("\\\\t", "\t"); //$NON-NLS-1$ //$NON-NLS-2$ value = value.replaceAll("\\\\r", "\r"); //$NON-NLS-1$ //$NON-NLS-2$ value = value.replaceAll("\\\\n", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ } return propsKey.getText() + Constants.EQUAL + EncodedProperties.saveConvert(value, true); } /** * Gets the key. * * @return the key */ public String getKey() { return propsKey.getText(); } /** * Check if "Create new file.." option is enabled * * @return the status */ public boolean isNewFile() { return newFile.getSelection(); } /** * Replaces the text in the current file */ public void replaceText(String replacement) { IDocument doc = getDocument(); ISelection sel = getSelectionProvider().getSelection(); if (ExternalizeStringsUtils.isSelectionCorrect(sel)) { try { /* * Get source text and new text */ TextSelection textSel = (TextSelection) sel; IStructuredSelection structuredSelection = (IStructuredSelection) sel; Object firstElement = structuredSelection.getFirstElement(); int offset = 0; int length = 0; /* * When user selection is empty * underlying node will e automatically selected. * Thus we need to correct replacement offsets. */ if ((textSel.getLength() != 0)) { offset = textSel.getOffset(); length = textSel.getLength(); } else if (firstElement instanceof TextImpl) { TextImpl ti = (TextImpl) firstElement; offset = ti.getStartOffset(); length = ti.getLength(); } else if (firstElement instanceof AttrImpl) { AttrImpl ai = (AttrImpl) firstElement; /* * Get offset and length without quotes ".." */ offset = ai.getValueRegionStartOffset() + 1; length = ai.getValueRegionText().length() - 2; } /* * Replace text in the editor with "key.value" */ doc.replace(offset, length, replacement); } catch (BadLocationException ex) { JspEditorPlugin.getPluginLog().logError(ex); } } } @Override public boolean isPageComplete() { boolean isPageComplete = false; /* * The page is ready when there are no error messages * and the bundle is selected * and "key=value" exists. */ if ((getErrorMessage() == null) && !Constants.EMPTY.equalsIgnoreCase(propsKey.getText().trim()) && !Constants.EMPTY.equalsIgnoreCase(propsValue.getText().trim()) && ((rbCombo.getSelectionIndex() != -1) || isNewFile())) { isPageComplete = true; } return isPageComplete; } @Override public boolean canFlipToNextPage() { return isPageComplete() && (getNextPage() != null) && isNewFile(); } /** * Initialize dialog's controls. * Fill in appropriate text and make validation. */ private void initializeFieldsAndAddLIsteners() { ISelection sel = getSelectionProvider().getSelection(); if (ExternalizeStringsUtils.isSelectionCorrect(sel)) { String text = Constants.EMPTY; String stringToUpdate = Constants.EMPTY; TextSelection textSelection = null; IStructuredSelection structuredSelection = (IStructuredSelection) sel; textSelection = (TextSelection) sel; text = textSelection.getText(); Object selectedElement = structuredSelection.getFirstElement(); /* * When selected text is empty * parse selected element and find a string to replace.. */ if ((text.trim().length() == 0)) { if (selectedElement instanceof org.w3c.dom.Text) { /* * ..it could be a plain text */ org.w3c.dom.Text textNode = (org.w3c.dom.Text) selectedElement; if (textNode.getNodeValue().trim().length() > 0) { stringToUpdate = textNode.getNodeValue(); getSelectionProvider().setSelection(new StructuredSelection(stringToUpdate)); } } else if (selectedElement instanceof Attr) { /* * ..or an attribute's value */ Attr attrNode = (Attr) selectedElement; if (attrNode.getNodeValue().trim().length() > 0) { stringToUpdate = attrNode.getNodeValue(); getSelectionProvider().setSelection(new StructuredSelection(stringToUpdate)); } } if ((stringToUpdate.trim().length() > 0)) { text = stringToUpdate; } } /* * https://issues.jboss.org/browse/JBIDE-9203 * Replace special characters with their string representation. * Key should be generated first. */ propsKey.setText(ExternalizeStringsUtils.generatePropertyKey(text)); /* * Replaced escaped symbols by strings. */ String value = text; if (value != null) { value = value.replaceAll("\t", "\\\\t"); //$NON-NLS-1$ //$NON-NLS-2$ value = value.replaceAll("\r", "\\\\r"); //$NON-NLS-1$ //$NON-NLS-2$ value = value.replaceAll("\n", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-2$ } propsValue.setText(value); /* * Initialize bundle messages field */ if (bm == null) { JspEditorPlugin.getDefault().logWarning(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_RB_IS_MISSING); } else { BundleEntry[] bundles = bm.getBundles(); Set<String> uriSet = new HashSet<String>(); for (BundleEntry bundleEntry : bundles) { if (!uriSet.contains(bundleEntry.uri)) { uriSet.add(bundleEntry.uri); rbCombo.add(bundleEntry.uri); } } /* * Select the first bundle if there is any in the list */ if (rbCombo.getItemCount() > 0) { rbCombo.select(0); setResourceBundlePath(rbCombo.getText()); } else { /* * https://jira.jboss.org/browse/JBIDE-7247 * Select 'Create new file' checkbox and * disable bundle group if no bundles are found. */ newFile.setSelection(true); enableBundleGroup(false); } } /* * Check the initial value status * When the same value is already externalized -- * suggest to use already created key as well. */ updatePropertiesValueStatus(); updateDuplicateValueStatus(); /* * When selected text is fine perform further validation */ if (propsValueStatus.isOK()) { /* * Check the bundle for the value in it. * if so -- show the warning and set correct key. */ if (!duplicateValueStatus.isOK()) { /* * Set the same key value */ propsKey.setText(getValueForKey(propsValue.getText())); /* * Set the new warning message */ applyStatus(this, new IStatus[] { duplicateValueStatus }); } else { /* * Check the initial key status * If there is the error - add sequence number to the key */ updateDuplicateKeyStatus(); while (!duplicateKeyStatus.isOK()) { int index = propsKey.getText().lastIndexOf('_'); String newKey = Constants.EMPTY; if (index != -1) { /* * String sequence at the end should be checked. * If it is a sequence number - it should be increased by 1. * If not - new number should be added. */ String numberString = propsKey.getText().substring(index + 1); int number; try { number = Integer.parseInt(numberString); number++; newKey = propsKey.getText().substring(0, index + 1) + number; } catch (NumberFormatException e) { newKey = propsKey.getText() + "_1"; //$NON-NLS-1$ } } else { /* * If the string has no sequence number - add it. */ newKey = propsKey.getText() + "_1"; //$NON-NLS-1$ } /* * Set the new key text */ propsKey.setText(newKey); updateDuplicateKeyStatus(); } /* * https://jira.jboss.org/browse/JBIDE-6945 * Set the greeting message only. * All the validation will take place in the fields' listeners * after user enters some new values. */ setMessage(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_ENTER_KEY_NAME, IMessageProvider.INFORMATION); } } else { /* * Set the message about wrong selected text. */ applyStatus(this, new IStatus[] { propsValueStatus }); } /* * Update the Buttons state. * When all the fields are correct -- * then user should be able to press OK */ setPageComplete(isPageComplete()); /* * Add selection listeners to the fields */ propsKey.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { updateStatus(); } }); propsKey.addVerifyListener(new VerifyListener() { public void verifyText(VerifyEvent e) { for (int i = 0; i < ExternalizeStringsUtils.REPLACED_CHARACTERS.length; i++) { /* * Entering of the forbidden characters will be prevented. * https://issues.jboss.org/browse/JBIDE-11551 * Dot(.) should be supported. */ if ((e.character == ExternalizeStringsUtils.REPLACED_CHARACTERS[i]) && (e.character != '.')) { e.doit = false; break; } } } }); propsValue.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { updateStatus(); } }); newFile.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { boolean selected = ((Button) e.getSource()).getSelection(); if (selected) { enableBundleGroup(false); } else { enableBundleGroup(true); } updateStatus(); } }); rbCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { setResourceBundlePath(rbCombo.getText()); updateDuplicateKeyStatus(); updateStatus(); } }); } else { JspEditorPlugin.getDefault().logWarning(JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_INITIALIZATION_ERROR); } } /** * Checks keys in the selected resource bundle. * * @param key the key name * @return <code>true</code> if there is a key with the specified name */ private boolean isKeyDuplicated(String key) { boolean isDuplicated = false; if ((tagsTable.getItemCount() > 0) && (null != key) && !isNewFile()) { TableItem[] items = tagsTable.getItems(); for (TableItem tableItem : items) { if (key.equalsIgnoreCase(tableItem.getText(0))) { isDuplicated = true; break; } } } return isDuplicated; } /** * Checks values in the selected resource bundle. * * @param value the text string to externalize * @return <code>true</code> if there is a key with the specified name */ private boolean isValueDuplicated(String value) { boolean isDuplicated = false; if ((tagsTable.getItemCount() > 0) && (null != value) && !isNewFile()) { TableItem[] items = tagsTable.getItems(); for (TableItem tableItem : items) { if (value.equalsIgnoreCase(tableItem.getText(1))) { isDuplicated = true; break; } } } return isDuplicated; } /** * Returns the key for the specified value * from the visual table. * * @param value - the value * @return the key or empty string */ private String getValueForKey(String value) { String key = Constants.EMPTY; if ((tagsTable.getItemCount() > 0) && (null != value) && !isNewFile()) { TableItem[] items = tagsTable.getItems(); for (TableItem tableItem : items) { if (value.equalsIgnoreCase(tableItem.getText(1))) { key = tableItem.getText(0); break; } } } return key; } /** * Enables or disables resource bundle information * * @param enabled shows the status */ private void enableBundleGroup(boolean enabled) { propsFilesGroup.setEnabled(enabled); propsFileLabel.setEnabled(enabled); propsFile.setEnabled(enabled); rbListLabel.setEnabled(enabled); rbCombo.setEnabled(enabled); tagsTable.setEnabled(enabled); } /** * Update duplicate key status. */ private void updateDuplicateKeyStatus() { if (isKeyDuplicated(propsKey.getText())) { duplicateKeyStatus = new Status(IStatus.ERROR, JspEditorPlugin.PLUGIN_ID, JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_KEY_ALREADY_EXISTS); } else { duplicateKeyStatus = new Status(IStatus.OK, JspEditorPlugin.PLUGIN_ID, Constants.EMPTY); } } /** * Update duplicate key status. */ private void updateDuplicateValueStatus() { if (isValueDuplicated(propsValue.getText())) { duplicateValueStatus = new Status(IStatus.WARNING, JspEditorPlugin.PLUGIN_ID, JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_VALUE_EXISTS); } else { duplicateValueStatus = new Status(IStatus.OK, JspEditorPlugin.PLUGIN_ID, Constants.EMPTY); } } private void updatePropertiesValueStatus() { String text = propsValue.getText(); if ((text == null) || (Constants.EMPTY.equalsIgnoreCase(text.trim())) || (text.indexOf(Constants.GT) != -1) || (text.indexOf(Constants.LT) != -1)) { propsValueStatus = new Status(IStatus.ERROR, JspEditorPlugin.PLUGIN_ID, JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_WRONG_SELECTED_TEXT); } else { propsValueStatus = new Status(IStatus.OK, JspEditorPlugin.PLUGIN_ID, Constants.EMPTY); } } /** * Update properties key status. */ private void updatePropertiesKeyStatus() { if ((propsKey.getText() == null) || (Constants.EMPTY.equalsIgnoreCase(propsKey.getText().trim()))) { propsKeyStatus = new Status(IStatus.ERROR, JspEditorPlugin.PLUGIN_ID, JstUIMessages.EXTERNALIZE_STRINGS_DIALOG_KEY_MUST_BE_SET); } else { propsKeyStatus = new Status(IStatus.OK, JspEditorPlugin.PLUGIN_ID, Constants.EMPTY); } } /** * Update page status. */ private void updateStatus() { /* * Update all statuses */ updatePropertiesKeyStatus(); updatePropertiesValueStatus(); updateDuplicateKeyStatus(); updateDuplicateValueStatus(); /* * Apply status to the dialog */ if (!duplicateValueStatus.isOK() && getValueForKey(propsValue.getText()).equalsIgnoreCase(propsKey.getText())) { applyStatus(this, new IStatus[] { propsKeyStatus, propsValueStatus, duplicateValueStatus }); } else { applyStatus(this, new IStatus[] { propsKeyStatus, propsValueStatus, duplicateKeyStatus }); } /* * Set page complete */ setPageComplete(isPageComplete()); } /** * Apply status to the dialog. * * @param page the page * @param statuses all the statuses */ private void applyStatus(DialogPage page, IStatus[] statuses) { IStatus severeStatus = statuses[0]; for (IStatus status : statuses) { severeStatus = severeStatus.getSeverity() >= status.getSeverity() ? severeStatus : status; } String message = severeStatus.getMessage(); switch (severeStatus.getSeverity()) { case IStatus.OK: page.setMessage(null, IMessageProvider.NONE); page.setErrorMessage(null); break; case IStatus.WARNING: page.setMessage(message, IMessageProvider.WARNING); page.setErrorMessage(null); break; case IStatus.INFO: page.setMessage(message, IMessageProvider.INFORMATION); page.setErrorMessage(null); break; default: if (message.length() == 0) { message = null; } page.setMessage(null); page.setErrorMessage(message); break; } } /** * Sets the resource bundle path according to the selection * from the bundles list. * * @param bundleName the resource bundle name */ private void setResourceBundlePath(String bundleName) { IFile bundleFile = bm.getBundleFile(bundleName); String bundlePath = Constants.EMPTY; if (bundleFile != null) { bundlePath = bundleFile.getFullPath().toString(); ExternalizeStringsUtils.populatePropertiesTable(tagsTable, bundleFile); } else { JspEditorPlugin.getDefault().logError("Could not get Bundle File for resource '" //$NON-NLS-1$ + bundleName + "'"); //$NON-NLS-1$ } propsFile.setText(bundlePath); } private ISelectionProvider getSelectionProvider() { return selectionProvider; } private IDocument getDocument() { return document; } }