private void processSection(PageSection section, List<StructNode> childNodes) throws IOException {
   for (StructNode sectionChildNode : childNodes) {
     String childLine = sectionChildNode.getName();
     if (isSectionDefinition(childLine)) {
       new PageSectionProcessor(pageSpecHandler, section).process(sectionChildNode);
     } else if (isObject(childLine)) {
       processObject(section, sectionChildNode);
     } else if (isRule(childLine)) {
       processSectionRule(section, sectionChildNode);
     } else {
       throw new SyntaxException(sectionChildNode, "Unknown statement: " + childLine);
     }
   }
 }
 public void process(StructNode sectionNode) throws IOException {
   if (sectionNode.getChildNodes() != null) {
     String sectionName =
         sectionNode.getName().substring(1, sectionNode.getName().length() - 1).trim();
     PageSection section = findSection(sectionName);
     if (section == null) {
       section = new PageSection(sectionName);
       if (parentSection != null) {
         parentSection.addSubSection(section);
       } else {
         pageSpecHandler.addSection(section);
       }
     }
     processSection(section, sectionNode.getChildNodes());
   }
 }
  private void processSpec(ObjectSpecs objectSpecs, StructNode specNode) {
    try {
      String specText = specNode.getName();
      boolean onlyWarn = false;
      if (specText.startsWith("%")) {
        specText = specText.substring(1);
        onlyWarn = true;
      }

      String alias = null;
      StringCharReader reader = new StringCharReader(specText);
      if (reader.firstNonWhiteSpaceSymbol() == '"') {
        alias = Expectations.doubleQuotedText().read(reader);
        specText = reader.getTheRest();
      }

      Spec spec = pageSpecHandler.getSpecReader().read(specText, pageSpecHandler.getContextPath());
      spec.setOnlyWarn(onlyWarn);
      spec.setAlias(alias);
      if (specNode.getSource() != null) {
        spec.setPlace(new Place(specNode.getSource(), specNode.getFileLineNumber()));
      }
      spec.setProperties(pageSpecHandler.getProperties());
      spec.setJsVariables(pageSpecHandler.getJsVariables());

      objectSpecs.getSpecs().add(spec);
    } catch (SyntaxException ex) {
      ex.setLine(new Line(specNode.getSource(), specNode.getFileLineNumber()));
      throw ex;
    }
  }
  private void processObject(PageSection section, StructNode objectNode) throws IOException {
    String name = objectNode.getName();
    String objectExpression = name.substring(0, name.length() - 1).trim();

    List<String> objectNames =
        findAllMatchingObjectNamesForExpression(objectExpression, objectNode);

    for (String objectName : objectNames) {
      if (objectNode.getChildNodes() != null && objectNode.getChildNodes().size() > 0) {
        ObjectSpecs objectSpecs = findObjectSpecsInSection(section, objectName);
        if (objectSpecs == null) {
          objectSpecs = new ObjectSpecs(objectName);
          section.addObjects(objectSpecs);
        }

        for (StructNode specNode : objectNode.getChildNodes()) {
          if (isRule(specNode.getName())) {
            processObjectLevelRule(objectSpecs, specNode);
          } else {
            processSpec(objectSpecs, specNode);
          }
        }
      }
    }
  }
  private void processObjectLevelRule(ObjectSpecs objectSpecs, StructNode sourceNode)
      throws IOException {
    String ruleText = sourceNode.getName().substring(1).trim();
    Pair<PageRule, Map<String, String>> rule = findAndProcessRule(ruleText, sourceNode);

    pageSpecHandler.setGlobalVariable("objectName", objectSpecs.getObjectName(), sourceNode);

    List<StructNode> specNodes =
        rule.getKey()
            .apply(pageSpecHandler, ruleText, objectSpecs.getObjectName(), rule.getValue());

    SpecGroup specGroup = new SpecGroup();
    specGroup.setName(ruleText);
    objectSpecs.addSpecGroup(specGroup);

    for (StructNode specNode : specNodes) {
      specGroup.addSpec(
          pageSpecHandler
              .getSpecReader()
              .read(specNode.getName(), pageSpecHandler.getContextPath()));
    }
  }
  private void processSectionRule(PageSection section, StructNode ruleNode) throws IOException {
    String ruleText = ruleNode.getName().substring(1).trim();

    Pair<PageRule, Map<String, String>> rule = findAndProcessRule(ruleText, ruleNode);

    PageSection ruleSection = new PageSection(ruleText);
    section.addSubSection(ruleSection);

    List<StructNode> resultingNodes;
    try {
      resultingNodes =
          rule.getKey().apply(pageSpecHandler, ruleText, NO_OBJECT_NAME, rule.getValue());
    } catch (Exception ex) {
      throw new SyntaxException(ruleNode, "Error processing custom rule", ex);
    }
    processSection(ruleSection, resultingNodes);
  }