blob: 9978d2b4eb4dece285115068f7c7de489905159b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.i18n;
import java.awt.Dialog;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.text.Caret;
import org.openide.cookies.EditorCookie;
import org.openide.DialogDescriptor;
import org.openide.loaders.DataObject;
import org.openide.NotifyDescriptor;
import org.openide.DialogDisplayer;
import org.netbeans.api.project.Project;
import org.openide.util.Exceptions;
import org.openide.util.RequestProcessor;
import org.openide.ErrorManager;
import org.openide.cookies.SaveCookie;
/**
* Manages performing of i18n action -> i18n-zation of one source.
*
* @author Peter Zavadsky
*/
public class I18nManager {
static final Logger LOG = Logger.getLogger(I18nManager.class.getName());
/** Singleton instance of I18nManager. */
private static I18nManager manager;
/** Support for this internatioanlize session. */
private I18nSupport support;
/** Weak reference to i18n panel. */
private WeakReference<I18nPanel> i18nPanelWRef = new WeakReference<I18nPanel>(null);
/** Weak reference to top component in which internationalizing will be provided. */
private WeakReference<Dialog> dialogWRef = new WeakReference<Dialog>(null);
/** Weak reference to caret in editor pane. */
private WeakReference<Caret> caretWRef;
/** Found hard coded string. */
private HardCodedString hcString;
private int replaceCount = 0;
/** Private constructor. To ge instance use <code>getI18nMananger</code> method instead. */
private I18nManager() {
}
/** Gets the only instance of I18nSupport. */
public static synchronized I18nManager getDefault() {
if (manager == null) {
manager = new I18nManager();
}
return manager;
}
/** Get i18n support. */
private void initSupport(DataObject sourceDataObject) throws IOException {
I18nSupport.Factory factory = FactoryRegistry.getFactory(sourceDataObject.getClass());
support = factory.create(sourceDataObject);
if(support == null && LOG.isLoggable(Level.SEVERE)) {
LOG.logp(Level.SEVERE, getClass().getName(),
"initSupport(DataObject)",
"I18nSupport is null for " + sourceDataObject);// NOI18N
}
}
/** The 'heart' method called by <code>I18nAction</code>. */
public void internationalize(final DataObject sourceDataObject) {
// If there is i18n action working -> cancel it.
closeDialog();
// Initilialize support.
try {
initSupport(sourceDataObject);
} catch (IOException ioe) {
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe);
return;
}
// initialize the component
final EditorCookie ec = sourceDataObject.getCookie(EditorCookie.class);
if (ec == null) {
return;
}
// Add i18n panel to top component.
getDialog(sourceDataObject);
final I18nPanel i18nPanel = i18nPanelWRef.get();
i18nPanel.showBundleMessage("TXT_SearchingForStrings"); //NOI18N
i18nPanel.getCancelButton().requestFocusInWindow();
final class SearchResultDisplayer implements Runnable {
private final boolean success;
SearchResultDisplayer(boolean success) {
this.success = success;
}
public void run() {
if (success) {
initCaret(ec);
highlightHCString();
fillDialogValues();
i18nPanel.getReplaceButton().requestFocusInWindow();
} else {
i18nPanel.showBundleMessage("TXT_NoHardcodedString");//NOI18N
i18nPanel.getCancelButton().requestFocusInWindow();
}
}
}
// do the search on background
RequestProcessor.getDefault().post(new Runnable() {
public void run() {
boolean found = find();
EventQueue.invokeLater(new SearchResultDisplayer(found));
}
});
}
/** Initializes caret. */
private void initCaret(EditorCookie ec) {
JEditorPane[] panes = ec.getOpenedPanes();
if (panes == null) {
NotifyDescriptor.Message message = new NotifyDescriptor.Message(
I18nUtil.getBundle().getString("MSG_CouldNotOpen"), //NOI18N
NotifyDescriptor.ERROR_MESSAGE);
DialogDisplayer.getDefault().notify(message);
return;
}
// Keep only weak ref to caret, the strong one maintains editor pane itself.
caretWRef = new WeakReference<Caret>(panes[0].getCaret());
}
/** Highlights found hasrdcoded string. */
private void highlightHCString() {
HardCodedString hStr = hcString;
if (hStr == null) {
return;
}
// Highlight found hard coded string.
Caret caret = caretWRef.get();
if (caret != null) {
caret.setDot(hStr.getStartPosition().getOffset());
caret.moveDot(hStr.getEndPosition().getOffset());
}
}
/** Finds hard coded string. */
private boolean find() {
// Actual find on finder.
hcString = support.getFinder().findNextHardCodedString();
if (hcString != null) {
return true;
}
// not found in entire source document
return false;
}
/** Fills values presented in internationalize dialog. */
private void fillDialogValues() {
// It has to work this way, at this time the strong reference in top component have to exist.
I18nPanel i18nPanel = i18nPanelWRef.get();
if(support == null) {
if(LOG.isLoggable(Level.SEVERE)) {
LOG.logp(Level.SEVERE, getClass().getName(),
"fillDialogValues()",
"I18nSupport is null"); // NOI18N
}
return;
}
i18nPanel.setI18nString(support.getDefaultI18nString(hcString));
showDialog();
}
/** Replaces current found hard coded string and continue the search for next one. */
private void replace() {
I18nString i18nString = null;
try {
// To call weak without check have to be save here cause strong reference in the top component have to exist.
i18nString = i18nPanelWRef.get().getI18nString();
} catch (IllegalStateException e) {
NotifyDescriptor.Message nd = new NotifyDescriptor.Message(
I18nUtil.getBundle().getString("EXC_BadKey"), //NOI18N
NotifyDescriptor.ERROR_MESSAGE);
DialogDisplayer.getDefault().notify(nd);
return;
}
// Try to add key to bundle.
support.getResourceHolder().addProperty(i18nString.getKey(), i18nString.getValue(), i18nString.getComment());
replaceCount++;
// Provide additional changes if they are available.
if (support.hasAdditionalCustomizer()) {
support.performAdditionalChanges();
}
// Replace hardcoded string.
support.getReplacer().replace(hcString, i18nString);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
skip();
}
});
}
/** Skips foudn hard coded string and conitnue to search for next one. */
private void skip() {
if (find()) {
highlightHCString();
fillDialogValues();
} else {
i18nPanelWRef.get().showBundleMessage("TXT_NoMoreStrings"); //NOI18N
i18nPanelWRef.get().getCancelButton().requestFocusInWindow();
}
}
/**
* Appends //NOI18N to the current line
*/
private void ignore() {
I18nString i18nString = null;
try {
// To call weak without check have to be save here cause strong reference in the top component have to exist.
i18nString = i18nPanelWRef.get().getI18nString();
} catch (IllegalStateException e) {
NotifyDescriptor.Message nd = new NotifyDescriptor.Message(
I18nUtil.getBundle().getString("EXC_BadKey"), //NOI18N
NotifyDescriptor.ERROR_MESSAGE);
DialogDisplayer.getDefault().notify(nd);
return;
}
i18nString.setKey(null);
support.getReplacer().replace(hcString, i18nString);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
skip();
}
});
}
/** Shows info about found hard coded string. */
private void showInfo() {
JPanel infoPanel = support.getInfo(hcString);
DialogDescriptor dd = new DialogDescriptor(
infoPanel,
I18nUtil.getBundle().getString("CTL_InfoPanelTitle")); //NOI18N
dd.setModal(true);
dd.setOptionType(DialogDescriptor.DEFAULT_OPTION);
dd.setOptions(new Object[] {DialogDescriptor.OK_OPTION});
dd.setAdditionalOptions(new Object[0]);
Dialog infoDialog = DialogDisplayer.getDefault().createDialog(dd);
infoDialog.setVisible(true);
}
/** Cancels current internationalizing session and re-layout top component to original layout. */
public void cancel() {
if (replaceCount>0) {
//Need to save resource
DataObject resource = support.getResourceHolder().getResource();
if (resource != null) {
SaveCookie save = resource.getCookie(SaveCookie.class);
if (save!=null) {
try {
save.save();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
}
replaceCount = 0;
// No memory leaks.
support = null;
if(LOG.isLoggable(Level.FINEST)) {
LOG.logp(Level.FINEST, getClass().getName(), "cancel()",
"Sets the I18nSupport to null"); // NOI18N
}
closeDialog();
}
/** Gets dialog. In our case it is a top component.
* @param name name of top component */
private void getDialog(DataObject sourceDataObject) {
Project project = Util.getProjectFor(sourceDataObject);
Dialog dialog = dialogWRef.get();
I18nPanel i18nPanel = i18nPanelWRef.get();
// Dialog was not created yet or garbaged already.
if (i18nPanel == null) {
// Create i18n panel.
i18nPanel = new I18nPanel(support.getPropertyPanel(),
project,
sourceDataObject.getPrimaryFile());
// Helper final.
final I18nPanel panel = i18nPanel;
// Set button listeners.
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
final Object source = evt.getSource();
if (source == panel.getReplaceButton()) {
replace();
} else if (source == panel.getSkipButton()) {
skip();
} else if (source == panel.getIgnoreButton()) {
ignore();
} else if (source == panel.getInfoButton()) {
showInfo();
} else if (source == panel.getCancelButton()) {
cancel();
}
}
};
i18nPanel.getReplaceButton().addActionListener(listener);
i18nPanel.getSkipButton().addActionListener(listener);
i18nPanel.getIgnoreButton().addActionListener(listener);
i18nPanel.getInfoButton().addActionListener(listener);
i18nPanel.getCancelButton().addActionListener(listener);
// Reset weak reference.
i18nPanelWRef = new WeakReference<I18nPanel>(i18nPanel);
} else {
// i18nPanel.setProject(project);
i18nPanel.setFile(sourceDataObject.getPrimaryFile());
}
// Set default i18n string.
i18nPanel.setI18nString(support.getDefaultI18nString());
i18nPanel.setDefaultResource(sourceDataObject);
if (dialog == null) {
String title = Util.getString("CTL_I18nDialogTitle"); // NOI18N
DialogDescriptor dd = new DialogDescriptor(
i18nPanel,
title,
false,
new Object[] {},
null,
DialogDescriptor.DEFAULT_ALIGN,
null,
null);
dialog = DialogDisplayer.getDefault().createDialog(dd);
dialog.setLocation(80, 80);
dialogWRef = new WeakReference<Dialog>(dialog);
}
dialog.setVisible(true);
}
/** Shows dialog. In our case opens top component if it is necessary and
* sets caret visible in editor part. */
private void showDialog() {
// Open dialog if available
Dialog dialog = dialogWRef.get();
if (dialog != null) {
dialog.setVisible(true);
}
// Set caret visible.
Caret caret = caretWRef.get();
if (caret != null) {
if (!caret.isVisible()) {
caret.setVisible(true);
}
}
}
/** Closes dialog. In our case removes <code>I18nPanel</code> from top component
* and 'reconstruct it' to it's original layout. */
private void closeDialog() {
Dialog dialog = dialogWRef.get();
if (dialog != null) {
dialog.setVisible(false);
}
}
}