Package juzu.impl.metamodel

Source Code of juzu.impl.metamodel.MetaModelObject

/*
* 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 <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public class MetaModelObject implements Serializable {

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

  /** The parents. */
  private final HashMap<MetaModelObject, Key<?>> parents = new HashMap<MetaModelObject, Key<?>>();

  /** . */
  protected MetaModel metaModel;

  public MetaModelObject() {
  }

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

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

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

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

  public final <O extends MetaModelObject> Collection<O> getChildren(Class<O> filter) {
    ArrayList<O> list = new ArrayList<O>(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 <O> 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 extends MetaModelObject> O addChild(Key<O> 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<MetaModelObject> 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 extends MetaModelObject> O removeChild(Key<O> 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<Key<?>>(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<Map.Entry<MetaModelObject, Key<?>>> iterator = parents.entrySet().iterator();
      Map.Entry<MetaModelObject, Key<?>> 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<MetaModelObject> findPath(MetaModelObject to) {
    if (children.values().contains(to)) {
      LinkedList<MetaModelObject> ret = new LinkedList<MetaModelObject>();
      ret.addFirst(this);
      return ret;
    } else {
      for (MetaModelObject child : children.values()) {
        LinkedList<MetaModelObject> found = child.findPath(to);
        if (found != null) {
          found.addFirst(this);
          return found;
        }
      }
      return null;
    }
  }
}
TOP

Related Classes of juzu.impl.metamodel.MetaModelObject

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.