/**
   * Sets the start quantity of the activity based on the data of the shape.
   *
   * @param activity
   * @param shape The resource shape
   */
  private void setStartAndCompletionQuantity(Activity activity, GenericShape shape) {

    /* Start quantity */

    String startQuantity = shape.getProperty("startquantity");
    if (startQuantity != null) {
      try {
        activity.setStartQuantity(BigInteger.valueOf(Integer.valueOf(startQuantity)));
      } catch (Exception e) {
        e.printStackTrace();
        /* Set to default value in case of an exception */
        activity.setStartQuantity(BigInteger.valueOf(1));
      }
    }

    /* Completion quantity */
    String completionQuantity = shape.getProperty("completionquantity");
    if (completionQuantity != null) {
      try {
        activity.setCompletionQuantity(BigInteger.valueOf(Integer.valueOf(completionQuantity)));
      } catch (Exception e) {
        /* Set to default value in case of an exception */
        e.printStackTrace();
        activity.setCompletionQuantity(BigInteger.valueOf(1));
      }
    }
  }
  /**
   * Creates a {@link StandardLoopCharacteristics} object based on the shape's properties.
   *
   * @param shape The resource shape
   * @return The {@link StandardLoopCharacteristics}
   */
  private LoopCharacteristics createStandardLoopCharacteristics(GenericShape shape) {
    StandardLoopCharacteristics standardLoop = new StandardLoopCharacteristics();

    /* Set loop condition */
    String loopConditionString = shape.getProperty("loopcondition");
    if (loopConditionString != null && !(loopConditionString.length() == 0)) {
      FormalExpression loopCondition = new FormalExpression(loopConditionString);
      standardLoop.setLoopCondition(loopCondition);
    }

    /* Determine the point in time to check the loop condition */
    String testBeforeString = shape.getProperty("testbefore");
    if (testBeforeString != null && testBeforeString.equalsIgnoreCase("true")) {
      standardLoop.setTestBefore(true);
    } else {
      standardLoop.setTestBefore(false);
    }

    /* Set the maximum number of loop iterations */
    try {
      standardLoop.setLoopMaximum(
          BigInteger.valueOf(Integer.parseInt(shape.getProperty("loopmaximum"))));
    } catch (Exception e) {
      /* In case of an exception do not set a loop iteration cap */
    }

    return standardLoop;
  }
  /**
   * Creates the loop characteristics for multiple instances loops.
   *
   * @param shape
   * @param loopType
   */
  private MultiInstanceLoopCharacteristics createMultiInstanceLoopCharacteristics(
      GenericShape shape, String loopType) {
    MultiInstanceLoopCharacteristics miLoop = new MultiInstanceLoopCharacteristics();

    /* Determine whether it is parallel or sequential */
    if (loopType.equalsIgnoreCase("Parallel")) miLoop.setIsSequential(false);
    else miLoop.setIsSequential(true);

    /* Set loop cardinality */
    String loopCardinalityString = shape.getProperty("loopcardinality");
    if (loopCardinalityString != null && !(loopCardinalityString.length() == 0)) {
      FormalExpression loopCardinality = new FormalExpression(loopCardinalityString);
      miLoop.setLoopCardinality(loopCardinality);
    }

    /* Reference required DataInput */
    // miLoop.setLoopDataInput(value)
    // Task t = null;
    // t.get

    /* Completion condition */
    String completionCondition = shape.getProperty("completioncondition");
    if (completionCondition != null && !(completionCondition.length() == 0)) {
      FormalExpression completionConditionExpr = new FormalExpression(completionCondition);
      miLoop.setCompletionCondition(completionConditionExpr);
    }

    /* Handle loop behavior */
    handleLoopBehaviorAttributes(shape, miLoop);

    return miLoop;
  }
  @StencilId("IntermediateCompensationEventThrowing")
  public IntermediateThrowEvent createIntermediateCompensationEvent(GenericShape shape) {
    IntermediateThrowEvent itEvent = new IntermediateThrowEvent();

    CompensateEventDefinition compDef = new CompensateEventDefinition();

    /* Activity Reference */
    String activityRef = shape.getProperty("activityref");
    if (activityRef != null && !(activityRef.length() == 0)) {
      Task taskRef = new Task();
      taskRef.setId(activityRef);
      compDef.setActivityRef(taskRef);
    }

    /* Wait for Completion */
    String waitForCompletion = shape.getProperty("waitforcompletion");
    if (waitForCompletion != null && waitForCompletion.equals("false")) {
      compDef.setWaitForCompletion(false);
    } else {
      compDef.setWaitForCompletion(true);
    }

    itEvent.getEventDefinition().add(compDef);

    return itEvent;
  }
  @StencilId("IntermediateMessageEventThrowing")
  public IntermediateThrowEvent createIntermediateMessageEvent(GenericShape shape)
      throws BpmnConverterException {
    IntermediateThrowEvent itEvent = new IntermediateThrowEvent();

    MessageEventDefinition msgDef = new MessageEventDefinition();

    /* Message name */
    String messageName = shape.getProperty("messagename");
    if (messageName != null && !(messageName.length() == 0)) {
      Message message = new Message();
      message.setName(messageName);
      msgDef.setMessageRef(message);
    }

    /* Operation name */
    String operationName = shape.getProperty("operationname");
    if (operationName != null && !(operationName.length() == 0)) {
      Operation operation = new Operation();
      operation.setName(operationName);
      msgDef.setOperationRef(operation);
    }

    itEvent.getEventDefinition().add(msgDef);

    return itEvent;
  }
  @StencilId("IntermediateEscalationEventThrowing")
  public IntermediateThrowEvent createIntermediateEscalationEvent(GenericShape shape) {
    IntermediateThrowEvent itEvent = new IntermediateThrowEvent();

    EscalationEventDefinition escalDef = new EscalationEventDefinition();

    Escalation escalation = new Escalation();

    /* Escalation name */
    String escalationName = shape.getProperty("escalationname");
    if (escalationName != null && !(escalationName.length() == 0)) {
      escalation.setName(escalationName);
    }

    /* Escalation code */
    String escalationCode = shape.getProperty("escalationcode");
    if (escalationCode != null && !(escalationCode.length() == 0)) {
      escalation.setEscalationCode(escalationCode);
    }

    escalDef.setEscalationRef(escalation);
    itEvent.getEventDefinition().add(escalDef);

    return itEvent;
  }
  /**
   * Processes the attributes that are related to the loop behavior.
   *
   * @param shape
   * @param miLoop
   */
  private void handleLoopBehaviorAttributes(
      GenericShape shape, MultiInstanceLoopCharacteristics miLoop) {
    String behavior = shape.getProperty("behavior");
    if (behavior != null && !(behavior.length() == 0)) {
      miLoop.setBehavior(MultiInstanceFlowCondition.fromValue(behavior));
    }

    /* Complex behavior */
    if (miLoop.getBehavior().equals(MultiInstanceFlowCondition.COMPLEX)) {
      try {
        String comBehavDefString = shape.getProperty("complexbehaviordefinition");
        JSONObject complexDef = new JSONObject(comBehavDefString);
        JSONArray complexDefItems = complexDef.getJSONArray("items");

        /*
         * Retrieve complex behavior definitions and process
         * them.
         */
        for (int i = 0; i < complexDefItems.length(); i++) {
          JSONObject complexDefItem = complexDefItems.getJSONObject(i);

          ComplexBehaviorDefinition comBehavDef = new ComplexBehaviorDefinition();

          /* Condition */
          String condition = complexDefItem.getString("cexpression");
          if (condition != null && !(condition.length() == 0))
            comBehavDef.setCondition(new FormalExpression(condition));

          /* Event */
          ImplicitThrowEvent event =
              new ImplicitThrowEvent(complexDefItem.getString("ceventdefinition"));
          comBehavDef.setEvent(event);

          miLoop.getComplexBehaviorDefinition().add(comBehavDef);
        }

      } catch (Exception e) {
        e.printStackTrace();
      }

      /* Handle none behavior choice */
    } else if (miLoop.getBehavior().equals(MultiInstanceFlowCondition.NONE)) {
      String noneBehavString = shape.getProperty("nonebehavioreventref");
      if (noneBehavString != null && !(noneBehavString.length() == 0)) {
        miLoop.setNoneBehaviorEventRef(EventDefinition.createEventDefinition(noneBehavString));
      }
      /* Handle one behavior choice */
    } else if (miLoop.getBehavior().equals(MultiInstanceFlowCondition.ONE)) {
      String oneBehavString = shape.getProperty("onebehavioreventref");
      if (oneBehavString != null && !(oneBehavString.length() == 0)) {
        miLoop.setOneBehaviorEventRef(EventDefinition.createEventDefinition(oneBehavString));
      }
    }
  }
  /**
   * Sets the attributes related to a data store element.
   *
   * @param dataStoreRef The @link {@link DataStoreReference}.
   * @param shape The data store {@link GenericShape}
   */
  private void setDataStoreRefAttributes(DataStoreReference dataStoreRef, GenericShape shape) {
    DataStore dataStore = dataStoreRef.getDataStoreRef();
    String dataStateName = shape.getProperty("state");
    /* Set attributes of the global data store */
    if (dataStore != null) {
      dataStore.setId(SignavioUUID.generate());
      dataStore.setName(shape.getProperty("name"));
      if (shape.getProperty("capacity") != null && !(shape.getProperty("capacity").length() == 0))
        dataStore.setCapacity(Integer.valueOf(shape.getProperty("capacity")).intValue());

      /* Set isUnlimited attribute */
      String isUnlimited = shape.getProperty("isunlimited");
      if (isUnlimited != null && isUnlimited.equalsIgnoreCase("true")) dataStore.setUnlimited(true);
      else dataStore.setUnlimited(false);

      /* Define DataState element */
      if (dataStateName != null && !(dataStateName.length() == 0)) {
        DataState dataState = new DataState(dataStateName);
        dataStore.setDataState(dataState);
      }
    }

    /* Set attributes of the data store reference */
    dataStoreRef.setName(shape.getProperty("name"));
    dataStoreRef.setId(shape.getResourceId());

    /* Define DataState element */
    if (dataStateName != null && !(dataStateName.length() == 0)) {
      DataState dataState = new DataState(dataStateName);
      dataStoreRef.setDataState(dataState);
    }
  }
  // @Override
  protected ConversationLink createProcessElement(GenericShape shape)
      throws BpmnConverterException {
    ConversationLink link = new ConversationLink();
    this.setCommonAttributes(link, shape);
    link.setId(shape.getResourceId());
    link.setName(shape.getProperty("name"));

    return link;
  }
  @StencilId("IntermediateLinkEventThrowing")
  public IntermediateThrowEvent createIntermediateLinkEvent(GenericShape shape) {
    IntermediateThrowEvent itEvent = new IntermediateThrowEvent();

    LinkEventDefinition linkDef = new LinkEventDefinition();

    /* Set required name attribute */
    String name = shape.getProperty("name");
    if (name != null && !(name.length() == 0)) linkDef.setName(name);

    /* Set target reference */
    String targetEntry = shape.getProperty("entry");
    if (targetEntry != null && targetEntry.length() != 0) {
      linkDef.setTarget(targetEntry);
    }

    itEvent.getEventDefinition().add(linkDef);

    return itEvent;
  }
  /**
   * @param shape
   * @return
   */
  protected List<Property> createPropertiesList(GenericShape shape) {
    ArrayList<Property> propertiesList = new ArrayList<Property>();

    String propertiesString = shape.getProperty("properties");
    if (propertiesString != null && !(propertiesString.length() == 0)) {
      try {
        JSONObject propertyObject = new JSONObject(propertiesString);
        JSONArray propertyItems = propertyObject.getJSONArray("items");

        /*
         * Retrieve property definitions and process
         * them.
         */
        for (int i = 0; i < propertyItems.length(); i++) {
          JSONObject propertyItem = propertyItems.getJSONObject(i);

          Property property = new Property();

          /* Name */
          String name = propertyItem.getString("name");
          if (name != null && !(name.length() == 0)) property.setName(name);

          /* Data State */
          String dataState = propertyItem.getString("datastate");
          if (dataState != null && !(dataState.length() == 0))
            property.setDataState(new DataState(dataState));

          /* ItemKind */
          String itemKind = propertyItem.getString("itemkind");
          if (itemKind != null && !(itemKind.length() == 0))
            property.setItemKind(ItemKind.fromValue(itemKind));

          /* Structure */
          String structureString = propertyItem.getString("structure");
          if (structureString != null && !(structureString.length() == 0))
            property.setStructure(structureString);

          /* isCollection */
          String isCollection = propertyItem.getString("iscollection");
          if (isCollection != null && isCollection.equalsIgnoreCase("false"))
            property.setCollection(false);
          else property.setCollection(true);

          propertiesList.add(property);
        }

      } catch (Exception e) {
        e.printStackTrace();
      }
    }

    return propertiesList;
  }
  // @Override
  protected IntermediateThrowEvent createProcessElement(GenericShape shape)
      throws BpmnConverterException {
    try {
      IntermediateThrowEvent itEvent = (IntermediateThrowEvent) this.invokeCreatorMethod(shape);
      itEvent.setId(shape.getResourceId());
      itEvent.setName(shape.getProperty("name"));

      return itEvent;
    } catch (Exception e) {
      /* Wrap exceptions into specific BPMNConverterException */
      throw new BpmnConverterException(
          "Error while creating the process element of " + shape.getStencilId(), e);
    }
  }
  /**
   * Entry method to create the {@link LoopCharacteristics} for a given activity's shape.
   *
   * @param activity
   * @param shape
   * @return
   */
  protected void createLoopCharacteristics(Activity activity, GenericShape shape) {

    /* Distinguish between standard and multiple instance loop types */
    String loopType = shape.getProperty("looptype");
    if (loopType != null && !(loopType.length() == 0)) {

      /* Loop type standard */
      if (loopType.equalsIgnoreCase("Standard"))
        activity.setLoopCharacteristics(createStandardLoopCharacteristics(shape));

      /* Loop type multiple instances */
      else if (loopType.equalsIgnoreCase("Parallel") || loopType.equalsIgnoreCase("Sequential")) {
        activity.setLoopCharacteristics(createMultiInstanceLoopCharacteristics(shape, loopType));
      }
    }
  }
  /**
   * Generic method to parse data input and output set properties.
   *
   * @param property Identifies the shape's property to handle either a output or input set.
   */
  private HashMap<String, IoOption> collectSetInfoFor(GenericShape shape, String property) {
    String ioSpecString = shape.getProperty(property);

    HashMap<String, IoOption> options = new HashMap<String, IoOption>();

    if (ioSpecString != null && !(ioSpecString.length() == 0)) {
      try {
        JSONObject ioSpecObject = new JSONObject(ioSpecString);
        JSONArray ioSpecItems = ioSpecObject.getJSONArray("items");

        /* Retrieve io spec option definitions */
        for (int i = 0; i < ioSpecItems.length(); i++) {
          JSONObject propertyItem = ioSpecItems.getJSONObject(i);

          IoOption ioOpt = new IoOption();

          /* Name */
          String name = propertyItem.getString("name");
          if (name == null || name.length() == 0) continue;

          /* Optional */
          String isOptional = propertyItem.getString("optional");
          if (isOptional != null && isOptional.equalsIgnoreCase("true")) ioOpt.setOptional(true);

          /* While executing */
          String whileExecuting = propertyItem.getString("whileexecuting");
          if (whileExecuting != null && whileExecuting.equalsIgnoreCase("true"))
            ioOpt.setOptional(true);

          options.put(name, ioOpt);
        }

      } catch (Exception e) {
        e.printStackTrace();
      }
    }

    return options;
  }
  @StencilId("IntermediateSignalEventThrowing")
  public IntermediateThrowEvent createIntermediateSignalEvent(GenericShape shape) {
    IntermediateThrowEvent itEvent = new IntermediateThrowEvent();

    SignalEventDefinition sigDef = new SignalEventDefinition();

    Signal signal = new Signal();

    /* Signal ID */
    signal.setId(SignavioUUID.generate());

    /* Signal name */
    String signalName = shape.getProperty("signalname");
    if (signalName != null && !(signalName.length() == 0)) {
      signal.setName(signalName);
    }

    sigDef.setSignalRef(signal);
    itEvent.getEventDefinition().add(sigDef);

    return itEvent;
  }
 /**
  * Process the compensation property.
  *
  * @param activity
  * @param shape
  */
 private void setCompensationProperty(Activity activity, GenericShape shape) {
   activity.setIsForCompensation(
       (shape.getProperty("isforcompensation") != null
           ? shape.getProperty("isforcompensation").equalsIgnoreCase("true")
           : false));
 }