code: /*--------------------------------------------------------------*
    Copyright (C) 2006-2015 OpenSim Ltd.
  
    This file is distributed WITHOUT ANY WARRANTY. See the file
    'License' for details on this and other legal matters.
  *--------------------------------------------------------------*/
  
  package org.omnetpp.ned.model.ex;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.eclipse.core.runtime.Assert;
  import org.omnetpp.common.util.StringUtils;
  import org.omnetpp.ned.model.DisplayString;
  import org.omnetpp.ned.model.INedElement;
  import org.omnetpp.ned.model.NedElement;
  import org.omnetpp.ned.model.interfaces.IChannelKindTypeElement;
  import org.omnetpp.ned.model.interfaces.IConnectableElement;
  import org.omnetpp.ned.model.interfaces.INedTypeElement;
  import org.omnetpp.ned.model.interfaces.INedTypeInfo;
  import org.omnetpp.ned.model.interfaces.INedTypeResolver;
  import org.omnetpp.ned.model.interfaces.ISubmoduleOrConnection;
  import org.omnetpp.ned.model.notification.NedModelEvent;
  import org.omnetpp.ned.model.pojo.ConnectionElement;
  import org.omnetpp.ned.model.pojo.ParametersElement;
  
  /**
   * Represents a connection in a compound module.
   *
   * @author rhornig
   */
  public class ConnectionElementEx extends ConnectionElement implements ISubmoduleOrConnection
  {
      private INedTypeResolver resolver;
      protected DisplayString displayString = null;
  
      protected ConnectionElementEx(INedTypeResolver resolver) {
          Assert.isNotNull(resolver, "This NED element type needs a resolver");
          this.resolver = resolver;
      }
  
      protected ConnectionElementEx(INedTypeResolver resolver, INedElement parent) {
          super(parent);
          Assert.isNotNull(resolver, "This NED element type needs a resolver");
          this.resolver = resolver;
      }
  
      public INedTypeResolver getResolver() {
          return resolver;
      }
  
      public IConnectableElement getSrcModuleRef() {
          return resolveConnectedModule(getSrcModule());
      }
  
      public IConnectableElement getDestModuleRef() {
          return resolveConnectedModule(getDestModule());
      }
  
      /**
       * Sets the source module of the connection
       */
      public void setSrcModuleRef(IConnectableElement srcModule) {
          setSrcModule(connectedModuleName(srcModule));
      }
  
      /**
       * Sets the destination module of the connection
       */
      public void setDestModuleRef(IConnectableElement destModule) {
          setDestModule(connectedModuleName(destModule));
      }
  
      protected String connectedModuleName(IConnectableElement module) {
          return module == null ? null : (module instanceof CompoundModuleElementEx) ? "" : module.getName();
      }
  
      // helper functions to set the module names using references
      protected IConnectableElement resolveConnectedModule(String moduleName) {
          if (moduleName == null)
              return null; // not connected
  
          CompoundModuleElementEx compoundModule = getCompoundModule();
          if (compoundModule == null)
              return null; // this is a detached connection
          else
              return moduleName.equals("") ? compoundModule : compoundModule.getSubmoduleByName(moduleName);
      }
  
      /**
       * Returns the identifier of the source module instance the connection connected to
       */
      //FIXME factor out common part with next one
      public String getSrcModuleWithIndex() {
          String module = getSrcModule();
          if (getSrcModuleIndex() != null && !"".equals(getSrcModuleIndex()))
              module += "["+getSrcModuleIndex()+"]";
  
          return module;
      }
  
      /**
       * Returns the identifier of the destination gate instance the connection connected to
       */
      public String getDestModuleWithIndex() {
          String module = getDestModule();
          if (getDestModuleIndex() != null && !"".equals(getDestModuleIndex()))
              module += "["+getDestModuleIndex()+"]";
  
          return module;
      }
  
      /**
       * Returns the fully qualified src gate name (with module, index, gate, index)
       */
      public String getSrcGateFullyQualified() {
          String result = getSrcModuleWithIndex();
          if (!"".equals(result)) result += ".";
          result += getSrcGateWithIndex();
          return result;
      }
  
      /**
       * Returns the fully qualified dest gate name (with module, index, gate, index)
       */
      public String getDestGateFullyQualified() {
          String result = getDestModuleWithIndex();
          if (!"".equals(result)) result += ".";
          result += getDestGateWithIndex();
          return result;
      }
  
      /**
       * Returns the identifier of the source gate instance the connection is connected to
       */
      public String getSrcGateWithIndex() {
          return getGateNameWithIndex(getSrcGate(), getSrcGateSubg(), getSrcGateIndex(), getSrcGatePlusplus());
      }
  
      /**
       * Returns the identifier of the destination gate instance the connection is connected to
       */
      public String getDestGateWithIndex() {
          return getGateNameWithIndex(getDestGate(), getDestGateSubg(), getDestGateIndex(), getDestGatePlusplus());
      }
  
      public static String getGateNameWithIndex(String name, int subgate, String index, boolean isPlusPlus) {
          String gate = name;
          if (subgate == NED_SUBGATE_I) gate += "$i";
          if (subgate == NED_SUBGATE_O) gate += "$o";
          if (isPlusPlus) gate += "++";
          if (StringUtils.isNotEmpty(index)) gate += "["+index+"]";
          return gate;
      }
  
      /**
       * Returns the name of the source gate (gate name plus subgate, but without index)
       */
      public String getSrcGateEx() {
          return getGateName(getSrcGate(), getSrcGateSubg());
      }
  
      /**
       * Returns the name of the source gate (gate name plus subgate, but without index)
       */
      public String getDestGateEx() {
          return getGateName(getDestGate(), getDestGateSubg());
      }
  
      public static String getGateName(String name, int subgate) {
          String gate = name;
          if (subgate == NED_SUBGATE_I) gate += "$i";
          if (subgate == NED_SUBGATE_O) gate += "$o";
          return gate;
      }
  
      @Override
      public void fireModelEvent(NedModelEvent event) {
          // invalidate cached display string because NED tree may have changed outside the DisplayString class
          if (!NedElementUtilEx.isDisplayStringUpToDate(displayString, this))
              displayString = null;
          super.fireModelEvent(event);
      }
  
      public DisplayString getDisplayString() {
          if (displayString == null)
              displayString = new DisplayString(this, NedElementUtilEx.getDisplayStringLiteral(this));
          displayString.setFallbackDisplayString(NedElement.displayStringOf(getTypeOrLikeTypeRef()));
          return displayString;
      }
  
      public DisplayString getDisplayString(INedTypeElement actualType) {
          if (displayString == null)
              displayString = new DisplayString(this, NedElementUtilEx.getDisplayStringLiteral(this));
          displayString.setFallbackDisplayString(actualType.getDisplayString());
          return displayString;
      }
  
      /**
       * Returns the compound module containing this connection, or null if the
       * connection is not part of the model (i.e. has no compound module parent).
       */
      public CompoundModuleElementEx getCompoundModule() {
          return (CompoundModuleElementEx)getEnclosingTypeElement();
      }
  
      /**
       * Checks whether the current connection is valid (has valid submodules at both end)
       */
      public boolean isValid() {
          return getSrcModuleRef() != null && getDestModuleRef() != null;
      }
  
      // type management
  
      public String getTypeOrLikeType() {
          String likeType = getLikeType();
          if (StringUtils.isEmpty(likeType)) {
              String type = getType();
              if (StringUtils.isEmpty(type)) {
                  Map<String, ParamElementEx> paramAssignments = getParamAssignments(null);
                  int delayChannelParameterCount = 0;
                  if (paramAssignments.containsKey("delay"))
                      delayChannelParameterCount++;
                  if (paramAssignments.containsKey("disabled"))
                      delayChannelParameterCount++;
                  if (paramAssignments.size() == 0)
                      return "ned.IdealChannel";
                  else if (paramAssignments.size() == delayChannelParameterCount)
                      return "ned.DelayChannel";
                  else
                      return "ned.DatarateChannel";
              }
              else
                  return getType();
          }
          else
              return likeType;
      }
  
      public INedTypeInfo getNedTypeInfo() {
          INedTypeInfo typeInfo = resolveTypeName(getTypeOrLikeType(), getCompoundModule());
          INedTypeElement typeElement = typeInfo==null ? null : typeInfo.getNedElement();
          return (typeElement instanceof IChannelKindTypeElement) ? typeInfo : null;
      }
  
      public INedTypeElement getTypeOrLikeTypeRef() {
          INedTypeInfo info = getNedTypeInfo();
          return info == null ? null : info.getNedElement();
      }
  
      /**
       * Returns a list of all parameters assigned in this module
       */
      public List<ParamElementEx> getOwnParams() {
          List<ParamElementEx> result = new ArrayList<ParamElementEx>();
  
          ParametersElement parametersElement = getFirstParametersChild();
          if (parametersElement != null)
              for (INedElement currChild : parametersElement)
                  if (currChild instanceof ParamElementEx)
                      result.add((ParamElementEx)currChild);
  
          return result;
      }
  
      // Parameter query support. Note: code is similar to SubmoduleElementEx
  
      /**
       * Returns parameter assignments of this channel, including those in the NED
       * type it instantiates. For "like" channels the actual channel type is unknown,
       * so the interface NED type is used.
       */
      public Map<String, ParamElementEx> getParamAssignments() {
          return getParamAssignments(getNedTypeInfo());
      }
  
      /**
       * Returns parameter assignments of this channel, including those in the NED
       * type it instantiates, assuming that the channel's actual type is the
       * compound or simple module type passed in the <code>channelType</code>
       * parameter. This is useful when the channel is a "like" channel, and the
       * caller knows the actual channel type (e.g. from an inifile).
       */
      public Map<String, ParamElementEx> getParamAssignments(INedTypeInfo channelType) {
          Map<String, ParamElementEx> result = new HashMap<String, ParamElementEx>();
  
          if (channelType != null)
              result.putAll(channelType.getParamAssignments());
  
          // add local parameter assignments
          for (ParamElementEx ownParam : getOwnParams())
              result.put(ownParam.getName(), ownParam);
  
          return result;
      }
  
      /**
       * Returns parameter declarations of this channel, including those in the NED
       * type it instantiates. For "like" channels the actual channel type is unknown,
       * so the interface NED type is used.
       */
      public Map<String, ParamElementEx> getParamDeclarations() {
          return getParamDeclarations(getNedTypeInfo());
      }
  
      /**
       * Returns parameter declarations of this channel, assuming that the channel's
       * actual type is the compound or simple module type passed in the
       * <code>channelType</code> parameter. This is useful when the channel is
       * a "like" channel, and the caller knows the actual channel type
       * (e.g. from an inifile).
       */
      public Map<String, ParamElementEx> getParamDeclarations(INedTypeInfo channelType) {
          return channelType == null ? new HashMap<String, ParamElementEx>() : channelType.getParamDeclarations();
      }
  
      public List<ParamElementEx> getParameterInheritanceChain(String parameterName) {
          List<ParamElementEx> chain = getNedTypeInfo().getParameterInheritanceChain(parameterName);
  
          for (ParamElementEx param : getOwnParams()) {
              if (parameterName.equals(param.getName())) {
                  chain.add(0, param);
                  break;
              }
          }
  
          return chain;
      }
  
      @Override
      public String toString() {
          String arrow = getIsBidirectional() ? "<-->" : "-->";
          String type = StringUtils.nullToEmpty(getType()) + " " + StringUtils.nullToEmpty(getLikeType());
          if (!StringUtils.isBlank(type))
              arrow += type + arrow;
          return getSrcGateFullyQualified() + arrow + getDestGateFullyQualified();
      }
  }
  