| /* |
| * 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.properties; |
| |
| import java.awt.Component; |
| import java.awt.datatransfer.Transferable; |
| import java.awt.Dialog; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.text.MessageFormat; |
| import java.util.List; |
| import java.util.Locale; |
| import org.openide.DialogDescriptor; |
| import org.openide.filesystems.FileObject; |
| import org.openide.loaders.DataNode; |
| import org.openide.loaders.DataObject; |
| import org.openide.nodes.Children; |
| import org.openide.nodes.Node; |
| import org.openide.nodes.NodeTransfer; |
| import org.openide.DialogDisplayer; |
| import org.openide.NotifyDescriptor; |
| import org.openide.nodes.PropertySupport; |
| import org.openide.nodes.Sheet; |
| import org.openide.util.Exceptions; |
| import org.openide.util.Lookup; |
| import org.openide.util.WeakListeners; |
| import org.openide.util.datatransfer.NewType; |
| import org.openide.util.datatransfer.PasteType; |
| import org.openide.util.NbBundle; |
| |
| /** |
| * Node representing a <code>PropertiesDataObject</code>. |
| * Its children ({@link PropertiesLocaleNode}s) represent |
| * the {@link PropertiesFileEntry} PropertyFileEntries. |
| * |
| * @author Petr Jiricka, Peter Zavadsky |
| * @see PropertiesDataObject |
| * @see org.openide.loaders.DataNode |
| */ |
| public class PropertiesDataNode extends DataNode { |
| |
| /** |
| * Listener for changes on <code>propDataObject</code> name and cookie properties. |
| * Changes display name of components accordingly. |
| */ |
| private final transient PropertyChangeListener dataObjectListener; |
| |
| private boolean multiLocale; |
| static final String PROPERTY_ENCODING = "projectEncoding"; |
| |
| PropertiesDataNode(PropertiesDataObject propDO, Lookup lookup) { |
| this(propDO, createChildren(propDO), lookup); |
| multiLocale = propDO.isMultiLocale(); |
| } |
| |
| /** Creates data node for a given data object. |
| * The provided children object will be used to hold all child nodes. |
| * @param dataObject object to work with |
| * @param children container for the node |
| */ |
| public PropertiesDataNode(DataObject dataObject, Children children, Lookup lookup) { |
| super(dataObject, children, lookup); |
| setIconBaseWithExtension("org/netbeans/modules/properties/propertiesObject.png"); // NOI18N |
| dataObjectListener = new NameUpdater(); |
| dataObject.addPropertyChangeListener( |
| WeakListeners.propertyChange(dataObjectListener, dataObject)); |
| } |
| |
| private static Children createChildren(PropertiesDataObject propDO) { |
| return ((PropertiesFileEntry) propDO.getPrimaryEntry()).getChildren(); |
| // used to use propDO.getChildren() if propDO.isMultiLocale() |
| // but that is now always false, and anyway too slow to call isMultiLocale here |
| // (observed 1811 msec), should avoid calculations unless and until children expanded |
| } |
| |
| /** |
| * Listener which listens on changes of the set of |
| * <code>PropertiesDataObject</code>'s files. |
| * When the set of files changes, we fire a change of the DataObject's name, |
| * thus forcing update of the display name. We need this update because |
| * the CVS status of the PropertiesDataObject may change when the set |
| * of files is changed. |
| */ |
| final class NameUpdater implements PropertyChangeListener { |
| |
| @Override |
| public void propertyChange(PropertyChangeEvent e) { |
| if (DataObject.PROP_FILES.equals(e.getPropertyName())) { |
| PropertiesDataObject propDO = (PropertiesDataObject) getDataObject(); |
| propDO.fireNameChange(); |
| |
| // If the number of locales changes to more than one or down to |
| // one, we must exchange the children. |
| boolean newMultiLocale = propDO.isMultiLocale(); |
| if (newMultiLocale != multiLocale) { |
| multiLocale = newMultiLocale; |
| setChildren(createChildren(propDO)); |
| } |
| } |
| } |
| |
| } |
| |
| /** Gets new types that can be created in this node. |
| * @return array with <code>NewLocaleType</code> */ |
| @Override |
| public NewType[] getNewTypes() { |
| PropertiesDataObject propDO = (PropertiesDataObject) getDataObject(); |
| if (propDO.isMultiLocale()) { |
| return new NewType[] {new NewLocaleType()}; |
| } else { |
| PropertiesFileEntry pfEntry = (PropertiesFileEntry) propDO.getPrimaryEntry(); |
| return new NewType[] { new NewLocaleType(), |
| new PropertiesLocaleNode.NewPropertyType(pfEntry) }; |
| } |
| } |
| |
| /** Indicates whether this node has customizer. Overrides superclass method. |
| * @return true */ |
| @Override |
| public boolean hasCustomizer() { |
| return true; |
| } |
| |
| /** Gets node customizer. Overrides superclass method. |
| * @return <code>BundleNodeCustomizer</code> instance. |
| * @see BundleNodeCustomizer */ |
| @Override |
| public Component getCustomizer() { |
| return new BundleNodeCustomizer((PropertiesDataObject)getDataObject()); |
| } |
| |
| @Override |
| public void createPasteTypes(Transferable transferable, |
| List<PasteType> types) { |
| super.createPasteTypes(transferable, types); |
| |
| Element.ItemElem item; |
| Node node = NodeTransfer.node(transferable, NodeTransfer.MOVE); |
| if (node != null && node.canDestroy()) { |
| item = node.getCookie(Element.ItemElem.class); |
| if (item == null) { |
| return; |
| } |
| Node itemNode = getChildren().findChild(item.getKey()); |
| if (node.equals(itemNode)) { |
| return; |
| } |
| types.add(new EntryPasteType(item, node)); |
| } else { |
| item = NodeTransfer.cookie(transferable, |
| NodeTransfer.COPY, |
| Element.ItemElem.class); |
| if (item != null) { |
| types.add(new EntryPasteType(item, null)); |
| } |
| } |
| } |
| |
| @Override |
| protected Sheet createSheet() { |
| Sheet sheet = super.createSheet(); |
| Sheet.Set set = new Sheet.Set(); |
| set.setName("encoding"); //NOI18N |
| set.setDisplayName(NbBundle.getMessage(PropertiesDataNode.class, "ENCODING_SET_Name")); |
| |
| if (set == null) { |
| set = Sheet.createPropertiesSet(); |
| sheet.put(set); |
| } |
| set.put(new ProjectEncodingProperty()); |
| sheet.put(set); |
| return sheet; |
| } |
| |
| private PropertiesDataObject getPropertiesDataObject() { |
| return (PropertiesDataObject)getDataObject(); |
| } |
| |
| private PropertiesFileEntry getPropertiesFileEntry() { |
| return (PropertiesFileEntry)getPropertiesDataObject().getPrimaryEntry(); |
| } |
| |
| private PropertiesStructure getPropertiesStructure() { |
| return getPropertiesFileEntry().getHandler().getStructure(); |
| } |
| |
| /** |
| * A {@link PasteType} for pasting the key nodes of properties files. This |
| * class adds or updates the property key, value and comment of the copied |
| * node to the properties file of this {@link PropertiesDataNode}. Also |
| * destroys the copied node in case a cut action was performed. |
| */ |
| private class EntryPasteType extends PasteType { |
| |
| /** |
| * The {@link Element.ItemElem} to paste. |
| */ |
| private final Element.ItemElem item; |
| |
| /** |
| * The {@link Node} to destroy in case of a cut action. |
| */ |
| private final Node node; |
| |
| /** |
| * Creates a new instance of {@link EntryPasteType}. |
| * |
| * @param item the {@link Element.ItemElem} to paste |
| * @param node the {@link Node} to destroy in case a cut action was |
| * performed, otherwise it should be {@code null} |
| */ |
| public EntryPasteType(final Element.ItemElem item, final Node node) { |
| this.item = item; |
| this.node = node; |
| } |
| |
| @Override |
| public Transferable paste() throws IOException { |
| final PropertiesStructure ps = getPropertiesStructure(); |
| final Element.ItemElem storedItem = ps.getItem(item.getKey()); |
| |
| if (storedItem == null) { |
| ps.addItem(item); |
| } else { |
| storedItem.setValue(item.getValue()); |
| storedItem.setComment(item.getComment()); |
| } |
| |
| if (node != null) { |
| node.destroy(); |
| } |
| return null; |
| } |
| } |
| |
| /** New type for properties node. It creates new locale for ths bundle. */ |
| private class NewLocaleType extends NewType { |
| |
| /** Overrides superclass method. */ |
| @Override |
| public String getName() { |
| return NbBundle.getBundle(PropertiesDataNode.class).getString("LAB_NewLocaleAction"); |
| } |
| |
| /** Overrides superclass method. */ |
| @Override |
| public void create() throws IOException { |
| final PropertiesDataObject propertiesDataObject = |
| (PropertiesDataObject)getCookie(DataObject.class); |
| |
| final Dialog[] dialog = new Dialog[1]; |
| final LocalePanel panel = new LocalePanel(); |
| |
| DialogDescriptor dialogDescriptor = new DialogDescriptor( |
| panel, |
| NbBundle.getBundle(PropertiesDataNode.class).getString("CTL_NewLocaleTitle"), |
| true, |
| DialogDescriptor.OK_CANCEL_OPTION, |
| DialogDescriptor.OK_OPTION, |
| new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent evt) { |
| if (evt.getSource() == DialogDescriptor.OK_OPTION) { |
| if (containsLocale(propertiesDataObject, panel.getLocale())) { |
| NotifyDescriptor.Message msg = new NotifyDescriptor.Message( |
| MessageFormat.format(NbBundle.getBundle(PropertiesDataNode.class).getString("MSG_LangExists"), panel.getLocale()), |
| NotifyDescriptor.ERROR_MESSAGE); |
| DialogDisplayer.getDefault().notify(msg); |
| } else { |
| Util.createLocaleFile(propertiesDataObject, panel.getLocale().toString(), true); |
| dialog[0].setVisible(false); |
| dialog[0].dispose(); |
| } |
| } |
| } |
| } |
| ); |
| dialogDescriptor.setClosingOptions(new Object [] { DialogDescriptor.CANCEL_OPTION }); |
| |
| dialog[0] = DialogDisplayer.getDefault().createDialog(dialogDescriptor); |
| dialog[0].setVisible(true); |
| } |
| |
| } // End of NewLocaleType class. |
| |
| private static boolean containsLocale(PropertiesDataObject propertiesDataObject, Locale locale) { |
| FileObject file = propertiesDataObject.getBundleStructure().getNthEntry(0).getFile(); |
| // FileObject file = propertiesDataObject.getPrimaryFile(); |
| String newName = file.getName() + PropertiesDataLoader.PRB_SEPARATOR_CHAR + locale; |
| BundleStructure structure = propertiesDataObject.getBundleStructure(); |
| for (int i = 0; i<structure.getEntryCount();i++) { |
| FileObject f = structure.getNthEntry(i).getFile(); |
| if (newName.startsWith(f.getName()) && f.getName().length() > file.getName().length()) |
| file = f; |
| } |
| return file.getName().equals(newName); |
| } |
| |
| private final class ProjectEncodingProperty extends PropertySupport { |
| |
| public ProjectEncodingProperty() { |
| super(PROPERTY_ENCODING, |
| Boolean.class, |
| NbBundle.getMessage(PropertiesDataNode.class, "PROP_ENCODING_Name"), // NOI18N |
| NbBundle.getMessage(PropertiesDataNode.class, "PROP_ENCODING_Hint"), // NOI18N |
| true, true); |
| } |
| |
| @Override |
| public Object getValue() throws InvocationTargetException { |
| Object attribute = getDataObject().getPrimaryFile().getAttribute(PROPERTY_ENCODING); |
| if (attribute == null) { |
| return false; |
| } |
| return attribute; |
| } |
| |
| @Override |
| public void setValue(Object val) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { |
| try { |
| getDataObject().getPrimaryFile().setAttribute(PROPERTY_ENCODING, val); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| PropertiesEditorSupport propertiesEditor = getPropertiesFileEntry().getPropertiesEditor(); |
| propertiesEditor.resetCharset(); |
| if (propertiesEditor.hasOpenedEditorComponent()) { |
| NotifyDescriptor.Message message = new NotifyDescriptor.Message( |
| NbBundle.getMessage(PropertiesDataNode.class, "PROP_ENCODING_Warning", PropertiesDataNode.this.getDisplayName()), |
| NotifyDescriptor.WARNING_MESSAGE); |
| |
| DialogDisplayer.getDefault().notify(message); |
| propertiesEditor.close(); |
| } |
| } |
| } |
| } |