/**
   * Looks for an element with the given tag name in the Tree data being parsed, returning the path
   * hierarchy to reach it.
   *
   * @param parser
   * @param tag The element name (can be qualified) to search for
   * @return If the tag is found, an array of strings is returned. If the tag is at the top level,
   *     the tag will be the only item in the array. If the tag is nested beneath the top level, the
   *     array is filled with the hierarchy with the tag name at the last index null if the the tag
   *     is not found.
   * @throws ServletException
   */
  protected static List<String> detectRecordElement(TreeReader parser, String tag)
      throws TreeReaderException {
    if (parser.current() == Token.Ignorable) {
      parser.next();
    }

    String localName = parser.getFieldName();
    String fullName = composeName(parser.getPrefix(), localName);
    if (tag.equals(parser.getFieldName()) || tag.equals(fullName)) {
      List<String> path = new LinkedList<String>();
      path.add(localName);

      return path;
    }

    while (parser.hasNext()) {
      Token eventType = parser.next();
      if (eventType == Token.EndEntity) { // XMLStreamConstants.END_ELEMENT) {
        break;
      } else if (eventType == Token.StartEntity) { // XMLStreamConstants.START_ELEMENT) {
        List<String> path = detectRecordElement(parser, tag);
        if (path != null) {
          path.add(0, localName);
          return path;
        }
      }
    }
    return null;
  }
  /**
   * @param project
   * @param parser
   * @param recordPath
   * @param pathIndex
   * @param rootColumnGroup
   * @throws ServletException
   */
  protected static void findRecord(
      Project project,
      TreeReader parser,
      String[] recordPath,
      int pathIndex,
      ImportColumnGroup rootColumnGroup,
      int limit)
      throws TreeReaderException {
    logger.trace(
        "findRecord(Project, TreeReader, String[], int, ImportColumnGroup - path:"
            + Arrays.toString(recordPath));

    if (parser.current() == Token.Ignorable) { // XMLStreamConstants.START_DOCUMENT){
      logger.warn("Cannot use findRecord method for START_DOCUMENT event");
      return;
    }

    String recordPathSegment = recordPath[pathIndex];

    String localName = parser.getFieldName();
    String fullName = composeName(parser.getPrefix(), localName);
    if (recordPathSegment.equals(localName) || recordPathSegment.equals(fullName)) {
      if (pathIndex < recordPath.length - 1) {
        while (parser.hasNext() && limit != 0) {
          Token eventType = parser.next();
          if (eventType == Token.StartEntity) {
            findRecord(project, parser, recordPath, pathIndex + 1, rootColumnGroup, limit--);
          } else if (eventType == Token.EndEntity) {
            break;
          } else if (eventType == Token.Value) {
            // This is when the user picks a specific field to import, not a whole object or
            // element.
            if (pathIndex == recordPath.length - 2) {
              String desiredFieldName = recordPath[pathIndex + 1];
              String currentFieldName = parser.getFieldName();
              if (desiredFieldName.equals(currentFieldName)) {
                processFieldAsRecord(project, parser, rootColumnGroup);
              }
            }
          }
        }
      } else {
        processRecord(project, parser, rootColumnGroup);
      }
    } else {
      skip(parser);
    }
  }
  /**
   * @param project
   * @param parser
   * @param columnGroup
   * @param record
   * @throws ServletException
   */
  protected static void processSubRecord(
      Project project,
      TreeReader parser,
      ImportColumnGroup columnGroup,
      ImportRecord record,
      int level)
      throws TreeReaderException {
    logger.trace(
        "processSubRecord(Project,TreeReader,ImportColumnGroup,ImportRecord) lvl:"
            + level
            + " "
            + columnGroup);

    if (parser.current() == Token.Ignorable) {
      return;
    }

    ImportColumnGroup thisColumnGroup =
        getColumnGroup(
            project, columnGroup, composeName(parser.getPrefix(), parser.getFieldName()));

    thisColumnGroup.nextRowIndex = Math.max(thisColumnGroup.nextRowIndex, columnGroup.nextRowIndex);

    int attributeCount = parser.getAttributeCount();
    for (int i = 0; i < attributeCount; i++) {
      String text = parser.getAttributeValue(i).trim();
      if (text.length() > 0) {
        addCell(
            project,
            thisColumnGroup,
            record,
            composeName(parser.getAttributePrefix(i), parser.getAttributeLocalName(i)),
            text);
      }
    }

    while (parser.hasNext()) {
      Token eventType = parser.next();
      if (eventType == Token.StartEntity) {
        processSubRecord(project, parser, thisColumnGroup, record, level + 1);
      } else if ( // eventType == XMLStreamConstants.CDATA ||
      eventType == Token.Value) { // XMLStreamConstants.CHARACTERS) {
        String text = parser.getFieldValue();
        String colName = parser.getFieldName();
        if (text != null) {
          text = text.trim();
          if (text.length() > 0) {
            addCell(project, thisColumnGroup, record, colName, text);
          }
        }
      } else if (eventType == Token.EndEntity) {
        break;
      } else if (eventType == Token.Ignorable) {
        continue;
      } else {
        logger.info("unknown event type " + eventType);
      }
    }

    int nextRowIndex = thisColumnGroup.nextRowIndex;
    for (ImportColumn column2 : thisColumnGroup.columns.values()) {
      nextRowIndex = Math.max(nextRowIndex, column2.nextRowIndex);
    }
    for (ImportColumnGroup columnGroup2 : thisColumnGroup.subgroups.values()) {
      nextRowIndex = Math.max(nextRowIndex, columnGroup2.nextRowIndex);
    }
    thisColumnGroup.nextRowIndex = nextRowIndex;
  }