/**
   * Returns the parent {@link Element} for this component, if there is one, else null. If the
   * component's immediate parent is an {@link Element}, returns the parent, else, goes up in the
   * hierarchy until it finds one. Returns null if none found.
   */
  public Element getParentElement() {
    AXIComponent parent = (AXIComponent) getParent();
    if (parent == null) return null;

    if (parent instanceof Element) return (Element) parent;

    return parent.getParentElement();
  }
 /**
  * Removing a content model is special. For example, if element shipTo and billTo are of type
  * USAddress, and USAddress gets deleted, then delete the proxy children of shipTo and billTo and
  * finally delete USAddress.
  */
 private void onContentModelDeleted(ContentModel contentModel) {
   List<AXIComponent> removeList = new ArrayList<AXIComponent>();
   for (AXIComponent child : getChildren()) {
     if (child.getContentModel() == contentModel) {
       removeList.add(child);
     }
   }
   for (AXIComponent child : removeList) {
     child.getParent().removeChild(Util.getProperty(child), child);
   }
 }
  /**
   * Returns the content model, this AXI component belongs to. Returns null for a local component.
   */
  public ContentModel getContentModel() {
    if (getComponentType() == ComponentType.PROXY) {
      return getOriginal().getContentModel();
    }

    if (this instanceof ContentModel) return (ContentModel) this;

    AXIComponent parent = getParent();
    if (parent == null || parent instanceof AXIDocument || parent instanceof Element) return null;

    return parent.getContentModel();
  }
  /** Overwritten so that it can fire events to the proxies. */
  public void removeChild(String property, AXIComponent child) {
    if (getModel() != child.getModel()) return;

    super.removeChild(property, child);
    if (pcs != null) {
      // fire event so that proxy children get deleted from their parents
      pcs.firePropertyChange(PROP_CHILD_REMOVED, child, null);
      // finally, remove all listeners from the shared child
      child.removeAllListeners();
    }
    if (this instanceof AXIDocumentImpl) ((AXIDocumentImpl) this).removeFromCache(child);
  }
 /**
  * Returns this component's index (relative or absolute) in the parent's children list. Returns -1
  * if child or parent are not in model or the child is not found in the parent's children list.
  *
  * @param absolute - true, relative (to its type in parent) - false
  */
 public int getIndex(boolean absolute) {
   AXIComponent parent = getParent();
   if (parent == null || !isInModel()) return -1;
   List<AXIComponent> childs = Collections.emptyList();
   if (absolute) childs = parent.getChildren();
   else childs = parent.getChildren((Class<AXIComponent>) this.getClass());
   for (int i = 0; i < childs.size(); i++) {
     if (childs.get(i) == this) {
       return i;
     }
   }
   return -1;
 }
  /** Sets the shared component. */
  protected void setSharedComponent(AXIComponent sharedComponent) {
    this.sharedComponent = sharedComponent;
    if (sharedComponent == null) {
      return;
    }
    AXIModelImpl thisModel = (AXIModelImpl) getModel();
    if (thisModel == sharedComponent.getModel()) {
      sharedComponent.addListener(this);
      return;
    }

    // keep listening to the other model
    thisModel.listenToReferencedModel(sharedComponent.getModel());
  }
  /** Sets the type. */
  public void setType(AXIType type) {
    if (type instanceof Attribute) {
      setRef((Attribute) type);
      return;
    }

    int index = this.getIndex();
    AXIComponent parent = getParent();
    Attribute a = getModel().getComponentFactory().createAttribute();
    a.setName(getReferent().getName());
    parent.removeChild(this);
    parent.insertAtIndex(Attribute.PROP_ATTRIBUTE, a, index);
    a.setType(type);
  }
  /**
   * Convenient method to remove a child. If the parent is a proxy, delegates to the shared
   * component.
   */
  public final void removeChild(AXIComponent child) {
    if (child.getComponentType() == ComponentType.REFERENCE) {
      removeChild(Util.getProperty(child), child);
      return;
    }

    // proxy child delete from UI: delete original child
    if (child.getComponentType() == ComponentType.PROXY && !getModel().inSync()) {
      AXIComponent oChild = child.getOriginal();
      oChild.getParent().removeChild(oChild);
      return;
    }

    removeChild(Util.getProperty(child), child);
  }
  private void onChildAdded(PropertyChangeEvent evt) {
    if (!isChildrenInitialized()) return;
    AXIComponent parent = (AXIComponent) evt.getSource();
    AXIComponent child = (AXIComponent) evt.getNewValue();
    int index = -1;
    for (int i = 0; i < parent.getChildren().size(); i++) {
      if (parent.getChildren().get(i) == child) {
        index = i;
        break;
      }
    }
    if (index == -1) return;

    AXIComponentFactory factory = getModel().getComponentFactory();
    AXIComponent proxy = factory.createProxy(child);
    insertAtIndex(Util.getProperty(child), proxy, index);
  }
  /** Add a listener to the shared component. */
  public void addListener(AXIComponent proxy) {
    if (getModel() != proxy.getModel()) return;

    if (pcs == null) pcs = new PropertyChangeSupport(this);

    PropertyChangeListener l = getWeakListener(proxy, false);
    if (l != null) pcs.addPropertyChangeListener(l);
  }
  /** Overwritten so that it can fire events to the proxies. */
  public void insertAtIndex(String property, AXIComponent child, int index) {
    if (getModel() != child.getModel()) return;

    super.insertAtIndex(property, child, index);
    if (pcs != null) pcs.firePropertyChange(PROP_CHILD_ADDED, null, child);

    if (this instanceof AXIDocumentImpl) ((AXIDocumentImpl) this).addToCache(child);
  }
  /** Overwritten so that it can fire events to the proxies. */
  protected void appendChild(String property, AXIComponent child) {
    if (getModel() != child.getModel()) return;

    super.appendChild(property, child);
    if (pcs != null) pcs.firePropertyChange(PROP_CHILD_ADDED, null, child);

    if (this instanceof AXIDocumentImpl) ((AXIDocumentImpl) this).addToCache(child);
  }
  private void onChildDeleted(PropertyChangeEvent evt) {
    AXIComponent parent = (AXIComponent) evt.getSource();
    AXIComponent child = (AXIComponent) evt.getOldValue();
    if (child instanceof ContentModel) {
      onContentModelDeleted((ContentModel) child);
      return;
    }
    AXIComponent deletedChild = null;
    for (AXIComponent c : getChildren()) {
      if (c.getSharedComponent() == child) {
        deletedChild = c;
        break;
      }
    }
    if (deletedChild == null) return;

    removeChild(Util.getProperty(deletedChild), deletedChild);
  }
  private void populateChildElements(
      List<AbstractElement> childrenElements, AXIComponent component) {
    for (AXIComponent child : component.getChildren()) {
      if (child instanceof ContentModel) continue;

      if (child instanceof AbstractElement) {
        childrenElements.add((AbstractElement) child);
        continue;
      }
      populateChildElements(childrenElements, child);
    }
  }
  /** The proxy component receives an event notification. */
  public void propertyChange(PropertyChangeEvent evt) {
    AXIComponent source = (AXIComponent) evt.getSource();
    String property = evt.getPropertyName();
    if (!isInModel()) {
      // Ideally it shouldn't come here. Remove this as listener
      // and make shared as null, so that it'll be GCed.
      source.removeListener(this);
      // setSharedComponent(null);
      return;
    }
    // if(evt.getOldValue() == null && evt.getNewValue() != null) {
    if (PROP_CHILD_ADDED.equals(property)) {
      onChildAdded(evt);
      return;
    }
    // if(evt.getOldValue() != null && evt.getNewValue() == null) {
    if (PROP_CHILD_REMOVED.equals(property)) {
      onChildDeleted(evt);
      return;
    }

    firePropertyChangeEvent(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
  }
  private PropertyChangeListener getWeakListener(AXIComponent proxy, boolean remove) {
    if (listenerMap == null) {
      listenerMap = new WeakHashMap<AXIComponent, PropertyChangeListener>();
    }
    if (remove) return listenerMap.remove(proxy);

    if (proxy.getComponentType() != ComponentType.PROXY) {
      Set<AXIComponent> keySet = listenerMap.keySet();
      for (AXIComponent key : keySet) {
        if (key.getPeer() == proxy.getPeer()) return null;
      }
    }

    PropertyChangeListener listener = listenerMap.get(proxy);
    if (listener == null) {
      listener =
          (PropertyChangeListener) WeakListeners.create(PropertyChangeListener.class, proxy, this);
      listenerMap.put(proxy, listener);
      return listener;
    }

    // if exists, return null.
    return null;
  }
 public AXIComponent copy(AXIComponent parent) {
   AXIComponentFactory f = parent.getModel().getComponentFactory();
   return f.copy(this);
 }