/**
   * This method projects a 'global' protocol model to a specified role's 'local' protocol model.
   *
   * @param model The 'global' protocol model
   * @param role The role to project
   * @param journal Journal for reporting issues
   * @param context The protocol context
   * @return The 'local' protocol model
   */
  public ProtocolModel project(
      ProtocolModel model, Role role, Journal journal, ProtocolContext context) {
    ProtocolModel ret = null;

    if (model == null || role == null) {
      throw new IllegalArgumentException("Model and/or role has not bee specified");
    }

    // Check that the supplied role has been defined within the model
    // being projected
    java.util.List<Role> roles = model.getRoles();
    int index = roles.indexOf(role);

    if (index == -1) {
      throw new IllegalArgumentException(
          "Role '" + role.getName() + "' is not defined within the protocol model");
    } else {
      // Obtain the role instance actually defined within the model,
      // as this can be used to locate the appropriate scope to be
      // projected
      role = roles.get(index);
    }

    // Check that role is defined within a role list, and its parent
    // link has not inadvertantly been reset
    if ((role.getParent() instanceof RoleList) == false) {
      throw new IllegalArgumentException(
          "Role is not contained within a role list, " + "and is therefore not the declared role");
    }

    DefaultProjectorContext projectorContext = new DefaultProjectorContext(context);

    ModelObject obj = projectorContext.project(model, role, journal);

    if (obj != null) {
      if (obj instanceof ProtocolModel) {
        ret = (ProtocolModel) obj;
      } else {
        String modelName = model.getProtocol().getName();

        if (model.getProtocol().getRole() != null) {
          modelName += "," + model.getProtocol().getRole();
        }

        journal.error(
            MessageFormat.format(
                java.util.PropertyResourceBundle.getBundle(
                        "org.scribble.protocol.projection.Messages")
                    .getString("_NOT_PROJECTED_MODEL"),
                modelName),
            null);
      }
    }

    return (ret);
  }
  /**
   * This method creates a WSDL document containing the partner link types.
   *
   * @param model The global protocol model
   * @param role The role of the process
   * @param localcm The local protocol model
   * @param bpelProcess The BPEL process
   * @param journal The feedback handler
   * @return The WSDL document containing the partner link types
   * @throws Exception Failed to generate the partner link types
   */
  public static org.w3c.dom.Document generatePartnerLinkTypes(
      ProtocolModel model,
      Role role,
      ProtocolModel localcm,
      TProcess bpelProcess,
      FeedbackHandler journal)
      throws Exception {
    org.w3c.dom.Document doc =
        javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
    org.w3c.dom.Element defn = doc.createElement(WSDL_DEFINITIONS);
    doc.appendChild(defn);

    defn.setAttribute(XMLNS_PLNK, PLNKTYPE_NS);
    defn.setAttribute(XMLNS_WSDL, WSDL_NS);

    defn.setAttribute(NAME_LABEL, localcm.getProtocol().getName());

    defn.setAttribute(TARGET_NAMESPACE_LABEL, bpelProcess.getTargetNamespace());

    ContractGenerator cg = ContractGeneratorFactory.getContractGenerator();

    // Add import to associated wsdl
    String wsdlName = WSDLGeneratorUtil.getWSDLFileName(role, localcm.getProtocol().getName(), "");

    org.w3c.dom.Element imp = doc.createElement(WSDL_IMPORT);

    imp.setAttribute("namespace", bpelProcess.getTargetNamespace());
    imp.setAttribute("location", wsdlName);

    defn.appendChild(imp);

    // Add imports for associated roles
    java.util.ListIterator<Role> roles = localcm.getProtocol().getRoles().listIterator();

    while (roles.hasNext()) {
      Role r = roles.next();

      // Check if role is a client parameter - if so,
      // don't include an import for it
      if (localcm.getProtocol().getParameterDefinition(r.getName()) != null) {
        continue;
      }

      Contract contract = null;

      if (cg != null) {
        contract = cg.generate(model.getProtocol(), null, r, journal);
      }

      if (contract != null) {
        boolean gen = false;

        java.util.Iterator<Interface> iter = contract.getInterfaces().iterator();

        while (gen == false && iter.hasNext()) {
          Interface intf = iter.next();

          if (intf.getMessageExchangePatterns().size() > 0) {
            gen = true;
          }
        }

        if (gen) {
          wsdlName = WSDLGeneratorUtil.getWSDLFileName(r, localcm.getProtocol().getName(), "");

          imp = doc.createElement(WSDL_IMPORT);

          imp.setAttribute("namespace", contract.getNamespace());
          imp.setAttribute("location", wsdlName);

          defn.appendChild(imp);
        }
      }
    }

    // Create partner link types
    java.util.Map<String, String> nsMap = new java.util.HashMap<String, String>();

    for (TPartnerLink pl : bpelProcess.getPartnerLinks().getPartnerLink()) {
      org.w3c.dom.Element plt = doc.createElement(PLNK_PARTNER_LINK_TYPE);

      plt.setAttribute(NAME_LABEL, pl.getPartnerLinkType().getLocalPart());

      if (pl.getPartnerRole() != null && pl.getPartnerRole().trim().length() > 0) {
        org.w3c.dom.Element plRole = doc.createElement(PLNK_ROLE);

        plt.appendChild(plRole);

        plRole.setAttribute(NAME_LABEL, pl.getPartnerRole());

        Role useRole = null;

        for (int i = 0; useRole == null && i < localcm.getProtocol().getRoles().size(); i++) {
          if (pl.getPartnerRole().startsWith(localcm.getProtocol().getRoles().get(i).getName())) {
            useRole = localcm.getProtocol().getRoles().get(i);
          }
        }

        Contract contract = null;

        if (cg != null && useRole != null) {
          contract = cg.generate(model.getProtocol(), null, useRole, journal);
        }

        if (contract != null) {
          Interface intf = null;

          if (pl.getMyRole() != null) {
            intf = contract.getInterface(pl.getMyRole());
          }

          if (intf == null && contract.getInterfaces().size() > 0) {
            intf = contract.getInterfaces().iterator().next();
          }

          if (intf != null) {
            String prefix = null;
            String portType = intf.getName();

            if (intf.getNamespace() != null) {
              prefix = XMLUtils.getPrefixForNamespace(intf.getNamespace(), nsMap);

              portType = prefix + ":" + portType;
            }

            plRole.setAttribute(PORT_TYPE_LABEL, portType);
          }
        }
      }

      if (pl.getMyRole() != null && pl.getMyRole().trim().length() > 0) {
        org.w3c.dom.Element plRole = doc.createElement(PLNK_ROLE);

        plt.appendChild(plRole);

        plRole.setAttribute(NAME_LABEL, pl.getMyRole());

        Contract contract = null;

        if (cg != null) {
          String linkName = pl.getPartnerLinkType().getLocalPart();

          int pos = linkName.indexOf("To");

          if (pos != -1) {
            java.util.Set<Role> clientRoles = new java.util.HashSet<Role>();

            clientRoles.add(new Role(linkName.substring(0, pos)));

            contract = cg.generate(model.getProtocol(), clientRoles, role, journal);
          } else {
            contract = cg.generate(model.getProtocol(), null, role, journal);
          }
        }

        if (contract != null) {
          Interface intf = null;

          if (pl.getMyRole() != null) {
            intf = contract.getInterface(pl.getMyRole());
          }

          if (intf == null && contract.getInterfaces().size() > 0) {
            intf = contract.getInterfaces().iterator().next();
          }

          if (intf != null) {
            String prefix = null;
            String portType = intf.getName();

            if (intf.getNamespace() != null) {
              prefix = XMLUtils.getPrefixForNamespace(intf.getNamespace(), nsMap);

              portType = prefix + ":" + portType;
            }

            plRole.setAttribute(PORT_TYPE_LABEL, portType);
          }
        }
      }

      defn.appendChild(plt);
    }

    // Create remaining namespace/prefix mappings
    java.util.Iterator<String> iter = nsMap.keySet().iterator();
    while (iter.hasNext()) {
      String ns = iter.next();
      String prefix = nsMap.get(ns);

      defn.setAttribute(XMLNS_PREFIX + prefix, ns);
    }

    return (doc);
  }
  protected void addServiceBehaviour(
      java.io.File implFile,
      Role role,
      java.util.List<Role> refRoles,
      ProtocolModel behaviour,
      String srcFolder,
      ResourceLocator locator)
      throws Exception {

    java.io.FileInputStream fis = new java.io.FileInputStream(implFile);
    byte[] b = new byte[fis.available()];
    fis.read(b);
    fis.close();

    // Create variable/role mapping
    java.util.Map<Role, String> roleMapping = new java.util.HashMap<Role, String>();
    for (Role r : refRoles) {
      String varName =
          "_" + Character.toLowerCase(r.getName().charAt(0)) + r.getName().substring(1);
      roleMapping.put(r, varName);
    }

    // Process the Java class to replace the operation bodies
    String str = new String(b);

    int index = 0;

    JavaBehaviourGenerator bg = new JavaBehaviourGenerator(behaviour, locator);

    // NOTE: Tried using Eclipse JDT, but pulled in many dependencies and
    // was an over complex approach to just replace a method body, so decided
    // to do it the low tech way :)
    do {
      index = str.indexOf("    public ", index);

      if (index != -1) {
        int startIndex = str.indexOf("{", index);
        int endIndex = str.indexOf("\n    }", startIndex);

        if (startIndex != -1 && endIndex != -1) {
          // Find out operation name
          String[] reg = str.substring(index, startIndex).split("[ \\(\\)]");

          String opbody = bg.getOperationBody(reg[6], reg[5], reg[7], reg[8], roleMapping);

          if (opbody != null) {
            str = str.substring(0, startIndex + 1) + opbody + str.substring(endIndex);
            index = startIndex + 1 + opbody.length();
          } else {
            index = endIndex;
          }
        } else {
          index = -1;
        }
      }

    } while (index != -1);

    // Insert the import statements
    String importStatements = bg.getImportStatements();

    if (importStatements != null) {
      int ind = str.indexOf("import");

      if (ind != -1) {
        str = str.substring(0, ind) + importStatements + "\r\n" + str.substring(ind);
      }
    }

    java.io.FileOutputStream fos = new java.io.FileOutputStream(implFile);

    fos.write(str.getBytes());

    fos.close();
  }
  public void createServiceComposite(
      Role role,
      java.util.List<Role> refRoles,
      String wsdlPath,
      java.util.List<String> refWsdlPaths,
      String resourceFolder,
      String srcFolder)
      throws Exception {
    javax.wsdl.Definition defn = getWSDLReader().readWSDL(wsdlPath);

    if (defn != null) {

      java.io.File wsdlFile = new java.io.File(wsdlPath);

      if (!wsdlFile.exists()) {
        java.net.URL url = ClassLoader.getSystemResource(wsdlPath);

        wsdlFile = new java.io.File(url.getFile());

        if (!wsdlFile.exists()) {
          logger.severe("Failed to find WSDL file '" + wsdlPath + "'");
        }
      }

      ResourceLocator locator = new DefaultResourceLocator(wsdlFile.getParentFile());

      StringBuffer composite = new StringBuffer();
      StringBuffer transformers = new StringBuffer();
      StringBuffer component = new StringBuffer();

      String targetNamespace = defn.getTargetNamespace();
      String name = role.getName();

      composite.append("<switchyard xmlns=\"urn:switchyard-config:switchyard:1.0\"\r\n");

      // TODO: May need to add other namespaces here

      composite.append("\t\ttargetNamespace=\"" + targetNamespace + "\"\r\n");
      composite.append("\t\tname=\"" + name + "\">\r\n");

      composite.append(
          "\t<composite xmlns=\"http://docs.oasis-open.org/ns/opencsa/sca/200912\"\r\n");
      composite.append("\t\t\ttargetNamespace=\"");
      composite.append(targetNamespace);
      composite.append("\"\r\n\t\t\tname=\"");
      composite.append(name);
      composite.append("\" >\r\n");

      @SuppressWarnings("unchecked")
      java.util.Iterator<PortType> portTypes = defn.getPortTypes().values().iterator();

      while (portTypes.hasNext()) {
        PortType portType = portTypes.next();

        // Define transformers
        addPortTypeTransformers(portType, true, transformers, defn, locator, srcFolder);

        String wsdlName = wsdlPath;
        int ind = wsdlName.lastIndexOf('/');

        String wsdlLocation = wsdlName;

        if (ind != -1) {
          wsdlLocation = wsdlName.substring(ind + 1);
          wsdlName =
              wsdlName.substring(ind + 1)
                  + "#wsdl.porttype("
                  + portType.getQName().getLocalPart()
                  + ")";
        }

        composite.append(
            "\t\t<service name=\""
                + portType.getQName().getLocalPart()
                + "\" promote=\""
                + portType.getQName().getLocalPart()
                + "Component/"
                + portType.getQName().getLocalPart()
                + "\">\r\n");

        composite.append("\t\t\t<interface.wsdl interface=\"wsdl/" + wsdlName + "\" />\r\n");
        composite.append(
            "\t\t\t<binding.soap xmlns=\"urn:switchyard-component-soap:config:1.0\">\r\n");
        composite.append("\t\t\t\t<wsdl>wsdl/" + wsdlLocation + "</wsdl>\r\n");
        composite.append("\t\t\t\t<socketAddr>:18001</socketAddr>\r\n");
        composite.append("\t\t\t</binding.soap>\r\n");

        composite.append("\t\t</service>\r\n");

        String pack = JavaGeneratorUtil.getJavaPackage(defn.getTargetNamespace());

        component.append(
            "\t\t<component name=\"" + portType.getQName().getLocalPart() + "Component\">\r\n");

        component.append(
            "\t\t\t<implementation.bean xmlns=\"urn:switchyard-component-bean:config:1.0\" "
                + "class=\""
                + pack
                + "."
                + portType.getQName().getLocalPart()
                + "Impl\"/>\r\n");

        component.append(
            "\t\t\t<service name=\"" + portType.getQName().getLocalPart() + "\" >\r\n");
        component.append(
            "\t\t\t\t<interface.java interface=\""
                + pack
                + "."
                + portType.getQName().getLocalPart()
                + "\"/>\r\n");
        component.append("\t\t\t</service>\r\n");

        for (int i = 0; i < refWsdlPaths.size(); i++) {
          String refWsdlPath = refWsdlPaths.get(i);
          javax.wsdl.Definition refDefn = getWSDLReader().readWSDL(refWsdlPath);

          java.io.File refWsdlFile = new java.io.File(refWsdlPath);

          if (!refWsdlFile.exists()) {
            java.net.URL url = ClassLoader.getSystemResource(refWsdlPath);

            refWsdlFile = new java.io.File(url.getFile());

            if (!refWsdlFile.exists()) {
              logger.severe("Failed to find ref WSDL file '" + refWsdlPath + "'");
            }
          }

          ResourceLocator refLocator = new DefaultResourceLocator(refWsdlFile.getParentFile());

          if (refDefn != null) {

            @SuppressWarnings("unchecked")
            java.util.Iterator<PortType> refPortTypes = refDefn.getPortTypes().values().iterator();

            while (refPortTypes.hasNext()) {
              PortType refPortType = refPortTypes.next();

              // Define transformers
              addPortTypeTransformers(
                  refPortType, false, transformers, refDefn, refLocator, srcFolder);

              String refWsdlName = refWsdlPath;

              wsdlLocation = refWsdlName;

              ind = refWsdlName.lastIndexOf('/');
              if (ind != -1) {
                wsdlLocation = refWsdlName.substring(ind + 1);
                refWsdlName =
                    refWsdlName.substring(ind + 1)
                        + "#wsdl.porttype("
                        + refPortType.getQName().getLocalPart()
                        + ")";
              }

              composite.append(
                  "\t\t<reference name=\""
                      + refPortType.getQName().getLocalPart()
                      + "\" promote=\""
                      + portType.getQName().getLocalPart()
                      + "Component/"
                      + refPortType.getQName().getLocalPart()
                      + "\" multiplicity=\"1..1\" >\r\n");

              composite.append(
                  "\t\t\t<interface.wsdl interface=\"wsdl/" + refWsdlName + "\" />\r\n");
              composite.append(
                  "\t\t\t<binding.soap xmlns=\"urn:switchyard-component-soap:config:1.0\">\r\n");
              composite.append("\t\t\t\t<wsdl>wsdl/" + wsdlLocation + "</wsdl>\r\n");
              composite.append("\t\t\t\t<socketAddr>:18001</socketAddr>\r\n");
              composite.append("\t\t\t</binding.soap>\r\n");
              composite.append("\t\t</reference>\r\n");

              String refPack = JavaGeneratorUtil.getJavaPackage(refDefn.getTargetNamespace());

              component.append(
                  "\t\t\t<reference name=\"" + refPortType.getQName().getLocalPart() + "\" >\r\n");
              component.append(
                  "\t\t\t\t<interface.java interface=\""
                      + refPack
                      + "."
                      + refPortType.getQName().getLocalPart()
                      + "\"/>\r\n");
              component.append("\t\t\t</reference>\r\n");
            }
          }
        }

        component.append("\t\t</component>\r\n");

        composite.append(component.toString());
      }

      composite.append("\t</composite>\r\n");

      if (transformers.length() > 0) {
        composite.append("\t<transforms xmlns:xform=\"urn:switchyard-config:transform:1.0\">\r\n");
        composite.append(transformers.toString());
        composite.append("\t</transforms>\r\n");
      }

      composite.append("</switchyard>\r\n");

      java.io.FileOutputStream fos =
          new java.io.FileOutputStream(
              resourceFolder + java.io.File.separatorChar + "switchyard.xml");

      fos.write(composite.toString().getBytes());

      fos.close();
    }
  }