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

juzu.impl.metamodel.MetaModelObject Maven / Gradle / Ivy

/*
 * Copyright 2013 eXo Platform SAS
 *
 * Licensed 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 juzu.impl.metamodel;

import juzu.impl.common.CycleDetectionException;
import juzu.impl.common.JSON;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

/** @author Julien Viet */
public class MetaModelObject implements Serializable {

  /** The children. */
  private final HashMap, MetaModelObject> children = new HashMap, MetaModelObject>();

  /** The parents. */
  private final HashMap> parents = new HashMap>();

  /** . */
  protected MetaModel metaModel;

  public MetaModelObject() {
  }

  public final Collection getParents() {
    return parents.keySet();
  }

  public final  O getChild(Key key) {
    MetaModelObject child = children.get(key);
    if (child != null) {
      return key.getType().cast(child);
    }
    else {
      return null;
    }
  }

  public final Collection getChildren() {
    return getChildren(MetaModelObject.class);
  }

  public final  Collection> getKeys(Class filter) {
    ArrayList> list = new ArrayList>(children.size());
    for (Key key : children.keySet()) {
      if (filter.isAssignableFrom(key.getType())) {
        // Yes : not great
        list.add((Key)key);
      }
    }
    return list;
  }

  public final  Collection getChildren(Class filter) {
    ArrayList list = new ArrayList(children.size());
    for (MetaModelObject child : children.values()) {
      if (filter.isInstance(child)) {
        list.add(filter.cast(child));
      }
    }
    return list;
  }

  /**
   * Add a child to this object.
   *
   * @param key the child key
   * @param child the child object
   * @param  the child parameter type
   * @return the child
   * @throws NullPointerException if any argument is null
   * @throws IllegalArgumentException when the child is already added or a child with the same name already exists
   * @throws IllegalStateException when a cycle is detected when creating a graph
   */
  public final  O addChild(Key key, O child) throws NullPointerException, IllegalArgumentException, IllegalStateException {
    return (O)_addChild(key, child);
  }

  private MetaModelObject _addChild(Key key, MetaModelObject child) throws NullPointerException, IllegalArgumentException, IllegalStateException {
    if (key == null) {
      throw new NullPointerException("No null key accepted");
    }
    if (child == null) {
      throw new NullPointerException("No null child accepted");
    }
    if (child == this) {
      throw new CycleDetectionException(Arrays.asList(this));
    }
    LinkedList path = child.findPath(this);
    if (path != null) {
      path.addLast(this);
      throw new CycleDetectionException(path);
    }
    else {
      if (child.parents.containsKey(this)) {
        throw new IllegalArgumentException("Child " + child + " cannot be added for key " + key + " because parent already contains it");
      } else {
        if (children.containsKey(key)) {
          throw new IllegalArgumentException("Object " + this + " has already a child named "  + key);
        }

        if (child.parents.size() == 0) {

          // Context
          if (this instanceof MetaModel) {
            child.metaModel = (MetaModel)this;
          } else {
            child.metaModel = metaModel;
          }

          // Post construct
          child.postConstruct();
        }

        // Wire
        children.put(key, child);
        child.parents.put(this, key);


        // Post attach
        child.postAttach(this);

        //
        return child;
      }
    }
  }

  public final  O removeChild(Key key) {
    MetaModelObject child = children.get(key);
    if (child != null) {
      if (child.parents.containsKey(this)) {

        // Detect orphan
        boolean remove = child.parents.size() == 1;

        //
        if (remove) {

          // Remove children recursively
          if (child.children.size() > 0) {
            for (Key key2 : new ArrayList>(child.children.keySet())) {
              child.removeChild(key2);
            }
          }
        }

        // Pre detach
        child.preDetach(this);

        // Break relationship
        if (children.remove(key) == null) {
          throw new AssertionError("Internal bug");
        }
        if (child.parents.remove(this) == null) {
          throw new AssertionError("Internal bug");
        }

        //
        if (remove) {
          // Remove callback
          child.preRemove();

          // Set model to null
          child.metaModel = null;
        }

        //
        return key.getType().cast(child);
      }
      else {
        throw new AssertionError("Internal bug");
      }
    }
    else {
      throw new IllegalArgumentException("The element is not child of this node");
    }
  }

  public final void remove() {
    // We remove a node by detaching it from all its parents
    while (parents.size() > 0) {
      Iterator>> iterator = parents.entrySet().iterator();
      Map.Entry> entry = iterator.next();
      MetaModelObject parent = entry.getKey();
      Key key = entry.getValue();
      parent.removeChild(key);
    }
  }

  public void queue(MetaModelEvent event) {
    metaModel.queue(event);
  }

  protected void postConstruct() {
  }

  protected void preDetach(MetaModelObject parent) {
  }

  protected void postAttach(MetaModelObject parent) {
  }

  protected void preRemove() {
  }

  public JSON toJSON() {
    return new JSON();
  }

  @Override
  public String toString() {
    return getClass().getSimpleName() + "[" + toJSON() + "]";
  }

  /**
   * Find a path between this object and the target.
   *
   * @param to the target object
   * @return the path or null when no path exists
   */
  private LinkedList findPath(MetaModelObject to) {
    if (children.values().contains(to)) {
      LinkedList ret = new LinkedList();
      ret.addFirst(this);
      return ret;
    } else {
      for (MetaModelObject child : children.values()) {
        LinkedList found = child.findPath(to);
        if (found != null) {
          found.addFirst(this);
          return found;
        }
      }
      return null;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy