protected NodeInstanceImpl readNodeInstanceContent(
      JBPMMessages.ProcessInstance.NodeInstance _node,
      MarshallerReaderContext context,
      WorkflowProcessInstance processInstance)
      throws IOException {
    NodeInstanceImpl nodeInstance = null;
    NodeInstanceContent _content = _node.getContent();
    switch (_content.getType()) {
      case RULE_SET_NODE:
        nodeInstance = new RuleSetNodeInstance();
        ((RuleSetNodeInstance) nodeInstance)
            .setRuleFlowGroup(_content.getRuleSet().getRuleFlowGroup());
        if (_content.getRuleSet().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getRuleSet().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((RuleSetNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }

        if (_content.getRuleSet().getMapEntryCount() > 0) {
          Map<String, FactHandle> factInfo = new HashMap<String, FactHandle>();

          for (TextMapEntry entry : _content.getRuleSet().getMapEntryList()) {
            factInfo.put(entry.getName(), new DefaultFactHandle(entry.getValue()));
          }

          ((RuleSetNodeInstance) nodeInstance).setFactHandles(factInfo);
        }
        break;
      case HUMAN_TASK_NODE:
        nodeInstance = new HumanTaskNodeInstance();
        ((HumanTaskNodeInstance) nodeInstance)
            .internalSetWorkItemId(_content.getHumanTask().getWorkItemId());
        if (_content.getHumanTask().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getHumanTask().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((HumanTaskNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }
        break;
      case WORK_ITEM_NODE:
        nodeInstance = new WorkItemNodeInstance();
        ((WorkItemNodeInstance) nodeInstance)
            .internalSetWorkItemId(_content.getWorkItem().getWorkItemId());
        if (_content.getWorkItem().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getWorkItem().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((WorkItemNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }
        break;
      case SUBPROCESS_NODE:
        nodeInstance = new SubProcessNodeInstance();
        ((SubProcessNodeInstance) nodeInstance)
            .internalSetProcessInstanceId(_content.getSubProcess().getProcessInstanceId());
        if (_content.getSubProcess().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getSubProcess().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((SubProcessNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }
        break;
      case MILESTONE_NODE:
        nodeInstance = new MilestoneNodeInstance();
        if (_content.getMilestone().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getMilestone().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((MilestoneNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }
        break;
      case TIMER_NODE:
        nodeInstance = new TimerNodeInstance();
        ((TimerNodeInstance) nodeInstance).internalSetTimerId(_content.getTimer().getTimerId());
        break;
      case EVENT_NODE:
        nodeInstance = new EventNodeInstance();
        break;
      case JOIN_NODE:
        nodeInstance = new JoinInstance();
        if (_content.getJoin().getTriggerCount() > 0) {
          Map<Long, Integer> triggers = new HashMap<Long, Integer>();
          for (JBPMMessages.ProcessInstance.NodeInstanceContent.JoinNode.JoinTrigger _join :
              _content.getJoin().getTriggerList()) {
            triggers.put(_join.getNodeId(), _join.getCounter());
          }
          ((JoinInstance) nodeInstance).internalSetTriggers(triggers);
        }
        break;
      case COMPOSITE_CONTEXT_NODE:
        nodeInstance = new CompositeContextNodeInstance();

        if (_content.getComposite().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getComposite().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((CompositeContextNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }
        break;
      case FOR_EACH_NODE:
        nodeInstance = new ForEachNodeInstance();
        break;
      case DYNAMIC_NODE:
        nodeInstance = new DynamicNodeInstance();
        if (_content.getComposite().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getComposite().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((CompositeContextNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }
        break;
      case STATE_NODE:
        nodeInstance = new StateNodeInstance();
        if (_content.getState().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getState().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((CompositeContextNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }
        break;
      case EVENT_SUBPROCESS_NODE:
        nodeInstance = new EventSubProcessNodeInstance();

        if (_content.getComposite().getTimerInstanceIdCount() > 0) {
          List<Long> timerInstances = new ArrayList<Long>();
          for (Long _timerId : _content.getComposite().getTimerInstanceIdList()) {
            timerInstances.add(_timerId);
          }
          ((CompositeContextNodeInstance) nodeInstance).internalSetTimerInstances(timerInstances);
        }
        break;
      default:
        throw new IllegalArgumentException("Unknown node type: " + _content.getType());
    }
    return nodeInstance;
  }
  protected JBPMMessages.ProcessInstance.NodeInstanceContent writeNodeInstanceContent(
      JBPMMessages.ProcessInstance.NodeInstance.Builder _node,
      NodeInstance nodeInstance,
      MarshallerWriteContext context)
      throws IOException {
    JBPMMessages.ProcessInstance.NodeInstanceContent.Builder _content = null;
    if (nodeInstance instanceof RuleSetNodeInstance) {
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.RULE_SET_NODE);
      List<Long> timerInstances = ((RuleSetNodeInstance) nodeInstance).getTimerInstances();
      JBPMMessages.ProcessInstance.NodeInstanceContent.RuleSetNode.Builder _ruleSet =
          JBPMMessages.ProcessInstance.NodeInstanceContent.RuleSetNode.newBuilder();
      _ruleSet.setRuleFlowGroup(((RuleSetNodeInstance) nodeInstance).getRuleFlowGroup());
      if (timerInstances != null) {

        for (Long id : timerInstances) {
          _ruleSet.addTimerInstanceId(id);
        }
      }

      Map<String, FactHandle> facts = ((RuleSetNodeInstance) nodeInstance).getFactHandles();
      if (facts != null && facts.size() > 0) {
        for (Map.Entry<String, FactHandle> entry : facts.entrySet()) {
          JBPMMessages.ProcessInstance.NodeInstanceContent.RuleSetNode.TextMapEntry.Builder
              _textMapEntry =
                  JBPMMessages.ProcessInstance.NodeInstanceContent.RuleSetNode.TextMapEntry
                      .newBuilder();
          _textMapEntry.setName(entry.getKey());
          _textMapEntry.setValue(entry.getValue().toExternalForm());

          _ruleSet.addMapEntry(_textMapEntry.build());
        }
      }
      _content.setRuleSet(_ruleSet.build());

    } else if (nodeInstance instanceof HumanTaskNodeInstance) {
      JBPMMessages.ProcessInstance.NodeInstanceContent.HumanTaskNode.Builder _task =
          JBPMMessages.ProcessInstance.NodeInstanceContent.HumanTaskNode.newBuilder()
              .setWorkItemId(((HumanTaskNodeInstance) nodeInstance).getWorkItemId());
      List<Long> timerInstances = ((HumanTaskNodeInstance) nodeInstance).getTimerInstances();
      if (timerInstances != null) {
        for (Long id : timerInstances) {
          _task.addTimerInstanceId(id);
        }
      }
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.HUMAN_TASK_NODE)
              .setHumanTask(_task.build());
    } else if (nodeInstance instanceof WorkItemNodeInstance) {
      JBPMMessages.ProcessInstance.NodeInstanceContent.WorkItemNode.Builder _wi =
          JBPMMessages.ProcessInstance.NodeInstanceContent.WorkItemNode.newBuilder()
              .setWorkItemId(((WorkItemNodeInstance) nodeInstance).getWorkItemId());

      List<Long> timerInstances = ((WorkItemNodeInstance) nodeInstance).getTimerInstances();
      if (timerInstances != null) {
        for (Long id : timerInstances) {
          _wi.addTimerInstanceId(id);
        }
      }
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.WORK_ITEM_NODE)
              .setWorkItem(_wi.build());
    } else if (nodeInstance instanceof SubProcessNodeInstance) {
      JBPMMessages.ProcessInstance.NodeInstanceContent.SubProcessNode.Builder _sp =
          JBPMMessages.ProcessInstance.NodeInstanceContent.SubProcessNode.newBuilder()
              .setProcessInstanceId(((SubProcessNodeInstance) nodeInstance).getProcessInstanceId());
      List<Long> timerInstances = ((SubProcessNodeInstance) nodeInstance).getTimerInstances();
      if (timerInstances != null) {
        for (Long id : timerInstances) {
          _sp.addTimerInstanceId(id);
        }
      }
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.SUBPROCESS_NODE)
              .setSubProcess(_sp.build());
    } else if (nodeInstance instanceof MilestoneNodeInstance) {
      JBPMMessages.ProcessInstance.NodeInstanceContent.MilestoneNode.Builder _ms =
          JBPMMessages.ProcessInstance.NodeInstanceContent.MilestoneNode.newBuilder();
      List<Long> timerInstances = ((MilestoneNodeInstance) nodeInstance).getTimerInstances();
      if (timerInstances != null) {
        for (Long id : timerInstances) {
          _ms.addTimerInstanceId(id);
        }
      }
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.MILESTONE_NODE)
              .setMilestone(_ms.build());
    } else if (nodeInstance instanceof EventNodeInstance) {
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.EVENT_NODE);
    } else if (nodeInstance instanceof TimerNodeInstance) {
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.TIMER_NODE)
              .setTimer(
                  JBPMMessages.ProcessInstance.NodeInstanceContent.TimerNode.newBuilder()
                      .setTimerId(((TimerNodeInstance) nodeInstance).getTimerId())
                      .build());
    } else if (nodeInstance instanceof JoinInstance) {
      JBPMMessages.ProcessInstance.NodeInstanceContent.JoinNode.Builder _join =
          JBPMMessages.ProcessInstance.NodeInstanceContent.JoinNode.newBuilder();
      Map<Long, Integer> triggers = ((JoinInstance) nodeInstance).getTriggers();
      List<Long> keys = new ArrayList<Long>(triggers.keySet());
      Collections.sort(
          keys,
          new Comparator<Long>() {
            public int compare(Long o1, Long o2) {
              return o1.compareTo(o2);
            }
          });
      for (Long key : keys) {
        _join.addTrigger(
            JBPMMessages.ProcessInstance.NodeInstanceContent.JoinNode.JoinTrigger.newBuilder()
                .setNodeId(key)
                .setCounter(triggers.get(key))
                .build());
      }
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.JOIN_NODE)
              .setJoin(_join.build());
    } else if (nodeInstance instanceof StateNodeInstance) {
      JBPMMessages.ProcessInstance.NodeInstanceContent.StateNode.Builder _state =
          JBPMMessages.ProcessInstance.NodeInstanceContent.StateNode.newBuilder();
      List<Long> timerInstances = ((StateNodeInstance) nodeInstance).getTimerInstances();
      if (timerInstances != null) {
        for (Long id : timerInstances) {
          _state.addTimerInstanceId(id);
        }
      }
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.STATE_NODE)
              .setState(_state.build());
    } else if (nodeInstance instanceof CompositeContextNodeInstance) {
      JBPMMessages.ProcessInstance.NodeInstanceContent.CompositeContextNode.Builder _composite =
          JBPMMessages.ProcessInstance.NodeInstanceContent.CompositeContextNode.newBuilder();
      JBPMMessages.ProcessInstance.NodeInstanceType _type = null;
      if (nodeInstance instanceof DynamicNodeInstance) {
        _type = JBPMMessages.ProcessInstance.NodeInstanceType.DYNAMIC_NODE;
      } else if (nodeInstance instanceof EventSubProcessNodeInstance) {
        _type = JBPMMessages.ProcessInstance.NodeInstanceType.EVENT_SUBPROCESS_NODE;
      } else {
        _type = JBPMMessages.ProcessInstance.NodeInstanceType.COMPOSITE_CONTEXT_NODE;
      }

      CompositeContextNodeInstance compositeNodeInstance =
          (CompositeContextNodeInstance) nodeInstance;
      List<Long> timerInstances = ((CompositeContextNodeInstance) nodeInstance).getTimerInstances();
      if (timerInstances != null) {
        for (Long id : timerInstances) {
          _composite.addTimerInstanceId(id);
        }
      }
      VariableScopeInstance variableScopeInstance =
          (VariableScopeInstance)
              compositeNodeInstance.getContextInstance(VariableScope.VARIABLE_SCOPE);
      if (variableScopeInstance != null) {
        List<Map.Entry<String, Object>> variables =
            new ArrayList<Map.Entry<String, Object>>(
                variableScopeInstance.getVariables().entrySet());
        Collections.sort(
            variables,
            new Comparator<Map.Entry<String, Object>>() {
              public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                return o1.getKey().compareTo(o2.getKey());
              }
            });
        for (Map.Entry<String, Object> variable : variables) {

          _composite.addVariable(
              ProtobufProcessMarshaller.marshallVariable(
                  context, variable.getKey(), variable.getValue()));
        }
      }

      List<NodeInstance> nodeInstances =
          new ArrayList<NodeInstance>(compositeNodeInstance.getNodeInstances());
      Collections.sort(
          nodeInstances,
          new Comparator<NodeInstance>() {
            public int compare(NodeInstance o1, NodeInstance o2) {
              return (int) (o1.getId() - o2.getId());
            }
          });
      for (NodeInstance subNodeInstance : nodeInstances) {
        _composite.addNodeInstance(writeNodeInstance(context, subNodeInstance));
      }
      List<ContextInstance> exclusiveGroupInstances =
          compositeNodeInstance.getContextInstances(ExclusiveGroup.EXCLUSIVE_GROUP);
      if (exclusiveGroupInstances != null) {
        for (ContextInstance contextInstance : exclusiveGroupInstances) {
          JBPMMessages.ProcessInstance.ExclusiveGroupInstance.Builder _excl =
              JBPMMessages.ProcessInstance.ExclusiveGroupInstance.newBuilder();
          ExclusiveGroupInstance exclusiveGroupInstance = (ExclusiveGroupInstance) contextInstance;
          Collection<NodeInstance> groupNodeInstances = exclusiveGroupInstance.getNodeInstances();
          for (NodeInstance groupNodeInstance : groupNodeInstances) {
            _excl.addGroupNodeInstanceId(groupNodeInstance.getId());
          }
          _composite.addExclusiveGroup(_excl.build());
        }
      }
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(_type)
              .setComposite(_composite.build());
    } else if (nodeInstance instanceof ForEachNodeInstance) {
      JBPMMessages.ProcessInstance.NodeInstanceContent.ForEachNode.Builder _foreach =
          JBPMMessages.ProcessInstance.NodeInstanceContent.ForEachNode.newBuilder();
      ForEachNodeInstance forEachNodeInstance = (ForEachNodeInstance) nodeInstance;
      List<NodeInstance> nodeInstances =
          new ArrayList<NodeInstance>(forEachNodeInstance.getNodeInstances());
      Collections.sort(
          nodeInstances,
          new Comparator<NodeInstance>() {
            public int compare(NodeInstance o1, NodeInstance o2) {
              return (int) (o1.getId() - o2.getId());
            }
          });
      for (NodeInstance subNodeInstance : nodeInstances) {
        if (subNodeInstance instanceof CompositeContextNodeInstance) {
          _foreach.addNodeInstance(writeNodeInstance(context, subNodeInstance));
        }
      }
      _content =
          JBPMMessages.ProcessInstance.NodeInstanceContent.newBuilder()
              .setType(NodeInstanceType.FOR_EACH_NODE)
              .setForEach(_foreach.build());
    } else {
      throw new IllegalArgumentException("Unknown node instance type: " + nodeInstance);
    }
    return _content.build();
  }