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

org.netbeans.modules.xml.xam.AbstractComponent Maven / Gradle / Ivy

The newest version!
/*
 * 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.xml.xam;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * An abstract implementation of the Component. It implies modification capabilities.
 *
 * @author rico
 * @author Vidhya Narayanan
 * @author Nam Nguyen
 * @author Chris Webster
 */
public abstract class AbstractComponent> implements Component {
    private C parent;
    private List children = null;
    private AbstractModel model;
    
    public AbstractComponent(AbstractModel model) {
        this.model = model;
    }
    
    protected abstract void appendChildQuietly(C component, List children);
    
    protected abstract void insertAtIndexQuietly(C newComponent, List children, int index);
    
    protected abstract void removeChildQuietly(C component, List children);

    protected abstract void populateChildren(List children);
    
    public final void removePropertyChangeListener(java.beans.PropertyChangeListener pcl) {
        if(model == null) return;
        model.removePropertyChangeListener(pcl);
    }
    
    public final void addPropertyChangeListener(java.beans.PropertyChangeListener pcl) {
        if(model == null) return;
        model.addPropertyChangeListener(new DelegateListener(pcl));
    }
    
    // Convenient method because WeakListener implmentation uses event source to unregister.
    public void removeComponentListener(ComponentListener cl) {
        if (getModel() != null) {
            getModel().removeComponentListener(cl);
        }
    }

    @Override
    public synchronized C getParent() {
        return parent;
    }
    
    protected synchronized void setParent(C component) {
        parent = component;
    }
    
    protected synchronized void setModel(AbstractModel aModel) {
        model = aModel;
        if (isChildrenInitialized()) {
            for (C component : getChildren()) {
                ((AbstractComponent)component).setModel(aModel);
            }
        }
    }
    
    private void _appendChildQuietly(C component, List children) {
        if (component.getModel() == null) {
            throw new IllegalStateException("Cannot add a removed component, " +
                    "should use a fresh or a copy component."); // NOI18N
        }
        appendChildQuietly(component, children);
        ((AbstractComponent)component).setModel(getModel());
        ((AbstractComponent)component).setParent(this);
    }

    private void _insertAtIndexQuietly(C component, List children, int index) {
        if (component.getModel() == null) {
            throw new IllegalStateException("Cannot add a removed component, " +
                    "should use a fresh or a copy component."); // NOI18N
        }
        insertAtIndexQuietly(component, children, index);
        ((AbstractComponent)component).setModel(getModel());
        ((AbstractComponent)component).setParent(this);
    }

    private void _removeChildQuietly(C component, List children) {
        removeChildQuietly(component, children);
        ((AbstractComponent)component).setModel(null);
        ((AbstractComponent)component).setParent(null);
    }
    
    /**
     * @return the contained elements, this is the  model element
     * representations of the DOM children. The returned list is unmodifiable.
     */
    @Override
    public List getChildren() {
        List result = new ArrayList(_getChildren());
        return Collections.unmodifiableList(result);
    }

    /**
     * This method guarantee that children are populated.
     * It's preferable to use it instead of getChildren() if children themselves
     * aren't necessary. This method works much faster then using getChildren()
     * in case of big amount of children because of absence of copying children
     * to a separate list.
     *
     * @since 1.6.1
     */
    public void checkChildrenPopulated() {
        _getChildren();
    }

    /**
     * Sometimes it's necessary to know amount of children but the children 
     * themselves aren't necessary. This method works much faster then using
     * getChildren().size() in case of big amount of children because of
     * absence of copying children to a separate list.
     *
     * @return number of children
     * @since 1.6.1
     */
    public int getChildrenCount() {
        return _getChildren().size();
    }

    /**
     * This method provides the ability to detect whether calling getChildren()
     * will trigger population of children. This can be used for meta models
     * to determine whether cleanup below a set of children is necessary. 
     */
    protected final boolean isChildrenInitialized() {
	return children != null;
    }
    
