All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.properties.PropertiesDataNode Maven / Gradle / Ivy

/*
 * 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 PropertiesDataObject.
 * 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 propDataObject 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
     * PropertiesDataObject'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 NewLocaleType */
    @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 BundleNodeCustomizer instance.
     * @see BundleNodeCustomizer */
    @Override
    public Component getCustomizer() {
        return new BundleNodeCustomizer((PropertiesDataObject)getDataObject());
    }
    
    @Override
    public void createPasteTypes(Transferable transferable,
                                 List 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 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();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy