AbstractComponent.java :  » IDE-Netbeans » xml » org » netbeans » modules » xml » xam » Java Open Source

Java Open Source » IDE Netbeans » xml 
xml » org » netbeans » modules » xml » xam » AbstractComponent.java
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 */

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;

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

    protected abstract void populateChildren(List<C> 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);
        }
    }
    
    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<C> children) {
        if (component.getModel() == null) {
            throw new IllegalStateException("Cannot add a removed component, should use a fresh or a copy component.");
        }
        appendChildQuietly(component, children);
        ((AbstractComponent)component).setModel(getModel());
        ((AbstractComponent)component).setParent(this);
    }

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

    private void _removeChildQuietly(C component, List<C> 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.
     */
    public List<C> getChildren() {
        List<C> result = new ArrayList<C>(_getChildren());
        return Collections.unmodifiableList(result);
    }
    
    /**
     * 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<C> _getChildren() {
        if (!isChildrenInitialized()) {
            children = new ArrayList<C>();
            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.
     */
    public synchronized <T extends C>List<T> getChildren(Class<T> type) {
        List<T> result = new ArrayList<T>(_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.
     */
    public List<C> getChildren(Collection<Class<? extends C>> typeList) {
        List<C> comps = new ArrayList<C>();
        // createChildren is not necessary because this method delegates
        // to another getChildren which ensures initialization
        for(Class<? extends C> type : typeList) {
            comps.addAll(getChildren(type));
        }
        return Collections.unmodifiableList(comps);
    }
    
    public synchronized AbstractModel getModel() {
        return 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 extends C> T getChild(Class<T> type) {
        List<T> 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<Class<? extends C>> 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<Class<? extends C>> 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<Class<? extends C>> typeList, boolean before) {
        assert(component != null);
        
        if (typeList == null) {
            throw new IllegalArgumentException("typeList == null"); //NOI18N
        }
        
        List<? extends C> childnodes = getChildren();
        if (typeList.isEmpty() || childnodes.isEmpty()) {
            _appendChildQuietly(component, _getChildren());
        } else {
            int lastIndex = before ? childnodes.size() : -1;
            for (Class<? extends C> 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<childnodes.size() ; i++) {
                    if (childnodes.get(i).getClass().equals(component.getClass())) {
                        lastIndex++;
                    } else {
                        break;
                    }
                }
            }
            _insertAtIndexQuietly(component, _getChildren(), lastIndex);
        }
    }

    protected void checkNullOrDuplicateChild(C child) {
        if (child == null) {
            throw new IllegalArgumentException("child == null"); //NOI18N
        }
        if (_getChildren().contains(child)) {
            throw new IllegalArgumentException("child already in children list"); //NOI18N
        }
    }
    
    protected synchronized void appendChild(String propertyName, C child) {
        verifyWrite();
        checkNullOrDuplicateChild(child);
        _appendChildQuietly(child, _getChildren());
        firePropertyChange(propertyName, null, child);
        fireChildAdded();
    }
    
    /**
     * Inserts a Component child at the specified index relative to
     * the provided type. This method is expected to be used only in
     * sequence.
     * @param propertyName to fire event on
     * @param component to insert
     * @param index relative to first instance of type, index = firstpos
     * @param type which index should be relative to
     */
    protected synchronized void insertAtIndex(String propertyName,
            C component, int index,
            Class<? extends C> 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<? extends C> classType, String propertyName,
            C newComponent, Collection<Class<? extends C>> typeList){
        setChildAfter(classType, propertyName, newComponent, typeList);
    }
    
    protected void setChildAfter(Class<? extends C> classType, String propertyName,
            C newComponent, Collection<Class<? extends C>> typeList){
        setChild(classType, propertyName, newComponent, typeList, false);
    }
    
    protected void setChildBefore(Class<? extends C> classType, String propertyName,
            C newComponent, Collection<Class<? extends C>> typeList){
        setChild(classType, propertyName, newComponent, typeList, true);
    }
    
    protected synchronized void setChild(Class<? extends C> classType, String propertyName,
            C newComponent, Collection<Class<? extends C>> typeList, boolean before){
        //remove all children of type classType
        verifyWrite();
        List<? extends C> 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;
        }
        
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getSource() == AbstractComponent.this) {
                delegate.propertyChange(evt);
            }
        }
        
        public boolean equals(Object obj) {
            return delegate == obj;
        }
        
        public int hashCode() {
            return delegate.hashCode();
        }
    }

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

java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.