    private synchronized List _getChildren() {
        if (!isChildrenInitialized()) {
            children = new ArrayList();
            populateChildren(children);
            for (C child : children) {
                ((AbstractComponent)child).setParent(this);
            }
        }
        return children;
    }
    
    /**
     * @return the contained elements, this is the  model
     * element representations of the DOM children.
     *
     * @param type Interested children type to
     *	return.
     */
    @Override
    public synchronized List getChildren(Class type) {
        List result = new ArrayList(_getChildren().size());
        for (C child : _getChildren()) {
            if (type.isAssignableFrom(child.getClass())) {
                result.add(type.cast(child));
            }
        }
        return Collections.unmodifiableList(result);
    }
    
    /**
     * @return the contained  elements, this is the  model
     * element representations of the DOM children.
     *
     * @param typeList Collection that accepts the interested types and filters
     *	the return list of Children.
     */
    @Override
    public List getChildren(Collection> typeList) {
        List comps = new ArrayList();
        // createChildren is not necessary because this method delegates
        // to another getChildren which ensures initialization
        for(Class type : typeList) {
            comps.addAll(getChildren(type));
        }
        return Collections.unmodifiableList(comps);
    }

    @Override
    public synchronized AbstractModel getModel() {
        return model;
    }
    
    /**
     * This method ensures that a transaction is currently in progress and
     * that the current thread is able to write the model.
     */
    protected void verifyWrite() {
        getModel().validateWrite();
    }
    
    protected void firePropertyChange(String propName, Object oldValue, Object newValue) {
        PropertyChangeEvent event =
                new PropertyChangeEvent(this,propName,oldValue,newValue);
        getModel().firePropertyChangeEvent(event);
    }
    
    protected void fireValueChanged() {
        getModel().fireComponentChangedEvent(new ComponentEvent(this,
                ComponentEvent.EventType.VALUE_CHANGED));
    }
    
    protected void fireChildRemoved() {
        getModel().fireComponentChangedEvent(new ComponentEvent(this,
                ComponentEvent.EventType.CHILD_REMOVED));
    }
    
    protected void fireChildAdded() {
        getModel().fireComponentChangedEvent(new ComponentEvent(this,
                ComponentEvent.EventType.CHILD_ADDED));
    }
    
    protected  T getChild(Class type) {
        List result = getChildren(type);
        T value = null;
        if (!result.isEmpty()) {
            value = result.get(0);
        }
        return value;
    }
    
    /**
     * Adds a  element before all other children whose types are in the typeList Collection.
     */
    protected synchronized void addBefore(String propertyName, C component,
            Collection> typeList){
        verifyWrite();
        checkNullOrDuplicateChild(component);
        addChild(propertyName, component, typeList, true);
        firePropertyChange(propertyName, null, component);
        fireChildAdded();
    }
    
    /**
     * Adds a  element after all other children whose types are in the typeList Collection.
     */
    protected synchronized void addAfter(String propertyName, C component,
            Collection> typeList){
        verifyWrite();
        checkNullOrDuplicateChild(component);
        addChild(propertyName, component, typeList, false);
        firePropertyChange(propertyName, null, component);
        fireChildAdded();
    }
    
