/**
   * Reads line by line and creates new entities
   *
   * @throws IOException
   * @throws IfcNotFoundException
   * @throws IfcParserException
   */
  public List<IfcEntity> parseEntities(
      IfcLineReader reader, IfcSchema schema, boolean isHeaderSection, boolean ignoreUnknownTypes)
      throws IOException, IfcNotFoundException, IfcParserException {

    this.schema = schema;
    this.reader = reader;

    List<IfcEntity> entities = new ArrayList<>();

    String statement;
    String[] tokens;
    //
    // getting entity headers
    //
    while ((statement = reader.getNextStatement()) != null) {

      IfcEntity entity;
      String entityAttributesString;

      if (!isHeaderSection) {
        tokens = RegexUtils.split2(statement, IfcVocabulary.StepFormat.LINE_NUMBER);
        if (tokens.length != 2) {
          continue;
        }

        tokens = RegexUtils.split2(tokens[1], IfcVocabulary.StepFormat.EQUAL);

        //
        // create entity
        //
        long lineNumber = Long.parseLong(tokens[0].trim());
        entity = getEntity(lineNumber);
        entityAttributesString = tokens[1].trim();

      } else {
        entity = new IfcEntity(0);
        entityAttributesString = statement;
      }

      //
      // set entity type
      //
      int indexOfOpeningBracket = entityAttributesString.indexOf(StringUtils.OPENING_ROUND_BRACKET);
      String entityTypeInfoName = entityAttributesString.substring(0, indexOfOpeningBracket).trim();

      IfcEntityTypeInfo entityTypeInfo;

      try {
        entityTypeInfo = schema.getEntityTypeInfo(entityTypeInfoName);
      } catch (IfcNotFoundException e) {
        if (ignoreUnknownTypes) {
          continue;
        } else {
          throw e;
        }
      }
      entity.setTypeInfo(entityTypeInfo);

      entityAttributesString =
          entityAttributesString.substring(
              indexOfOpeningBracket + 1, entityAttributesString.length() - 1);

      List<IfcAttributeInfo> attributeInfos = entityTypeInfo.getInheritedAttributeInfos();

      //
      // parse attribute string to get attribute values
      //
      List<IfcValue> attributeValues =
          parseAttributeValues(
              new StrBuilderWrapper(entityAttributesString), entity, attributeInfos, null, null);

      setEntityAttributeValues(entity, attributeInfos, attributeValues);

      //
      // add entity to the model
      //
      entities.add(entity);
    }

    if (!isHeaderSection) {
      for (IfcEntity entity : entities) {
        entity.bindInverseLinks();
      }
    }

    return entities;
  }
  /**
   * Set entity attribute values
   *
   * @param entity
   * @param attributeInfos
   * @param attributeValues
   * @throws IfcParserException
   */
  private void setEntityAttributeValues(
      IfcEntity entity, List<IfcAttributeInfo> attributeInfos, List<IfcValue> attributeValues)
      throws IfcParserException {
    try {

      if (attributeValues.size() == attributeInfos.size()) {
        boolean isLiteralValueContainer = true;
        for (int attributeIndex = 0; attributeIndex < attributeValues.size(); ++attributeIndex) {

          IfcAttributeInfo attributeInfo = attributeInfos.get(attributeIndex);
          IfcValue attributeValue = attributeValues.get(attributeIndex);

          Boolean isLiteralValue = attributeValue.isLiteralType();

          if (isLiteralValue != null) {
            isLiteralValueContainer = isLiteralValueContainer && isLiteralValue == Boolean.TRUE;
            if (isLiteralValue) {

              if (attributeValue instanceof IfcTemporaryCollectionValueWrapper) {

                if (attributeInfo.isCollection()) {

                  IfcLiteralValueCollection values = new IfcLiteralValueCollection();
                  for (IfcValue value :
                      ((IfcTemporaryCollectionValueWrapper) attributeValue).getValues()) {
                    values.add((IfcLiteralValue) value);
                  }

                  entity.addLiteralAttribute(
                      new IfcLiteralAttribute(attributeInfo, attributeIndex, values));

                } else {

                  for (IfcValue value :
                      ((IfcTemporaryCollectionValueWrapper) attributeValue).getValues()) {
                    entity.addLiteralAttribute(
                        new IfcLiteralAttribute(attributeInfo, attributeIndex, value));
                  }
                }

              } else {

                assert (attributeValue instanceof IfcLiteralValue)
                    : String.format(
                        "Object is not a literal value, line number: %d, attributeInfo: %s, attribute value: %s, value type: %s",
                        entity.getLineNumber(),
                        attributeInfo.getName(),
                        attributeValue,
                        attributeInfo.getAttributeTypeInfo().getValueTypes());
                entity.addLiteralAttribute(
                    new IfcLiteralAttribute(attributeInfo, attributeIndex, attributeValue));
              }

            } else { // attributeInfo instanceof IfcLinkInfo

              if (attributeValue instanceof IfcTemporaryCollectionValueWrapper) {

                if (attributeInfo.isCollection()) {

                  IfcEntityCollection destinations = new IfcEntityCollection();
                  for (IfcValue destination :
                      ((IfcTemporaryCollectionValueWrapper) attributeValue).getValues()) {
                    destinations.add((IfcEntity) destination);
                  }

                  entity.addOutgoingLink(
                      new IfcLink(
                          (IfcLinkInfo) attributeInfo, attributeIndex, entity, destinations));

                } else {

                  for (IfcValue destination :
                      ((IfcTemporaryCollectionValueWrapper) attributeValue).getValues()) {
                    IfcLink link =
                        new IfcLink(
                            (IfcLinkInfo) attributeInfo,
                            attributeIndex,
                            entity,
                            (IfcEntityBase) destination);
                    entity.addOutgoingLink(link);
                  }
                }

              } else {

                assert (attributeValue instanceof IfcEntityBase)
                    : String.format(
                        "Object is not an entity, line number: %d, attributeInfo: %s, attribute value: %s, value type: %s",
                        entity.getLineNumber(),
                        attributeInfo.getName(),
                        attributeValue,
                        attributeInfo.getAttributeTypeInfo().getValueTypes());
                IfcLink link =
                    new IfcLink(
                        (IfcLinkInfo) attributeInfo,
                        attributeIndex,
                        entity,
                        (IfcEntityBase) attributeValue);
                entity.addOutgoingLink(link);
              }
            }
          }
        }
        entity.setLiteralValueContainer(isLiteralValueContainer);

      } else {
        throw new IfcParserException(
            String.format(
                "Type %s: Expected %d attributes, but %d were found: %s, %s",
                entity.getTypeInfo().getName(),
                attributeInfos.size(),
                attributeValues.size(),
                attributeInfos,
                attributeValues));
      }

    } catch (Exception e) {
      throw new IfcParserException(
          String.format(
              "Error parsing entity %s (line %d): %s",
              entity.toString(), entity.getLineNumber(), e.getMessage()),
          e);
    }
  }