/**
   * Bind an operation by copying the signature from the actual.
   *
   * @param copy the copier
   * @param actual the actual. If an operation, its signature is copied to the template
   * @param operation The operation template
   * @return
   */
  public static Operation instantiateOperation(
      LazyCopier copy, Element actual, Operation operation) {
    try {
      Operation newOperation = copy.getCopy(operation);
      if (actual instanceof Operation) {
        for (Parameter parameter : ((Operation) actual).getOwnedParameters()) {
          Parameter newParam = EcoreUtil.copy(parameter); // copy parameter via EcoreUtil
          newParam.setType(copy.getCopy(parameter.getType()));
          newOperation.getOwnedParameters().add(newParam);
        }
      }
      TransformationContext.classifier = newOperation.getClass_();
      if (actual instanceof Classifier) {
        bindOperation(newOperation, (Classifier) actual);
      }
      String newName = AcceleoDriverWrapper.evaluate(operation.getName(), actual, null);
      newOperation.setName(newName);

      return newOperation;
    } catch (TransformationException e) {
      // throw runtime exception
      throw new RuntimeException(
          String.format(Messages.TemplateInstantiationListener_TrafoException, e.getMessage()));
    }
  }
 /**
  * Instantiate a behavior
  *
  * @param copy copier
  * @param actual actual in template instantiation
  * @param opaqueBehavior behavior with body in form of an Acceleo template.
  * @return instantiated (bound) behavior.
  * @throws TransformationException
  */
 public static OpaqueBehavior instantiateBehavior(
     LazyCopier copy, Element actual, OpaqueBehavior opaqueBehavior)
     throws TransformationException {
   OpaqueBehavior newBehavior = copy.getCopy(opaqueBehavior);
   if (actual instanceof NamedElement) {
     String newName = AcceleoDriverWrapper.evaluate(opaqueBehavior.getName(), actual, null);
     newBehavior.setName(newName);
   }
   EList<String> bodyList = newBehavior.getBodies();
   for (int i = 0; i < bodyList.size(); i++) {
     String body = bodyList.get(i);
     TransformationContext.classifier = (Classifier) newBehavior.getOwner();
     // pass qualified operation name as template name. Used to identify script in case of an error
     String newBody =
         AcceleoDriverWrapper.evaluate(body, newBehavior.getQualifiedName(), actual, null);
     bodyList.set(i, newBody);
   }
   return newBehavior;
 }
  /**
   * Bind a named element. Besides of binding the passed element, this operation will bind all
   * elements that are referenced (required) by the passed element.
   *
   * <p>In consequence, typically only a small part of a package template is actually created within
   * the bound package. We call this mechanism lazy instantiation/binding
   *
   * @param copier Source and target model
   * @param namedElement A member within the package template which should be bound, i.e. for which
   *     template instantiation should be performed.
   * @param binding The binding between the bound package and the package template
   * @param args Acceleo arguments
   */
  @SuppressWarnings("unchecked")
  public <T extends NamedElement> T bindNamedElement(T namedElement)
      throws TransformationException {
    if (namedElement == null) {
      // user should never see this exception
      throw new TransformationException(Messages.TemplateInstantiation_TemplateIsNull);
    }

    Package boundPackage = (Package) binding.getBoundElement();
    EList<Namespace> path = TemplateUtils.relativePathWithMerge(namedElement, packageTemplate);
    Template template = UMLUtil.getStereotypeApplication(namedElement, Template.class);
    BindingHelper helper = (template != null) ? template.getHelper() : null;

    /*
    if((templateKind == TemplateKind.ACCUMULATE) || (templateKind == TemplateKind.LATE_EVALUATION)) {
    	// TODO: not very clean yet
    	path = TemplateUtils.relativePathWithMerge(namedElement, copy.source);
    	if(path == null) {
    		// element is imported
    		path = namedElement.allNamespaces();
    	}
    	boundPackage = copy.target; // CreationUtils.getAndCreate
    								// (sat.target, "accumulate");
    }
    */

    if (path != null) {
      // register owning package template (template can be defined in
      // multiple packages)
      Element owner = TemplateUtils.getTemplateOwner(namedElement, signature);
      if (owner != null) {
        // note that we might overwrite an existing value
        copier.put(owner, boundPackage);
      }
    } else {
      // element is not part of the package template referenced by the
      // binding
      if (namedElement instanceof TemplateableElement) {
        // check whether the referenced element is part of another
        // package template,
        // (for which we allow for implicit binding with the first
        // template parameter)
        TemplateSignature signatureOfNE =
            TemplateUtils.getSignature((TemplateableElement) namedElement);
        if ((signatureOfNE != null) && (signature != signatureOfNE)) {
          TemplateBinding subBinding =
              TemplateUtils.getSubBinding(
                  copier.target, (TemplateableElement) namedElement, binding);
          TemplateInstantiation ti = new TemplateInstantiation(copier, subBinding, args);
          NamedElement ret = ti.bindNamedElement(namedElement);
          return (T) ret;
        }
      }

      // => nothing to do with respect to template instantiation, but
      // since the template is potentially instantiated in another model,
      // the referenced element might need to be copied.

      return copier.getCopy(namedElement);
    }
    // element is contained in the template package, examine whether it
    // already exists in the
    // bound package.

    NamedElement existingMember = (NamedElement) copier.get(namedElement);
    /*
    if((existingMember != null) && (templateKind != TemplateKind.ACCUMULATE)) {
    	// element is already existing (and thus bound), nothing to do
    	// additional check, whether the ACCUMULATE information is unset)
    	// however: if the element is a package, existence is not sufficient
    	// since it might have been created via getAndCreate above

    	//if(namedElement instanceof Package) {
    	//	bindPackage((Package)namedElement);
    	//}
    	return (T)existingMember;
    }
    */
    if (existingMember == null) {
      FilterTemplate.getInstance().setActive(false);
      T copiedElement = copier.getCopy(namedElement);
      FilterTemplate.getInstance().setActive(true);
      copier.setPackageTemplate(null, null);
      return copiedElement;
    }

    return (T) existingMember;
  }