    /**
     * Adds the New Element in the DOM model.
     *
     * @param component The  element that needs to be set
     * @param typeList The collection list that contains the class names
     *		of  types of children
     * @param before boolean to indicate to add before/after the typelist
     */
    private void addChild(String propertyName, C component,
            Collection> typeList, boolean before) {
        assert(component != null);
        
        if (typeList == null) {
            throw new IllegalArgumentException("typeList == null"); //NOI18N
        }
        
        List childnodes = getChildren();
        if (typeList.isEmpty() || childnodes.isEmpty()) {
            _appendChildQuietly(component, _getChildren());
        } else {
            int lastIndex = before ? childnodes.size() : -1;
            for (Class type : typeList) {
                for (C child : childnodes) {
                    if (type.isAssignableFrom(child.getClass())) {
                        int i = childnodes.indexOf(child);
                        if (!before) {
                            if (i > lastIndex) lastIndex = i;
                        } else {
                            if (i < lastIndex) lastIndex = i;
                        }
                    }
                }
            }
            if (!before) {
                lastIndex++;
                for (int i=lastIndex ; i type) {
        verifyWrite();
        checkNullOrDuplicateChild(component);
        if (type != null) {
            int trueIndex = 0;
            for (C child: getChildren()) {
                if (type.isAssignableFrom(child.getClass())) {
                    break;
                }
                trueIndex++;
            }
            index += trueIndex;
        }
        _insertAtIndexQuietly(component, _getChildren(), index);
        firePropertyChange(propertyName, null, component);
        fireChildAdded();
    }
    
    public synchronized void insertAtIndex(String propertyName, C component, int index) {
        insertAtIndex(propertyName, component, index, null);
    }    
    
    public synchronized void removeChild(String propertyName, C component) {
        verifyWrite();
        if (component == null) {
            throw new IllegalArgumentException("component == null"); //NOI18N
        }
        if (! _getChildren().contains(component)) {
            throw new IllegalArgumentException(
                    "component to be deleted is not a child"); //NOI18N
        }
        _removeChildQuietly(component, _getChildren());
        firePropertyChange(propertyName, component, null);
        fireChildRemoved();
    }
    
    /**
     * When a child element is set using this method:
     * (1) All children that are of the same or derived type as classType are removed.
     * (2) newEl is added as a child after any children that are of the same
     * type as any of the types listed in typeList
     * @param classType Class of the Component that is being added as a child
     * @param propertyName Property name used for firing events
     * @param newComponent Component that is being added as a child
     * @param typeList Collection of java.lang.Class-es. newEl will be added as
     * a child after any children whose types belong to any listed in this. An
     * empty collection will append the child
     */
    protected void setChild(Class classType, String propertyName,
            C newComponent, Collection> typeList){
        //
        setChildAfter(classType, propertyName, newComponent, typeList);
    }
    
    protected void setChildAfter(Class classType, 
            String propertyName, C newComponent,
            Collection> typeList){
        //
        setChild(classType, propertyName, newComponent, typeList, false);
    }
    
    protected void setChildBefore(Class classType, 
            String propertyName, C newComponent,
            Collection> typeList){
        //
        setChild(classType, propertyName, newComponent, typeList, true);
    }
    
    protected synchronized void setChild(Class classType, 
            String propertyName, C newComponent,
            Collection> typeList, boolean before){
        //
        //remove all children of type classType
        verifyWrite();
        List childComponents = getChildren(classType);
        if (childComponents.contains(newComponent)) {
            return; // no change
        }
        C old = childComponents.isEmpty() ?
            null : childComponents.get(childComponents.size()-1);
        for (C child : childComponents) {
            _removeChildQuietly(child, _getChildren());
            fireChildRemoved();
        }
        if (newComponent != null) {
            addChild(propertyName, newComponent, typeList, before);
            fireChildAdded();
        }
        
        firePropertyChange(propertyName, old, newComponent);
    }
    
    private class DelegateListener implements PropertyChangeListener {
        private final PropertyChangeListener delegate;
        
        public DelegateListener(PropertyChangeListener pcl) {
            delegate = pcl;
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getSource() == AbstractComponent.this) {
                delegate.propertyChange(evt);
            }
        }
        
        @Override
        public boolean equals(Object obj) {
            return delegate == obj;
        }
        
        @Override
        public int hashCode() {
            return delegate.hashCode();
        }
    }

    
    /**
     * Default implementation, subclass need to override if needed.
     */
    @Override
    public boolean canPaste(Component child) {
        return true;
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy