@Override
  public void validate(Definition definition) throws WorkflowException {
    State initialState = definition.getInitialState();

    if (initialState == null) {
      throw new WorkflowException("No initial state defined");
    }

    List<State> terminalStates = definition.getTerminalStates();

    if (terminalStates.isEmpty()) {
      throw new WorkflowException("No terminal states defined");
    }

    if (definition.getForksCount() != definition.getJoinsCount()) {
      throw new WorkflowException("There are unbalanced fork and join nodes");
    }

    Collection<Node> nodes = definition.getNodes();

    for (Node node : nodes) {
      NodeValidator<Node> nodeValidator =
          _nodeValidatorRegistry.getNodeValidator(node.getNodeType());

      nodeValidator.validate(definition, node);
    }
  }
  protected void parseTransition(Definition definition, Element nodeElement) {
    String sourceName = nodeElement.elementText("name");

    Node sourceNode = definition.getNode(sourceName);

    Element transitionsElement = nodeElement.element("transitions");

    if (transitionsElement == null) {
      return;
    }

    List<Element> transitionElements = transitionsElement.elements("transition");

    for (Element transitionElement : transitionElements) {
      String transitionName = transitionElement.elementText("name");

      String targetName = transitionElement.elementText("target");

      Node targetNode = definition.getNode(targetName);

      boolean defaultValue = GetterUtil.getBoolean(transitionElement.elementText("default"), true);

      Transition transition = new Transition(transitionName, sourceNode, targetNode, defaultValue);

      Element timerElement = transitionElement.element("timer");

      if (timerElement != null) {
        Timer timer = parseTimerElement(timerElement, false);

        transition.setTimers(timer);
      }

      sourceNode.addTransition(transition);
    }
  }
  @Override
  public KaleoDefinition incrementKaleoDefinition(
      Definition definition, String title, ServiceContext serviceContext) throws PortalException {

    KaleoDefinition kaleoDefinition =
        getLatestKaleoDefinition(definition.getName(), serviceContext);

    return addKaleoDefinition(
        definition.getName(),
        title,
        definition.getDescription(),
        definition.getContent(),
        kaleoDefinition.getVersion() + 1,
        serviceContext);
  }
  protected String doExport(Definition definition) {
    try {
      Document document = SAXReaderUtil.createDocument();

      Element workflowDefinitionElement = document.addElement("workflow-definition");

      workflowDefinitionElement.addAttribute(
          "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
      workflowDefinitionElement.addAttribute(
          "xsi:schemaLocation",
          "urn:liferay.com:liferay-workflow_"
              + _version
              + " http://www.liferay.com/dtd/liferay-workflow-definition_"
              + _schemaVersion
              + ".xsd");
      workflowDefinitionElement.addNamespace("", "urn:liferay.com:liferay-workflow_" + _version);

      Element nameElement = workflowDefinitionElement.addElement("name", _namespace);

      nameElement.addText(definition.getName());

      if (Validator.isNotNull(definition.getDescription())) {
        Element descriptionElement =
            workflowDefinitionElement.addElement("description", _namespace);

        descriptionElement.addText(definition.getDescription());
      }

      Element versionElement = workflowDefinitionElement.addElement("version", _namespace);

      versionElement.addText(String.valueOf(definition.getVersion()));

      Collection<Node> nodes = definition.getNodes();

      for (Node node : nodes) {
        NodeExporter nodeExporter = NodeExporterRegistry.getNodeExporter(node.getNodeType());

        nodeExporter.exportNode(node, workflowDefinitionElement, _namespace);
      }

      return document.formattedString();
    } catch (IOException ioe) {
      throw new SystemException("Unable to export definition", ioe);
    }
  }
  protected Definition doParse(InputStream inputStream) throws Exception {
    Document document = SAXReaderUtil.read(inputStream, _validate);

    Element rootElement = document.getRootElement();

    String name = rootElement.elementText("name");
    String description = rootElement.elementText("description");
    int version = GetterUtil.getInteger(rootElement.elementText("version"));

    Definition definition = new Definition(name, description, document.formattedString(), version);

    List<Element> conditionElements = rootElement.elements("condition");

    for (Element conditionElement : conditionElements) {
      Condition condition = parseCondition(conditionElement);

      definition.addNode(condition);
    }

    List<Element> forkElements = rootElement.elements("fork");

    for (Element forkElement : forkElements) {
      Fork fork = parseFork(forkElement);

      definition.addNode(fork);
    }

    List<Element> joinElements = rootElement.elements("join");

    for (Element joinElement : joinElements) {
      Join join = parseJoin(joinElement);

      definition.addNode(join);
    }

    List<Element> stateElements = rootElement.elements("state");

    for (Element stateElement : stateElements) {
      State state = parseState(stateElement);

      definition.addNode(state);
    }

    List<Element> taskElements = rootElement.elements("task");

    for (Element taskElement : taskElements) {
      Task task = parseTask(taskElement);

      definition.addNode(task);
    }

    parseTransitions(
        definition, conditionElements, forkElements, joinElements, stateElements, taskElements);

    return definition;
  }