public List<String> getPartIterationSelectedAttributes(List<String> selects) {
   List<String> attributesSelect = new ArrayList<>();
   for (String select : selects) {
     if (select.contains(QueryField.PART_REVISION_ATTRIBUTES_PREFIX)) {
       attributesSelect.add(select);
     }
   }
   return attributesSelect;
 }
 public List<String> getPathDataSelectedAttributes(List<String> selects) {
   List<String> attributesSelect = new ArrayList<>();
   for (String select : selects) {
     if (select.contains(QueryField.PATH_DATA_ATTRIBUTES_PREFIX)) {
       attributesSelect.add(select);
     }
   }
   return attributesSelect;
 }
  private void generateJSONResponse(OutputStream outputStream, QueryResult queryResult)
      throws UnsupportedEncodingException, NamingException {

    String charSet = "UTF-8";
    JsonGenerator jg = Json.createGenerator(new OutputStreamWriter(outputStream, charSet));
    jg.writeStartArray();

    List<String> selects = queryResult.getQuery().getSelects();
    List<String> partIterationSelectedAttributes = getPartIterationSelectedAttributes(selects);
    List<String> pathDataSelectedAttributes = getPathDataSelectedAttributes(selects);

    context = new InitialContext();
    IProductInstanceManagerLocal productInstanceService =
        (IProductInstanceManagerLocal)
            context.lookup(
                "java:global/docdoku-server-ear/docdoku-server-ejb/ProductInstanceManagerBean");

    for (QueryResultRow row : queryResult.getRows()) {

      QueryContext queryContext = row.getContext();

      PartRevision part = row.getPartRevision();
      PartIteration lastCheckedInIteration = part.getLastCheckedInIteration();

      jg.writeStartObject();

      jg.write(QueryField.PART_REVISION_PART_KEY, part.getPartNumber() + '-' + part.getVersion());

      // PartMaster data

      if (selects.contains(QueryField.PART_MASTER_NUMBER)) {
        jg.write(QueryField.PART_MASTER_NUMBER, part.getPartNumber());
      }

      if (selects.contains(QueryField.PART_MASTER_NAME)) {
        String sName = part.getPartName();
        jg.write(QueryField.PART_MASTER_NAME, sName != null ? sName : "");
      }

      if (selects.contains(QueryField.PART_MASTER_TYPE)) {
        String sType = part.getType();
        jg.write(QueryField.PART_MASTER_TYPE, sType != null ? sType : "");
      }

      // PartRevision data

      if (selects.contains(QueryField.PART_REVISION_MODIFICATION_DATE)) {
        PartIteration pi = part.getLastIteration();
        if (pi != null) {
          writeDate(jg, QueryField.PART_REVISION_MODIFICATION_DATE, pi.getModificationDate());
        }
      }

      if (selects.contains(QueryField.PART_REVISION_CREATION_DATE)) {
        writeDate(jg, QueryField.PART_REVISION_CREATION_DATE, part.getCreationDate());
      }

      if (selects.contains(QueryField.PART_REVISION_CHECKOUT_DATE)) {
        writeDate(jg, QueryField.PART_REVISION_CHECKOUT_DATE, part.getCheckOutDate());
      }

      if (selects.contains(QueryField.PART_REVISION_CHECKIN_DATE)) {
        writeDate(
            jg,
            QueryField.PART_REVISION_CHECKIN_DATE,
            lastCheckedInIteration != null ? lastCheckedInIteration.getCheckInDate() : null);
      }

      if (selects.contains(QueryField.PART_REVISION_VERSION)) {
        String version = part.getVersion();
        jg.write(QueryField.PART_REVISION_VERSION, version);
      }

      if (selects.contains(QueryField.PART_REVISION_LIFECYCLE_STATE)) {
        String lifeCycleState = part.getLifeCycleState();
        jg.write(
            QueryField.PART_REVISION_LIFECYCLE_STATE, lifeCycleState != null ? lifeCycleState : "");
      }

      if (selects.contains(QueryField.PART_REVISION_STATUS)) {
        PartRevision.RevisionStatus status = part.getStatus();
        jg.write(QueryField.PART_REVISION_STATUS, status.toString());
      }

      if (selects.contains(QueryField.AUTHOR_LOGIN)) {
        User user = part.getAuthor();
        jg.write(QueryField.AUTHOR_LOGIN, user.getLogin());
      }

      if (selects.contains(QueryField.AUTHOR_NAME)) {
        User user = part.getAuthor();
        jg.write(QueryField.AUTHOR_NAME, user.getName());
      }

      if (selects.contains(QueryField.CTX_DEPTH)) {
        jg.write(QueryField.CTX_DEPTH, row.getDepth());
      }

      if (selects.contains(QueryField.PART_ITERATION_LINKED_DOCUMENTS)) {

        StringBuilder sb = new StringBuilder();

        if (null != queryContext && null != queryContext.getSerialNumber()) {
          try {

            ProductInstanceMaster productInstanceMaster =
                productInstanceService.getProductInstanceMaster(
                    new ProductInstanceMasterKey(
                        queryContext.getSerialNumber(),
                        queryContext.getWorkspaceId(),
                        queryContext.getConfigurationItemId()));
            ProductInstanceIteration lastIteration = productInstanceMaster.getLastIteration();
            ProductBaseline basedOn = lastIteration.getBasedOn();
            PartCollection partCollection = basedOn.getPartCollection();
            BaselinedPart baselinedPart =
                partCollection.getBaselinedPart(
                    new BaselinedPartKey(
                        partCollection.getId(),
                        queryContext.getWorkspaceId(),
                        part.getPartNumber()));
            PartIteration targetPart = baselinedPart.getTargetPart();
            Set<DocumentLink> linkedDocuments = targetPart.getLinkedDocuments();
            DocumentCollection documentCollection = basedOn.getDocumentCollection();

            for (DocumentLink documentLink : linkedDocuments) {
              DocumentRevision targetDocument = documentLink.getTargetDocument();
              BaselinedDocument baselinedDocument =
                  documentCollection.getBaselinedDocument(
                      new BaselinedDocumentKey(
                          documentCollection.getId(),
                          queryContext.getWorkspaceId(),
                          targetDocument.getDocumentMasterId(),
                          targetDocument.getVersion()));
              if (null != baselinedDocument) {
                DocumentIteration targetDocumentIteration = baselinedDocument.getTargetDocument();
                sb.append(targetDocumentIteration.toString() + ",");
              }
            }

          } catch (UserNotFoundException
              | UserNotActiveException
              | WorkspaceNotFoundException
              | ProductInstanceMasterNotFoundException e) {
            LOGGER.log(Level.FINEST, null, e);
          }
        } else {
          if (lastCheckedInIteration != null) {
            Set<DocumentLink> linkedDocuments = lastCheckedInIteration.getLinkedDocuments();

            for (DocumentLink documentLink : linkedDocuments) {
              DocumentRevision targetDocument = documentLink.getTargetDocument();
              DocumentIteration targetDocumentLastCheckedInIteration =
                  targetDocument.getLastCheckedInIteration();
              if (targetDocumentLastCheckedInIteration != null) {
                sb.append(targetDocumentLastCheckedInIteration.toString() + ",");
              }
            }
          }
        }

        jg.write(QueryField.PART_ITERATION_LINKED_DOCUMENTS, sb.toString());
      }

      for (String attributeSelect : partIterationSelectedAttributes) {

        String attributeSelectType =
            attributeSelect
                .substring(0, attributeSelect.indexOf("."))
                .substring(QueryField.PART_REVISION_ATTRIBUTES_PREFIX.length());

        String attributeSelectName = attributeSelect.substring(attributeSelect.indexOf(".") + 1);

        String attributeValue = "";

        PartIteration pi = part.getLastIteration();

        if (pi != null) {
          List<InstanceAttribute> attributes = pi.getInstanceAttributes();

          if (attributes != null) {
            jg.writeStartArray(attributeSelect);

            for (InstanceAttribute attribute : attributes) {
              InstanceAttributeDescriptor attributeDescriptor =
                  new InstanceAttributeDescriptor(attribute);

              if (attributeDescriptor.getName().equals(attributeSelectName)
                  && attributeDescriptor.getStringType().equals(attributeSelectType)) {

                attributeValue = attribute.getValue() + "";

                if (attribute instanceof InstanceDateAttribute) {
                  attributeValue =
                      getFormattedDate(((InstanceDateAttribute) attribute).getDateValue());
                } else if (attribute instanceof InstanceListOfValuesAttribute) {
                  attributeValue = ((InstanceListOfValuesAttribute) attribute).getSelectedName();
                }

                jg.write(attributeValue);
              }
            }

            jg.writeEnd();

          } else {
            jg.write(attributeSelect, attributeValue);
          }

        } else {
          // TODO: maybe this line is useless and should be removed
          jg.write(attributeSelect, attributeValue);
        }
      }

      for (String attributeSelect : pathDataSelectedAttributes) {

        String attributeSelectType =
            attributeSelect
                .substring(0, attributeSelect.indexOf("."))
                .substring(QueryField.PATH_DATA_ATTRIBUTES_PREFIX.length());

        String attributeSelectName = attributeSelect.substring(attributeSelect.indexOf(".") + 1);

        String attributeValue = "";

        PathDataIteration pdi = row.getPathDataIteration();

        if (pdi != null) {
          List<InstanceAttribute> attributes = pdi.getInstanceAttributes();

          if (attributes != null) {
            jg.writeStartArray(attributeSelect);

            for (InstanceAttribute attribute : attributes) {
              InstanceAttributeDescriptor attributeDescriptor =
                  new InstanceAttributeDescriptor(attribute);

              if (attributeDescriptor.getName().equals(attributeSelectName)
                  && attributeDescriptor.getStringType().equals(attributeSelectType)) {

                attributeValue = attribute.getValue() + "";

                if (attribute instanceof InstanceDateAttribute) {
                  attributeValue =
                      getFormattedDate(((InstanceDateAttribute) attribute).getDateValue());
                } else if (attribute instanceof InstanceListOfValuesAttribute) {
                  attributeValue = ((InstanceListOfValuesAttribute) attribute).getSelectedName();
                }

                jg.write(attributeValue);
              }
            }

            jg.writeEnd();

          } else {
            jg.write(attributeSelect, attributeValue);
          }
        }
      }

      if (selects.contains(QueryField.CTX_PRODUCT_ID)) {
        String configurationItemId =
            queryContext != null ? queryContext.getConfigurationItemId() : "";
        jg.write(QueryField.CTX_PRODUCT_ID, configurationItemId);
      }
      if (selects.contains(QueryField.CTX_SERIAL_NUMBER)) {
        String serialNumber = queryContext != null ? queryContext.getSerialNumber() : "";
        jg.write(QueryField.CTX_SERIAL_NUMBER, serialNumber != null ? serialNumber : "");
      }
      if (selects.contains(QueryField.CTX_AMOUNT)) {
        String amount = row.getAmount() + "";
        jg.write(QueryField.CTX_AMOUNT, amount);
      }

      if (selects.contains(QueryField.CTX_P2P_SOURCE)) {
        Map<String, List<PartLinkList>> sources = row.getSources();
        String partLinksAsString = Tools.getPartLinksAsHumanString(sources);
        jg.write(QueryField.CTX_P2P_SOURCE, partLinksAsString);
      }

      if (selects.contains(QueryField.CTX_P2P_TARGET)) {
        Map<String, List<PartLinkList>> targets = row.getTargets();
        String partLinksAsString = Tools.getPartLinksAsHumanString(targets);
        jg.write(QueryField.CTX_P2P_TARGET, partLinksAsString);
      }

      if (selects.contains(QueryField.PART_MASTER_IS_STANDARD)) {
        boolean isStandard = row.getPartRevision().getPartMaster().isStandardPart();
        jg.write(QueryField.PART_MASTER_IS_STANDARD, isStandard);
      }

      jg.writeEnd();
    }

    jg.writeEnd();
    jg.flush();
  }