Package org.fusesource.ide.camel.model

Source Code of org.fusesource.ide.camel.model.AbstractNode

/*******************************************************************************
* Copyright (c) 2013 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Red Hat, Inc. - initial API and implementation
******************************************************************************/

package org.fusesource.ide.camel.model;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import org.apache.camel.ExchangePattern;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.model.ChoiceDefinition;
import org.apache.camel.model.DescriptionDefinition;
import org.apache.camel.model.ExpressionNode;
import org.apache.camel.model.LoadBalanceDefinition;
import org.apache.camel.model.OptionalIdentifiedDefinition;
import org.apache.camel.model.OtherwiseDefinition;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.model.SetHeaderDefinition;
import org.apache.camel.model.ToDefinition;
import org.apache.camel.model.WhenDefinition;
import org.apache.camel.model.language.ExpressionDefinition;
import org.apache.camel.model.language.XPathExpression;
import org.apache.camel.model.loadbalancer.CustomLoadBalancerDefinition;
import org.apache.camel.model.loadbalancer.FailoverLoadBalancerDefinition;
import org.apache.camel.model.loadbalancer.RandomLoadBalancerDefinition;
import org.apache.camel.model.loadbalancer.RoundRobinLoadBalancerDefinition;
import org.apache.camel.model.loadbalancer.StickyLoadBalancerDefinition;
import org.apache.camel.model.loadbalancer.TopicLoadBalancerDefinition;
import org.apache.camel.model.loadbalancer.WeightedLoadBalancerDefinition;
import org.apache.camel.spi.NodeIdFactory;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ICellEditorValidator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.PropertyDescriptor;
import org.eclipse.ui.views.properties.TextPropertyDescriptor;
import org.fusesource.ide.camel.model.generated.Bean;
import org.fusesource.ide.camel.model.generated.Catch;
import org.fusesource.ide.camel.model.generated.Choice;
import org.fusesource.ide.camel.model.generated.ConvertBody;
import org.fusesource.ide.camel.model.generated.Enrich;
import org.fusesource.ide.camel.model.generated.Finally;
import org.fusesource.ide.camel.model.generated.InOnly;
import org.fusesource.ide.camel.model.generated.InOut;
import org.fusesource.ide.camel.model.generated.InterceptSendToEndpoint;
import org.fusesource.ide.camel.model.generated.LoadBalance;
import org.fusesource.ide.camel.model.generated.Log;
import org.fusesource.ide.camel.model.generated.Marshal;
import org.fusesource.ide.camel.model.generated.Messages;
import org.fusesource.ide.camel.model.generated.Multicast;
import org.fusesource.ide.camel.model.generated.NodeFactory;
import org.fusesource.ide.camel.model.generated.OnException;
import org.fusesource.ide.camel.model.generated.Otherwise;
import org.fusesource.ide.camel.model.generated.PollEnrich;
import org.fusesource.ide.camel.model.generated.RemoveHeader;
import org.fusesource.ide.camel.model.generated.RemoveProperty;
import org.fusesource.ide.camel.model.generated.Rollback;
import org.fusesource.ide.camel.model.generated.SetExchangePattern;
import org.fusesource.ide.camel.model.generated.Sort;
import org.fusesource.ide.camel.model.generated.Tooltips;
import org.fusesource.ide.camel.model.generated.Try;
import org.fusesource.ide.camel.model.generated.Unmarshal;
import org.fusesource.ide.camel.model.generated.When;
import org.fusesource.ide.camel.model.util.Expressions;
import org.fusesource.ide.commons.camel.tools.CamelModelUtils;
import org.fusesource.ide.commons.util.Objects;
import org.fusesource.ide.commons.util.Predicate;
import org.fusesource.ide.commons.util.Strings;
import org.fusesource.ide.preferences.PreferenceManager;
import org.fusesource.ide.preferences.PreferencesConstants;


/**
* @author lhein
*/
public abstract class AbstractNode implements IPropertySource, IAdaptable {

  protected static final boolean lazyCreateIds = false;
  protected static final boolean useCamelIds = true;

  public static final String PROPERTY_LAYOUT_NODE = "AbstractNode.Layout";
  public static final String SOURCE_CONNECTIONS = "AbstractNode.SourceConn";
  public static final String TARGET_CONNECTIONS = "AbstractNode.TargetConn";
  public static final String PROPERTY_ID = "AbstractNode.Id";
  public static final String PROPERTY_DESCRIPTION = "AbstractNode.Description";
  public static final String PROPERTY_INHERITERRORHANDLER = "AbstractNode.InheritErrorHandler";

  private static final String ICON = "generic.png";

  public static final Rectangle DEFAULT_LAYOUT = new Rectangle(0, 0, 140, 80);
  protected static final ICellEditorValidator DEFAULT_STRING_VALIDATOR = new ICellEditorValidator() {
    /*
     * (non-Javadoc)
     *
     * @see
     * org.eclipse.jface.viewers.ICellEditorValidator#isValid(java.lang.
     * Object)
     */
    @Override
    public String isValid(Object value) {
      String val = (String) value;
      return val != null && val.trim().length() > 0 ? null : Messages.invalidValidatorValueLabel;
    }
  };

  private static transient NodeIdFactory nodeIdFactory;

  private String name;
  private String id;
  private String description;
  private Rectangle layout;
  private List<Flow> sourceConnections;
  private List<Flow> targetConnections;
  private RouteContainer parent;
  private transient Map<String, PropertyDescriptor> descriptors;
  private transient PropertyChangeSupport listeners;
  private boolean disableListeners;
  private Image image;
  private Image smallImage;
  private Boolean inheritErrorHandler;

  /**
   * default constructor
   */
  public AbstractNode() {
    this.sourceConnections = new ArrayList<Flow>();
    this.targetConnections = new ArrayList<Flow>();
    this.listeners = new PropertyChangeSupport(this);
    this.layout = DEFAULT_LAYOUT;

    this.descriptors = new HashMap<String, PropertyDescriptor>();
    this.descriptors.put(PROPERTY_ID, new TextPropertyDescriptor(PROPERTY_ID, Messages.propertyLabelId));
    this.descriptors.get(PROPERTY_ID).setValidator(new ICellEditorValidator() {
      /*
       * (non-Javadoc)
       *
       * @see
       * org.eclipse.jface.viewers.ICellEditorValidator#isValid(java.lang
       * .Object)
       */
      @Override
      public String isValid(Object value) {
        String val = (String) value;
        return val != null && val.trim().length() > 0 && AbstractNode.this.isUniqueId(val) ? null
            : Messages.invalidValidatorUniqueValueLabel;
      }
    });
    this.descriptors.put(PROPERTY_DESCRIPTION, new TextPropertyDescriptor(PROPERTY_DESCRIPTION,
        Messages.propertyLabelDescription));
    this.descriptors.get(PROPERTY_DESCRIPTION).setValidator(DEFAULT_STRING_VALIDATOR);

    // now let the subclasses add stuff
    addCustomProperties(this.descriptors);
  }

