Значение позиции или смещения в тексте HTMLDocument

Я пытаюсь понять, как позиции/смещения работают в HTMLDocument. Позиция/семантика офсетной описана здесь. Моя интерпретация заключается в том, что это индексы в последовательности экранных символов, представленных HTMLDocument.

Рассмотрим пример HTML из документации HTMLDocument:

 <html>
   <head>
     <title>An example HTMLDocument</title>
     <style type="text/css">
       div { background-color: silver; }
       ul { color: red; }
     </style>
   </head>
   <body>
     <div id="BOX">
       <p>Paragraph 1</p>
       <p>Paragraph 2</p>
     </div>
   </body>
 </html>

Когда я открываю этот HTML-код в браузере, я вижу только "Параграф 1" и "Параграф 2" (и никаких ведущих пробелов или новых строк). Поэтому я думаю, что "Пункт 1" начинается со смещения 0.

Но рассмотрим следующий код, где я печатаю текст в примере HTML и смещение тела:

import java.io.StringReader;
import javax.swing.text.Element;
import javax.swing.text.html.*;

public class Test {
    public static void main(String[] args) throws Exception {
        String html = " <html>\n"
                    + "   <head>\n"
                    + "     <title>An example HTMLDocument</title>\n"
                    + "     <style type=\"text/css\">\n"
                    + "       div { background-color: silver; }\n"
                    + "       ul { color: red; }\n"
                    + "     </style>\n"
                    + "   </head>\n"
                    + "   <body>\n"
                    + "     <div id=\"BOX\">\n"
                    + "       <p>Paragraph 1</p>\n"
                    + "       <p>Paragraph 2</p>\n"
                    + "     </div>\n"
                    + "   </body>\n"
                    + " </html>\n";

        HTMLEditorKit htmlKit = new HTMLEditorKit();
        HTMLDocument doc = (HTMLDocument) htmlKit.createDefaultDocument();
        htmlKit.read(new StringReader(html), doc, 0);

        System.out.println("doc length: " + doc.getLength());
        String text = doc.getText(0, doc.getLength());
        System.out.println("doc text, surrounded by quotes, with newlines replaced with /: \""
                + text.replace('\n', '/') + "\"");

        Element element = doc.getDefaultRootElement().getElement(1);
        System.out.println("element name: " + element.getName());
        int offset = element.getStartOffset();
        System.out.println("offset of body: " + offset);
    }
}

Выход:

doc length: 26
doc text, surrounded by quotes, with newlines replaced with /: "  /Paragraph 1/Paragraph 2"
element name: body
offset of body: 3

Основные вопросы: Почему "Пункт 1" (начало тела) по индексу 3? Откуда берутся первые три символа (два пробела и новая строка) текста? Я неверно истолковываю, что означает "смещение"?

Заданный вопрос: учитывая некоторый HTML (достаточно простой, чтобы полностью понять путем проверки), как я могу точно определить смещения всех элементов DOM вручную?


Больше информации:

Если я удалю тег style из HTML, я получаю тот же результат (смещение тела 3). Если я также удалю title, я получаю смещение тела 1. Если я окончательно удалю head целиком, я получаю смещение тела 0 (как и ожидалось). Таким образом, по-видимому, style вносит 0, title вносит 2, а head вносит 1 в смещение тела? Каковы причины этого?

На это также не влияет пробел в строке HTML.

Ответы

Ответ 1

Хороший вопрос. Вы можете определить смещения (и, следовательно, необходимые позиции каретки в JEditorPane) в соответствии с несколькими правилами - вы уже упоминали наиболее важные из них.

Возможно, несколько ключевых тегов:

  • <head> +1
  • <title> +2
  • <meta> +1
  • <p> длина текста +1 (для CR)

Если вы еще не нашли его, самый простой способ увидеть этот список смещений и то, как они разбиваются, это HTMLDocument.dump(System.out); , Например, для примера HTML выше:

<html
  name=html
>
  <head
    name=head
  >
    <p-implied
      name=p-implied
    >
      <title
        name=title
      >
        [0,1][ ]
      <title
        endtag=true
        name=title
      >
        [1,2][ ]
      <content
        CR=true
        name=content
      >
        [2,3][
]
  <body
    name=body
  >
    <div
      id=BOX
      name=div
    >
      <p
        name=p
      >
        <content
          name=content
        >
          [3,14][Paragraph 1]
        <content
          CR=true
          name=content
        >
          [14,15][
]
      <p
        name=p
      >
        <content
          name=content
        >
          [15,26][Paragraph 2]
        <content
          CR=true
          name=content
        >
          [26,27][
]
<bidi root>
  <bidi level
    bidiLevel=0
  >
    [0,27][  
Paragraph 1
Paragraph 2
]

Если вам интересно углубиться, это будет означать изучение правил в логике разбора Swing для HTML. Существует множество правил для разных типов тегов - вы можете увидеть список в источнике.

Каждый тег использует класс "Action" в этой иерархии:

swing-html-actions

Например, <p> является ParagraphAction, а <head> является HeadAction, и оба они являются типами BlockAction. A <div> также является прямым BlockAction.

BlockAction может добавить этот дополнительный элемент <content CR...>, чтобы закончить блок, следовательно, дополнительный +1 в смещении. Обычно это происходит только в том случае, если в теге есть прямой текстовый контент. Однако для <head> подкласс HeadAction добавляет <p-implied> вы можете увидеть в дампе выше, что вызывает один из дополнительных смещений. (Вы не можете видеть это в этом примере, но стоит отметить, что <div> с текстовым контентом также вставляет дополнительный <p-implied> - для хранения текста блока).

Вещи становятся все более конкретными оттуда. Например, <title> (вместе с <applet> и <object>) кажется "непустым" HiddenActions. Это означает, что элемент вставлен как для начального, так и для концевого тегов. <meta> хотя, например, это пустой HiddenAction, поэтому просто получает один элемент для стартового тега.

Надеюсь, этого достаточно для объяснения того, как определить смещение для любого заданного тега. Если вы просматриваете источник для классов XxxActions, найдите строки, такие как new ElementSpec(..., 0, 1) - последний параметр - это длина.

Вы также указали, что пробелы игнорируются. Это, по крайней мере, нормально в разборе HTML, в браузерах тоже. Пробелы между тегами или до и после текста обычно игнорируются - сохраняется только пробел между словами. И тогда последовательности пробелов сворачиваются в одно пробелы.


Все сказанное, я все еще не понимаю, зачем нужны дополнительные смещения для <head> и <title>. Например, если вы используете setCaretPosition(x) для JEditorPane на основе doc и htmlKit выше, вы видите только htmlKit, если x равен 3 или более. Возможно, кто-то еще может пролить свет на это...