Package org.mevenide.idea.psi.util

Source Code of org.mevenide.idea.psi.util.PsiPropertyChangeListener

package org.mevenide.idea.psi.util;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiTreeChangeAdapter;
import com.intellij.psi.PsiTreeChangeEvent;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlText;
import com.jgoodies.binding.beans.ExtendedPropertyChangeSupport;
import java.beans.PropertyChangeListener;
import org.mevenide.idea.util.event.PropertyObservable;

/**
* This class manages a list of XML tag paths, and corresponding property names. It listens to PSI
* changes and maps them to appropriate registered tag paths. If a PSI change is found to be inside
* a registered tag path, a property-change event is triggered with the corresponding property
* name.
*
* @author Arik
*/
public abstract class PsiPropertyChangeListener extends PsiTreeChangeAdapter
        implements PropertyObservable {
    /**
     * Property change listeners container.
     */
    private final ExtendedPropertyChangeSupport props = new ExtendedPropertyChangeSupport(
            this);

    /**
     * Is the current set of PSI events generated by the XML tag path, or by an outside source?
     */
    protected boolean ignoreEvents = false;

    /**
     * Registers the given property change listener for all property change events.
     *
     * @param pListener the listener to register
     */
    public final void addPropertyChangeListener(final PropertyChangeListener pListener) {
        props.addPropertyChangeListener(pListener);
    }

    /**
     * Unregisters the given property change listener from all property change events.
     *
     * @param pListener the listener to unregister
     */
    public final void removePropertyChangeListener(final PropertyChangeListener pListener) {
        props.removePropertyChangeListener(pListener);
    }

    /**
     * Registers the given property change listener for the given property.
     *
     * @param pListener the listener to register
     */
    public final void addPropertyChangeListener(final String pPropertyName,
                                                final PropertyChangeListener pListener) {
        props.addPropertyChangeListener(pPropertyName, pListener);
    }

    /**
     * Unregisters the given property change listener from events for the given property.
     *
     * @param pListener the listener to unregister
     */
    public final void removePropertyChangeListener(final String pPropertyName,
                                                   final PropertyChangeListener pListener) {
        props.removePropertyChangeListener(pPropertyName, pListener);
    }

    @Override
    public final void childAdded(final PsiTreeChangeEvent pEvent) {
        if (ignoreEvents)
            return;

        final PsiElement child = pEvent.getChild();
        final String changedProperty = getPropertyForElement(child);
        if (changedProperty != null) {
            if (child instanceof XmlTag)
                notifyAddition(changedProperty);
            else if (child instanceof XmlText)
                notifyChange(changedProperty);
        }
    }

    @Override
    public final void childRemoved(final PsiTreeChangeEvent pEvent) {
        if (ignoreEvents)
            return;

        final PsiElement child = pEvent.getChild();
        final XmlTag parent = PsiTreeUtil.getParentOfType(pEvent.getParent(),
                                                          XmlTag.class,
                                                          false);
        if (parent == null)
            return;

        if (child instanceof XmlText) {
            final String changedProperty = getPropertyForElement(parent);
            if (changedProperty != null)
                notifyChange(changedProperty);
        }
        else if (child instanceof XmlTag) {
            final XmlTag tag = (XmlTag) child;
            final String[] tagPath = PsiUtils.getPathAndConcat(parent, tag.getName());
            final String changedProperty = getPropertyForPath(tagPath);
            if (changedProperty != null)
                notifyRemoval(changedProperty);
        }
    }

    @Override
    public final void childReplaced(final PsiTreeChangeEvent pEvent) {
        if (ignoreEvents)
            return;

        final PsiElement oldChild = pEvent.getOldChild();
        final XmlTag parent = PsiTreeUtil.getParentOfType(pEvent.getParent(),
                                                          XmlTag.class,
                                                          false);
        if (parent == null)
            return;

        String oldChangedProperty = null;
        String newChangedProperty = null;

        if (oldChild instanceof XmlText)
            oldChangedProperty = getPropertyForElement(parent);
        else if (oldChild instanceof XmlTag) {
            final XmlTag tag = (XmlTag) oldChild;
            final String[] tagPath = PsiUtils.getPathAndConcat(parent, tag.getName());
            oldChangedProperty = getPropertyForPath(tagPath);
        }

        final PsiElement newChild = pEvent.getNewChild();
        if (newChild instanceof XmlText)
            newChangedProperty = getPropertyForElement(newChild);
        else if (newChild instanceof XmlTag) {
            final XmlTag tag = (XmlTag) newChild;
            final String[] tagPath = PsiUtils.getPathAndConcat(parent, tag.getName());
            newChangedProperty = getPropertyForPath(tagPath);
        }

        if (oldChangedProperty == null && newChangedProperty == null)
            return;

        if (oldChangedProperty != null && oldChangedProperty.equals(newChangedProperty))
            notifyChange(newChangedProperty);
        else {
            if (oldChangedProperty != null)
                notifyRemoval(oldChangedProperty);
            if (newChangedProperty != null)
                notifyAddition(newChangedProperty);
        }
    }

    protected void notifyAddition(final String pPropertyName) {
        notifyChange(pPropertyName);
    }

    protected void notifyRemoval(final String pPropertyName) {
        notifyChange(pPropertyName);
    }

    /**
     * @todo currently all propertyChange events' old value is null - due to limitation of IDEA api
     */
    protected void notifyChange(final String pProperty) {
        final XmlTagPath path = getPropertyTagPath(pProperty);
        final String value = path == null ? null : path.getStringValue();
        props.firePropertyChange(pProperty, null, value);
    }

    public final String getValue(final String pPropertyName) {
        return getPropertyTagPath(pPropertyName).getStringValue();
    }

    public final void setValue(final String pPropertyName, final Object pValue) {
        ignoreEvents = true;
        try {
            final String value = pValue == null ? null : pValue.toString();
            getPropertyTagPath(pPropertyName).setValueProtected(value);
        }
        finally {
            ignoreEvents = false;
        }
    }

    protected abstract String getPropertyForElement(final PsiElement pElement);

    protected abstract String getPropertyForPath(final String[] pPath);

    protected abstract XmlTagPath getPropertyTagPath(final String pProperty);
}
TOP

Related Classes of org.mevenide.idea.psi.util.PsiPropertyChangeListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.