private void writeHeaderCell(ObjectField field) throws IOException {
   page.writeStart("th");
   page.writeHtml(field.getDisplayName());
   page.writeEnd();
 }
  protected void writeTableHtml(Iterable<?> items) throws IOException {
    HttpServletRequest request = page.getRequest();

    page.putOverride(
        Recordable.class,
        (HtmlWriter writer, Recordable object) -> {
          ToolPageContext page = (ToolPageContext) writer;

          page.writeObjectLabel(object);
        });

    page.putOverride(
        Metric.class,
        (HtmlWriter writer, Metric object) -> {
          writer.write(Double.toString(object.getSum()));
        });

    page.putOverride(
        Content.class,
        (HtmlWriter writer, Content content) -> {
          ToolPageContext page = (ToolPageContext) writer;

          page.writeStart(
              "a", "href", page.objectUrl("/content/edit.jsp", content), "target", "_top");
          page.writeObjectLabel(content);
          page.writeEnd();
        });

    page.putOverride(
        StorageItem.class,
        (HtmlWriter writer, StorageItem item) -> {
          ToolPageContext page = (ToolPageContext) writer;

          page.writeElement(
              "img",
              "height",
              100,
              "src",
              ImageEditor.Static.getDefault() != null
                  ? new ImageTag.Builder(item).setHeight(100).toUrl()
                  : item.getPublicUrl());
        });

    page.writeStart("table", "class", "searchResultTable links table-striped pageThumbnails");
    page.writeStart("thead");
    page.writeStart("tr");
    page.writeStart("th");
    page.writeElement("input", "type", "checkbox", "class", "searchResult-checkAll");
    page.writeEnd();

    if (sortField != null && ObjectField.DATE_TYPE.equals(sortField.getInternalType())) {

      page.writeStart("th", "colspan", 2);
      page.writeHtml(sortField.getDisplayName());
      page.writeEnd();
    }

    if (showSiteLabel) {
      page.writeStart("th");
      page.writeHtml(page.localize(ListSearchResultView.class, "label.site"));
      page.writeEnd();
    }

    if (showTypeLabel) {
      page.writeStart("th");
      page.writeHtml(page.localize(ListSearchResultView.class, "label.type"));
      page.writeEnd();
    }

    page.writeStart("th");
    page.writeHtml(page.localize(ListSearchResultView.class, "label.label"));
    page.writeEnd();

    if (sortField != null && !ObjectField.DATE_TYPE.equals(sortField.getInternalType())) {

      page.writeStart("th");
      page.writeHtml(sortField.getDisplayName());
      page.writeEnd();
    }

    ToolUser user = page.getUser();

    if (user != null) {
      ObjectType selectedType = search.getSelectedType();
      List<String> fieldNames =
          user.getSearchResultFieldsByTypeId()
              .get(selectedType != null ? selectedType.getId().toString() : "");

      if (fieldNames == null) {
        for (Class<? extends SearchResultField> c :
            ClassFinder.Static.findClasses(SearchResultField.class)) {
          if (!c.isInterface() && !Modifier.isAbstract(c.getModifiers())) {
            SearchResultField field = TypeDefinition.getInstance(c).newInstance();

            if (field.isDefault(selectedType) && !ObjectUtils.equals(sortField, field)) {
              field.writeTableHeaderCellHtml(page);
            }
          }
        }

        if (selectedType != null) {
          for (ObjectField field : selectedType.getFields()) {
            if (Boolean.TRUE.equals(field.as(ToolUi.class).getDefaultSearchResult())) {
              writeHeaderCell(field);
            }
          }
        }

      } else {
        for (String fieldName : fieldNames) {
          Class<?> fieldNameClass = ObjectUtils.getClassByName(fieldName);

          if (fieldNameClass != null && SearchResultField.class.isAssignableFrom(fieldNameClass)) {
            @SuppressWarnings("unchecked")
            SearchResultField field =
                TypeDefinition.getInstance((Class<? extends SearchResultField>) fieldNameClass)
                    .newInstance();

            if (field.isSupported(selectedType) && !ObjectUtils.equals(sortField, field)) {
              field.writeTableHeaderCellHtml(page);
            }

          } else {
            ObjectField field = null;
            if (selectedType != null) {
              field = selectedType.getField(fieldName);
            }

            if (field == null) {
              field = Database.Static.getDefault().getEnvironment().getField(fieldName);
            }

            if (field != null && !ObjectUtils.equals(sortField, field)) {
              writeHeaderCell(field);
            }
          }
        }
      }
    }
    page.writeEnd();
    page.writeEnd();

    page.writeStart("tbody");
    for (Object item : items) {
      State itemState = State.getInstance(item);
      String permalink = itemState.as(Directory.ObjectModification.class).getPermalink();
      Integer embedWidth = null;

      if (ObjectUtils.isBlank(permalink)) {
        ObjectType type = itemState.getType();

        if (type != null) {
          Renderer.TypeModification rendererData = type.as(Renderer.TypeModification.class);
          int previewWidth = rendererData.getEmbedPreviewWidth();

          if (previewWidth > 0 && !ObjectUtils.isBlank(rendererData.getEmbedPath())) {

            permalink = "/_preview?_embed=true&_cms.db.previewId=" + itemState.getId();
            embedWidth = 320;
          }
        }
      }

      page.writeStart(
          "tr",
          "data-preview-url",
          permalink,
          "data-preview-embed-width",
          embedWidth,
          "class",
          State.getInstance(item).getId().equals(page.param(UUID.class, "id")) ? "selected" : null);

      page.writeStart("td");
      itemWriter.writeCheckboxHtml(page, search, item);
      page.writeEnd();

      if (sortField != null && ObjectField.DATE_TYPE.equals(sortField.getInternalType())) {

        DateTime dateTime = page.toUserDateTime(itemState.getByPath(sortField.getInternalName()));

        if (dateTime == null) {
          page.writeStart("td", "colspan", 2);
          page.writeHtml("N/A");
          page.writeEnd();

        } else {
          String date = page.formatUserDate(dateTime);

          page.writeStart("td", "class", "date");
          if (!ObjectUtils.equals(date, request.getAttribute(PREVIOUS_DATE_ATTRIBUTE))) {
            request.setAttribute(PREVIOUS_DATE_ATTRIBUTE, date);
            page.writeHtml(date);
          }
          page.writeEnd();

          page.writeStart("td", "class", "time");
          page.writeHtml(page.formatUserTime(dateTime));
          page.writeEnd();
        }
      }

      if (showSiteLabel) {
        page.writeStart("td");
        page.writeObjectLabel(itemState.as(Site.ObjectModification.class).getOwner());
        page.writeEnd();
      }

      if (showTypeLabel) {
        page.writeStart("td");
        page.writeTypeLabel(item);
        page.writeEnd();
      }

      page.writeStart("td", "data-preview-anchor", "");
      itemWriter.writeBeforeHtml(page, search, item);
      page.writeObjectLabel(item);
      itemWriter.writeAfterHtml(page, search, item);
      page.writeEnd();

      if (sortField != null && !ObjectField.DATE_TYPE.equals(sortField.getInternalType())) {

        String sortFieldName = sortField.getInternalName();
        Object value = itemState.getByPath(sortFieldName);

        page.writeStart("td");
        if (value instanceof Metric) {
          page.writeStart("span", "style", page.cssString("white-space", "nowrap"));
          Double maxSum = (Double) request.getAttribute(MAX_SUM_ATTRIBUTE);

          if (maxSum == null) {
            Object maxObject = search.toQuery(page.getSite()).sortDescending(sortFieldName).first();
            maxSum =
                maxObject != null
                    ? ((Metric) State.getInstance(maxObject).get(sortFieldName)).getSum()
                    : 1.0;

            request.setAttribute(MAX_SUM_ATTRIBUTE, maxSum);
          }

          Metric valueMetric = (Metric) value;
          Map<DateTime, Double> sumEntries =
              valueMetric.groupSumByDate(
                  new MetricInterval.Daily(),
                  new DateTime().dayOfMonth().roundFloorCopy().minusDays(7),
                  null);

          double sum = valueMetric.getSum();
          long sumLong = (long) sum;

          if (sumLong == sum) {
            page.writeHtml(String.format("%,2d ", sumLong));

          } else {
            page.writeHtml(String.format("%,2.2f ", sum));
          }

          if (!sumEntries.isEmpty()) {
            long minMillis = Long.MAX_VALUE;
            long maxMillis = Long.MIN_VALUE;

            for (Map.Entry<DateTime, Double> sumEntry : sumEntries.entrySet()) {
              long sumMillis = sumEntry.getKey().getMillis();

              if (sumMillis < minMillis) {
                minMillis = sumMillis;
              }

              if (sumMillis > maxMillis) {
                maxMillis = sumMillis;
              }
            }

            double cumulativeSum = 0.0;
            StringBuilder path = new StringBuilder();
            double xRange = maxMillis - minMillis;
            int width = 35;
            int height = 18;

            for (Map.Entry<DateTime, Double> sumEntry : sumEntries.entrySet()) {
              cumulativeSum += sumEntry.getValue();

              path.append('L');
              path.append((sumEntry.getKey().getMillis() - minMillis) / xRange * width);
              path.append(',');
              path.append(height - cumulativeSum / maxSum * height);
            }

            path.setCharAt(0, 'M');

            page.writeStart(
                "svg",
                "xmlns",
                "http://www.w3.org/2000/svg",
                "width",
                width,
                "height",
                height,
                "style",
                page.cssString(
                    "display", "inline-block",
                    "vertical-align", "middle"));

            page.writeStart("path", "fill", "none", "stroke", "#444444", "d", path.toString());
            page.writeEnd();
            page.writeEnd();
          }
          page.writeEnd();

        } else if (value instanceof Recordable) {
          page.writeHtml(((Recordable) value).getState().getLabel());

        } else {
          for (Iterator<Object> i = CollectionUtils.recursiveIterable(value).iterator();
              i.hasNext(); ) {
            Object collectionValue = i.next();

            page.writeObject(collectionValue);

            if (i.hasNext()) {
              page.writeHtml(", ");
            }
          }
        }
        page.writeEnd();
      }

      if (user != null) {
        ObjectType selectedType = search.getSelectedType();
        List<String> fieldNames =
            user.getSearchResultFieldsByTypeId()
                .get(selectedType != null ? selectedType.getId().toString() : "");
        ObjectType itemType = itemState.getType();

        if (fieldNames == null) {
          for (Class<? extends SearchResultField> c :
              ClassFinder.Static.findClasses(SearchResultField.class)) {
            if (!c.isInterface() && !Modifier.isAbstract(c.getModifiers())) {
              SearchResultField field = TypeDefinition.getInstance(c).newInstance();

              if (field.isDefault(itemType) && !ObjectUtils.equals(sortField, field)) {
                field.writeTableDataCellHtml(page, item);
              }
            }
          }

          if (selectedType != null) {
            for (ObjectField field : selectedType.getFields()) {
              if (Boolean.TRUE.equals(field.as(ToolUi.class).getDefaultSearchResult())) {
                writeDataCell(itemState, field.getInternalName());
              }
            }
          }

        } else {
          for (String fieldName : fieldNames) {
            Class<?> fieldNameClass = ObjectUtils.getClassByName(fieldName);

            if (fieldNameClass != null
                && SearchResultField.class.isAssignableFrom(fieldNameClass)) {
              @SuppressWarnings("unchecked")
              SearchResultField field =
                  TypeDefinition.getInstance((Class<? extends SearchResultField>) fieldNameClass)
                      .newInstance();

              if (field.isSupported(itemState.getType()) && !ObjectUtils.equals(sortField, field)) {
                field.writeTableDataCellHtml(page, item);
              }

            } else {
              ObjectField field = itemState.getField(fieldName);

              if (field == null) {
                field = Database.Static.getDefault().getEnvironment().getField(fieldName);
              }

              if (field != null && !ObjectUtils.equals(sortField, field)) {
                writeDataCell(itemState, fieldName);
              }
            }
          }
        }
      }
      page.writeEnd();
    }
    page.writeEnd();
    page.writeEnd();

    page.writeStart("div", "id", page.createId());
    page.writeEnd();

    page.writeStart("script", "type", "text/javascript");
    page.writeRaw(
        "$('#" + page.getId() + "').siblings('.searchResultTable').find('tr').each(function() {");
    page.writeRaw("    if ($(this).find('td > a').size() > 1) {");
    page.writeRaw(
        "        $(this).closest('.searchResultTable').addClass('multipleLinkedColumns');");
    page.writeRaw("        return false;");
    page.writeRaw("    }");
    page.writeRaw(" });");
    page.writeEnd();
  }