  public AbstractNode(RouteContainer parent) {
    this();
    this.parent = parent;
    if (parent != null) {
      parent.addChild(this);
    }
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    return false;
  }

  public String getCamelContextId() {
    RouteContainer rc = null;
    if (getParent() == null) {
      rc = (RouteContainer)this;
    } else {
      rc = getParent();
      if (rc.getParent() != null) {
        rc = rc.getParent();
     
    }   
    if (rc != null) {
      return rc.getContextId();
    }
    return null;
  }
 
  /**
   * Clears any EMF / diagram related resources
   */
  public void clearResources() {
    //    this.eSetDirectResource(null);
    //    //this.eSetResource(null, null);
    //
    //    Resource resource = this.eResource();
    //    if (resource != null) {
    //      throw new IllegalStateException("Should have cleared the resources!!!");
    //    }


    List<AbstractNode> children = getOutputs();
    for (AbstractNode node : children) {
      node.clearResources();
    }
  }

  /*
   * (non-Javadoc) Method declared on IAdaptable
   */
  @Override
  public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
    if (adapter == IWorkbenchAdapter.class)
      return new IWorkbenchAdapter() {

      @Override
      public Object[] getChildren(Object o) {
        if (o instanceof AbstractNode) {
          AbstractNode node = (AbstractNode) o;
          node.getChildren().toArray();
        }
        return null;
      }

      @Override
      public ImageDescriptor getImageDescriptor(Object o) {
        if (o instanceof AbstractNode) {
          // TODO no dependency on Activator...
          // return node.getImageDescriptor();
          // return Activator.getDefault().getImage(iconName);
          return null;

        }
        return null;
      }

      @Override
      public String getLabel(Object o) {
        if (o instanceof AbstractNode) {
          AbstractNode node = (AbstractNode) o;
          return node.getDisplayText();
        }
        return null;
      }

      @Override
      public Object getParent(Object o) {
        if (o instanceof AbstractNode) {
          AbstractNode node = (AbstractNode) o;
          return node.getParent();
        }
        return null;
      }

    };
    if (adapter == IPropertySource.class)
      return this;
    if (adapter == AbstractNode.class)
      return this;
    return null;
  }

  /**
   * checks whether the id is unique among all other nodes
   *
   * @param id
   * @return
   */
  protected boolean isUniqueId(String id) {
    // TODO not sure we need this logic now as we use Camel to create unique
    // ids...
    return true;
    /*
     * Route route = (this instanceof Route) ? (Route)this :
     * (Route)getParent(); boolean unique = true; for (AbstractNode child :
     * route.getChildren()) { if (child.getId().equals(id)) { unique =
     * false; break; } } return unique;
     */}

  public List<AbstractNode> getChildren() {
    return Collections.EMPTY_LIST;
  }

  /**
   * returns the name under which the icon is registered in the image registry
   * of the bundle
   *
   * @return the name / key of the icon
   */
  public String getIconName() {
    return ICON;
  }


  public String getCategoryName() {
    return "Routing";
  }


  public String getSmallIconName() {
    String iconName = getIconName();
    return iconName.replace(".png", "16.png");
  }

  public Image getImage() {
    if (image == null) {
      String name = getIconName();
      image = Activator.getDefault().getImage(name);
    }
    return image;
  }

  public Image getSmallImage() {
    if (smallImage == null) {
      String name = getSmallIconName();
      smallImage = Activator.getDefault().getImage(name);
    }
    return smallImage;
  }

  /**
   * Invoke when content which defines the image kind is changed
   */
  protected void clearImages() {
    image = null;
    smallImage = null;
  }

  /**
   * Returns true if this node can have output added so that a new Flow can be
   * added from this node to something else
   */
  public boolean canAcceptOutput() {
    Class<?> aClass = getCamelDefinitionClass();
    ProcessorDefinition def = createCamelDefinition();
    return CamelModelUtils.canAcceptOutput(aClass, def);
  }

  /**
   * By default we load consective steps in a route as children of the each node so that
   * we load from(a).to(b).to(c) as from -> to(b) -> to(c) with the parent changing.
   *
   * However sometimes we don't want to do that for certain nodes as they can already accept input
   */
  public boolean isNextSiblingStepAddedAsNodeChild() {
    Class<?> aClass = getCamelDefinitionClass();
    ProcessorDefinition def = createCamelDefinition();
    return CamelModelUtils.isNextSiblingStepAddedAsNodeChild(aClass, def);
  }

  /**
   * Returns true if this node can have input added so that a new Flow can be
   * added to this node from something else
   */
  public boolean canAcceptInput(AbstractNode newInput) {
    if (newInput.getParent() instanceof RouteSupport &&
        newInput instanceof Endpoint) {
      // only if all inputs are rootlevel inputs we allow them
      return true;
    }
    // nodes by default do not support multiple inputs
    return getInputs().size() <= 0;
  }

  public Class<?> getCamelDefinitionClass() {
    return null;
  }

  /**
   * override this method to register class specific property descriptors
   *
   * @param descriptors
   *            a map of descriptors
   */
  protected void addCustomProperties(Map<String, PropertyDescriptor> descriptors) {
    // nothing to do here
  }

  /**
   * Attach a non-null PropertyChangeListener to this object.
   *
   * @param l
   *            a non-null PropertyChangeListener instance
   * @throws IllegalArgumentException
   *             if the parameter is null
   */
  public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
    if (l == null) {
      throw new IllegalArgumentException();
    }
    listeners.addPropertyChangeListener(l);
  }

  public void firePropertyChange(String prop, Object old, Object newValue) {
    // hack to ensure empty strings will be set as null instead
    // this ensures that the generated source dont output empty attributes / nodes etc
    // as the value should be null instead of an empty string
    if (newValue instanceof String && "".equals(newValue)) {
      // must disable listeners while setting null value
      disableListeners = true;
      setPropertyValue(prop, null);
      disableListeners = false;
    }
    if (!disableListeners && hasPropertyListener(prop)) {
      listeners.firePropertyChange(prop, old, newValue);
    }
  }

  protected void fireChildAdded(String prop, Object child, Object index) {
    if (hasPropertyListener(prop)) {
      listeners.firePropertyChange(prop, index, child);
    }
  }

  protected void fireChildRemoved(String prop, Object child) {
    if (hasPropertyListener(prop)) {
      listeners.firePropertyChange(prop, child, null);
    }
  }

  protected void fireStructureChange(String prop, Object child) {
    if (hasPropertyListener(prop)) {
      listeners.firePropertyChange(prop, null, child);
    }
  }

  protected boolean hasPropertyListener(String prop) {
    return listeners != null && listeners.hasListeners(prop);
  }

  /**
   * Remove a PropertyChangeListener from this component.
   *
   * @param l
   *            a PropertyChangeListener instance
   */
  public synchronized void removePropertyChangeListener(PropertyChangeListener l) {
    if (l != null && listeners != null) {
      listeners.removePropertyChangeListener(l);
    }
  }

  /**
   * @return the id
   */
  public synchronized String getId() {
    if (id == null) {
      if (lazyCreateIds) {
        id = getNewID();
      }
    }
    return this.id;
  }

  /**
   * @param id
   *            the id to set
   */
  public void setId(String id) {
    String oldId = this.id;
    this.id = id;
    if (!Objects.equal(this.id, oldId)) {
      firePropertyChange(PROPERTY_ID, oldId, this.id);
    }
  }

  public Boolean getInheritErrorHandler() {
    return inheritErrorHandler;
  }

  public void setInheritErrorHandler(Boolean inheritErrorHandler) {
    Boolean old = this.inheritErrorHandler;
    this.inheritErrorHandler = inheritErrorHandler;
    if (!Objects.equal(inheritErrorHandler, old)) {
      firePropertyChange(PROPERTY_INHERITERRORHANDLER, old, this.inheritErrorHandler);
    }
  }

  /**
   * creates a random id
   *
   * @return a random id
   */
  public String getNewID() {
    String answer = null;
    if (useCamelIds) {
      ProcessorDefinition definition = createCamelDefinition();
      if (definition != null) {
        answer = definition.idOrCreate(getNodeIdFactory());
      }
    }
    if (answer == null) {
      answer = UUID.randomUUID().toString();
    }
    return answer;
  }

  protected synchronized static NodeIdFactory getNodeIdFactory() {
    if (nodeIdFactory == null) {
      nodeIdFactory = new DefaultCamelContext().getNodeIdFactory();
    }
    return nodeIdFactory;
  }

  /**
   * @return the description
   */
  public synchronized String getDescription() {
    /*
    if (description == null) {
      this.description = toString();
    }
     */
    return this.description;
  }

  /**
   * @param description
   *            the description to set
   */
  public void setDescription(String description) {
    String oldDesc = this.description;
    this.description = description;
    if (!Objects.equal(description, oldDesc)) {
      firePropertyChange(PROPERTY_DESCRIPTION, oldDesc, this.description);
    }
  }

  /**
   * @return the parent
   */
  public RouteContainer getParent() {
    return this.parent;
  }

  /**
   * @param parent
   *            the parent to set
   */
  public void setParent(RouteContainer parent) {
    if (this.parent != null) {
      this.parent.removeChild(this);
    }
    this.parent = parent;
    if (this.parent != null) {
      this.parent.addChild(this);
    }
  }

  /**
   * @return the layout
   */
  public Rectangle getLayout() {
    return this.layout;
  }

  /**
   * @param layout
   *            the layout to set
   */
  public void setLayout(Rectangle layout) {
    Rectangle oldLayout = this.layout;
    this.layout = layout;
    firePropertyChange(PROPERTY_LAYOUT_NODE, oldLayout, this.layout);
  }

  /**
   * Returns the text to display on the shape such as the URI of an endpoint
   * or ref/method of a bean. This defaults to the ID of the node if none is
   * available
   *
   * @return the string to be displayed as decoration on the shape figure
   */
  public final String getDisplayText() {
    return getDisplayText(true);
  }

  public final String getDisplayText(boolean useID) {

    // honor the PREFER_ID_AS_LABEL preference
    // we initially set it to the value of the contains method
    boolean preferID = false;
    if (useID) {
      preferID = PreferenceManager.getInstance().containsPreference(
          PreferencesConstants.EDITOR_PREFER_ID_AS_LABEL);
      // as second step if the value is there, we use it for the flag
      if (PreferenceManager.getInstance().containsPreference(PreferencesConstants.EDITOR_PREFER_ID_AS_LABEL)) {
        preferID = PreferenceManager.getInstance().loadPreferenceAsBoolean(
            PreferencesConstants.EDITOR_PREFER_ID_AS_LABEL);
      }
    }

    // we only return the id if we are told so by the preference AND the
    // value of the ID is set != null
    if (preferID && getId() != null) {
      return getId();
    }

    if (this instanceof Endpoint) {
      Endpoint node = (Endpoint) this;
      if (node.getUri() != null && node.getUri().trim().length()>0) {
        // uri specified, use it
        return node.getUri();
      }
    } else if (this instanceof Bean) {
      Bean node = (Bean) this;
      return "bean " + Strings.getOrElse(node.getRef());
    } else if (this instanceof Catch) {
      Catch node = (Catch) this;
      List exceptions = node.getExceptions();
      if (exceptions != null && exceptions.size() > 0) {
        return "catch " + exceptions;
      } else {
        return "catch " + Expressions.getExpressionOrElse(node.getHandled());
      }
    } else if (this instanceof Choice) {
      return "choice";
    } else if (this instanceof ConvertBody) {
      ConvertBody node = (ConvertBody) this;
      return "convertBody " + Strings.getOrElse(node.getType());
    } else if (this instanceof Enrich) {
      Enrich node = (Enrich) this;
      return "enrich " + Strings.getOrElse(node.getResourceUri());
    } else if (this instanceof Finally) {
      return "finally";
    } else if (this instanceof InOnly) {
      InOnly node = (InOnly) this;
      return "inOnly " + Strings.getOrElse(node.getUri());
    } else if (this instanceof InOut) {
      InOut node = (InOut) this;
      return "inOut " + Strings.getOrElse(node.getUri());
    } else if (this instanceof InterceptSendToEndpoint) {
      InterceptSendToEndpoint node = (InterceptSendToEndpoint) this;
      return "intercept " + Strings.getOrElse(node.getUri());
    } else if (this instanceof Log) {
      Log node = (Log) this;
      return "log " + Strings.getOrElse(node.getLogName());
    } else if (this instanceof Marshal) {
      return "marshal";
    } else if (this instanceof OnException) {
      OnException node = (OnException) this;
      return "on exception " + Strings.getOrElse(node.getExceptions());
    } else if (this instanceof Otherwise) {
      return "otherwise";
    } else if (this instanceof PollEnrich) {
      PollEnrich node = (PollEnrich) this;
      return "poll enrich " + Strings.getOrElse(node.getResourceUri());
    } else if (this instanceof RemoveHeader) {
      RemoveHeader node = (RemoveHeader) this;
      return "remove header " + Strings.getOrElse(node.getHeaderName());
    } else if (this instanceof RemoveProperty) {
      RemoveProperty node = (RemoveProperty) this;
      return "remove property " + Strings.getOrElse(node.getPropertyName());
    } else if (this instanceof Rollback) {
      Rollback node = (Rollback) this;
      return "rollback " + Strings.getOrElse(node.getMessage());
    } else if (this instanceof SetExchangePattern) {
      SetExchangePattern node = (SetExchangePattern) this;
      ExchangePattern pattern = node.getPattern();
      if (pattern == null) {
        return "setExchangePattern";
      } else {
        return "set " + pattern;
      }
    } else if (this instanceof Sort) {
      Sort node = (Sort) this;
      return "sort " + Expressions.getExpressionOrElse(node.getExpression());
    } else if (this instanceof When) {
      When node = (When) this;
      return "when " + Expressions.getExpressionOrElse(node.getExpression());
    } else if (this instanceof Unmarshal) {
      return "unmarshal";
    } else if (this instanceof Try) {
      return "try";
    } else if (this instanceof LoadBalance) {
      LoadBalance load = (LoadBalance) this;
      if (load.getRef() != null) {
        return "custom " + Strings.getOrElse(load.getRef());
      } else if (load.getLoadBalancerType() != null) {
        if (load.getLoadBalancerType().getClass().isAssignableFrom(CustomLoadBalancerDefinition.class)) {
          CustomLoadBalancerDefinition custom = (CustomLoadBalancerDefinition) load.getLoadBalancerType();
          return "custom " + Strings.getOrElse(custom.getRef());
        } else if (load.getLoadBalancerType().getClass().isAssignableFrom(FailoverLoadBalancerDefinition.class)) {
          return "failover";
        } else if (load.getLoadBalancerType().getClass().isAssignableFrom(RandomLoadBalancerDefinition.class)) {
          return "random";
        } else if (load.getLoadBalancerType().getClass().isAssignableFrom(RoundRobinLoadBalancerDefinition.class)) {
          return "round robin";
        } else if (load.getLoadBalancerType().getClass().isAssignableFrom(StickyLoadBalancerDefinition.class)) {
          return "sticky";
        } else if (load.getLoadBalancerType().getClass().isAssignableFrom(TopicLoadBalancerDefinition.class)) {
          return "topic";
        } else if (load.getLoadBalancerType().getClass().isAssignableFrom(WeightedLoadBalancerDefinition.class)) {
          return "weighted";
        }
      } else {
        return "load balance";
      }
    }

    String answer = null;
    @SuppressWarnings("rawtypes")
    ProcessorDefinition camelDef = createCamelDefinition();
    if (camelDef != null) {
      try {
        answer = camelDef.getLabel();
      } catch (Exception e) {
        // ignore errors in Camel
      }
    }
    if (Strings.isBlank(answer)) {
      answer = getId();
    }
    if (Strings.isBlank(answer)) {
      answer = getPatternName();
    }
    return answer;
  }

  /**
   * Returns the pattern name
   */
  public String getPatternName() {
    return getClass().getSimpleName();
  }

  /**
   * Returns the text to display as the tooltip of a shape figure.
   *
   * @return the tooltip to display for the shape figure
   */
  public String getDisplayToolTip() {
    if (this instanceof When) {
      When node = (When) this;
      return "when " + Expressions.getExpressionOrElse(node.getExpression());
    }
    String answer = Tooltips.tooltip(getPatternName());
    if (answer == null) {
      ProcessorDefinition camelDef = createCamelDefinition();
      if (camelDef != null) {
        if (camelDef instanceof RouteDefinition) {
          RouteDefinition route = (RouteDefinition) camelDef;
          return "Route " + (route.getId() != null ? route.getId() : "");
        } else if (camelDef instanceof ToDefinition && isNotTarget()) {
          // if its the first in the route and its an endpoint, then use From instead of To
          // (notice that createCamelDefinition returns a ToDefinition for all kind of Endpoints)
          return "From " + camelDef.getLabel();
        }
        return camelDef.getShortName() + " " + camelDef.getLabel();
      }
      return getDescription();
    }
    return answer;
  }

  /**
   * Returns the documentation file name if available or null
   */
  public String getDocumentationFileName() {
    return null;
  }

  /*
   * (non-Javadoc)
   *
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return String.format("%s: %s", getClass().getSimpleName(), getDisplayText());
  }

  /**
   * Returns a list of all the nodes which this node links to
   */
  public List<AbstractNode> getOutputs() {
    ArrayList<AbstractNode> answer = new ArrayList<AbstractNode>();
    List<Flow> list = getSourceConnections();
    for (Flow flow : list) {
      answer.add(flow.getTarget());
    }
    return answer;
  }

  public List<AbstractNode> getInputs() {
    ArrayList<AbstractNode> answer = new ArrayList<AbstractNode>();
    List<Flow> list = getTargetConnections();
    for (Flow flow : list) {
      answer.add(flow.getSource());
    }
    return answer;
  }

  // TODO is target the right name for, from(this).to(to)
  public void addTargetNode(AbstractNode to) {
    new Flow(this, to);
  }

  /**
   * Adds the given node as the last step in this tree of steps.
   * For example for a Choice node then we add this to the last When / Otherwise branches etc.
   */
  public void addTargetNodeAsLastStep(AbstractNode node) {
    List<AbstractNode> children = getOutputs();
    if (children == null || children.isEmpty()) {
      addTargetNode(node);
    } else {
      for (AbstractNode child : children) {
        child.addTargetNodeAsLastStep(node);
      }
    }
  }


  /**
   * Add an incoming or outgoing connection to this shape.
   *
   * @param conn
   *            a non-null connection instance
   * @throws IllegalArgumentException
   *             if the connection is null or has not distinct endpoints
   */
  public void addConnection(Flow conn) {
    AbstractNode target = conn.getTarget();
    if (conn == null || conn.getSource() == target) {
      throw new IllegalArgumentException();
    }

    if (containsFlow(sourceConnections, conn) || containsFlow(targetConnections, conn)) {
      // ignore duplicates
      return;
    }

    if (conn.getSource() == this) {
      Predicate<Flow> before = createBeforePredicate(target);
      boolean added = false;
      if (before != null) {
        int idx = 0;
        for (Flow f : sourceConnections) {
          if (before.matches(f)) {
            sourceConnections.add(idx, conn);
            added = true;
            break;
          }
          idx++;
        }
      }
      if (!added) {
        sourceConnections.add(conn);
      }
      firePropertyChange(SOURCE_CONNECTIONS, null, conn);
    } else if (target == this) {
      targetConnections.add(conn);
      firePropertyChange(TARGET_CONNECTIONS, null, conn);
    }
  }

  protected boolean containsFlow(Collection<Flow> coll, Flow flow) {
    for (Flow aFlow : coll) {
      boolean sourceEqual = Objects.equal(aFlow.getSource(), flow.getSource());
      boolean targetEqual = Objects.equal(aFlow.getTarget(), flow.getTarget());
      if (sourceEqual && targetEqual) {
        return true;
      }
    }
    return false;
  }

  /**
   * Creates a predicate to decide if a flow for the given target should be added
   * before a flow
   */
  protected Predicate<Flow> createBeforePredicate(AbstractNode target) {
    if (this instanceof Choice) {
      // when is first
      if (target instanceof When) {
        return new Predicate<Flow>() {
          @Override
          public boolean matches(Flow f) {
            return f.getTarget() instanceof Otherwise || !(f.getTarget() instanceof When);
          }
        };
      } else if (target instanceof Otherwise) {
        return new Predicate<Flow>() {
          @Override
          public boolean matches(Flow f) {
            return !(f.getTarget() instanceof When);
          }
        };
      }
    } else if (this instanceof Try) {
      if (target instanceof Catch) {
        // add before finally
        return new Predicate<Flow>() {
          @Override
          public boolean matches(Flow f) {
            return f.getTarget() instanceof Finally;
          }
        };
      } else if (!(target instanceof Finally)) {
        // add before catch/finally
        return new Predicate<Flow>() {
          @Override
          public boolean matches(Flow f) {
            return f.getTarget() instanceof Catch || f.getTarget() instanceof Finally;
          }
        };
      }
    }
    return null;
  }

  public void addConnections(List<Flow> flows) {
    for (Flow flow : flows) {
      addConnection(flow);
    }
  }

  /**
   * Remove an incoming or outgoing connection from this shape.
   *
   * @param conn
   *            a non-null connection instance
   * @throws IllegalArgumentException
   *             if the parameter is null
   */
  void removeConnection(Flow conn) {
    if (conn == null) {
      throw new IllegalArgumentException();
    }

    if (conn.getSource() == this) {
      sourceConnections.remove(conn);
      firePropertyChange(SOURCE_CONNECTIONS, null, conn);
    } else if (conn.getTarget() == this) {
      targetConnections.remove(conn);
      firePropertyChange(TARGET_CONNECTIONS, null, conn);
    }
  }

  /**
   * As this node is being deleted from the diagram we need to remove all the
   * flows from its neighbours
   */
  public void delete(List<Flow> deletedFlows) {
    // sourceConnections have source == this
    for (Flow flow : sourceConnections) {
      flow.getTarget().removeConnection(flow);
      deletedFlows.add(flow);
    }
    for (Flow flow : targetConnections) {
      flow.getSource().removeConnection(flow);
      deletedFlows.add(flow);
    }
  }

  /**
   * Return a List of outgoing Connections.
   */
  public List<Flow> getSourceConnections() {
    return new ArrayList<Flow>(sourceConnections);
  }

  /**
   * Return a List of incoming Connections.
   */
  public List<Flow> getTargetConnections() {
    return new ArrayList<Flow>(targetConnections);
  }

  public List<AbstractNode> getTargetNodes() {
    List<AbstractNode> answer = new ArrayList<AbstractNode>();
    for (Flow connection : targetConnections) {
      answer.add(connection.getTarget());
    }
    return answer;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.ui.views.properties.IPropertySource#getEditableValue()
   */
  @Override
  public Object getEditableValue() {
    return this;
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
   */
  @Override
  public IPropertyDescriptor[] getPropertyDescriptors() {
    return this.descriptors.values().toArray(new IPropertyDescriptor[this.descriptors.size()]);
  }

  public IPropertyDescriptor getPropertyDescriptor(Object id) {
    IPropertyDescriptor[] array = getPropertyDescriptors();
    for (IPropertyDescriptor descriptor : array) {
      if (Objects.equal(id, descriptor.getId())) {
        return descriptor;
      }
    }
    return null;
  }

  /*
  public <T> T getPropertyDefaultValue(Object id) {

  }
   */

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java
   * .lang.Object)
   */
  @Override
  public Object getPropertyValue(Object id) {
    if (PROPERTY_ID.equals(id)) {
      return getId();
    } else if (PROPERTY_DESCRIPTION.equals(id)) {
      return getDescription();
    } else if (PROPERTY_INHERITERRORHANDLER.equals(id)) {
      return getInheritErrorHandler();
    }
    return null;
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.ui.views.properties.IPropertySource#isPropertySet(java.lang
   * .Object)
   */
  @Override
  public boolean isPropertySet(Object id) {
    return descriptors.containsKey(id);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.ui.views.properties.IPropertySource#resetPropertyValue(java
   * .lang.Object)
   */
  @Override
  public void resetPropertyValue(Object id) {
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.ui.views.properties.IPropertySource#setPropertyValue(java
   * .lang.Object, java.lang.Object)
   */
  @Override
  public void setPropertyValue(Object id, Object value) {
    if (PROPERTY_ID.equals(id)) {
      setId((String) value);
    } else if (PROPERTY_DESCRIPTION.equals(id)) {
      setDescription((String) value);
    } else if (PROPERTY_INHERITERRORHANDLER.equals(id)) {
      setInheritErrorHandler((Boolean) value);
    }
  }

  /**
   * Returns true if the given name is a valid property name on this model.
   */
  public boolean isPropertyName(String name) {
    return name != null && descriptors.containsKey(name);
  }

  /**
   * Creates a new Camel model definition object
   */
  @SuppressWarnings("rawtypes")
  public abstract ProcessorDefinition createCamelDefinition();

  protected void loadPropertiesFromCamelDefinition(ProcessorDefinition processor) {
    description = processor.getDescriptionText();
    id = processor.getId();
    inheritErrorHandler = null;
    Boolean bool = processor.isInheritErrorHandler();
    if (bool != null && bool.booleanValue()) {
      inheritErrorHandler = true;
    }
  }

  @SuppressWarnings("rawtypes")
  protected void loadChildrenFromCamelDefinition(ProcessorDefinition processor) {
    List<ProcessorDefinition> outputs = getOutputs(processor);
    AbstractNode parent = this;
    for (ProcessorDefinition childProcessor : outputs) {
      AbstractNode node = NodeFactory.createNode(childProcessor, getParent());
      parent.addTargetNode(node);
      if (!parent.isMulticastNode(parent, node) && node.isNextSiblingStepAddedAsNodeChild()) {
        parent = node;
      }
    }
    // Activator.getLogger().debug("Now " + this + " with outputs " + getOutputs() +
    // " from camel outputs " + outputs);
  }

  /**
   * Returns true if this node is a multicast node - i.e. all immediate children are rendered connecting to this node.
   */
  protected boolean isMulticastNode(AbstractNode parent, AbstractNode child) {
    return parent instanceof Multicast ||
        (parent instanceof Choice && (child instanceof When || child instanceof Otherwise)) ||
        (parent instanceof Try && (child instanceof Catch || child instanceof Finally));
  }

  /**
   * Workaround of Camel bug for some reason getOutputs() on a
   * ChoiceDefinition does not return the children; but it returns the outputs
   * on the When?
   */
  protected List<ProcessorDefinition> getOutputs(ProcessorDefinition processor) { //
    if (processor instanceof ChoiceDefinition) {
      ChoiceDefinition choice = (ChoiceDefinition) processor;
      List<ProcessorDefinition> list = new ArrayList<ProcessorDefinition>();
      List<WhenDefinition> whenClauses = choice.getWhenClauses();
      if (whenClauses != null) {
        list.addAll(whenClauses);
      }
      OtherwiseDefinition otherwise = choice.getOtherwise();
      if (otherwise != null) {
        list.add(otherwise);
      }
      return list;
    } else {
      return processor.getOutputs();
    }
  }

  /**
   * Workaround of Camel bug for some reason addOutput() on a ChoiceDefinition
   * does not work with When/Otherwise
   */
  protected void addCamelOutput(ProcessorDefinition processor, ProcessorDefinition toNode) {
    // special for choice
    if (processor instanceof ChoiceDefinition) {
      ChoiceDefinition choice = (ChoiceDefinition) processor;
      if (toNode instanceof WhenDefinition) {
        choice.getWhenClauses().add((WhenDefinition) toNode);
      } else if (toNode instanceof OtherwiseDefinition) {
        choice.setOtherwise((OtherwiseDefinition) toNode);
      } else {
        // there may be a nested choice so we need to add it on its parent
        ProcessorDefinition grandParent = choice.getParent();
        if (grandParent != null) {
          grandParent.addOutput(toNode);
        } else {
          Activator.getLogger().warning("No parent of " + choice + " so cannot add output " + toNode);
        }
      }
    } else {
      // Work around to avoid NPE in Camel
      if (processor instanceof ExpressionNode) {
        ExpressionNode en = (ExpressionNode) processor;
        if (en.getExpression() == null) {
          en.setExpression(new ExpressionDefinition());
        }
      }
      processor.addOutput(toNode);
    }
  }

  @SuppressWarnings("rawtypes")
  public void savePropertiesToCamelDefinition(ProcessorDefinition processor) {
    if (inheritErrorHandler != null && inheritErrorHandler.booleanValue()) {
      processor.setInheritErrorHandler(inheritErrorHandler);
    } else {
      // zap false values as we do not want them in the generated XML
      // but we should preserve "false" for the load balance EIP, as it may be
      // needed to explicit configured inheritErrorHandler="false" for the failover EIP
      if (processor.getClass().isAssignableFrom(LoadBalanceDefinition.class)) {
        if (inheritErrorHandler != null) {
          processor.setInheritErrorHandler(inheritErrorHandler);
        } else {
          processor.setInheritErrorHandler(null);
        }
      } else {
        processor.setInheritErrorHandler(null);
      }
    }
    if (description != null && description.trim().length() > 0) {
      DescriptionDefinition descriptionDefinition = new DescriptionDefinition();
      descriptionDefinition.setText(description);
      processor.setDescription(descriptionDefinition);
    }
    if (id != null && id.trim().length() > 0) {
      processor.setId(id);
      resetCustomId(processor);
    }
  }

  @SuppressWarnings("rawtypes")
  public void saveChildrenToCamelDefinitions(ProcessorDefinition processor, ArrayList<AbstractNode> processedNodes) {
    // now lets recurse into any children...
    // TODO not sure why we use SourceConnections - shouldn't it be
    // TargetConnections?
    List<Flow> children = getSourceConnections();
    for (Flow flow : children) {
      AbstractNode target = flow.getTarget();
      ProcessorDefinition toNode = target.createCamelDefinition();

      if (!processedNodes.contains(target)) {
        addCamelOutput(processor, toNode);
        processedNodes.add(target);
      }


      // Convert any expressions to the real underlying XML
      IPropertyDescriptor[] propertyDescriptors = getPropertyDescriptors();
      for (IPropertyDescriptor descriptor : propertyDescriptors) {
        if (descriptor instanceof ExpressionPropertyDescriptor) {

        }
      }


      ProcessorDefinition outputNode;
      if (target.canAcceptOutput()) {
        outputNode = toNode;
      } else {
        outputNode = processor;
      }

      target.saveChildrenToCamelDefinitions(outputNode, processedNodes);
    }
  }

  /**
   * Allow values to be transformed before they are turned into JAXB objects, such as to unwrap custom objects like {@link LanguageExpressionBean}
   * into the correct underlying JAXB model object
   */
  protected <T> T toXmlPropertyValue(Object id, T value) {
    if (value instanceof LanguageExpressionBean) {
      LanguageExpressionBean lb = (LanguageExpressionBean) value;
      return (T) lb.toXmlExpression();
    }
    if (CamelModelHelper.isPropertyListOFSetHeaders(id) && value instanceof List) {
      List list = (List) value;
      for (Object object : list) {
        Object newValue = object;
        if (object instanceof SetHeaderDefinition) {
          SetHeaderDefinition sh = (SetHeaderDefinition) object;
          ExpressionDefinition expression = sh.getExpression();
          if (expression instanceof LanguageExpressionBean) {
            LanguageExpressionBean lb = (LanguageExpressionBean) expression;
            sh.setExpression(lb.toXmlExpression());
          }
        }
      }
    }
    return value;
  }

  /**
   * Returns true if this node is not a source of connections (i.e. its a From
   * node or a node that is not connected to anything
   */
  public boolean isNotTarget() {
    // TODO really should this be getSourceConnections()??
    return getTargetConnections().isEmpty();
  }

  /**
   * recalculate layout
   */
  public void layout() {
    // nothing to do
  }

  /**
   * Returns all source and target connections
   */
  public Set<Flow> getAllConnections() {
    Set<Flow> set = new HashSet<Flow>();
    set.addAll(getSourceConnections());
    set.addAll(getTargetConnections());
    return set;
  }

  /**
   * Returns true if this node can support more output(s).
   * <p/>
   * Some nodes can support multiple outputs, where others can only support exactly one output.
   *
   * @return true if this node can support more output(s)
   */
  public boolean canSupportOutput() {
    // we can only support output if either if node supports outputs
    boolean output = canAcceptOutput();
    if (output) {
      return true;
    }

    // or if node does not support outputs, but there are no existing flows
    for (Flow flow : getAllConnections()) {
      if (flow.getSource() == this) {
        // there is already a flow on this node so we cannot accept more outputs
        return false;
      }
    }

    return true;
  }
 
  /**
   * checks if the node is the from node and therefore
   * the first node in my route
   *
   * @return
   */
  public boolean isFirstNodeInRoute() {
    return getInputs().isEmpty();
  }
 
  /**
   * checks if the node is an expression node
   *
   * @return
   */
  public boolean supportsBreakpoint() {
    return !isFirstNodeInRoute() &&         // not working on the From node
        this instanceof When == false &&    // not working for When nodes
        this instanceof Otherwise == false; /**&&  // not working for Otherwise nodes
        Strings.isBlank(getCamelContextId()) == false && // not working if no Camel Context Id is set
        Strings.isBlank(getId()) == false;    // not working if no ID is set **/
  }

  /**
   * Returns true if this node can be the source and be connected to the
   * target in a diagram like <code>this -> target</code>
   *
   * @param target
   *            the node that this node is trying to be connected to.
   * @return true if this source can be connected to the given target
   */
  public boolean canConnectTo(AbstractNode target) {
    if (target == this || target instanceof RouteContainer) {
      return false;
    }
    if (target == null) {
      return true;
    }
    // lets check that we are not already connected to this node
    // either as a source or as a target
    for (Flow flow : getAllConnections()) {
      if (target.equals(flow.getSource()) || target.equals(flow.getTarget())) {
        return false;
      }
    }

    // detect loop between this and target
    if (detectLoops(this, target)) {
      return false;
    }

    // detect loops between this and target inputs
    if (detectInputLoops(this, target)) {
      return false;
    }

    if (!target.canAcceptInput(this)) {
      return false;
    }

    boolean isOtherwise = target instanceof Otherwise;
    boolean isWhen = target instanceof When;
    boolean whenOrOtherwise = isWhen || isOtherwise;
    boolean thisIsChoice = this instanceof Choice;

    // try / catch / finally is not like choice/when/otherwise as try can
    // connect to anything
    if (thisIsChoice) {
      if (isOtherwise) {
        List<AbstractNode> outputs = getOutputs(Otherwise.class);
        return outputs.size() == 0;
      }
      // you must also be able to connect to anything from choice
      // as it may be a node thats _after_ the content based router
      return true;
    } else if (whenOrOtherwise) {
      if (thisIsChoice) {
        return true;
      } else {
        if (isWhen) {
          // various classes in the model have a when claus, lets find
          // them all
          Class<?> camelClass = getCamelDefinitionClass();
          if (camelClass != null) {
            Field[] fields = camelClass.getDeclaredFields();
            for (Field field : fields) {
              if (WhenDefinition.class.isAssignableFrom(field.getType())) {
                return true;
              }
            }
          }
        }
        return false;
      }
    } else {
      return true;
    }
  }

  /**
   * Gets the input of the given node (recursive), that matches the criteria(s).
   *
   * @param node  the node.
   * @param inputMustAcceptOutput whether the input must accept outputs (will recursive until matching this parameter)
   * @return the input or <tt>null</tt> if no input was found
   */
  private AbstractNode getMatchedInput(AbstractNode node, boolean inputCanAcceptOutput) {
    if (node == null) {
      return null;
    }

    for (AbstractNode input : node.getInputs()) {
      if (!inputCanAcceptOutput) {
        return input;
      } else if (input.canAcceptOutput()) {
        return input;
      } else {
        // recursive
        AbstractNode child = getMatchedInput(input, inputCanAcceptOutput);
        if (child != null) {
          return child;
        }
      }
    }

    return null;
  }

  /**
   * crawls all nodes to see if there is some loop
   *
   * @param sourceNode
   *            the connection source
   * @param targetNode
   *            the connection target
   * @return true if there was a loop detected
   */
  protected boolean detectLoops(AbstractNode sourceNode, AbstractNode targetNode) {
    List<AbstractNode> nodes = sourceNode.getInputs();
    for (AbstractNode n : nodes) {
      if (n.equals(targetNode)) {
        // the target is already used as source somewhere in route
        return true;
      } else {
        // check if the inputs of that node contain out target
        if (detectLoops(n, targetNode)) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Crawl the inputs of the nodes and detect if they will become looped if source and target would become connected.
   *
   * @param sourceNode  the connection source
   * @param targetNode  the connection target
   * @return true if a loop detected
   */
  protected boolean detectInputLoops(AbstractNode sourceNode, AbstractNode targetNode) {
    // we cannot connect if target and this node has same nested parent (that accepts multiple outputs)
    // as it would become a circular connection
    AbstractNode thisParent = getMatchedInput(sourceNode, true);
    AbstractNode targetParent = getMatchedInput(targetNode, true);
    if (thisParent != null && targetParent != null && thisParent.equals(targetParent)) {
      return true;
    }
    // no loop detected
    return false;
  }

  public List<AbstractNode> getOutputs(Class<? extends AbstractNode> aClass) {
    List<AbstractNode> list = getOutputs();
    List<AbstractNode> answer = new ArrayList<AbstractNode>();
    for (AbstractNode node : list) {
      if (aClass.isInstance(node)) {
        answer.add(node);
      }
    }
    return answer;
  }

  /**
   * Appends all the endpoint URIs used by this node to the given set
   */
  public void appendEndpointUris(Set<String> uris) {
    List<AbstractNode> outputs = getOutputs();
    for (AbstractNode node : outputs) {
      node.appendEndpointUris(uris);
    }
  }

  protected void appendDescendents(Set<AbstractNode> answer) {
    answer.add(this);
    List<AbstractNode> list = getOutputs();
    for (AbstractNode node : list) {
      node.appendDescendents(answer);
    }
  }

  /**
   * Removes the connection to the given node
   */
  public void removeConnection(AbstractNode target) {
    List<Flow> list = getSourceConnections();
    for (Flow flow : list) {
      if (flow.getTarget() == target) {
        flow.disconnect();
        return;
      }
    }
  }

  /**
   * Detatches this node from the route it contains and any source/target connections
   */
  public void detach() {
    RouteContainer p = getParent();
    if (p != null) {
      p.removeChild(this);
    }

    disconnect(getSourceConnections());
    disconnect(getTargetConnections());

  }

  private void disconnect(List<Flow> list) {
    for (Flow flow : list) {
      flow.disconnect();
    }
  }


  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  protected boolean isSame(Object a, Object b) {
    // null checks
    if (a == null && b == null) return true;
    if (a == null && b != null) return false;
    if (a != null && b == null) return false;

    if (a instanceof String && b instanceof String) {
      return ((String)a).equals(b);
    }

    if (a instanceof ExpressionDefinition &&
        b instanceof ExpressionDefinition) {
      // compare expressions
      ExpressionDefinition e_a = (ExpressionDefinition)a;
      ExpressionDefinition e_b = (ExpressionDefinition)b;

      if (!isSameExpression(e_a, e_b)) {
        return false;
      }
    } else {
      // do reflection crawling
      if (!isSameByReflectionCompare(a, b)) {
        return false;
      }
    }

    return true;
  }

  /**
   * compares two expressions
   *
   * @param e_a
   * @param e_b
   * @return
   */
  private boolean isSameExpression(ExpressionDefinition e_a, ExpressionDefinition e_b) {
    if (!e_a.getExpression().equals(e_b.getExpression()) ||
        !e_a.getLanguage().equals(e_b.getLanguage())) {
      return false;
    }

    XPathExpression xp_a = null;
    XPathExpression xp_b = null;

    if (e_a instanceof XPathExpression) {
      xp_a = (XPathExpression)e_a;
      xp_b = null;

      if (e_b instanceof XPathExpression) {
        xp_b = (XPathExpression)e_b;
      } else {
        try {
          Field f_original = e_b.getClass().getDeclaredField("original");
          f_original.setAccessible(true);
          xp_b = (XPathExpression)f_original.get(e_b);
        } catch (Exception ex) {
          Activator.getLogger().error(ex);
        }
      }

      if (xp_a.getResultTypeName() != xp_b.getResultTypeName()) {
        return false;
      }
    } else if (e_b instanceof XPathExpression) {
      xp_a = null;
      xp_b = (XPathExpression)e_b;

      if (e_a instanceof XPathExpression) {
        xp_a = (XPathExpression)e_a;
      } else {
        try {
          Field f_original = e_a.getClass().getDeclaredField("original");
          f_original.setAccessible(true);
          xp_a = (XPathExpression)f_original.get(e_a);
        } catch (Exception ex) {
          Activator.getLogger().error(ex);
        }
      }

      if (xp_a.getResultTypeName() != xp_b.getResultTypeName()) {
        return false;
      }
    }
    return true;
  }

  private boolean isSameByReflectionCompare(Object a, Object b) {
    // now use reflection to compare all common fields of the objects
    boolean isSame = compareObjects(a, b);
    if (isSame) {
      // now opposite order to compare really each field of each object
      isSame = compareObjects(b, a);
    }
    return isSame;
  }

  private boolean compareObjects(Object a, Object b) {
    Class ca = a.getClass();
    Class cb = b.getClass();

    boolean found = false;
    for (Field fa : ca.getDeclaredFields()) {
      fa.setAccessible(true);
      found = false;
      for (Field fb : cb.getDeclaredFields()) {
        fb.setAccessible(true);
        if (fb.getName().equals(fa.getName())) {
          found = true;
          try {
            Object valA = fa.get(a);
            Object valB = fb.get(b);
            if (valB == null && valA != null) return false;
            if (valB != null && valA != null && !valB.equals(valA)) {
              return false;
            }
          } catch (Exception ex) {
            Activator.getLogger().error(ex);
          }
        }
      }
      if (!found) {
        return false;
      }
    }

    Class sca = ca.getSuperclass();
    Class scb = cb.getSuperclass();
    // both must have the same super class or both no super class
    if (sca == null && scb == null) {
      // all fine
    } else if (sca != null && scb != null) {
      // all fine
    } else {
      return false;
    }

    // both super classes must be equal
    if (!sca.getName().equals(scb.getName())) {
      return false;
    }

    return true;
  }
 
  /**
   * this is a workaround for a bug in Camel 2.12 and will be
   * most likely removed when the bug has been fixed
   * (see https://issues.apache.org/jira/browse/CAMEL-7544)
   *
   * @param def definition
   */
  @Deprecated
  protected void resetCustomId(OptionalIdentifiedDefinition def) {
    def.setCustomId(null);
  }
}
TOP

Related Classes of org.fusesource.ide.camel.model.AbstractNode

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.